diff options
466 files changed, 107173 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..8b79ceb --- /dev/null +++ b/Android.mk @@ -0,0 +1,29 @@ +# +# 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) + +ifneq ($(TARGET_SIMULATOR),true) + include $(call first-makefiles-under,$(LOCAL_PATH)) +else + include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \ + adb \ + libcutils \ + liblog \ + libnetutils \ + libpixelflinger \ + libzipfile \ + )) +endif @@ -0,0 +1,20 @@ + +The system/ directory is intended for pieces of the world that are the +core of the embedded linux platform at the heart of Android. These +essential bits are required for basic booting, operation, and debugging. + +They should not depend on libraries outside of system/... (some of them +do currently -- they need to be updated or changed) and they should not +be required for the simulator build. + +The license for all these pieces should be clean (Apache2, BSD, or MIT). + +Currently system/bluetooth/... and system/extra/... have some pieces +with GPL/LGPL licensed code. + +Assorted Issues: + +- pppd depends on libutils for logging +- pppd depends on libcrypt/libcrypto +- init, linker, debuggerd, toolbox, usbd depend on libcutils +- should probably rename bionic to libc diff --git a/adb/Android.mk b/adb/Android.mk new file mode 100644 index 0000000..2296610 --- /dev/null +++ b/adb/Android.mk @@ -0,0 +1,140 @@ +# Copyright 2005 The Android Open Source Project +# +# Android.mk for adb +# + +LOCAL_PATH:= $(call my-dir) + +# adb host tool +# ========================================================= +ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean (also unused with the sim) +include $(CLEAR_VARS) + +# Default to a virtual (sockets) usb interface +USB_SRCS := +EXTRA_SRCS := + +ifeq ($(HOST_OS),linux) + USB_SRCS := usb_linux.c + EXTRA_SRCS := get_my_path_linux.c + LOCAL_LDLIBS += -lrt -lncurses -lpthread +endif + +ifeq ($(HOST_OS),darwin) + USB_SRCS := usb_osx.c + EXTRA_SRCS := get_my_path_darwin.c + LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon +endif + +ifeq ($(HOST_OS),windows) + USB_SRCS := usb_windows.c + EXTRA_SRCS := get_my_path_windows.c + EXTRA_STATIC_LIBS := AdbWinApi + LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api/ + ifneq ($(strip $(USE_CYGWIN)),) + LOCAL_LDLIBS += -lpthread + else + LOCAL_LDLIBS += -lws2_32 + USE_SYSDEPS_WIN32 := 1 + endif +endif + +LOCAL_SRC_FILES := \ + adb.c \ + console.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + commandline.c \ + adb_client.c \ + sockets.c \ + services.c \ + file_sync_client.c \ + $(EXTRA_SRCS) \ + $(USB_SRCS) \ + shlist.c \ + utils.c \ + + +ifneq ($(USE_SYSDEPS_WIN32),) + LOCAL_SRC_FILES += sysdeps_win32.c +endif + +LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY +LOCAL_MODULE := adb + +LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS) +ifeq ($(USE_SYSDEPS_WIN32),) + LOCAL_STATIC_LIBRARIES += libcutils +endif + +include $(BUILD_HOST_EXECUTABLE) + +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) + +ifeq ($(HOST_OS),windows) +$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll +endif + +endif + +# adbd device daemon +# ========================================================= + +# build adbd in all non-simulator builds +BUILD_ADBD := false +ifneq ($(TARGET_SIMULATOR),true) + BUILD_ADBD := true +endif + +# build adbd for the Linux simulator build +# so we can use it to test the adb USB gadget driver on x86 +ifeq ($(HOST_OS),linux) + BUILD_ADBD := true +endif + + +ifeq ($(BUILD_ADBD),true) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + adb.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + sockets.c \ + services.c \ + file_sync_service.c \ + jdwp_service.c \ + framebuffer_service.c \ + remount_service.c \ + usb_linux_client.c \ + log_service.c \ + utils.c \ + +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +# TODO: This should probably be board specific, whether or not the kernel has +# the gadget driver; rather than relying on the architecture type. +ifeq ($(TARGET_ARCH),arm) +LOCAL_CFLAGS += -DANDROID_GADGET=1 +endif + +LOCAL_MODULE := adbd + +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_STATIC_LIBRARIES := libcutils + LOCAL_LDLIBS += -lpthread + include $(BUILD_HOST_EXECUTABLE) +else + LOCAL_STATIC_LIBRARIES := libcutils libc + include $(BUILD_EXECUTABLE) +endif + +endif diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT new file mode 100644 index 0000000..6a5191a --- /dev/null +++ b/adb/OVERVIEW.TXT @@ -0,0 +1,139 @@ +Implementation notes regarding ADB. + +I. General Overview: + +The Android Debug Bridge (ADB) is used to: + +- keep track of all Android devices and emulators instances + connected to or running on a given host developer machine + +- implement various control commands (e.g. "adb shell", "adb pull", etc..) + for the benefit of clients (command-line users, or helper programs like + DDMS). These commands are what is called a 'service' in ADB. + +As a whole, everything works through the following components: + + 1. The ADB server + + This is a background process that runs on the host machine. Its purpose + if to sense the USB ports to know when devices are attached/removed, + as well as when emulator instances start/stop. + + It thus maintains a list of "connected devices" and assigns a 'state' + to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on + this below). + + The ADB server is really one giant multiplexing loop whose purpose is + to orchestrate the exchange of data (packets, really) between clients, + services and devices. + + + 2. The ADB daemon (adbd) + + The 'adbd' program runs as a background process within an Android device + or emulated system. Its purpose is to connect to the ADB server + (through USB for devices, through TCP for emulators) and provide a + few services for clients that run on the host. + + The ADB server considers that a device is ONLINE when it has succesfully + connected to the adbd program within it. Otherwise, the device is OFFLINE, + meaning that the ADB server detected a new device/emulator, but could not + connect to the adbd daemon. + + the BOOTLOADER and RECOVERY states correspond to alternate states of + devices when they are in the bootloader or recovery mode. + + 3. The ADB command-line client + + The 'adb' command-line program is used to run adb commands from a shell + or a script. It first tries to locate the ADB server on the host machine, + and will start one automatically if none is found. + + then, the client sends its service requests to the ADB server. It doesn't + need to know. + + Currently, a single 'adb' binary is used for both the server and client. + this makes distribution and starting the server easier. + + + 4. Services + + There are essentially two kinds of services that a client can talk to. + + Host Services: + these services run within the ADB Server and thus do not need to + communicate with a device at all. A typical example is "adb devices" + which is used to return the list of currently known devices and their + state. They are a few couple other services though. + + Local Services: + these services either run within the adbd daemon, or are started by + it on the device. The ADB server is used to multiplex streams + between the client and the service running in adbd. In this case + its role is to initiate the connection, then of being a pass-through + for the data. + + +II. Protocol details: + + 1. Client <-> Server protocol: + + This details the protocol used between ADB clients and the ADB + server itself. The ADB server listens on TCP:localhost:5037. + + A client sends a request using the following format: + + 1. A 4-byte hexadecimal string giving the length of the payload + 2. Followed by the payload itself. + + For example, to query the ADB server for its internal version number, + the client will do the following: + + 1. Connect to tcp:localhost:5037 + 2. Send the string "000Chost:version" to the corresponding socket + + The 'host:' prefix is used to indicate that the request is addressed + to the server itself (we will talk about other kinds of requests later). + The content length is encoded in ASCII for easier debugging. + + The server should answer a request with one of the following: + + 1. For success, the 4-byte "OKAY" string + + 2. For failure, the 4-byte "FAIL" string, followed by a + 4-byte hex length, followed by a string giving the reason + for failure. + + 3. As a special exception, for 'host:version', a 4-byte + hex string corresponding to the server's internal version number + + Note that the connection is still alive after an OKAY, which allows the + client to make other requests. But in certain cases, an OKAY will even + change the state of the connection. + + For example, the case of the 'host:transport:<serialnumber>' request, + where '<serialnumber>' is used to identify a given device/emulator; after + the "OKAY" answer, all further requests made by the client will go + directly to the corresponding adbd daemon. + + The file SERVICES.TXT lists all services currently implemented by ADB. + + + 2. Transports: + + An ADB transport models a connection between the ADB server and one device + or emulator. There are currently two kinds of transports: + + - USB transports, for physical devices through USB + + - Local transports, for emulators running on the host, connected to + the server through TCP + + In theory, it should be possible to write a local transport that proxies + a connection between an ADB server and a device/emulator connected to/ + running on another machine. This hasn't been done yet though. + + Each transport can carry one or more multiplexed streams between clients + and the device/emulator they point to. The ADB server must handle + unexpected transport disconnections (e.g. when a device is physically + unplugged) properly. diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT new file mode 100644 index 0000000..b0124a4 --- /dev/null +++ b/adb/SERVICES.TXT @@ -0,0 +1,236 @@ +This file tries to document all requests a client can make +to the ADB server of an adbd daemon. See the OVERVIEW.TXT document +to understand what's going on here. + +HOST SERVICES: + +host:version + Ask the ADB server for its internal version number. + + As a special exception, the server will respond with a 4-byte + hex string corresponding to its internal version number, without + any OKAY or FAIL. + +host:kill + Ask the ADB server to quit immediately. This is used when the + ADB client detects that an obsolete server is running after an + upgrade. + +host:devices + Ask to return the list of available Android devices and their + state. After the OKAY, this is followed by a 4-byte hex len, + and a string that will be dumped as-is by the client, then + the connection is closed + +host:track-devices + This is a variant of host:devices which doesn't close the + connection. Instead, a new device list description is sent + each time a device is added/removed or the state of a given + device changes (hex4 + content). This allows tools like DDMS + to track the state of connected devices in real-time without + polling the server repeatedly. + +host:emulator:<port> + This is a special query that is sent to the ADB server when a + new emulator starts up. <port> is a decimal number corresponding + to the emulator's ADB control port, i.e. the TCP port that the + emulator will forward automatically to the adbd daemon running + in the emulator system. + + This mechanism allows the ADB server to know when new emulator + instances start. + +host:transport:<serial-number> + Ask to switch the connection to the device/emulator identified by + <serial-number>. After the OKAY response, every client request will + be sent directly to the adbd daemon running on the device. + (Used to implement the -s option) + +host:transport-usb + Ask to switch the connection to one device connected through USB + to the host machine. This will fail if there are more than one such + devices. (Used to implement the -d convenience option) + +host:transport-local + Ask to switch the connection to one emulator connected through TCP. + This will fail if there is more than one such emulator instance + running. (Used to implement the -e convenience option) + +host:transport-any + Another host:transport variant. Ask to switch the connection to + either the device or emulator connect to/running on the host. + Will fail if there is more than one such device/emulator available. + (Used when neither -s, -d or -e are provided) + +host-serial:<serial-number>:<request> + This is a special form of query, where the 'host-serial:<serial-number>:' + prefix can be used to indicate that the client is asking the ADB server + for information related to a specific device. <request> can be in one + of the format described below. + +host-usb:<request> + A variant of host-serial used to target the single USB device connected + to the host. This will fail if there is none or more than one. + +host-local:<request> + A variant of host-serial used to target the single emulator instance + running on the host. This will fail if therre is none or more than one. + +host:<request> + When asking for information related to a device, 'host:' can also be + interpreted as 'any single device or emulator connected to/running on + the host'. + +<host-prefix>:get-product + XXX + +<host-prefix>:get-serialno + Returns the serial number of the corresponding device/emulator. + Note that emulator serial numbers are of the form "emulator-5554" + +<host-prefix>:get-state + Returns the state of a given device as a string. + +<host-prefix>:forward:<local>;<remote> + Asks the ADB server to forward local connections from <local> + to the <remote> address on a given device. + + There, <host-prefix> can be one of the + host-serial/host-usb/host-local/host prefixes as described previously + and indicates which device/emulator to target. + + the format of <local> is one of: + + tcp:<port> -> TCP connection on localhost:<port> + local:<path> -> Unix local domain socket on <path> + + the format of <remote> is one of: + + tcp:<port> -> TCP localhost:<port> on device + local:<path> -> Unix local domain socket on device + jdwp:<pid> -> JDWP thread on VM process <pid> + + or even any one of the local services described below. + + + +LOCAL SERVICES: + +All the queries below assumed that you already switched the transport +to a real device, or that you have used a query prefix as described +above. + +shell:command arg1 arg2 ... + Run 'command arg1 arg2 ...' in a shell on the device, and return + its output and error streams. Note that arguments must be separated + by spaces. If an argument contains a space, it must be quoted with + double-quotes. Arguments cannot contain double quotes or things + will go very wrong. + + Note that this is the non-interactive version of "adb shell" + +shell: + Start an interactive shell session on the device. Redirect + stdin/stdout/stderr as appropriate. Note that the ADB server uses + this to implement "adb shell", but will also cook the input before + sending it to the device (see interactive_shell() in commandline.c) + +remount: + Ask adbd to remount the device's filesystem in read-write mode, + instead of read-only. This is usually necessary before performing + an "adb sync" or "adb push" request. + + This request may not succeed on certain builds which do not allow + that. + +dev:<path> + Opens a device file and connects the client directly to it for + read/write purposes. Useful for debugging, but may require special + priviledges and thus may not run on all devices. <path> is a full + path from the root of the filesystem. + +tcp:<port> + Tries to connect to tcp port <port> on localhost. + +tcp:<port>:<server-name> + Tries to connect to tcp port <port> on machine <server-name> from + the device. This can be useful to debug some networking/proxy + issues that can only be revealed on the device itself. + +local:<path> + Tries to connect to a Unix domain socket <path> on the device + +localreserved:<path> +localabstract:<path> +localfilesystem:<path> + Variants of local:<path> that are used to access other Android + socket namespaces. + +log:<name> + Opens one of the system logs (/dev/log/<name>) and allows the client + to read them directly. Used to implement 'adb logcat'. The stream + will be read-only for the client. + +framebuffer: + This service is used to send snapshots of the framebuffer to a client. + It requires sufficient priviledges but works as follow: + + After the OKAY, the service sends 16-byte binary structure + containing the following fields (little-endian format): + + depth: uint32_t: framebuffer depth + size: uint32_t: framebuffer size in bytes + width: uint32_t: framebuffer width in pixels + height: uint32_t: framebuffer height in pixels + + With the current implementation, depth is always 16, and + size is always width*height*2 + + Then, each time the client wants a snapshot, it should send + one byte through the channel, which will trigger the service + to send it 'size' bytes of framebuffer data. + + If the adbd daemon doesn't have sufficient priviledges to open + the framebuffer device, the connection is simply closed immediately. + +dns:<server-name> + This service is an exception because it only runs within the ADB server. + It is used to implement USB networking, i.e. to provide a network connection + to the device through the host machine (note: this is the exact opposite of + network thetering). + + It is used to perform a gethostbyname(<address>) on the host and return + the corresponding IP address as a 4-byte string. + +recover:<size> + This service is used to upload a recovery image to the device. <size> + must be a number corresponding to the size of the file. The service works + by: + + - creating a file named /tmp/update + - reading 'size' bytes from the client and writing them to /tmp/update + - when everything is read succesfully, create a file named /tmp/update.start + + This service can only work when the device is in recovery mode. Otherwise, + the /tmp directory doesn't exist and the connection will be closed immediately. + +jdwp:<pid> + Connects to the JDWP thread running in the VM of process <pid>. + +track-jdwp + This is used to send the list of JDWP pids periodically to the client. + The format of the returned data is the following: + + <hex4>: the length of all content as a 4-char hexadecimal string + <content>: a series of ASCII lines of the following format: + <pid> "\n" + + This service is used by DDMS to know which debuggable processes are running + on the device/emulator. + + Note that there is no single-shot service to retrieve the list only once. + +sync: + This starts the file synchronisation service, used to implement "adb push" + and "adb pull". Since this service is pretty complex, it will be detailed + in a companion document named SYNC.TXT diff --git a/adb/adb.c b/adb/adb.c new file mode 100644 index 0000000..fa5269f --- /dev/null +++ b/adb/adb.c @@ -0,0 +1,1083 @@ +/* + * 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 TRACE_TAG TRACE_ADB + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <time.h> + +#include "sysdeps.h" +#include "adb.h" + +#if !ADB_HOST +#include <private/android_filesystem_config.h> +#endif + + +int HOST = 0; + +static const char *adb_device_banner = "device"; + +void fatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +void fatal_errno(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: %s: ", strerror(errno)); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +int adb_trace_mask; + +/* read a comma/space/colum/semi-column separated list of tags + * from the ADB_TRACE environment variable and build the trace + * mask from it. note that '1' and 'all' are special cases to + * enable all tracing + */ +void adb_trace_init(void) +{ + const char* p = getenv("ADB_TRACE"); + const char* q; + + static const struct { + const char* tag; + int flag; + } tags[] = { + { "1", 0 }, + { "all", 0 }, + { "adb", TRACE_ADB }, + { "sockets", TRACE_SOCKETS }, + { "packets", TRACE_PACKETS }, + { "rwx", TRACE_RWX }, + { "usb", TRACE_USB }, + { "sync", TRACE_SYNC }, + { "sysdeps", TRACE_SYSDEPS }, + { "transport", TRACE_TRANSPORT }, + { "jdwp", TRACE_JDWP }, + { NULL, 0 } + }; + + if (p == NULL) + return; + + /* use a comma/column/semi-colum/space separated list */ + while (*p) { + int len, tagn; + + q = strpbrk(p, " ,:;"); + if (q == NULL) { + q = p + strlen(p); + } + len = q - p; + + for (tagn = 0; tags[tagn].tag != NULL; tagn++) + { + int taglen = strlen(tags[tagn].tag); + + if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) + { + int flag = tags[tagn].flag; + if (flag == 0) { + adb_trace_mask = ~0; + return; + } + adb_trace_mask |= (1 << flag); + break; + } + } + p = q; + if (*p) + p++; + } +} + + +apacket *get_apacket(void) +{ + apacket *p = malloc(sizeof(apacket)); + if(p == 0) fatal("failed to allocate an apacket"); + memset(p, 0, sizeof(apacket) - MAX_PAYLOAD); + return p; +} + +void put_apacket(apacket *p) +{ + free(p); +} + +void handle_online(void) +{ + D("adb: online\n"); +#if !ADB_HOST + property_set("adb.connected","1"); +#endif +} + +void handle_offline(atransport *t) +{ + D("adb: offline\n"); + //Close the associated usb + run_transport_disconnects(t); +#if !ADB_HOST + property_set("adb.connected",""); +#endif +} + +#if TRACE_PACKETS +#define DUMPMAX 32 +void print_packet(const char *label, apacket *p) +{ + char *tag; + char *x; + unsigned count; + + switch(p->msg.command){ + case A_SYNC: tag = "SYNC"; break; + case A_CNXN: tag = "CNXN" ; break; + case A_OPEN: tag = "OPEN"; break; + case A_OKAY: tag = "OKAY"; break; + case A_CLSE: tag = "CLSE"; break; + case A_WRTE: tag = "WRTE"; break; + default: tag = "????"; break; + } + + fprintf(stderr, "%s: %s %08x %08x %04x \"", + label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length); + count = p->msg.data_length; + x = (char*) p->data; + if(count > DUMPMAX) { + count = DUMPMAX; + tag = "\n"; + } else { + tag = "\"\n"; + } + while(count-- > 0){ + if((*x >= ' ') && (*x < 127)) { + fputc(*x, stderr); + } else { + fputc('.', stderr); + } + x++; + } + fprintf(stderr, tag); +} +#endif + +static void send_ready(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_ready \n"); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_close(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_close \n"); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_connect(atransport *t) +{ + D("Calling send_connect \n"); + apacket *cp = get_apacket(); + cp->msg.command = A_CNXN; + cp->msg.arg0 = A_VERSION; + cp->msg.arg1 = MAX_PAYLOAD; + snprintf((char*) cp->data, sizeof cp->data, "%s::", + HOST ? "host" : adb_device_banner); + cp->msg.data_length = strlen((char*) cp->data) + 1; + send_packet(cp, t); +#if ADB_HOST + /* XXX why sleep here? */ + // allow the device some time to respond to the connect message + adb_sleep_ms(1000); +#endif +} + +static char *connection_state_name(atransport *t) +{ + if (t == NULL) { + return "unknown"; + } + + switch(t->connection_state) { + case CS_BOOTLOADER: + return "bootloader"; + case CS_DEVICE: + return "device"; + case CS_OFFLINE: + return "offline"; + default: + return "unknown"; + } +} + +void parse_banner(char *banner, atransport *t) +{ + char *type, *product, *end; + + D("parse_banner: %s\n", banner); + type = banner; + product = strchr(type, ':'); + if(product) { + *product++ = 0; + } else { + product = ""; + } + + /* remove trailing ':' */ + end = strchr(product, ':'); + if(end) *end = 0; + + /* save product name in device structure */ + if (t->product == NULL) { + t->product = strdup(product); + } else if (strcmp(product, t->product) != 0) { + free(t->product); + t->product = strdup(product); + } + + if(!strcmp(type, "bootloader")){ + D("setting connection_state to CS_BOOTLOADER\n"); + t->connection_state = CS_BOOTLOADER; + update_transports(); + return; + } + + if(!strcmp(type, "device")) { + D("setting connection_state to CS_DEVICE\n"); + t->connection_state = CS_DEVICE; + update_transports(); + return; + } + + if(!strcmp(type, "recovery")) { + D("setting connection_state to CS_RECOVERY\n"); + t->connection_state = CS_RECOVERY; + update_transports(); + return; + } + + t->connection_state = CS_HOST; +} + +void handle_packet(apacket *p, atransport *t) +{ + asocket *s; + + D("handle_packet() %d\n", p->msg.command); + + print_packet("recv", p); + + switch(p->msg.command){ + case A_SYNC: + if(p->msg.arg0){ + send_packet(p, t); + if(HOST) send_connect(t); + } else { + t->connection_state = CS_OFFLINE; + handle_offline(t); + send_packet(p, t); + } + return; + + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ + /* XXX verify version, etc */ + if(t->connection_state != CS_OFFLINE) { + t->connection_state = CS_OFFLINE; + handle_offline(t); + } + parse_banner((char*) p->data, t); + handle_online(); + if(!HOST) send_connect(t); + break; + + case A_OPEN: /* OPEN(local-id, 0, "destination") */ + if(t->connection_state != CS_OFFLINE) { + char *name = (char*) p->data; + name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; + s = create_local_service_socket(name); + if(s == 0) { + send_close(0, p->msg.arg0, t); + } else { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + send_ready(s->id, s->peer->id, t); + s->ready(s); + } + } + break; + + case A_OKAY: /* READY(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + if(s->peer == 0) { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + } + s->ready(s); + } + } + break; + + case A_CLSE: /* CLOSE(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + s->close(s); + } + } + break; + + case A_WRTE: + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + unsigned rid = p->msg.arg0; + p->len = p->msg.data_length; + + if(s->enqueue(s, p) == 0) { + D("Enqueue the socket\n"); + send_ready(s->id, rid, t); + } + return; + } + } + break; + + default: + printf("handle_packet: what is %08x?!\n", p->msg.command); + } + + put_apacket(p); +} + +alistener listener_list = { + .next = &listener_list, + .prev = &listener_list, +}; + +static void ss_listener_event_func(int _fd, unsigned ev, void *_l) +{ + asocket *s; + + if(ev & FDE_READ) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = adb_socket_accept(_fd, &addr, &alen); + if(fd < 0) return; + + adb_socket_setbufsize(fd, CHUNK_SIZE); + + s = create_local_socket(fd); + if(s) { + connect_to_smartsocket(s); + return; + } + + adb_close(fd); + } +} + +static void listener_event_func(int _fd, unsigned ev, void *_l) +{ + alistener *l = _l; + asocket *s; + + if(ev & FDE_READ) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = adb_socket_accept(_fd, &addr, &alen); + if(fd < 0) return; + + s = create_local_socket(fd); + if(s) { + s->transport = l->transport; + connect_to_remote(s, l->connect_to); + return; + } + + adb_close(fd); + } +} + +static void free_listener(alistener* l) +{ + if (l->next) { + l->next->prev = l->prev; + l->prev->next = l->next; + l->next = l->prev = l; + } + + // closes the corresponding fd + fdevent_remove(&l->fde); + + if (l->local_name) + free((char*)l->local_name); + + if (l->connect_to) + free((char*)l->connect_to); + + if (l->transport) { + remove_transport_disconnect(l->transport, &l->disconnect); + } + free(l); +} + +static void listener_disconnect(void* _l, atransport* t) +{ + alistener* l = _l; + + free_listener(l); +} + +int local_name_to_fd(const char *name) +{ + int port; + + if(!strncmp("tcp:", name, 4)){ + int ret; + port = atoi(name + 4); + ret = socket_loopback_server(port, SOCK_STREAM); + return ret; + } +#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */ + // It's non-sensical to support the "reserved" space on the adb host side + if(!strncmp(name, "local:", 6)) { + return socket_local_server(name + 6, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localabstract:", 14)) { + return socket_local_server(name + 14, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localfilesystem:", 16)) { + return socket_local_server(name + 16, + ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM); + } + +#endif + printf("unknown local portname '%s'\n", name); + return -1; +} + +static int remove_listener(const char *local_name, const char *connect_to, atransport* transport) +{ + alistener *l; + + for (l = listener_list.next; l != &listener_list; l = l->next) { + if (!strcmp(local_name, l->local_name) && + !strcmp(connect_to, l->connect_to) && + l->transport && l->transport == transport) { + + listener_disconnect(l, transport); + return 0; + } + } + + return -1; +} + +static int install_listener(const char *local_name, const char *connect_to, atransport* transport) +{ + alistener *l; + + //printf("install_listener('%s','%s')\n", local_name, connect_to); + + for(l = listener_list.next; l != &listener_list; l = l->next){ + if(strcmp(local_name, l->local_name) == 0) { + char *cto; + + /* can't repurpose a smartsocket */ + if(l->connect_to[0] == '*') { + return -1; + } + + cto = strdup(connect_to); + if(cto == 0) { + return -1; + } + + //printf("rebinding '%s' to '%s'\n", local_name, connect_to); + free((void*) l->connect_to); + l->connect_to = cto; + if (l->transport != transport) { + remove_transport_disconnect(l->transport, &l->disconnect); + l->transport = transport; + add_transport_disconnect(l->transport, &l->disconnect); + } + return 0; + } + } + + if((l = calloc(1, sizeof(alistener))) == 0) goto nomem; + if((l->local_name = strdup(local_name)) == 0) goto nomem; + if((l->connect_to = strdup(connect_to)) == 0) goto nomem; + + + l->fd = local_name_to_fd(local_name); + if(l->fd < 0) { + free((void*) l->local_name); + free((void*) l->connect_to); + free(l); + printf("cannot bind '%s'\n", local_name); + return -2; + } + + close_on_exec(l->fd); + if(!strcmp(l->connect_to, "*smartsocket*")) { + fdevent_install(&l->fde, l->fd, ss_listener_event_func, l); + } else { + fdevent_install(&l->fde, l->fd, listener_event_func, l); + } + fdevent_set(&l->fde, FDE_READ); + + l->next = &listener_list; + l->prev = listener_list.prev; + l->next->prev = l; + l->prev->next = l; + l->transport = transport; + + if (transport) { + l->disconnect.opaque = l; + l->disconnect.func = listener_disconnect; + add_transport_disconnect(transport, &l->disconnect); + } + return 0; + +nomem: + fatal("cannot allocate listener"); + return 0; +} + +#ifdef HAVE_FORKEXEC +static void sigchld_handler(int n) +{ + int status; + while(waitpid(-1, &status, WNOHANG) > 0) ; +} +#endif + +#ifdef HAVE_WIN32_PROC +static BOOL WINAPI ctrlc_handler(DWORD type) +{ + exit(STATUS_CONTROL_C_EXIT); + return TRUE; +} +#endif + +static void adb_cleanup(void) +{ + usb_cleanup(); +} + +void start_logging(void) +{ +#ifdef HAVE_WIN32_PROC + char temp[ MAX_PATH ]; + FILE* fnul; + FILE* flog; + + GetTempPath( sizeof(temp) - 8, temp ); + strcat( temp, "adb.log" ); + + /* Win32 specific redirections */ + fnul = fopen( "NUL", "rt" ); + if (fnul != NULL) + stdin[0] = fnul[0]; + + flog = fopen( temp, "at" ); + if (flog == NULL) + flog = fnul; + + setvbuf( flog, NULL, _IONBF, 0 ); + + stdout[0] = flog[0]; + stderr[0] = flog[0]; + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); +#else + int fd; + + fd = unix_open("/dev/null", O_RDONLY); + dup2(fd, 0); + + fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640); + if(fd < 0) { + fd = unix_open("/dev/null", O_WRONLY); + } + dup2(fd, 1); + dup2(fd, 2); + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); +#endif +} + +#if !ADB_HOST +void start_device_log(void) +{ + int fd; + char path[100]; + + snprintf(path, sizeof path, "/data/adb_%ld.txt", (long)time(NULL)); + fd = unix_open(path, O_WRONLY | O_CREAT | O_APPEND, 0640); + if (fd < 0) + return; + + // redirect stdout and stderr to the log file + dup2(fd, 1); + dup2(fd, 2); + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); + + fd = unix_open("/dev/null", O_RDONLY); + dup2(fd, 0); + + // log everything + adb_trace_mask = ~0; + // except TRACE_RWX is a bit too verbose + adb_trace_mask &= ~TRACE_RWX; +} +#endif + +#if ADB_HOST +int launch_server() +{ +#ifdef HAVE_WIN32_PROC + /* we need to start the server in the background */ + /* we create a PIPE that will be used to wait for the server's "OK" */ + /* message since the pipe handles must be inheritable, we use a */ + /* security attribute */ + HANDLE pipe_read, pipe_write; + SECURITY_ATTRIBUTES sa; + STARTUPINFO startup; + PROCESS_INFORMATION pinfo; + char program_path[ MAX_PATH ]; + int ret; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* create pipe, and ensure its read handle isn't inheritable */ + ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 ); + if (!ret) { + fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() ); + return -1; + } + + SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 ); + + ZeroMemory( &startup, sizeof(startup) ); + startup.cb = sizeof(startup); + startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE ); + startup.hStdOutput = pipe_write; + startup.hStdError = GetStdHandle( STD_ERROR_HANDLE ); + startup.dwFlags = STARTF_USESTDHANDLES; + + ZeroMemory( &pinfo, sizeof(pinfo) ); + + /* get path of current program */ + GetModuleFileName( NULL, program_path, sizeof(program_path) ); + + ret = CreateProcess( + program_path, /* program path */ + "adb fork-server server", + /* the fork-server argument will set the + debug = 2 in the child */ + NULL, /* process handle is not inheritable */ + NULL, /* thread handle is not inheritable */ + TRUE, /* yes, inherit some handles */ + DETACHED_PROCESS, /* the new process doesn't have a console */ + NULL, /* use parent's environment block */ + NULL, /* use parent's starting directory */ + &startup, /* startup info, i.e. std handles */ + &pinfo ); + + CloseHandle( pipe_write ); + + if (!ret) { + fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() ); + CloseHandle( pipe_read ); + return -1; + } + + CloseHandle( pinfo.hProcess ); + CloseHandle( pinfo.hThread ); + + /* wait for the "OK\n" message */ + { + char temp[3]; + DWORD count; + + ret = ReadFile( pipe_read, temp, 3, &count, NULL ); + CloseHandle( pipe_read ); + if ( !ret ) { + fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() ); + return -1; + } + if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { + fprintf(stderr, "ADB server didn't ACK\n" ); + return -1; + } + } +#elif defined(HAVE_FORKEXEC) + char path[PATH_MAX]; + int fd[2]; + + // set up a pipe so the child can tell us when it is ready. + // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child. + if (pipe(fd)) { + fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno); + return -1; + } + get_my_path(path); + pid_t pid = fork(); + if(pid < 0) return -1; + + if (pid == 0) { + // child side of the fork + + // redirect stderr to the pipe + // we use stderr instead of stdout due to stdout's buffering behavior. + adb_close(fd[0]); + dup2(fd[1], STDERR_FILENO); + adb_close(fd[1]); + + // child process + int result = execl(path, "adb", "fork-server", "server", NULL); + // this should not return + fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); + } else { + // parent side of the fork + + char temp[3]; + + temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C'; + // wait for the "OK\n" message + adb_close(fd[1]); + int ret = adb_read(fd[0], temp, 3); + adb_close(fd[0]); + if (ret < 0) { + fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno); + return -1; + } + if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { + fprintf(stderr, "ADB server didn't ACK\n" ); + return -1; + } + + setsid(); + } +#else +#error "cannot implement background server start on this platform" +#endif + return 0; +} +#endif + +int adb_main(int is_daemon) +{ +#if !ADB_HOST + int secure = 0; + char value[PROPERTY_VALUE_MAX]; + + // prevent the OOM killer from killing us + char text[64]; + snprintf(text, sizeof text, "/proc/%d/oom_adj", (int)getpid()); + int fd = adb_open(text, O_WRONLY); + if (fd >= 0) { + // -17 should make us immune to OOM + snprintf(text, sizeof text, "%d", -17); + adb_write(fd, text, strlen(text)); + adb_close(fd); + } else { + D("adb: unable to open %s\n", text); + } +#endif + + atexit(adb_cleanup); +#ifdef HAVE_WIN32_PROC + SetConsoleCtrlHandler( ctrlc_handler, TRUE ); +#elif defined(HAVE_FORKEXEC) + signal(SIGCHLD, sigchld_handler); + signal(SIGPIPE, SIG_IGN); +#endif + + init_transport_registration(); + + +#if ADB_HOST + HOST = 1; + usb_init(); + local_init(); + + if(install_listener("tcp:5037", "*smartsocket*", NULL)) { + exit(1); + } +#else + /* run adbd in secure mode if ro.secure is set and + ** we are not in the emulator + */ + property_get("ro.kernel.qemu", value, ""); + if (strcmp(value, "1") != 0) { + property_get("ro.secure", value, ""); + if (strcmp(value, "1") == 0) + secure = 1; + } + + /* don't listen on port 5037 if we are running in secure mode */ + /* don't run as root if we are running in secure mode */ + if (secure) { + /* add extra groups: + ** AID_ADB to access the USB driver + ** AID_LOG to read system logs (adb logcat) + ** AID_INPUT to diagnose input issues (getevent) + ** AID_INET to diagnose network issues (netcfg, ping) + ** AID_GRAPHICS to access the frame buffer + */ + gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS }; + setgroups(sizeof(groups)/sizeof(groups[0]), groups); + + /* then switch user and group to "shell" */ + setgid(AID_SHELL); + setuid(AID_SHELL); + + D("Local port 5037 disabled\n"); + } else { + if(install_listener("tcp:5037", "*smartsocket*", NULL)) { + exit(1); + } + } + + /* for the device, start the usb transport if the + ** android usb device exists, otherwise start the + ** network transport. + */ + if(access("/dev/android_adb", F_OK) == 0 || + access("/dev/android", F_OK) == 0) { + usb_init(); + } else { + local_init(); + } + init_jdwp(); +#endif + + if (is_daemon) + { + // inform our parent that we are up and running. +#ifdef HAVE_WIN32_PROC + DWORD count; + WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL ); +#elif defined(HAVE_FORKEXEC) + fprintf(stderr, "OK\n"); +#endif + start_logging(); + } + + fdevent_loop(); + + usb_cleanup(); + + return 0; +} + +int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) +{ + atransport *transport = NULL; + char buf[4096]; + + if(!strcmp(service, "kill")) { + fprintf(stderr,"adb server killed by remote request\n"); + fflush(stdout); + adb_write(reply_fd, "OKAY", 4); + usb_cleanup(); + exit(0); + } + +#if ADB_HOST + // "transport:" is used for switching transport with a specified serial number + // "transport-usb:" is used for switching transport to the only USB transport + // "transport-local:" is used for switching transport to the only local transport + // "transport-any:" is used for switching transport to the only transport + if (!strncmp(service, "transport", strlen("transport"))) { + char* error_string = "unknown failure"; + transport_type type = kTransportAny; + + if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { + type = kTransportUsb; + } else if (!strncmp(service, "transport-local", strlen("transport-local"))) { + type = kTransportLocal; + } else if (!strncmp(service, "transport-any", strlen("transport-any"))) { + type = kTransportAny; + } else if (!strncmp(service, "transport:", strlen("transport:"))) { + service += strlen("transport:"); + serial = strdup(service); + } + + transport = acquire_one_transport(CS_ANY, type, serial, &error_string); + + if (transport) { + s->transport = transport; + adb_write(reply_fd, "OKAY", 4); + } else { + sendfailmsg(reply_fd, error_string); + } + return 1; + } + + // return a list of all connected devices + if (!strcmp(service, "devices")) { + char buffer[4096]; + memset(buf, 0, sizeof(buf)); + memset(buffer, 0, sizeof(buffer)); + D("Getting device list \n"); + list_transports(buffer, sizeof(buffer)); + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); + D("Wrote device list \n"); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + + // returns our value for ADB_SERVER_VERSION + if (!strcmp(service, "version")) { + char version[12]; + snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION); + snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + + if(!strncmp(service,"get-serialno",strlen("get-serialno"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->serial) { + out = transport->serial; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + // indicates a new emulator instance has started + if (!strncmp(service,"emulator:",9)) { + int port = atoi(service+9); + local_connect(port); + /* we don't even need to send a reply */ + return 0; + } +#endif // ADB_HOST + + if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) { + char *local, *remote, *err; + int r; + atransport *transport; + + int createForward = strncmp(service,"kill",4); + + local = service + (createForward ? 8 : 12); + remote = strchr(local,';'); + if(remote == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + *remote++ = 0; + if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + transport = acquire_one_transport(CS_ANY, ttype, serial, &err); + if (!transport) { + sendfailmsg(reply_fd, err); + return 0; + } + + if (createForward) { + r = install_listener(local, remote, transport); + } else { + r = remove_listener(local, remote, transport); + } + if(r == 0) { + /* 1st OKAY is connect, 2nd OKAY is status */ + writex(reply_fd, "OKAYOKAY", 8); + return 0; + } + + if (createForward) { + sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket"); + } else { + sendfailmsg(reply_fd, "cannot remove listener"); + } + return 0; + } + + if(!strncmp(service,"get-state",strlen("get-state"))) { + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + char *state = connection_state_name(transport); + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + return -1; +} + +#if !ADB_HOST +int recovery_mode = 0; +#endif + +int main(int argc, char **argv) +{ + adb_trace_init(); +#if ADB_HOST + adb_sysdeps_init(); + return adb_commandline(argc - 1, argv + 1); +#else + if((argc > 1) && (!strcmp(argv[1],"recovery"))) { + adb_device_banner = "recovery"; + recovery_mode = 1; + } +#if ADB_DEVICE_LOG + start_device_log(); +#endif + return adb_main(0); +#endif +} + diff --git a/adb/adb.h b/adb/adb.h new file mode 100644 index 0000000..a17c8dd --- /dev/null +++ b/adb/adb.h @@ -0,0 +1,407 @@ +/* + * 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 __ADB_H +#define __ADB_H + +#include <limits.h> + +#define MAX_PAYLOAD 4096 + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 + +#define A_VERSION 0x01000000 // ADB protocol version + +#define ADB_VERSION_MAJOR 1 // Used for help/version information +#define ADB_VERSION_MINOR 0 // Used for help/version information + +#define ADB_SERVER_VERSION 20 // Increment this when we want to force users to start a new adb server + +typedef struct amessage amessage; +typedef struct apacket apacket; +typedef struct asocket asocket; +typedef struct alistener alistener; +typedef struct aservice aservice; +typedef struct atransport atransport; +typedef struct adisconnect adisconnect; +typedef struct usb_handle usb_handle; + +struct amessage { + unsigned command; /* command identifier constant */ + unsigned arg0; /* first argument */ + unsigned arg1; /* second argument */ + unsigned data_length; /* length of payload (0 is allowed) */ + unsigned data_check; /* checksum of data payload */ + unsigned magic; /* command ^ 0xffffffff */ +}; + +struct apacket +{ + apacket *next; + + unsigned len; + unsigned char *ptr; + + amessage msg; + unsigned char data[MAX_PAYLOAD]; +}; + +/* An asocket represents one half of a connection between a local and +** remote entity. A local asocket is bound to a file descriptor. A +** remote asocket is bound to the protocol engine. +*/ +struct asocket { + /* chain pointers for the local/remote list of + ** asockets that this asocket lives in + */ + asocket *next; + asocket *prev; + + /* the unique identifier for this asocket + */ + unsigned id; + + /* flag: set when the socket's peer has closed + ** but packets are still queued for delivery + */ + int closing; + + /* the asocket we are connected to + */ + + asocket *peer; + + /* For local asockets, the fde is used to bind + ** us to our fd event system. For remote asockets + ** these fields are not used. + */ + fdevent fde; + int fd; + + /* queue of apackets waiting to be written + */ + apacket *pkt_first; + apacket *pkt_last; + + /* enqueue is called by our peer when it has data + ** for us. It should return 0 if we can accept more + ** data or 1 if not. If we return 1, we must call + ** peer->ready() when we once again are ready to + ** receive data. + */ + int (*enqueue)(asocket *s, apacket *pkt); + + /* ready is called by the peer when it is ready for + ** us to send data via enqueue again + */ + void (*ready)(asocket *s); + + /* close is called by the peer when it has gone away. + ** we are not allowed to make any further calls on the + ** peer once our close method is called. + */ + void (*close)(asocket *s); + + /* socket-type-specific extradata */ + void *extra; + + /* A socket is bound to atransport */ + atransport *transport; +}; + + +/* the adisconnect structure is used to record a callback that +** will be called whenever a transport is disconnected (e.g. by the user) +** this should be used to cleanup objects that depend on the +** transport (e.g. remote sockets, listeners, etc...) +*/ +struct adisconnect +{ + void (*func)(void* opaque, atransport* t); + void* opaque; + adisconnect* next; + adisconnect* prev; +}; + + +/* a transport object models the connection to a remote device or emulator +** there is one transport per connected device/emulator. a "local transport" +** connects through TCP (for the emulator), while a "usb transport" through +** USB (for real devices) +** +** note that kTransportHost doesn't really correspond to a real transport +** object, it's a special value used to indicate that a client wants to +** connect to a service implemented within the ADB server itself. +*/ +typedef enum transport_type { + kTransportUsb, + kTransportLocal, + kTransportAny, + kTransportHost, +} transport_type; + +struct atransport +{ + atransport *next; + atransport *prev; + + int (*read_from_remote)(apacket *p, atransport *t); + int (*write_to_remote)(apacket *p, atransport *t); + void (*close)(atransport *t); + void (*kick)(atransport *t); + + int fd; + int transport_socket; + fdevent transport_fde; + int ref_count; + unsigned sync_token; + int connection_state; + transport_type type; + + /* usb handle or socket fd as needed */ + usb_handle *usb; + int sfd; + + /* used to identify transports for clients */ + char *serial; + char *product; + + /* a list of adisconnect callbacks called when the transport is kicked */ + int kicked; + adisconnect disconnects; +}; + + +/* A listener is an entity which binds to a local port +** and, upon receiving a connection on that port, creates +** an asocket to connect the new local connection to a +** specific remote service. +** +** TODO: some listeners read from the new connection to +** determine what exact service to connect to on the far +** side. +*/ +struct alistener +{ + alistener *next; + alistener *prev; + + fdevent fde; + int fd; + + const char *local_name; + const char *connect_to; + atransport *transport; + adisconnect disconnect; +}; + + +void print_packet(const char *label, apacket *p); + +asocket *find_local_socket(unsigned id); +void install_local_socket(asocket *s); +void remove_socket(asocket *s); +void close_all_sockets(atransport *t); + +#define LOCAL_CLIENT_PREFIX "emulator-" + +asocket *create_local_socket(int fd); +asocket *create_local_service_socket(const char *destination); + +asocket *create_remote_socket(unsigned id, atransport *t); +void connect_to_remote(asocket *s, const char *destination); +void connect_to_smartsocket(asocket *s); + +void fatal(const char *fmt, ...); +void fatal_errno(const char *fmt, ...); + +void handle_packet(apacket *p, atransport *t); +void send_packet(apacket *p, atransport *t); + +void get_my_path(char s[PATH_MAX]); +int launch_server(); +int adb_main(int is_daemon); + + +/* transports are ref-counted +** get_device_transport does an acquire on your behalf before returning +*/ +void init_transport_registration(void); +int list_transports(char *buf, size_t bufsize); +void update_transports(void); + +asocket* create_device_tracker(void); + +/* Obtain a transport from the available transports. +** If state is != CS_ANY, only transports in that state are considered. +** If serial is non-NULL then only the device with that serial will be chosen. +** If no suitable transport is found, error is set. +*/ +atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out); +void add_transport_disconnect( atransport* t, adisconnect* dis ); +void remove_transport_disconnect( atransport* t, adisconnect* dis ); +void run_transport_disconnects( atransport* t ); +void kick_transport( atransport* t ); + +/* initialize a transport object's func pointers and state */ +int init_socket_transport(atransport *t, int s, int port); +void init_usb_transport(atransport *t, usb_handle *usb); + +/* for MacOS X cleanup */ +void close_usb_devices(); + +/* cause new transports to be init'd and added to the list */ +void register_socket_transport(int s, const char *serial, int port); +void register_usb_transport(usb_handle *h, const char *serial); + +int service_to_fd(const char *name); +#if ADB_HOST +asocket *host_service_to_socket(const char* name, const char *serial); +#endif + +#if !ADB_HOST +int init_jdwp(void); +asocket* create_jdwp_service_socket(); +asocket* create_jdwp_tracker_service_socket(); +int create_jdwp_connection_fd(int jdwp_pid); +#endif + +#if !ADB_HOST +void framebuffer_service(int fd, void *cookie); +void log_service(int fd, void *cookie); +void remount_service(int fd, void *cookie); +char * get_log_file_path(const char * log_name); +#endif + +/* packet allocator */ +apacket *get_apacket(void); +void put_apacket(apacket *p); + +int check_header(apacket *p); +int check_data(apacket *p); + +/* convenience wrappers around read/write that will retry on +** EINTR and/or short read/write. Returns 0 on success, -1 +** on error or EOF. +*/ +int readx(int fd, void *ptr, size_t len); +int writex(int fd, const void *ptr, size_t len); + +/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ + +#define ADB_TRACE 1 + +/* IMPORTANT: if you change the following list, don't + * forget to update the corresponding 'tags' table in + * the adb_trace_init() function implemented in adb.c + */ +typedef enum { + TRACE_ADB = 0, + TRACE_SOCKETS, + TRACE_PACKETS, + TRACE_TRANSPORT, + TRACE_RWX, + TRACE_USB, + TRACE_SYNC, + TRACE_SYSDEPS, + TRACE_JDWP, +} AdbTrace; + +#if ADB_TRACE + + int adb_trace_mask; + + void adb_trace_init(void); + +# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) + + /* you must define TRACE_TAG before using this macro */ + #define D(...) \ + do { \ + if (ADB_TRACING) \ + fprintf(stderr, __VA_ARGS__ ); \ + } while (0) +#else +# define D(...) ((void)0) +# define ADB_TRACING 0 +#endif + + +/* set this to log to /data/adb/adb_<time>.txt on the device. + * has no effect if the /data/adb/ directory does not exist. + */ +#define ADB_DEVICE_LOG 0 + +#if !TRACE_PACKETS +#define print_packet(tag,p) do {} while (0) +#endif + +#define ADB_PORT 5037 +#define ADB_LOCAL_TRANSPORT_PORT 5555 + +// Google's USB Vendor ID +#define VENDOR_ID_GOOGLE 0x18d1 +// HTC's USB Vendor ID +#define VENDOR_ID_HTC 0x0bb4 + +// products for VENDOR_ID_GOOGLE +#define PRODUCT_ID_SOONER 0xd00d // Sooner bootloader +#define PRODUCT_ID_SOONER_COMP 0xdeed // Sooner composite device + +// products for VENDOR_ID_HTC +#define PRODUCT_ID_DREAM 0x0c01 // Dream bootloader +#define PRODUCT_ID_DREAM_COMP 0x0c02 // Dream composite device + +void local_init(); +int local_connect(int port); + +/* usb host/client interface */ +void usb_init(); +void usb_cleanup(); +int usb_write(usb_handle *h, const void *data, int len); +int usb_read(usb_handle *h, void *data, int len); +int usb_close(usb_handle *h); +void usb_kick(usb_handle *h); + +/* used for USB device detection */ +int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol); + +unsigned host_to_le32(unsigned n); +int adb_commandline(int argc, char **argv); + +int connection_state(atransport *t); + +#define CS_ANY -1 +#define CS_OFFLINE 0 +#define CS_BOOTLOADER 1 +#define CS_DEVICE 2 +#define CS_HOST 3 +#define CS_RECOVERY 4 +#define CS_ERROR 5 + +extern int HOST; + +#define CHUNK_SIZE (64*1024) + +int sendfailmsg(int fd, const char *reason); +int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); + +#endif diff --git a/adb/adb_client.c b/adb/adb_client.c new file mode 100644 index 0000000..5868744 --- /dev/null +++ b/adb/adb_client.c @@ -0,0 +1,318 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <zipfile/zipfile.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb_client.h" + +static transport_type __adb_transport = kTransportAny; +static const char* __adb_serial = NULL; + +void adb_set_transport(transport_type type, const char* serial) +{ + __adb_transport = type; + __adb_serial = serial; +} + +int adb_get_emulator_console_port(void) +{ + const char* serial = __adb_serial; + int port; + + if (serial == NULL) { + /* if no specific device was specified, we need to look at */ + /* the list of connected devices, and extract an emulator */ + /* name from it. two emulators is an error */ + char* tmp = adb_query("host:devices"); + char* p = tmp; + if(!tmp) { + printf("no emulator connected\n"); + return -1; + } + while (*p) { + char* q = strchr(p, '\n'); + if (q != NULL) + *q++ = 0; + else + q = p + strlen(p); + + if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) { + if (serial != NULL) { /* more than one emulator listed */ + free(tmp); + return -2; + } + serial = p; + } + + p = q; + } + free(tmp); + + if (serial == NULL) + return -1; /* no emulator found */ + } + else { + if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0) + return -1; /* not an emulator */ + } + + serial += sizeof(LOCAL_CLIENT_PREFIX)-1; + port = strtol(serial, NULL, 10); + return port; +} + +static char __adb_error[256] = { 0 }; + +const char *adb_error(void) +{ + return __adb_error; +} + +static int switch_socket_transport(int fd) +{ + char service[64]; + char tmp[5]; + int len; + + if (__adb_serial) + snprintf(service, sizeof service, "host:transport:%s", __adb_serial); + else { + char* transport_type = "???"; + + switch (__adb_transport) { + case kTransportUsb: + transport_type = "transport-usb"; + break; + case kTransportLocal: + transport_type = "transport-local"; + break; + case kTransportAny: + transport_type = "transport-any"; + break; + case kTransportHost: + // no switch necessary + return 0; + break; + } + + snprintf(service, sizeof service, "host:%s", transport_type); + } + len = strlen(service); + snprintf(tmp, sizeof tmp, "%04x", len); + + if(writex(fd, tmp, 4) || writex(fd, service, len)) { + strcpy(__adb_error, "write failure during connection"); + adb_close(fd); + return -1; + } + D("Switch transport in progress\n"); + + if(adb_status(fd)) { + adb_close(fd); + D("Switch transport failed\n"); + return -1; + } + D("Switch transport success\n"); + return 0; +} + +int adb_status(int fd) +{ + unsigned char buf[5]; + unsigned len; + + if(readx(fd, buf, 4)) { + strcpy(__adb_error, "protocol fault (no status)"); + return -1; + } + + if(!memcmp(buf, "OKAY", 4)) { + return 0; + } + + if(memcmp(buf, "FAIL", 4)) { + sprintf(__adb_error, + "protocol fault (status %02x %02x %02x %02x?!)", + buf[0], buf[1], buf[2], buf[3]); + return -1; + } + + if(readx(fd, buf, 4)) { + strcpy(__adb_error, "protocol fault (status len)"); + return -1; + } + buf[4] = 0; + len = strtoul((char*)buf, 0, 16); + if(len > 255) len = 255; + if(readx(fd, __adb_error, len)) { + strcpy(__adb_error, "protocol fault (status read)"); + return -1; + } + __adb_error[len] = 0; + return -1; +} + +int _adb_connect(const char *service) +{ + char tmp[5]; + int len; + int fd; + + D("_adb_connect: %s\n", service); + len = strlen(service); + if((len < 1) || (len > 1024)) { + strcpy(__adb_error, "service name too long"); + return -1; + } + snprintf(tmp, sizeof tmp, "%04x", len); + + fd = socket_loopback_client(ADB_PORT, SOCK_STREAM); + if(fd < 0) { + strcpy(__adb_error, "cannot connect to daemon"); + return -2; + } + + if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) { + return -1; + } + + if(writex(fd, tmp, 4) || writex(fd, service, len)) { + strcpy(__adb_error, "write failure during connection"); + adb_close(fd); + return -1; + } + + if(adb_status(fd)) { + adb_close(fd); + return -1; + } + + return fd; +} + +int adb_connect(const char *service) +{ + // first query the adb server's version + int fd = _adb_connect("host:version"); + + if(fd == -2) { + fprintf(stdout,"* daemon not running. starting it now *\n"); + start_server: + if(launch_server(0)) { + fprintf(stderr,"* failed to start daemon *\n"); + return -1; + } else { + fprintf(stdout,"* daemon started successfully *\n"); + } + /* give the server some time to start properly and detect devices */ + adb_sleep_ms(2000); + // fall through to _adb_connect + } else { + // if server was running, check its version to make sure it is not out of date + char buf[100]; + int n; + int version = ADB_SERVER_VERSION - 1; + + // if we have a file descriptor, then parse version result + if(fd >= 0) { + if(readx(fd, buf, 4)) goto error; + + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n > (int)sizeof(buf)) goto error; + if(readx(fd, buf, n)) goto error; + adb_close(fd); + + if (sscanf(buf, "%04x", &version) != 1) goto error; + } else { + // if fd is -1, then check for "unknown host service", + // which would indicate a version of adb that does not support the version command + if (strcmp(__adb_error, "unknown host service") != 0) + return fd; + } + + if(version != ADB_SERVER_VERSION) { + printf("adb server is out of date. killing...\n"); + fd = _adb_connect("host:kill"); + adb_close(fd); + + /* XXX can we better detect its death? */ + adb_sleep_ms(2000); + goto start_server; + } + } + + // if the command is start-server, we are done. + if (!strcmp(service, "host:start-server")) + return 0; + + fd = _adb_connect(service); + if(fd == -2) { + fprintf(stderr,"** daemon still not running"); + } + + return fd; +error: + adb_close(fd); + return -1; +} + + +int adb_command(const char *service) +{ + int fd = adb_connect(service); + if(fd < 0) { + return -1; + } + + if(adb_status(fd)) { + adb_close(fd); + return -1; + } + + return 0; +} + +char *adb_query(const char *service) +{ + char buf[5]; + unsigned n; + char *tmp; + + D("adb_query: %s\n", service); + int fd = adb_connect(service); + if(fd < 0) { + fprintf(stderr,"error: %s\n", __adb_error); + return 0; + } + + if(readx(fd, buf, 4)) goto oops; + + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n > 1024) goto oops; + + tmp = malloc(n + 1); + if(tmp == 0) goto oops; + + if(readx(fd, tmp, n) == 0) { + tmp[n] = 0; + adb_close(fd); + return tmp; + } + free(tmp); + +oops: + adb_close(fd); + return 0; +} + + diff --git a/adb/adb_client.h b/adb/adb_client.h new file mode 100644 index 0000000..8061579 --- /dev/null +++ b/adb/adb_client.h @@ -0,0 +1,49 @@ +#ifndef _ADB_CLIENT_H_ +#define _ADB_CLIENT_H_ + +#include "adb.h" + +/* connect to adb, connect to the named service, and return +** a valid fd for interacting with that service upon success +** or a negative number on failure +*/ +int adb_connect(const char *service); +int _adb_connect(const char *service); + +/* connect to adb, connect to the named service, return 0 if +** the connection succeeded AND the service returned OKAY +*/ +int adb_command(const char *service); + +/* connect to adb, connect to the named service, return +** a malloc'd string of its response upon success or NULL +** on failure. +*/ +char *adb_query(const char *service); + +/* Set the preferred transport to connect to. +*/ +void adb_set_transport(transport_type type, const char* serial); + +/* Return the console port of the currently connected emulator (if any) + * of -1 if there is no emulator, and -2 if there is more than one. + * assumes adb_set_transport() was alled previously... + */ +int adb_get_emulator_console_port(void); + +/* send commands to the current emulator instance. will fail if there + * is zero, or more than one emulator connected (or if you use -s <serial> + * with a <serial> that does not designate an emulator) + */ +int adb_send_emulator_command(int argc, char** argv); + +/* return verbose error string from last operation */ +const char *adb_error(void); + +/* read a standard adb status response (OKAY|FAIL) and +** return 0 in the event of OKAY, -1 in the event of FAIL +** or protocol error +*/ +int adb_status(int fd); + +#endif diff --git a/adb/commandline.c b/adb/commandline.c new file mode 100644 index 0000000..be596ce --- /dev/null +++ b/adb/commandline.c @@ -0,0 +1,1249 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <assert.h> + +#include "sysdeps.h" + +#ifdef HAVE_TERMIO_H +#include <termios.h> +#endif + +#define TRACE_TAG TRACE_ADB +#include "adb.h" +#include "adb_client.h" +#include "file_sync_service.h" + +#ifdef SH_HISTORY +#include "shlist.h" +#include "history.h" +#endif + +enum { + IGNORE_DATA, + WIPE_DATA, + FLASH_DATA +}; + +static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); + +void get_my_path(char s[PATH_MAX]); +int find_sync_dirs(const char *srcarg, + char **android_srcdir_out, char **data_srcdir_out); +int install_app(transport_type transport, char* serial, int argc, char** argv); +int uninstall_app(transport_type transport, char* serial, int argc, char** argv); + +static const char *gProductOutPath = NULL; + +static char *product_file(const char *extra) +{ + int n; + char *x; + + if (gProductOutPath == NULL) { + fprintf(stderr, "adb: Product directory not specified; " + "use -p or define ANDROID_PRODUCT_OUT\n"); + exit(1); + } + + n = strlen(gProductOutPath) + strlen(extra) + 2; + x = malloc(n); + if (x == 0) { + fprintf(stderr, "adb: Out of memory (product_file())\n"); + exit(1); + } + + snprintf(x, (size_t)n, "%s" OS_PATH_SEPARATOR_STR "%s", gProductOutPath, extra); + return x; +} + +void version(FILE * out) { + fprintf(out, "Android Debug Bridge version %d.%d.%d\n", + ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION); +} + +void help() +{ + version(stderr); + + fprintf(stderr, + "\n" + " -d - directs command to the only connected USB device\n" + " returns an error if more than one USB device is present.\n" + " -e - directs command to the only running emulator.\n" + " returns an error if more than one emulator is running.\n" + " -s <serial number> - directs command to the USB device or emulator with\n" + " the given serial number\n" + " -p <product name or path> - simple product name like 'sooner', or\n" + " a relative/absolute path to a product\n" + " out directory like 'out/target/product/sooner'.\n" + " If -p is not specified, the ANDROID_PRODUCT_OUT\n" + " environment variable is used, which must\n" + " be an absolute path.\n" + " devices - list all connected devices\n" + "\n" + "device commands:\n" + " adb push <local> <remote> - copy file/dir to device\n" + " adb pull <remote> <local> - copy file/dir from device\n" + " adb sync [ <directory> ] - copy host->device only if changed\n" + " (see 'adb help all')\n" + " adb shell - run remote shell interactively\n" + " adb shell <command> - run remote shell command\n" + " adb emu <command> - run emulator console command\n" + " adb logcat [ <filter-spec> ] - View device log\n" + " adb forward <local> <remote> - forward socket connections\n" + " forward specs are one of: \n" + " tcp:<port>\n" + " localabstract:<unix domain socket name>\n" + " localreserved:<unix domain socket name>\n" + " localfilesystem:<unix domain socket name>\n" + " dev:<character device name>\n" + " jdwp:<process pid> (remote only)\n" + " adb jdwp - list PIDs of processes hosting a JDWP transport\n" + " adb install [-l] [-r] <file> - push this package file to the device and install it\n" + " ('-l' means forward-lock the app)\n" + " ('-r' means reinstall the app, keeping its data)\n" + " adb uninstall [-k] <package> - remove this app package from the device\n" + " ('-k' means keep the data and cache directories)\n" + " adb bugreport - return all information from the device\n" + " that should be included in a bug report.\n" + "\n" + " adb help - show this help message\n" + " adb version - show version num\n" + "\n" + "DATAOPTS:\n" + " (no option) - don't touch the data partition\n" + " -w - wipe the data partition\n" + " -d - flash the data partition\n" + "\n" + "scripting:\n" + " adb wait-for-device - block until device is online\n" + " adb start-server - ensure that there is a server running\n" + " adb kill-server - kill the server if it is running\n" + " adb get-state - prints: offline | bootloader | device\n" + " adb get-serialno - prints: <serial-number>\n" + " adb status-window - continuously print device status for a specified device\n" + " adb remount - remounts the /system partition on the device read-write\n" + "\n" + "networking:\n" + " adb ppp <tty> [parameters] - Run PPP over USB.\n" + " Note: you should not automatically start a PDP connection.\n" + " <tty> refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1\n" + " [parameters] - Eg. defaultroute debug dump local notty usepeerdns\n" + "\n" + "adb sync notes: adb sync [ <directory> ]\n" + " <localdir> can be interpreted in several ways:\n" + "\n" + " - If <directory> is not specified, both /system and /data partitions will be updated.\n" + "\n" + " - If it is \"system\" or \"data\", only the corresponding partition\n" + " is updated.\n" + ); +} + +int usage() +{ + help(); + return 1; +} + +#ifdef HAVE_TERMIO_H +static struct termios tio_save; + +static void stdin_raw_init(int fd) +{ + struct termios tio; + + if(tcgetattr(fd, &tio)) return; + if(tcgetattr(fd, &tio_save)) return; + + tio.c_lflag = 0; /* disable CANON, ECHO*, etc */ + + /* no timeout but request at least one character per read */ + tio.c_cc[VTIME] = 0; + tio.c_cc[VMIN] = 1; + + tcsetattr(fd, TCSANOW, &tio); + tcflush(fd, TCIFLUSH); +} + +static void stdin_raw_restore(int fd) +{ + tcsetattr(fd, TCSANOW, &tio_save); + tcflush(fd, TCIFLUSH); +} +#endif + +static void read_and_dump(int fd) +{ + char buf[4096]; + int len; + + while(fd >= 0) { + len = adb_read(fd, buf, 4096); + if(len == 0) { + break; + } + + if(len < 0) { + if(errno == EINTR) continue; + break; + } + /* we want to output to stdout, so no adb_write here !! */ + unix_write(1, buf, len); + } +} + +#ifdef SH_HISTORY +int shItemCmp( void *val, void *idata ) +{ + return( (strcmp( val, idata ) == 0) ); +} +#endif + +static void *stdin_read_thread(void *x) +{ + int fd, fdi; + unsigned char buf[1024]; +#ifdef SH_HISTORY + unsigned char realbuf[1024], *buf_ptr; + SHLIST history; + SHLIST *item = &history; + int cmdlen = 0, ins_flag = 0; +#endif + int r, n; + int state = 0; + + int *fds = (int*) x; + fd = fds[0]; + fdi = fds[1]; + free(fds); + +#ifdef SH_HISTORY + shListInitList( &history ); +#endif + for(;;) { + /* fdi is really the client's stdin, so use read, not adb_read here */ + r = unix_read(fdi, buf, 1024); + if(r == 0) break; + if(r < 0) { + if(errno == EINTR) continue; + break; + } +#ifdef SH_HISTORY + if( (r == 3) && /* Arrow processing */ + (memcmp( (void *)buf, SH_ARROW_ANY, 2 ) == 0) ) { + switch( buf[2] ) { + case SH_ARROW_UP: + item = shListGetNextItem( &history, item ); + break; + case SH_ARROW_DOWN: + item = shListGetPrevItem( &history, item ); + break; + default: + item = NULL; + break; + } + memset( buf, SH_DEL_CHAR, cmdlen ); + if( item != NULL ) { + n = snprintf( (char *)(&buf[cmdlen]), sizeof buf - cmdlen, "%s", (char *)(item->data) ); + memcpy( realbuf, item->data, n ); + } + else { /* Clean buffer */ + item = &history; + n = 0; + } + r = n + cmdlen; + cmdlen = n; + ins_flag = 0; + if( r == 0 ) + continue; + } + else { +#endif + for(n = 0; n < r; n++){ + switch(buf[n]) { + case '\n': +#ifdef SH_HISTORY + if( ins_flag && (SH_BLANK_CHAR <= realbuf[0]) ) { + buf_ptr = malloc(cmdlen + 1); + if( buf_ptr != NULL ) { + memcpy( buf_ptr, realbuf, cmdlen ); + buf_ptr[cmdlen] = '\0'; + if( (item = shListFindItem( &history, (void *)buf_ptr, shItemCmp )) == NULL ) { + shListInsFirstItem( &history, (void *)buf_ptr ); + item = &history; + } + } + } + cmdlen = 0; + ins_flag = 0; +#endif + state = 1; + break; + case '\r': + state = 1; + break; + case '~': + if(state == 1) state++; + break; + case '.': + if(state == 2) { + fprintf(stderr,"\n* disconnect *\n"); + #ifdef HAVE_TERMIO_H + stdin_raw_restore(fdi); + #endif + exit(0); + } + default: +#ifdef SH_HISTORY + if( buf[n] == SH_DEL_CHAR ) { + if( cmdlen > 0 ) + cmdlen--; + } + else { + realbuf[cmdlen] = buf[n]; + cmdlen++; + } + ins_flag = 1; +#endif + state = 0; + } + } +#ifdef SH_HISTORY + } +#endif + r = adb_write(fd, buf, r); + if(r <= 0) { + break; + } + } +#ifdef SH_HISTORY + shListDelAllItems( &history, (shListFree)free ); +#endif + return 0; +} + +int interactive_shell(void) +{ + adb_thread_t thr; + int fdi, fd; + int *fds; + + fd = adb_connect("shell:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + fdi = 0; //dup(0); + + fds = malloc(sizeof(int) * 2); + fds[0] = fd; + fds[1] = fdi; + +#ifdef HAVE_TERMIO_H + stdin_raw_init(fdi); +#endif + adb_thread_create(&thr, stdin_read_thread, fds); + read_and_dump(fd); +#ifdef HAVE_TERMIO_H + stdin_raw_restore(fdi); +#endif + return 0; +} + + +static void format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial) +{ + if (serial) { + snprintf(buffer, buflen, "host-serial:%s:%s", serial, command); + } else { + const char* prefix = "host"; + if (ttype == kTransportUsb) + prefix = "host-usb"; + else if (ttype == kTransportLocal) + prefix = "host-local"; + + snprintf(buffer, buflen, "%s:%s", prefix, command); + } +} + +static void status_window(transport_type ttype, const char* serial) +{ + char command[4096]; + char *state = 0; + char *laststate = 0; + + /* silence stderr */ +#ifdef _WIN32 + /* XXX: TODO */ +#else + int fd; + fd = unix_open("/dev/null", O_WRONLY); + dup2(fd, 2); + adb_close(fd); +#endif + + format_host_command(command, sizeof command, "get-state", ttype, serial); + + for(;;) { + adb_sleep_ms(250); + + if(state) { + free(state); + state = 0; + } + + state = adb_query(command); + + if(state) { + if(laststate && !strcmp(state,laststate)){ + continue; + } else { + if(laststate) free(laststate); + laststate = strdup(state); + } + } + + printf("%c[2J%c[2H", 27, 27); + printf("Android Debug Bridge\n"); + printf("State: %s\n", state ? state : "offline"); + fflush(stdout); + } +} + +/** duplicate string and quote all \ " ( ) chars + space character. */ +static char * +dupAndQuote(const char *s) +{ + const char *ts; + size_t alloc_len; + char *ret; + char *dest; + + ts = s; + + alloc_len = 0; + + for( ;*ts != '\0'; ts++) { + alloc_len++; + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + alloc_len++; + } + } + + ret = (char *)malloc(alloc_len + 1); + + ts = s; + dest = ret; + + for ( ;*ts != '\0'; ts++) { + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + *dest++ = '\\'; + } + + *dest++ = *ts; + } + + *dest++ = '\0'; + + return ret; +} + +/** + * Run ppp in "notty" mode against a resource listed as the first parameter + * eg: + * + * ppp dev:/dev/omap_csmi_tty0 <ppp options> + * + */ +int ppp(int argc, char **argv) +{ +#ifdef HAVE_WIN32_PROC + fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]); + return -1; +#else + char *adb_service_name; + pid_t pid; + int fd; + + if (argc < 2) { + fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n", + argv[0]); + + return 1; + } + + adb_service_name = argv[1]; + + fd = adb_connect(adb_service_name); + + if(fd < 0) { + fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n", + adb_service_name, adb_error()); + return 1; + } + + pid = fork(); + + if (pid < 0) { + perror("from fork()"); + return 1; + } else if (pid == 0) { + int err; + int i; + const char **ppp_args; + + // copy args + ppp_args = (const char **) alloca(sizeof(char *) * argc + 1); + ppp_args[0] = "pppd"; + for (i = 2 ; i < argc ; i++) { + //argv[2] and beyond become ppp_args[1] and beyond + ppp_args[i - 1] = argv[i]; + } + ppp_args[i-1] = NULL; + + // child side + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + adb_close(STDERR_FILENO); + adb_close(fd); + + err = execvp("pppd", (char * const *)ppp_args); + + if (err < 0) { + perror("execing pppd"); + } + exit(-1); + } else { + // parent side + + adb_close(fd); + return 0; + } +#endif /* !HAVE_WIN32_PROC */ +} + +static int send_shellcommand(transport_type transport, char* serial, char* buf) +{ + int fd, ret; + + for(;;) { + fd = adb_connect(buf); + if(fd >= 0) + break; + fprintf(stderr,"- waiting for device -\n"); + adb_sleep_ms(1000); + do_cmd(transport, serial, "wait-for-device", 0); + } + + read_and_dump(fd); + ret = adb_close(fd); + if (ret) + perror("close"); + + return ret; +} + +static int logcat(transport_type transport, char* serial, int argc, char **argv) +{ + char buf[4096]; + + char *log_tags; + char *quoted_log_tags; + + log_tags = getenv("ANDROID_LOG_TAGS"); + quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags); + + snprintf(buf, sizeof(buf), + "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat", + quoted_log_tags); + + free(quoted_log_tags); + + argc -= 1; + argv += 1; + while(argc-- > 0) { + char *quoted; + + quoted = dupAndQuote (*argv++); + + strncat(buf, " ", sizeof(buf)-1); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + } + + send_shellcommand(transport, serial, buf); + return 0; +} + +#define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make" +static int top_works(const char *top) +{ + if (top != NULL && adb_is_absolute_host_path(top)) { + char path_buf[PATH_MAX]; + snprintf(path_buf, sizeof(path_buf), + "%s" OS_PATH_SEPARATOR_STR SENTINEL_FILE, top); + return access(path_buf, F_OK) == 0; + } + return 0; +} + +static char *find_top_from(const char *indir, char path_buf[PATH_MAX]) +{ + strcpy(path_buf, indir); + while (1) { + if (top_works(path_buf)) { + return path_buf; + } + char *s = adb_dirstop(path_buf); + if (s != NULL) { + *s = '\0'; + } else { + path_buf[0] = '\0'; + return NULL; + } + } +} + +static char *find_top(char path_buf[PATH_MAX]) +{ + char *top = getenv("ANDROID_BUILD_TOP"); + if (top != NULL && top[0] != '\0') { + if (!top_works(top)) { + fprintf(stderr, "adb: bad ANDROID_BUILD_TOP value \"%s\"\n", top); + return NULL; + } + } else { + top = getenv("TOP"); + if (top != NULL && top[0] != '\0') { + if (!top_works(top)) { + fprintf(stderr, "adb: bad TOP value \"%s\"\n", top); + return NULL; + } + } else { + top = NULL; + } + } + + if (top != NULL) { + /* The environment pointed to a top directory that works. + */ + strcpy(path_buf, top); + return path_buf; + } + + /* The environment didn't help. Walk up the tree from the CWD + * to see if we can find the top. + */ + char dir[PATH_MAX]; + top = find_top_from(getcwd(dir, sizeof(dir)), path_buf); + if (top == NULL) { + /* If the CWD isn't under a good-looking top, see if the + * executable is. + */ + get_my_path(dir); + top = find_top_from(dir, path_buf); + } + return top; +} + +/* <hint> may be: + * - A simple product name + * e.g., "sooner" +TODO: debug? sooner-debug, sooner:debug? + * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir + * e.g., "out/target/product/sooner" + * - An absolute path to the PRODUCT_OUT dir + * e.g., "/src/device/out/target/product/sooner" + * + * Given <hint>, try to construct an absolute path to the + * ANDROID_PRODUCT_OUT dir. + */ +static const char *find_product_out_path(const char *hint) +{ + static char path_buf[PATH_MAX]; + + if (hint == NULL || hint[0] == '\0') { + return NULL; + } + + /* If it's already absolute, don't bother doing any work. + */ + if (adb_is_absolute_host_path(hint)) { + strcpy(path_buf, hint); + return path_buf; + } + + /* If there are any slashes in it, assume it's a relative path; + * make it absolute. + */ + if (adb_dirstart(hint) != NULL) { + if (getcwd(path_buf, sizeof(path_buf)) == NULL) { + fprintf(stderr, "adb: Couldn't get CWD: %s\n", strerror(errno)); + return NULL; + } + if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) { + fprintf(stderr, "adb: Couldn't assemble path\n"); + return NULL; + } + strcat(path_buf, OS_PATH_SEPARATOR_STR); + strcat(path_buf, hint); + return path_buf; + } + + /* It's a string without any slashes. Try to do something with it. + * + * Try to find the root of the build tree, and build a PRODUCT_OUT + * path from there. + */ + char top_buf[PATH_MAX]; + const char *top = find_top(top_buf); + if (top == NULL) { + fprintf(stderr, "adb: Couldn't find top of build tree\n"); + return NULL; + } +//TODO: if we have a way to indicate debug, look in out/debug/target/... + snprintf(path_buf, sizeof(path_buf), + "%s" OS_PATH_SEPARATOR_STR + "out" OS_PATH_SEPARATOR_STR + "target" OS_PATH_SEPARATOR_STR + "product" OS_PATH_SEPARATOR_STR + "%s", top_buf, hint); + if (access(path_buf, F_OK) < 0) { + fprintf(stderr, "adb: Couldn't find a product dir " + "based on \"-p %s\"; \"%s\" doesn't exist\n", hint, path_buf); + return NULL; + } + return path_buf; +} + +int adb_commandline(int argc, char **argv) +{ + char buf[4096]; + int no_daemon = 0; + int is_daemon = 0; + int persist = 0; + int r; + int quote; + transport_type ttype = kTransportAny; + char* serial = NULL; + + /* If defined, this should be an absolute path to + * the directory containing all of the various system images + * for a particular product. If not defined, and the adb + * command requires this information, then the user must + * specify the path using "-p". + */ + gProductOutPath = getenv("ANDROID_PRODUCT_OUT"); + if (gProductOutPath == NULL || gProductOutPath[0] == '\0') { + gProductOutPath = NULL; + } + // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint + + /* modifiers and flags */ + while(argc > 0) { + if(!strcmp(argv[0],"nodaemon")) { + no_daemon = 1; + } else if (!strcmp(argv[0], "fork-server")) { + /* this is a special flag used only when the ADB client launches the ADB Server */ + is_daemon = 1; + } else if(!strcmp(argv[0],"persist")) { + persist = 1; + } else if(!strncmp(argv[0], "-p", 2)) { + const char *product = NULL; + if (argv[0][2] == '\0') { + if (argc < 2) return usage(); + product = argv[1]; + argc--; + argv++; + } else { + product = argv[1] + 2; + } + gProductOutPath = find_product_out_path(product); + if (gProductOutPath == NULL) { + fprintf(stderr, "adb: could not resolve \"-p %s\"\n", + product); + return usage(); + } + } else if (argv[0][0]=='-' && argv[0][1]=='s') { + if (isdigit(argv[0][2])) { + serial = argv[0] + 2; + } else { + if(argc < 2) return usage(); + serial = argv[1]; + argc--; + argv++; + } + } else if (!strcmp(argv[0],"-d")) { + ttype = kTransportUsb; + } else if (!strcmp(argv[0],"-e")) { + ttype = kTransportLocal; + } else { + /* out of recognized modifiers and flags */ + break; + } + argc--; + argv++; + } + + adb_set_transport(ttype, serial); + + if ((argc > 0) && (!strcmp(argv[0],"server"))) { + if (no_daemon || is_daemon) { + r = adb_main(is_daemon); + } else { + r = launch_server(); + } + if(r) { + fprintf(stderr,"* could not start server *\n"); + } + return r; + } + +top: + if(argc == 0) { + return usage(); + } + + /* adb_connect() commands */ + + if(!strcmp(argv[0], "devices")) { + char *tmp; + snprintf(buf, sizeof buf, "host:%s", argv[0]); + tmp = adb_query(buf); + if(tmp) { + printf("List of devices attached \n"); + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + if (!strcmp(argv[0], "emu")) { + return adb_send_emulator_command(argc, argv); + } + + if(!strcmp(argv[0], "shell")) { + int r; + int fd; + + if(argc < 2) { + return interactive_shell(); + } + + snprintf(buf, sizeof buf, "shell:%s", argv[1]); + argc -= 2; + argv += 2; + while(argc-- > 0) { + strcat(buf, " "); + + /* quote empty strings and strings with spaces */ + quote = (**argv == 0 || strchr(*argv, ' ')); + if (quote) + strcat(buf, "\""); + strcat(buf, *argv++); + if (quote) + strcat(buf, "\""); + } + + for(;;) { + fd = adb_connect(buf); + if(fd >= 0) { + read_and_dump(fd); + adb_close(fd); + r = 0; + } else { + fprintf(stderr,"error: %s\n", adb_error()); + r = -1; + } + + if(persist) { + fprintf(stderr,"\n- waiting for device -\n"); + adb_sleep_ms(1000); + do_cmd(ttype, serial, "wait-for-device", 0); + } else { + return r; + } + } + } + + if(!strcmp(argv[0], "kill-server")) { + int fd; + fd = _adb_connect("host:kill"); + if(fd == -1) { + fprintf(stderr,"* server not running *\n"); + return 1; + } + return 0; + } + + if(!strcmp(argv[0], "remount")) { + int fd = adb_connect("remount:"); + if(fd >= 0) { + read_and_dump(fd); + adb_close(fd); + return 0; + } + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(!strcmp(argv[0], "bugreport")) { + if (argc != 1) { + return 1; + } + do_cmd(ttype, serial, "shell", "dumpstate", "-", 0); + return 0; + } + + /* adb_command() wrapper commands */ + + if(!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) { + char* service = argv[0]; + if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) { + if (ttype == kTransportUsb) { + service = "wait-for-usb"; + } else if (ttype == kTransportLocal) { + service = "wait-for-local"; + } else { + service = "wait-for-any"; + } + } + + format_host_command(buf, sizeof buf, service, ttype, serial); + + if (adb_command(buf)) { + D("failure: %s *\n",adb_error()); + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + /* Allow a command to be run after wait-for-device, + * e.g. 'adb wait-for-device shell'. + */ + if(argc > 1) { + argc--; + argv++; + goto top; + } + return 0; + } + + if(!strcmp(argv[0], "forward")) { + if(argc != 3) return usage(); + if (serial) { + snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial,argv[1],argv[2]); + } else { + snprintf(buf, sizeof buf, "host:forward:%s;%s",argv[1],argv[2]); + } + if(adb_command(buf)) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + return 0; + } + + /* do_sync_*() commands */ + + if(!strcmp(argv[0], "ls")) { + if(argc != 2) return usage(); + return do_sync_ls(argv[1]); + } + + if(!strcmp(argv[0], "push")) { + if(argc != 3) return usage(); + return do_sync_push(argv[1], argv[2], 0 /* no verify APK */); + } + + if(!strcmp(argv[0], "pull")) { + if(argc != 3) return usage(); + return do_sync_pull(argv[1], argv[2]); + } + + if(!strcmp(argv[0], "install")) { + if (argc < 2) return usage(); + return install_app(ttype, serial, argc, argv); + } + + if(!strcmp(argv[0], "uninstall")) { + if (argc < 2) return usage(); + return uninstall_app(ttype, serial, argc, argv); + } + + if(!strcmp(argv[0], "sync")) { + char *srcarg, *android_srcpath, *data_srcpath; + int ret; + if(argc < 2) { + /* No local path was specified. */ + srcarg = NULL; + } else if(argc == 2) { + /* A local path or "android"/"data" arg was specified. */ + srcarg = argv[1]; + } else { + return usage(); + } + ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath); + if(ret != 0) return usage(); + + if(android_srcpath != NULL) + ret = do_sync_sync(android_srcpath, "/system"); + if(ret == 0 && data_srcpath != NULL) + ret = do_sync_sync(data_srcpath, "/data"); + + free(android_srcpath); + free(data_srcpath); + return ret; + } + + /* passthrough commands */ + + if(!strcmp(argv[0],"get-state") || + !strcmp(argv[0],"get-serialno")) + { + char *tmp; + + format_host_command(buf, sizeof buf, argv[0], ttype, serial); + tmp = adb_query(buf); + if(tmp) { + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + /* other commands */ + + if(!strcmp(argv[0],"status-window")) { + status_window(ttype, serial); + return 0; + } + + if(!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat")) { + return logcat(ttype, serial, argc, argv); + } + + if(!strcmp(argv[0],"ppp")) { + return ppp(argc, argv); + } + + if (!strcmp(argv[0], "start-server")) { + return adb_connect("host:start-server"); + } + + if (!strcmp(argv[0], "jdwp")) { + int fd = adb_connect("jdwp"); + if (fd >= 0) { + read_and_dump(fd); + adb_close(fd); + return 0; + } else { + fprintf(stderr, "error: %s\n", adb_error()); + return -1; + } + } + + /* "adb /?" is a common idiom under Windows */ + if(!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) { + help(); + return 0; + } + + if(!strcmp(argv[0], "version")) { + version(stdout); + return 0; + } + + usage(); + return 1; +} + +static int do_cmd(transport_type ttype, char* serial, char *cmd, ...) +{ + char *argv[16]; + int argc; + va_list ap; + + va_start(ap, cmd); + argc = 0; + + if (serial) { + argv[argc++] = "-s"; + argv[argc++] = serial; + } else if (ttype == kTransportUsb) { + argv[argc++] = "-d"; + } else if (ttype == kTransportLocal) { + argv[argc++] = "-e"; + } + + argv[argc++] = cmd; + while((argv[argc] = va_arg(ap, char*)) != 0) argc++; + va_end(ap); + +#if 0 + int n; + fprintf(stderr,"argc = %d\n",argc); + for(n = 0; n < argc; n++) { + fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]); + } +#endif + + return adb_commandline(argc, argv); +} + +int find_sync_dirs(const char *srcarg, + char **android_srcdir_out, char **data_srcdir_out) +{ + char *android_srcdir, *data_srcdir; + + if(srcarg == NULL) { + android_srcdir = product_file("system"); + data_srcdir = product_file("data"); + } else { + /* srcarg may be "data", "system" or NULL. + * if srcarg is NULL, then both data and system are synced + */ + if(strcmp(srcarg, "system") == 0) { + android_srcdir = product_file("system"); + data_srcdir = NULL; + } else if(strcmp(srcarg, "data") == 0) { + android_srcdir = NULL; + data_srcdir = product_file("data"); + } else { + /* It's not "system" or "data". + */ + return 1; + } + } + + if(android_srcdir_out != NULL) + *android_srcdir_out = android_srcdir; + else + free(android_srcdir); + + if(data_srcdir_out != NULL) + *data_srcdir_out = data_srcdir; + else + free(data_srcdir); + + return 0; +} + +static int pm_command(transport_type transport, char* serial, + int argc, char** argv) +{ + char buf[4096]; + + snprintf(buf, sizeof(buf), "shell:pm"); + + while(argc-- > 0) { + char *quoted; + + quoted = dupAndQuote(*argv++); + + strncat(buf, " ", sizeof(buf)-1); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + } + + send_shellcommand(transport, serial, buf); + return 0; +} + +int uninstall_app(transport_type transport, char* serial, int argc, char** argv) +{ + /* if the user choose the -k option, we refuse to do it until devices are + out with the option to uninstall the remaining data somehow (adb/ui) */ + if (argc == 3 && strcmp(argv[1], "-k") == 0) + { + printf( + "The -k option uninstalls the application while retaining the data/cache.\n" + "At the moment, there is no way to remove the remaining data.\n" + "You will have to reinstall the application with the same signature, and fully uninstall it.\n" + "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]); + return -1; + } + + /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ + return pm_command(transport, serial, argc, argv); +} + +static int delete_file(transport_type transport, char* serial, char* filename) +{ + char buf[4096]; + char* quoted; + + snprintf(buf, sizeof(buf), "shell:rm "); + quoted = dupAndQuote(filename); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + + send_shellcommand(transport, serial, buf); + return 0; +} + +int install_app(transport_type transport, char* serial, int argc, char** argv) +{ + struct stat st; + int err; + const char *const WHERE = "/data/local/tmp/%s"; + char to[PATH_MAX]; + char* filename = argv[argc - 1]; + const char* p; + + p = adb_dirstop(filename); + if (p) { + p++; + snprintf(to, sizeof to, WHERE, p); + } else { + snprintf(to, sizeof to, WHERE, filename); + } + if (p[0] == '\0') { + } + + err = stat(filename, &st); + if (err != 0) { + fprintf(stderr, "can't find '%s' to install\n", filename); + return 1; + } + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "can't install '%s' because it's not a file\n", + filename); + return 1; + } + + if (!(err = do_sync_push(filename, to, 1 /* verify APK */))) { + /* file in place; tell the Package Manager to install it */ + argv[argc - 1] = to; /* destination name, not source location */ + pm_command(transport, serial, argc, argv); + delete_file(transport, serial, to); + } + + return err; +} diff --git a/adb/console.c b/adb/console.c new file mode 100644 index 0000000..b813d33 --- /dev/null +++ b/adb/console.c @@ -0,0 +1,45 @@ +#include "sysdeps.h" +#include "adb.h" +#include "adb_client.h" +#include <stdio.h> + +static int connect_to_console(void) +{ + int fd, port; + + port = adb_get_emulator_console_port(); + if (port < 0) { + if (port == -2) + fprintf(stderr, "error: more than one emulator detected. use -s option\n"); + else + fprintf(stderr, "error: no emulator detected\n"); + return -1; + } + fd = socket_loopback_client( port, SOCK_STREAM ); + if (fd < 0) { + fprintf(stderr, "error: could not connect to TCP port %d\n", port); + return -1; + } + return fd; +} + + +int adb_send_emulator_command(int argc, char** argv) +{ + int fd, nn; + + fd = connect_to_console(); + if (fd < 0) + return 1; + +#define QUIT "quit\n" + + for (nn = 1; nn < argc; nn++) { + adb_write( fd, argv[nn], strlen(argv[nn]) ); + adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 ); + } + adb_write( fd, QUIT, sizeof(QUIT)-1 ); + adb_close(fd); + + return 0; +} diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c new file mode 100644 index 0000000..4e6d385 --- /dev/null +++ b/adb/file_sync_client.c @@ -0,0 +1,1022 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <time.h> +#include <dirent.h> +#include <limits.h> +#include <sys/types.h> +#include <zipfile/zipfile.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_client.h" +#include "file_sync_service.h" + + +static unsigned total_bytes; +static long long start_time; + +static long long NOW() +{ + struct timeval tv; + gettimeofday(&tv, 0); + return ((long long) tv.tv_usec) + + 1000000LL * ((long long) tv.tv_sec); +} + +static void BEGIN() +{ + total_bytes = 0; + start_time = NOW(); +} + +static void END() +{ + long long t = NOW() - start_time; + if(total_bytes == 0) return; + + if (t == 0) /* prevent division by 0 :-) */ + t = 1000000; + + fprintf(stderr,"%lld KB/s (%d bytes in %lld.%03llds)\n", + ((((long long) total_bytes) * 1000000LL) / t) / 1024LL, + total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); +} + +void sync_quit(int fd) +{ + syncmsg msg; + + msg.req.id = ID_QUIT; + msg.req.namelen = 0; + + writex(fd, &msg.req, sizeof(msg.req)); +} + +typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie); + +int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie) +{ + syncmsg msg; + char buf[257]; + int len; + + len = strlen(path); + if(len > 1024) goto fail; + + msg.req.id = ID_LIST; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + goto fail; + } + + for(;;) { + if(readx(fd, &msg.dent, sizeof(msg.dent))) break; + if(msg.dent.id == ID_DONE) return 0; + if(msg.dent.id != ID_DENT) break; + + len = ltohl(msg.dent.namelen); + if(len > 256) break; + + if(readx(fd, buf, len)) break; + buf[len] = 0; + + func(ltohl(msg.dent.mode), + ltohl(msg.dent.size), + ltohl(msg.dent.time), + buf, cookie); + } + +fail: + adb_close(fd); + return -1; +} + +typedef struct syncsendbuf syncsendbuf; + +struct syncsendbuf { + unsigned id; + unsigned size; + char data[SYNC_DATA_MAX]; +}; + +static syncsendbuf send_buffer; + +int sync_readtime(int fd, const char *path, unsigned *timestamp) +{ + syncmsg msg; + int len = strlen(path); + + msg.req.id = ID_STAT; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + return -1; + } + + if(readx(fd, &msg.stat, sizeof(msg.stat))) { + return -1; + } + + if(msg.stat.id != ID_STAT) { + return -1; + } + + *timestamp = ltohl(msg.stat.time); + return 0; +} + +static int sync_start_readtime(int fd, const char *path) +{ + syncmsg msg; + int len = strlen(path); + + msg.req.id = ID_STAT; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + return -1; + } + + return 0; +} + +static int sync_finish_readtime(int fd, unsigned int *timestamp, + unsigned int *mode, unsigned int *size) +{ + syncmsg msg; + + if(readx(fd, &msg.stat, sizeof(msg.stat))) + return -1; + + if(msg.stat.id != ID_STAT) + return -1; + + *timestamp = ltohl(msg.stat.time); + *mode = ltohl(msg.stat.mode); + *size = ltohl(msg.stat.size); + + return 0; +} + +int sync_readmode(int fd, const char *path, unsigned *mode) +{ + syncmsg msg; + int len = strlen(path); + + msg.req.id = ID_STAT; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + return -1; + } + + if(readx(fd, &msg.stat, sizeof(msg.stat))) { + return -1; + } + + if(msg.stat.id != ID_STAT) { + return -1; + } + + *mode = ltohl(msg.stat.mode); + return 0; +} + +static int write_data_file(int fd, const char *path, syncsendbuf *sbuf) +{ + int lfd, err = 0; + + lfd = adb_open(path, O_RDONLY); + if(lfd < 0) { + fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno)); + return -1; + } + + sbuf->id = ID_DATA; + for(;;) { + int ret; + + ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX); + if(!ret) + break; + + if(ret < 0) { + if(errno == EINTR) + continue; + fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno)); + break; + } + + sbuf->size = htoll(ret); + if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){ + err = -1; + break; + } + total_bytes += ret; + } + + adb_close(lfd); + return err; +} + +static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf) +{ + int err = 0; + int total = 0; + + sbuf->id = ID_DATA; + while (total < size) { + int count = size - total; + if (count > SYNC_DATA_MAX) { + count = SYNC_DATA_MAX; + } + + memcpy(sbuf->data, &file_buffer[total], count); + sbuf->size = htoll(count); + if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){ + err = -1; + break; + } + total += count; + total_bytes += count; + } + + return err; +} + +#ifdef HAVE_SYMLINKS +static int write_data_link(int fd, const char *path, syncsendbuf *sbuf) +{ + int len, ret; + + len = readlink(path, sbuf->data, SYNC_DATA_MAX-1); + if(len < 0) { + fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno)); + return -1; + } + sbuf->data[len] = '\0'; + + sbuf->size = htoll(len + 1); + sbuf->id = ID_DATA; + + ret = writex(fd, sbuf, sizeof(unsigned) * 2 + len + 1); + if(ret) + return -1; + + total_bytes += len + 1; + + return 0; +} +#endif + +static int sync_send(int fd, const char *lpath, const char *rpath, + unsigned mtime, mode_t mode, int verifyApk) +{ + syncmsg msg; + int len, r; + syncsendbuf *sbuf = &send_buffer; + char* file_buffer = NULL; + int size = 0; + char tmp[64]; + + len = strlen(rpath); + if(len > 1024) goto fail; + + snprintf(tmp, sizeof(tmp), ",%d", mode); + r = strlen(tmp); + + if (verifyApk) { + int lfd; + zipfile_t zip; + zipentry_t entry; + int amt; + + // if we are transferring an APK file, then sanity check to make sure + // we have a real zip file that contains an AndroidManifest.xml + // this requires that we read the entire file into memory. + lfd = adb_open(lpath, O_RDONLY); + if(lfd < 0) { + fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno)); + return -1; + } + + size = adb_lseek(lfd, 0, SEEK_END); + if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) { + fprintf(stderr, "error seeking in file '%s'\n", lpath); + adb_close(lfd); + return 1; + } + + file_buffer = (char *)malloc(size); + if (file_buffer == NULL) { + fprintf(stderr, "could not allocate buffer for '%s'\n", + lpath); + adb_close(lfd); + return 1; + } + amt = adb_read(lfd, file_buffer, size); + if (amt != size) { + fprintf(stderr, "error reading from file: '%s'\n", lpath); + adb_close(lfd); + free(file_buffer); + return 1; + } + + adb_close(lfd); + + zip = init_zipfile(file_buffer, size); + if (zip == NULL) { + fprintf(stderr, "file '%s' is not a valid zip file\n", + lpath); + free(file_buffer); + return 1; + } + + entry = lookup_zipentry(zip, "AndroidManifest.xml"); + release_zipfile(zip); + if (entry == NULL) { + fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n", + lpath); + free(file_buffer); + return 1; + } + } + + msg.req.id = ID_SEND; + msg.req.namelen = htoll(len + r); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, rpath, len) || writex(fd, tmp, r)) { + free(file_buffer); + goto fail; + } + + if (file_buffer) { + write_data_buffer(fd, file_buffer, size, sbuf); + free(file_buffer); + } else if (S_ISREG(mode)) + write_data_file(fd, lpath, sbuf); +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(mode)) + write_data_link(fd, lpath, sbuf); +#endif + else + goto fail; + + msg.data.id = ID_DONE; + msg.data.size = htoll(mtime); + if(writex(fd, &msg.data, sizeof(msg.data))) + goto fail; + + if(readx(fd, &msg.status, sizeof(msg.status))) + return -1; + + if(msg.status.id != ID_OKAY) { + if(msg.status.id == ID_FAIL) { + len = ltohl(msg.status.msglen); + if(len > 256) len = 256; + if(readx(fd, sbuf->data, len)) { + return -1; + } + sbuf->data[len] = 0; + } else + strcpy(sbuf->data, "unknown reason"); + + fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data); + return -1; + } + + return 0; + +fail: + fprintf(stderr,"protocol failure\n"); + adb_close(fd); + return -1; +} + +static int mkdirs(char *name) +{ + int ret; + char *x = name + 1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(name, 0775); + *x = OS_PATH_SEPARATOR; + if((ret < 0) && (errno != EEXIST)) { + return ret; + } + x++; + } + return 0; +} + +int sync_recv(int fd, const char *rpath, const char *lpath) +{ + syncmsg msg; + int len; + int lfd = -1; + char *buffer = send_buffer.data; + unsigned id; + + len = strlen(rpath); + if(len > 1024) return -1; + + msg.req.id = ID_RECV; + msg.req.namelen = htoll(len); + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, rpath, len)) { + return -1; + } + + if(readx(fd, &msg.data, sizeof(msg.data))) { + return -1; + } + id = msg.data.id; + + if((id == ID_DATA) || (id == ID_DONE)) { + adb_unlink(lpath); + mkdirs((char *)lpath); + lfd = adb_creat(lpath, 0644); + if(lfd < 0) { + fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno)); + return -1; + } + goto handle_data; + } else { + goto remote_error; + } + + for(;;) { + if(readx(fd, &msg.data, sizeof(msg.data))) { + return -1; + } + id = msg.data.id; + + handle_data: + len = ltohl(msg.data.size); + if(id == ID_DONE) break; + if(id != ID_DATA) goto remote_error; + if(len > SYNC_DATA_MAX) { + fprintf(stderr,"data overrun\n"); + adb_close(lfd); + return -1; + } + + if(readx(fd, buffer, len)) { + adb_close(lfd); + return -1; + } + + if(writex(lfd, buffer, len)) { + fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno)); + adb_close(lfd); + return -1; + } + + total_bytes += len; + } + + adb_close(lfd); + return 0; + +remote_error: + adb_close(lfd); + adb_unlink(lpath); + + if(id == ID_FAIL) { + len = ltohl(msg.data.size); + if(len > 256) len = 256; + if(readx(fd, buffer, len)) { + return -1; + } + buffer[len] = 0; + } else { + memcpy(buffer, &id, 4); + buffer[4] = 0; +// strcpy(buffer,"unknown reason"); + } + fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer); + return 0; +} + + + +/* --- */ + + +static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time, + const char *name, void *cookie) +{ + printf("%08x %08x %08x %s\n", mode, size, time, name); +} + +int do_sync_ls(const char *path) +{ + int fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(sync_ls(fd, path, do_sync_ls_cb, 0)) { + return 1; + } else { + sync_quit(fd); + return 0; + } +} + +typedef struct copyinfo copyinfo; + +struct copyinfo +{ + copyinfo *next; + const char *src; + const char *dst; + unsigned int time; + unsigned int mode; + unsigned int size; + int flag; + //char data[0]; +}; + +copyinfo *mkcopyinfo(const char *spath, const char *dpath, + const char *name, int isdir) +{ + int slen = strlen(spath); + int dlen = strlen(dpath); + int nlen = strlen(name); + int ssize = slen + nlen + 2; + int dsize = dlen + nlen + 2; + + copyinfo *ci = malloc(sizeof(copyinfo) + ssize + dsize); + if(ci == 0) { + fprintf(stderr,"out of memory\n"); + abort(); + } + + ci->next = 0; + ci->time = 0; + ci->mode = 0; + ci->size = 0; + ci->flag = 0; + ci->src = (const char*)(ci + 1); + ci->dst = ci->src + ssize; + snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name); + snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name); + +// fprintf(stderr,"mkcopyinfo('%s','%s')\n", ci->src, ci->dst); + return ci; +} + + +static int local_build_list(copyinfo **filelist, + const char *lpath, const char *rpath) +{ + DIR *d; + struct dirent *de; + struct stat st; + copyinfo *dirlist = 0; + copyinfo *ci, *next; + +// fprintf(stderr,"local_build_list('%s','%s')\n", lpath, rpath); + + d = opendir(lpath); + if(d == 0) { + fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno)); + return -1; + } + + while((de = readdir(d))) { + char stat_path[PATH_MAX]; + char *name = de->d_name; + + if(name[0] == '.') { + if(name[1] == 0) continue; + if((name[1] == '.') && (name[2] == 0)) continue; + } + + /* + * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs + * always returns DT_UNKNOWN, so we just use stat() for all cases. + */ + if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) + continue; + strcpy(stat_path, lpath); + strcat(stat_path, de->d_name); + stat(stat_path, &st); + + if (S_ISDIR(st.st_mode)) { + ci = mkcopyinfo(lpath, rpath, name, 1); + ci->next = dirlist; + dirlist = ci; + } else { + ci = mkcopyinfo(lpath, rpath, name, 0); + if(lstat(ci->src, &st)) { + closedir(d); + fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); + return -1; + } + if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + fprintf(stderr, "skipping special file '%s'\n", ci->src); + free(ci); + } else { + ci->time = st.st_mtime; + ci->mode = st.st_mode; + ci->size = st.st_size; + ci->next = *filelist; + *filelist = ci; + } + } + } + + closedir(d); + + for(ci = dirlist; ci != 0; ci = next) { + next = ci->next; + local_build_list(filelist, ci->src, ci->dst); + free(ci); + } + + return 0; +} + + +static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps) +{ + copyinfo *filelist = 0; + copyinfo *ci, *next; + int pushed = 0; + int skipped = 0; + + if((lpath[0] == 0) || (rpath[0] == 0)) return -1; + if(lpath[strlen(lpath) - 1] != '/') { + int tmplen = strlen(lpath)+2; + char *tmp = malloc(tmplen); + if(tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/",lpath); + lpath = tmp; + } + if(rpath[strlen(rpath) - 1] != '/') { + int tmplen = strlen(rpath)+2; + char *tmp = malloc(tmplen); + if(tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/",rpath); + rpath = tmp; + } + + if(local_build_list(&filelist, lpath, rpath)) { + return -1; + } + + if(checktimestamps){ + for(ci = filelist; ci != 0; ci = ci->next) { + if(sync_start_readtime(fd, ci->dst)) { + return 1; + } + } + for(ci = filelist; ci != 0; ci = ci->next) { + unsigned int timestamp, mode, size; + if(sync_finish_readtime(fd, ×tamp, &mode, &size)) + return 1; + if(size == ci->size) { + /* for links, we cannot update the atime/mtime */ + if((S_ISREG(ci->mode & mode) && timestamp == ci->time) || + (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) + ci->flag = 1; + } + } + } + for(ci = filelist; ci != 0; ci = next) { + next = ci->next; + if(ci->flag == 0) { + fprintf(stderr,"push: %s -> %s\n", ci->src, ci->dst); + if(sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){ + return 1; + } + pushed++; + } else { + skipped++; + } + free(ci); + } + + fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n", + pushed, (pushed == 1) ? "" : "s", + skipped, (skipped == 1) ? "" : "s"); + + return 0; +} + + +int do_sync_push(const char *lpath, const char *rpath, int verifyApk) +{ + struct stat st; + unsigned mode; + int fd; + + fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(stat(lpath, &st)) { + fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno)); + sync_quit(fd); + return 1; + } + + if(S_ISDIR(st.st_mode)) { + BEGIN(); + if(copy_local_dir_remote(fd, lpath, rpath, 0)) { + return 1; + } else { + END(); + sync_quit(fd); + } + } else { + if(sync_readmode(fd, rpath, &mode)) { + return 1; + } + if((mode != 0) && S_ISDIR(mode)) { + /* if we're copying a local file to a remote directory, + ** we *really* want to copy to remotedir + "/" + localfilename + */ + const char *name = adb_dirstop(lpath); + if(name == 0) { + name = lpath; + } else { + name++; + } + int tmplen = strlen(name) + strlen(rpath) + 2; + char *tmp = malloc(strlen(name) + strlen(rpath) + 2); + if(tmp == 0) return 1; + snprintf(tmp, tmplen, "%s/%s", rpath, name); + rpath = tmp; + } + BEGIN(); + if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) { + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } + } + + return 0; +} + + +typedef struct { + copyinfo **filelist; + copyinfo **dirlist; + const char *rpath; + const char *lpath; +} sync_ls_build_list_cb_args; + +void +sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time, + const char *name, void *cookie) +{ + sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie; + copyinfo *ci; + + if (S_ISDIR(mode)) { + copyinfo **dirlist = args->dirlist; + + /* Don't try recursing down "." or ".." */ + if (name[0] == '.') { + if (name[1] == '\0') return; + if ((name[1] == '.') && (name[2] == '\0')) return; + } + + ci = mkcopyinfo(args->rpath, args->lpath, name, 1); + ci->next = *dirlist; + *dirlist = ci; + } else if (S_ISREG(mode) || S_ISLNK(mode)) { + copyinfo **filelist = args->filelist; + + ci = mkcopyinfo(args->rpath, args->lpath, name, 0); + ci->time = time; + ci->mode = mode; + ci->size = size; + ci->next = *filelist; + *filelist = ci; + } else { + fprintf(stderr, "skipping special file '%s'\n", name); + } +} + +static int remote_build_list(int syncfd, copyinfo **filelist, + const char *rpath, const char *lpath) +{ + copyinfo *dirlist = NULL; + sync_ls_build_list_cb_args args; + + args.filelist = filelist; + args.dirlist = &dirlist; + args.rpath = rpath; + args.lpath = lpath; + + /* Put the files/dirs in rpath on the lists. */ + if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) { + return 1; + } + + /* Recurse into each directory we found. */ + while (dirlist != NULL) { + copyinfo *next = dirlist->next; + if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) { + return 1; + } + free(dirlist); + dirlist = next; + } + + return 0; +} + +static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, + int checktimestamps) +{ + copyinfo *filelist = 0; + copyinfo *ci, *next; + int pulled = 0; + int skipped = 0; + + /* Make sure that both directory paths end in a slash. */ + if (rpath[0] == 0 || lpath[0] == 0) return -1; + if (rpath[strlen(rpath) - 1] != '/') { + int tmplen = strlen(rpath) + 2; + char *tmp = malloc(tmplen); + if (tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/", rpath); + rpath = tmp; + } + if (lpath[strlen(lpath) - 1] != '/') { + int tmplen = strlen(lpath) + 2; + char *tmp = malloc(tmplen); + if (tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/", lpath); + lpath = tmp; + } + + fprintf(stderr, "pull: building file list...\n"); + /* Recursively build the list of files to copy. */ + if (remote_build_list(fd, &filelist, rpath, lpath)) { + return -1; + } + +#if 0 + if (checktimestamps) { + for (ci = filelist; ci != 0; ci = ci->next) { + if (sync_start_readtime(fd, ci->dst)) { + return 1; + } + } + for (ci = filelist; ci != 0; ci = ci->next) { + unsigned int timestamp, mode, size; + if (sync_finish_readtime(fd, ×tamp, &mode, &size)) + return 1; + if (size == ci->size) { + /* for links, we cannot update the atime/mtime */ + if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) || + (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) + ci->flag = 1; + } + } + } +#endif + for (ci = filelist; ci != 0; ci = next) { + next = ci->next; + if (ci->flag == 0) { + fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst); + if (sync_recv(fd, ci->src, ci->dst)) { + return 1; + } + pulled++; + } else { + skipped++; + } + free(ci); + } + + fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n", + pulled, (pulled == 1) ? "" : "s", + skipped, (skipped == 1) ? "" : "s"); + + return 0; +} + +int do_sync_pull(const char *rpath, const char *lpath) +{ + unsigned mode; + struct stat st; + + int fd; + + fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(sync_readmode(fd, rpath, &mode)) { + return 1; + } + if(mode == 0) { + fprintf(stderr,"remote object '%s' does not exist\n", rpath); + return 1; + } + + if(S_ISREG(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { + if(stat(lpath, &st) == 0) { + if(S_ISDIR(st.st_mode)) { + /* if we're copying a remote file to a local directory, + ** we *really* want to copy to localdir + "/" + remotefilename + */ + const char *name = adb_dirstop(rpath); + if(name == 0) { + name = rpath; + } else { + name++; + } + int tmplen = strlen(name) + strlen(lpath) + 2; + char *tmp = malloc(tmplen); + if(tmp == 0) return 1; + snprintf(tmp, tmplen, "%s/%s", lpath, name); + lpath = tmp; + } + } + BEGIN(); + if(sync_recv(fd, rpath, lpath)) { + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } + } else if(S_ISDIR(mode)) { + BEGIN(); + if (copy_remote_dir_local(fd, rpath, lpath, 0)) { + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } + } else { + fprintf(stderr,"remote object '%s' not a file or directory\n", rpath); + return 1; + } +} + +int do_sync_sync(const char *lpath, const char *rpath) +{ + fprintf(stderr,"syncing %s...\n",rpath); + + int fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + BEGIN(); + if(copy_local_dir_remote(fd, lpath, rpath, 1)){ + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } +} diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c new file mode 100644 index 0000000..a231e93 --- /dev/null +++ b/adb/file_sync_service.c @@ -0,0 +1,412 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <utime.h> + +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SYNC +#include "adb.h" +#include "file_sync_service.h" + +static int mkdirs(char *name) +{ + int ret; + char *x = name + 1; + + if(name[0] != '/') return -1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(name, 0775); + if((ret < 0) && (errno != EEXIST)) { + D("mkdir(\"%s\") -> %s\n", name, strerror(errno)); + *x = '/'; + return ret; + } + *x++ = '/'; + } + return 0; +} + +static int do_stat(int s, const char *path) +{ + syncmsg msg; + struct stat st; + + msg.stat.id = ID_STAT; + + if(lstat(path, &st)) { + msg.stat.mode = 0; + msg.stat.size = 0; + msg.stat.time = 0; + } else { + msg.stat.mode = htoll(st.st_mode); + msg.stat.size = htoll(st.st_size); + msg.stat.time = htoll(st.st_mtime); + } + + return writex(s, &msg.stat, sizeof(msg.stat)); +} + +static int do_list(int s, const char *path) +{ + DIR *d; + struct dirent *de; + struct stat st; + syncmsg msg; + int len; + + char tmp[1024 + 256 + 1]; + char *fname; + + len = strlen(path); + memcpy(tmp, path, len); + tmp[len] = '/'; + fname = tmp + len + 1; + + msg.dent.id = ID_DENT; + + d = opendir(path); + if(d == 0) goto done; + + while((de = readdir(d))) { + int len = strlen(de->d_name); + + /* not supposed to be possible, but + if it does happen, let's not buffer overrun */ + if(len > 256) continue; + + strcpy(fname, de->d_name); + if(lstat(tmp, &st) == 0) { + msg.dent.mode = htoll(st.st_mode); + msg.dent.size = htoll(st.st_size); + msg.dent.time = htoll(st.st_mtime); + msg.dent.namelen = htoll(len); + + if(writex(s, &msg.dent, sizeof(msg.dent)) || + writex(s, de->d_name, len)) { + return -1; + } + } + } + + closedir(d); + +done: + msg.dent.id = ID_DONE; + msg.dent.mode = 0; + msg.dent.size = 0; + msg.dent.time = 0; + msg.dent.namelen = 0; + return writex(s, &msg.dent, sizeof(msg.dent)); +} + +static int fail_message(int s, const char *reason) +{ + syncmsg msg; + int len = strlen(reason); + + D("sync: failure: %s\n", reason); + + msg.data.id = ID_FAIL; + msg.data.size = htoll(len); + if(writex(s, &msg.data, sizeof(msg.data)) || + writex(s, reason, len)) { + return -1; + } else { + return 0; + } +} + +static int fail_errno(int s) +{ + return fail_message(s, strerror(errno)); +} + +static int handle_send_file(int s, char *path, mode_t mode, char *buffer) +{ + syncmsg msg; + unsigned int timestamp = 0; + int fd; + + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + if(fd < 0 && errno == ENOENT) { + mkdirs(path); + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + } + if(fd < 0 && errno == EEXIST) { + fd = adb_open_mode(path, O_WRONLY, mode); + } + if(fd < 0) { + if(fail_errno(s)) + return -1; + fd = -1; + } + + for(;;) { + unsigned int len; + + if(readx(s, &msg.data, sizeof(msg.data))) + goto fail; + + if(msg.data.id != ID_DATA) { + if(msg.data.id == ID_DONE) { + timestamp = ltohl(msg.data.size); + break; + } + fail_message(s, "invalid data message"); + goto fail; + } + len = ltohl(msg.data.size); + if(len > SYNC_DATA_MAX) { + fail_message(s, "oversize data message"); + goto fail; + } + if(readx(s, buffer, len)) + goto fail; + + if(fd < 0) + continue; + if(writex(fd, buffer, len)) { + adb_close(fd); + adb_unlink(path); + fd = -1; + if(fail_errno(s)) return -1; + } + } + + if(fd >= 0) { + struct utimbuf u; + adb_close(fd); + u.actime = timestamp; + u.modtime = timestamp; + utime(path, &u); + + msg.status.id = ID_OKAY; + msg.status.msglen = 0; + if(writex(s, &msg.status, sizeof(msg.status))) + return -1; + } + return 0; + +fail: + if(fd >= 0) + adb_close(fd); + adb_unlink(path); + return -1; +} + +#ifdef HAVE_SYMLINKS +static int handle_send_link(int s, char *path, char *buffer) +{ + syncmsg msg; + unsigned int len; + int ret; + + if(readx(s, &msg.data, sizeof(msg.data))) + return -1; + + if(msg.data.id != ID_DATA) { + fail_message(s, "invalid data message: expected ID_DATA"); + return -1; + } + + len = ltohl(msg.data.size); + if(len > SYNC_DATA_MAX) { + fail_message(s, "oversize data message"); + return -1; + } + if(readx(s, buffer, len)) + return -1; + + ret = symlink(buffer, path); + if(ret && errno == ENOENT) { + mkdirs(path); + ret = symlink(buffer, path); + } + if(ret) { + fail_errno(s); + return -1; + } + + if(readx(s, &msg.data, sizeof(msg.data))) + return -1; + + if(msg.data.id == ID_DONE) { + msg.status.id = ID_OKAY; + msg.status.msglen = 0; + if(writex(s, &msg.status, sizeof(msg.status))) + return -1; + } else { + fail_message(s, "invalid data message: expected ID_DONE"); + return -1; + } + + return 0; +} +#endif /* HAVE_SYMLINKS */ + +static int do_send(int s, char *path, char *buffer) +{ + char *tmp; + mode_t mode; + int is_link, ret; + + tmp = strrchr(path,','); + if(tmp) { + *tmp = 0; + errno = 0; + mode = strtoul(tmp + 1, NULL, 0); +#ifndef HAVE_SYMLINKS + is_link = 0; +#else + is_link = S_ISLNK(mode); +#endif + mode &= 0777; + } + if(!tmp || errno) { + mode = 0644; + is_link = 0; + } + + adb_unlink(path); + + +#ifdef HAVE_SYMLINKS + if(is_link) + ret = handle_send_link(s, path, buffer); + else { +#else + { +#endif + /* copy user permission bits to "group" and "other" permissions */ + mode |= ((mode >> 3) & 0070); + mode |= ((mode >> 3) & 0007); + + ret = handle_send_file(s, path, mode, buffer); + } + + return ret; +} + +static int do_recv(int s, const char *path, char *buffer) +{ + syncmsg msg; + int fd, r; + + fd = adb_open(path, O_RDONLY); + if(fd < 0) { + if(fail_errno(s)) return -1; + return 0; + } + + msg.data.id = ID_DATA; + for(;;) { + r = adb_read(fd, buffer, SYNC_DATA_MAX); + if(r <= 0) { + if(r == 0) break; + if(errno == EINTR) continue; + r = fail_errno(s); + adb_close(fd); + return r; + } + msg.data.size = htoll(r); + if(writex(s, &msg.data, sizeof(msg.data)) || + writex(s, buffer, r)) { + adb_close(fd); + return -1; + } + } + + adb_close(fd); + + msg.data.id = ID_DONE; + msg.data.size = 0; + if(writex(s, &msg.data, sizeof(msg.data))) { + return -1; + } + + return 0; +} + +void file_sync_service(int fd, void *cookie) +{ + syncmsg msg; + char name[1025]; + unsigned namelen; + + char *buffer = malloc(SYNC_DATA_MAX); + if(buffer == 0) goto fail; + + for(;;) { + D("sync: waiting for command\n"); + + if(readx(fd, &msg.req, sizeof(msg.req))) { + fail_message(fd, "command read failure"); + break; + } + namelen = ltohl(msg.req.namelen); + if(namelen > 1024) { + fail_message(fd, "invalid namelen"); + break; + } + if(readx(fd, name, namelen)) { + fail_message(fd, "filename read failure"); + break; + } + name[namelen] = 0; + + msg.req.namelen = 0; + D("sync: '%s' '%s'\n", (char*) &msg.req, name); + + switch(msg.req.id) { + case ID_STAT: + if(do_stat(fd, name)) goto fail; + break; + case ID_LIST: + if(do_list(fd, name)) goto fail; + break; + case ID_SEND: + if(do_send(fd, name, buffer)) goto fail; + break; + case ID_RECV: + if(do_recv(fd, name, buffer)) goto fail; + break; + case ID_QUIT: + goto fail; + default: + fail_message(fd, "unknown command"); + goto fail; + } + } + +fail: + if(buffer != 0) free(buffer); + D("sync: done\n"); + adb_close(fd); +} diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h new file mode 100644 index 0000000..4ee40ba --- /dev/null +++ b/adb/file_sync_service.h @@ -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. + */ + +#ifndef _FILE_SYNC_SERVICE_H_ +#define _FILE_SYNC_SERVICE_H_ + +#ifdef __ppc__ +static inline unsigned __swap_uint32(unsigned x) +{ + return (((x) & 0xFF000000) >> 24) + | (((x) & 0x00FF0000) >> 8) + | (((x) & 0x0000FF00) << 8) + | (((x) & 0x000000FF) << 24); +} +#define htoll(x) __swap_uint32(x) +#define ltohl(x) __swap_uint32(x) +#define MKID(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24)) +#else +#define htoll(x) (x) +#define ltohl(x) (x) +#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) +#endif + +#define ID_STAT MKID('S','T','A','T') +#define ID_LIST MKID('L','I','S','T') +#define ID_ULNK MKID('U','L','N','K') +#define ID_SEND MKID('S','E','N','D') +#define ID_RECV MKID('R','E','C','V') +#define ID_DENT MKID('D','E','N','T') +#define ID_DONE MKID('D','O','N','E') +#define ID_DATA MKID('D','A','T','A') +#define ID_OKAY MKID('O','K','A','Y') +#define ID_FAIL MKID('F','A','I','L') +#define ID_QUIT MKID('Q','U','I','T') + +typedef union { + unsigned id; + struct { + unsigned id; + unsigned namelen; + } req; + struct { + unsigned id; + unsigned mode; + unsigned size; + unsigned time; + } stat; + struct { + unsigned id; + unsigned mode; + unsigned size; + unsigned time; + unsigned namelen; + } dent; + struct { + unsigned id; + unsigned size; + } data; + struct { + unsigned id; + unsigned msglen; + } status; +} syncmsg; + + +void file_sync_service(int fd, void *cookie); +int do_sync_ls(const char *path); +int do_sync_push(const char *lpath, const char *rpath, int verifyApk); +int do_sync_sync(const char *lpath, const char *rpath); +int do_sync_pull(const char *rpath, const char *lpath); + +#define SYNC_DATA_MAX (64*1024) + +#endif diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c new file mode 100644 index 0000000..0de0dd5 --- /dev/null +++ b/adb/framebuffer_service.c @@ -0,0 +1,70 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> + +#include <cutils/fdevent.h> +#include "adb.h" + +#include <linux/fb.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +/* TODO: +** - grab the current buffer, not the first buffer +** - sync with vsync to avoid tearing +*/ + +void framebuffer_service(int fd, void *cookie) +{ + struct fb_var_screeninfo vinfo; + int fb; + void *ptr = MAP_FAILED; + char x; + + unsigned fbinfo[4]; + + fb = open("/dev/graphics/fb0", O_RDONLY); + if(fb < 0) goto done; + + if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done; + fcntl(fb, F_SETFD, FD_CLOEXEC); + + fbinfo[0] = 16; + fbinfo[1] = vinfo.xres * vinfo.yres * 2; + fbinfo[2] = vinfo.xres; + fbinfo[3] = vinfo.yres; + + ptr = mmap(0, fbinfo[1], PROT_READ, MAP_SHARED, fb, 0); + if(ptr == MAP_FAILED) goto done; + + if(writex(fd, fbinfo, sizeof(unsigned) * 4)) goto done; + + for(;;) { + if(readx(fd, &x, 1)) goto done; + if(writex(fd, ptr, fbinfo[1])) goto done; + } + +done: + if(ptr != MAP_FAILED) munmap(ptr, fbinfo[1]); + if(fb >= 0) close(fb); + close(fd); +} + diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c new file mode 100644 index 0000000..00dfee4 --- /dev/null +++ b/adb/get_my_path_darwin.c @@ -0,0 +1,31 @@ +/* + * 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 <utils/executablepath.h> +#import <Carbon/Carbon.h> +#include <unistd.h> + +void get_my_path(char s[PATH_MAX]) +{ + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8); +} + diff --git a/adb/get_my_path_linux.c b/adb/get_my_path_linux.c new file mode 100644 index 0000000..f516e59 --- /dev/null +++ b/adb/get_my_path_linux.c @@ -0,0 +1,33 @@ +/* + * 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 <sys/types.h> +#include <unistd.h> +#include <limits.h> +#include <stdio.h> + +void get_my_path(char exe[PATH_MAX]) +{ + char proc[64]; + snprintf(proc, sizeof proc, "/proc/%d/exe", getpid()); + int err = readlink(proc, exe, PATH_MAX - 1); + if(err > 0) { + exe[err] = 0; + } else { + exe[0] = 0; + } +} + diff --git a/adb/get_my_path_windows.c b/adb/get_my_path_windows.c new file mode 100644 index 0000000..fc7143c --- /dev/null +++ b/adb/get_my_path_windows.c @@ -0,0 +1,31 @@ +/* + * 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 <limits.h> +#include <assert.h> +#include <windows.h> + +void get_my_path(char exe[PATH_MAX]) +{ + char* r; + + GetModuleFileName( NULL, exe, PATH_MAX-1 ); + exe[PATH_MAX-1] = 0; + r = strrchr( exe, '\\' ); + if (r) + *r = 0; +} + diff --git a/adb/history.h b/adb/history.h new file mode 100755 index 0000000..ef86ad9 --- /dev/null +++ b/adb/history.h @@ -0,0 +1,13 @@ +#ifndef _HISTORY_H_
+#define _HISTORY_H_
+
+#define SH_ARROW_ANY "\x1b\x5b"
+#define SH_ARROW_UP '\x41'
+#define SH_ARROW_DOWN '\x42'
+#define SH_ARROW_RIGHT '\x43'
+#define SH_ARROW_LEFT '\x44'
+#define SH_DEL_CHAR '\x7F'
+#define SH_BLANK_CHAR '\x20'
+
+#endif
+
diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c new file mode 100644 index 0000000..ae7f12d --- /dev/null +++ b/adb/jdwp_service.c @@ -0,0 +1,709 @@ +/* implement the "debug-ports" and "track-debug-ports" device services */ +#include "sysdeps.h" +#define TRACE_TAG TRACE_JDWP +#include "adb.h" +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/* here's how these things work. + + when adbd starts, it creates a unix server socket + named @vm-debug-control (@ is a shortcut for "first byte is zero" + to use the private namespace instead of the file system) + + when a new JDWP daemon thread starts in a new VM process, it creates + a connection to @vm-debug-control to announce its availability. + + + JDWP thread @vm-debug-control + | | + |-------------------------------> | + | hello I'm in process <pid> | + | | + | | + + the connection is kept alive. it will be closed automatically if + the JDWP process terminates (this allows adbd to detect dead + processes). + + adbd thus maintains a list of "active" JDWP processes. it can send + its content to clients through the "device:debug-ports" service, + or even updates through the "device:track-debug-ports" service. + + when a debugger wants to connect, it simply runs the command + equivalent to "adb forward tcp:<hostport> jdwp:<pid>" + + "jdwp:<pid>" is a new forward destination format used to target + a given JDWP process on the device. when sutch a request arrives, + adbd does the following: + + - first, it calls socketpair() to create a pair of equivalent + sockets. + + - it attaches the first socket in the pair to a local socket + which is itself attached to the transport's remote socket: + + + - it sends the file descriptor of the second socket directly + to the JDWP process with the help of sendmsg() + + + JDWP thread @vm-debug-control + | | + | <----------------------| + | OK, try this file descriptor | + | | + | | + + then, the JDWP thread uses this new socket descriptor as its + pass-through connection to the debugger (and receives the + JDWP-Handshake message, answers to it, etc...) + + this gives the following graphics: + ____________________________________ + | | + | ADB Server (host) | + | | + Debugger <---> LocalSocket <----> RemoteSocket | + | ^^ | + |___________________________||_______| + || + Transport || + (TCP for emulator - USB for device) || + || + ___________________________||_______ + | || | + | ADBD (device) || | + | VV | + JDWP <======> LocalSocket <----> RemoteSocket | + | | + |____________________________________| + + due to the way adb works, this doesn't need a special socket + type or fancy handling of socket termination if either the debugger + or the JDWP process closes the connection. + + THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN + TO HAVE A BETTER IDEA, LET ME KNOW - Digit + +**********************************************************************/ + +/** JDWP PID List Support Code + ** for each JDWP process, we record its pid and its connected socket + **/ + +#define MAX_OUT_FDS 4 + +#if !ADB_HOST + +#include <sys/socket.h> +#include <sys/un.h> + +typedef struct JdwpProcess JdwpProcess; +struct JdwpProcess { + JdwpProcess* next; + JdwpProcess* prev; + int pid; + int socket; + fdevent* fde; + + char in_buff[4]; /* input character to read PID */ + int in_len; /* number from JDWP process */ + + int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */ + int out_count; /* to send to the JDWP process */ +}; + +static JdwpProcess _jdwp_list; + +static int +jdwp_process_list( char* buffer, int bufferlen ) +{ + char* end = buffer + bufferlen; + char* p = buffer; + JdwpProcess* proc = _jdwp_list.next; + + for ( ; proc != &_jdwp_list; proc = proc->next ) { + int len; + + /* skip transient connections */ + if (proc->pid < 0) + continue; + + len = snprintf(p, end-p, "%d\n", proc->pid); + if (p + len >= end) + break; + p += len; + } + p[0] = 0; + return (p - buffer); +} + + +static int +jdwp_process_list_msg( char* buffer, int bufferlen ) +{ + char head[5]; + int len = jdwp_process_list( buffer+4, bufferlen-4 ); + snprintf(head, sizeof head, "%04x", len); + memcpy(buffer, head, 4); + return len + 4; +} + + +static void jdwp_process_list_updated(void); + +static void +jdwp_process_free( JdwpProcess* proc ) +{ + if (proc) { + int n; + + proc->prev->next = proc->next; + proc->next->prev = proc->prev; + + if (proc->socket >= 0) { + shutdown(proc->socket, SHUT_RDWR); + adb_close(proc->socket); + proc->socket = -1; + } + + if (proc->fde != NULL) { + fdevent_destroy(proc->fde); + proc->fde = NULL; + } + proc->pid = -1; + + for (n = 0; n < proc->out_count; n++) { + adb_close(proc->out_fds[n]); + } + proc->out_count = 0; + + free(proc); + + jdwp_process_list_updated(); + } +} + + +static void jdwp_process_event(int, unsigned, void*); /* forward */ + + +static JdwpProcess* +jdwp_process_alloc( int socket ) +{ + JdwpProcess* proc = calloc(1,sizeof(*proc)); + + if (proc == NULL) { + D("not enough memory to create new JDWP process\n"); + return NULL; + } + + proc->socket = socket; + proc->pid = -1; + proc->next = proc; + proc->prev = proc; + + proc->fde = fdevent_create( socket, jdwp_process_event, proc ); + if (proc->fde == NULL) { + D("could not create fdevent for new JDWP process\n" ); + free(proc); + return NULL; + } + + proc->fde->state |= FDE_DONT_CLOSE; + proc->in_len = 0; + proc->out_count = 0; + + /* append to list */ + proc->next = &_jdwp_list; + proc->prev = proc->next->prev; + + proc->prev->next = proc; + proc->next->prev = proc; + + /* start by waiting for the PID */ + fdevent_add(proc->fde, FDE_READ); + + return proc; +} + + +static void +jdwp_process_event( int socket, unsigned events, void* _proc ) +{ + JdwpProcess* proc = _proc; + + if (events & FDE_READ) { + if (proc->pid < 0) { + /* read the PID as a 4-hexchar string */ + char* p = proc->in_buff + proc->in_len; + int size = 4 - proc->in_len; + char temp[5]; + while (size > 0) { + int len = recv( socket, p, size, 0 ); + if (len < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + return; + /* this can fail here if the JDWP process crashes very fast */ + D("weird unknown JDWP process failure: %s\n", + strerror(errno)); + + goto CloseProcess; + } + if (len == 0) { /* end of stream ? */ + D("weird end-of-stream from unknown JDWP process\n"); + goto CloseProcess; + } + p += len; + proc->in_len += len; + size -= len; + } + /* we have read 4 characters, now decode the pid */ + memcpy(temp, proc->in_buff, 4); + temp[4] = 0; + + if (sscanf( temp, "%04x", &proc->pid ) != 1) { + D("could not decode JDWP %p PID number: '%s'\n", proc, temp); + goto CloseProcess; + } + + /* all is well, keep reading to detect connection closure */ + D("Adding pid %d to jdwp process list\n", proc->pid); + jdwp_process_list_updated(); + } + else + { + /* the pid was read, if we get there it's probably because the connection + * was closed (e.g. the JDWP process exited or crashed) */ + char buf[32]; + + for (;;) { + int len = recv(socket, buf, sizeof(buf), 0); + + if (len <= 0) { + if (len < 0 && errno == EINTR) + continue; + if (len < 0 && errno == EAGAIN) + return; + else { + D("terminating JDWP %d connection: %s\n", proc->pid, + strerror(errno)); + break; + } + } + else { + D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n", + proc->pid, len ); + } + } + + CloseProcess: + if (proc->pid >= 0) + D( "remove pid %d to jdwp process list\n", proc->pid ); + jdwp_process_free(proc); + return; + } + } + + if (events & FDE_WRITE) { + D("trying to write to JDWP pid controli (count=%d first=%d) %d\n", + proc->pid, proc->out_count, proc->out_fds[0]); + if (proc->out_count > 0) { + int fd = proc->out_fds[0]; + int n, ret; + struct cmsghdr* cmsg; + struct msghdr msg; + struct iovec iov; + char dummy = '!'; + char buffer[sizeof(struct cmsghdr) + sizeof(int)]; + + iov.iov_base = &dummy; + iov.iov_len = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = buffer; + msg.msg_controllen = sizeof(buffer); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + ((int*)CMSG_DATA(cmsg))[0] = fd; + + for (;;) { + ret = sendmsg(proc->socket, &msg, 0); + if (ret >= 0) + break; + if (errno == EINTR) + continue; + D("sending new file descriptor to JDWP %d failed: %s\n", + proc->pid, strerror(errno)); + goto CloseProcess; + } + + D("sent file descriptor %d to JDWP process %d\n", + fd, proc->pid); + + for (n = 1; n < proc->out_count; n++) + proc->out_fds[n-1] = proc->out_fds[n]; + + if (--proc->out_count == 0) + fdevent_del( proc->fde, FDE_WRITE ); + } + } +} + + +int +create_jdwp_connection_fd(int pid) +{ + JdwpProcess* proc = _jdwp_list.next; + + D("looking for pid %d in JDWP process list\n", pid); + for ( ; proc != &_jdwp_list; proc = proc->next ) { + if (proc->pid == pid) { + goto FoundIt; + } + } + D("search failed !!\n"); + return -1; + +FoundIt: + { + int fds[2]; + + if (proc->out_count >= MAX_OUT_FDS) { + D("%s: too many pending JDWP connection for pid %d\n", + __FUNCTION__, pid); + return -1; + } + + if (adb_socketpair(fds) < 0) { + D("%s: socket pair creation failed: %s\n", + __FUNCTION__, strerror(errno)); + return -1; + } + + proc->out_fds[ proc->out_count ] = fds[1]; + if (++proc->out_count == 1) + fdevent_add( proc->fde, FDE_WRITE ); + + return fds[0]; + } +} + +/** VM DEBUG CONTROL SOCKET + ** + ** we do implement a custom asocket to receive the data + **/ + +/* name of the debug control Unix socket */ +#define JDWP_CONTROL_NAME "\0jdwp-control" +#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1) + +typedef struct { + int listen_socket; + fdevent* fde; + +} JdwpControl; + + +static void +jdwp_control_event(int s, unsigned events, void* user); + + +static int +jdwp_control_init( JdwpControl* control, + const char* sockname, + int socknamelen ) +{ + struct sockaddr_un addr; + socklen_t addrlen; + int s; + int maxpath = sizeof(addr.sun_path); + int pathlen = socknamelen; + + if (pathlen >= maxpath) { + D( "vm debug control socket name too long (%d extra chars)\n", + pathlen+1-maxpath ); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, sockname, socknamelen); + + s = socket( AF_UNIX, SOCK_STREAM, 0 ); + if (s < 0) { + D( "could not create vm debug control socket. %d: %s\n", + errno, strerror(errno)); + return -1; + } + + addrlen = (pathlen + sizeof(addr.sun_family)); + + if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) { + D( "could not bind vm debug control socket: %d: %s\n", + errno, strerror(errno) ); + adb_close(s); + return -1; + } + + if ( listen(s, 4) < 0 ) { + D("listen failed in jdwp control socket: %d: %s\n", + errno, strerror(errno)); + adb_close(s); + return -1; + } + + control->listen_socket = s; + + control->fde = fdevent_create(s, jdwp_control_event, control); + if (control->fde == NULL) { + D( "could not create fdevent for jdwp control socket\n" ); + adb_close(s); + return -1; + } + + /* only wait for incoming connections */ + fdevent_add(control->fde, FDE_READ); + + D("jdwp control socket started (%d)\n", control->listen_socket); + return 0; +} + + +static void +jdwp_control_event( int s, unsigned events, void* _control ) +{ + JdwpControl* control = (JdwpControl*) _control; + + if (events & FDE_READ) { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int s = -1; + JdwpProcess* proc; + + do { + s = adb_socket_accept( control->listen_socket, &addr, &addrlen ); + if (s < 0) { + if (errno == EINTR) + continue; + if (errno == ECONNABORTED) { + /* oops, the JDWP process died really quick */ + D("oops, the JDWP process died really quick\n"); + return; + } + /* the socket is probably closed ? */ + D( "weird accept() failed on jdwp control socket: %s\n", + strerror(errno) ); + return; + } + } + while (s < 0); + + proc = jdwp_process_alloc( s ); + if (proc == NULL) + return; + } +} + + +static JdwpControl _jdwp_control; + +/** "jdwp" local service implementation + ** this simply returns the list of known JDWP process pids + **/ + +typedef struct { + asocket socket; + int pass; +} JdwpSocket; + +static void +jdwp_socket_close( asocket* s ) +{ + asocket* peer = s->peer; + + remove_socket(s); + + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + free(s); +} + +static int +jdwp_socket_enqueue( asocket* s, apacket* p ) +{ + /* you can't write to this asocket */ + put_apacket(p); + s->peer->close(s->peer); + return -1; +} + + +static void +jdwp_socket_ready( asocket* s ) +{ + JdwpSocket* jdwp = (JdwpSocket*)s; + asocket* peer = jdwp->socket.peer; + + /* on the first call, send the list of pids, + * on the second one, close the connection + */ + if (jdwp->pass == 0) { + apacket* p = get_apacket(); + p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD); + peer->enqueue(peer, p); + jdwp->pass = 1; + } + else { + peer->close(peer); + } +} + +asocket* +create_jdwp_service_socket( void ) +{ + JdwpSocket* s = calloc(sizeof(*s),1); + + if (s == NULL) + return NULL; + + install_local_socket(&s->socket); + + s->socket.ready = jdwp_socket_ready; + s->socket.enqueue = jdwp_socket_enqueue; + s->socket.close = jdwp_socket_close; + s->pass = 0; + + return &s->socket; +} + +/** "track-jdwp" local service implementation + ** this periodically sends the list of known JDWP process pids + ** to the client... + **/ + +typedef struct JdwpTracker JdwpTracker; + +struct JdwpTracker { + asocket socket; + JdwpTracker* next; + JdwpTracker* prev; + int need_update; +}; + +static JdwpTracker _jdwp_trackers_list; + + +static void +jdwp_process_list_updated(void) +{ + char buffer[1024]; + int len; + JdwpTracker* t = _jdwp_trackers_list.next; + + len = jdwp_process_list_msg(buffer, sizeof(buffer)); + + for ( ; t != &_jdwp_trackers_list; t = t->next ) { + apacket* p = get_apacket(); + asocket* peer = t->socket.peer; + memcpy(p->data, buffer, len); + p->len = len; + peer->enqueue( peer, p ); + } +} + +static void +jdwp_tracker_close( asocket* s ) +{ + JdwpTracker* tracker = (JdwpTracker*) s; + asocket* peer = s->peer; + + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + + remove_socket(s); + + tracker->prev->next = tracker->next; + tracker->next->prev = tracker->prev; + + free(s); +} + +static void +jdwp_tracker_ready( asocket* s ) +{ + JdwpTracker* t = (JdwpTracker*) s; + + if (t->need_update) { + apacket* p = get_apacket(); + t->need_update = 0; + p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data)); + s->peer->enqueue(s->peer, p); + } +} + +static int +jdwp_tracker_enqueue( asocket* s, apacket* p ) +{ + /* you can't write to this socket */ + put_apacket(p); + s->peer->close(s->peer); + return -1; +} + + +asocket* +create_jdwp_tracker_service_socket( void ) +{ + JdwpTracker* t = calloc(sizeof(*t),1); + + if (t == NULL) + return NULL; + + t->next = &_jdwp_trackers_list; + t->prev = t->next->prev; + + t->next->prev = t; + t->prev->next = t; + + install_local_socket(&t->socket); + + t->socket.ready = jdwp_tracker_ready; + t->socket.enqueue = jdwp_tracker_enqueue; + t->socket.close = jdwp_tracker_close; + t->need_update = 1; + + return &t->socket; +} + + +int +init_jdwp(void) +{ + _jdwp_list.next = &_jdwp_list; + _jdwp_list.prev = &_jdwp_list; + + _jdwp_trackers_list.next = &_jdwp_trackers_list; + _jdwp_trackers_list.prev = &_jdwp_trackers_list; + + return jdwp_control_init( &_jdwp_control, + JDWP_CONTROL_NAME, + JDWP_CONTROL_NAME_LEN ); +} + +#endif /* !ADB_HOST */ + diff --git a/adb/log_service.c b/adb/log_service.c new file mode 100644 index 0000000..6e9bdee --- /dev/null +++ b/adb/log_service.c @@ -0,0 +1,92 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <cutils/logger.h> +#include "sysdeps.h" +#include "adb.h" + +#define LOG_FILE_DIR "/dev/log/" + +void write_log_entry(int fd, struct logger_entry *buf); + +void log_service(int fd, void *cookie) +{ + /* get the name of the log filepath to read */ + char * log_filepath = cookie; + + /* open the log file. */ + int logfd = unix_open(log_filepath, O_RDONLY); + if (logfd < 0) { + goto done; + } + + // temp buffer to read the entries + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); + struct logger_entry *entry = (struct logger_entry *) buf; + + while (1) { + int ret; + + ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN); + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + // perror("logcat read"); + goto done; + } + else if (!ret) { + // fprintf(stderr, "read: Unexpected EOF!\n"); + goto done; + } + + /* NOTE: driver guarantees we read exactly one full entry */ + + entry->msg[entry->len] = '\0'; + + write_log_entry(fd, entry); + } + +done: + unix_close(fd); + free(log_filepath); +} + +/* returns the full path to the log file in a newly allocated string */ +char * get_log_file_path(const char * log_name) { + char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1); + + strcpy(log_device, LOG_FILE_DIR); + strcat(log_device, log_name); + + return log_device; +} + + +/* prints one log entry into the file descriptor fd */ +void write_log_entry(int fd, struct logger_entry *buf) +{ + size_t size = sizeof(struct logger_entry) + buf->len; + + writex(fd, buf, size); +} diff --git a/adb/mutex_list.h b/adb/mutex_list.h new file mode 100644 index 0000000..eebe0df --- /dev/null +++ b/adb/mutex_list.h @@ -0,0 +1,14 @@ +/* the list of mutexes used by addb */ +#ifndef ADB_MUTEX +#error ADB_MUTEX not defined when including this file +#endif + +ADB_MUTEX(dns_lock) +ADB_MUTEX(socket_list_lock) +ADB_MUTEX(transport_lock) +#if ADB_HOST +ADB_MUTEX(local_transports_lock) +#endif +ADB_MUTEX(usb_lock) + +#undef ADB_MUTEX diff --git a/adb/protocol.txt b/adb/protocol.txt new file mode 100644 index 0000000..d0f307c --- /dev/null +++ b/adb/protocol.txt @@ -0,0 +1,252 @@ + +--- a replacement for aproto ------------------------------------------- + +When it comes down to it, aproto's primary purpose is to forward +various streams between the host computer and client device (in either +direction). + +This replacement further simplifies the concept, reducing the protocol +to an extremely straightforward model optimized to accomplish the +forwarding of these streams and removing additional state or +complexity. + +The host side becomes a simple comms bridge with no "UI", which will +be used by either commandline or interactive tools to communicate with +a device or emulator that is connected to the bridge. + +The protocol is designed to be straightforward and well-defined enough +that if it needs to be reimplemented in another environment (Java +perhaps), there should not problems ensuring perfect interoperability. + +The protocol discards the layering aproto has and should allow the +implementation to be much more robust. + + +--- protocol overview and basics --------------------------------------- + +The transport layer deals in "messages", which consist of a 24 byte +header followed (optionally) by a payload. The header consists of 6 +32 bit words which are sent across the wire in little endian format. + +struct message { + unsigned command; /* command identifier constant */ + unsigned arg0; /* first argument */ + unsigned arg1; /* second argument */ + unsigned data_length; /* length of payload (0 is allowed) */ + unsigned data_crc32; /* crc32 of data payload */ + unsigned magic; /* command ^ 0xffffffff */ +}; + +Receipt of an invalid message header, corrupt message payload, or an +unrecognized command MUST result in the closing of the remote +connection. The protocol depends on shared state and any break in the +message stream will result in state getting out of sync. + +The following sections describe the six defined message types in +detail. Their format is COMMAND(arg0, arg1, payload) where the payload +is represented by a quoted string or an empty string if none should be +sent. + +The identifiers "local-id" and "remote-id" are always relative to the +*sender* of the message, so for a receiver, the meanings are effectively +reversed. + + + +--- CONNECT(version, maxdata, "system-identity-string") ---------------- + +The CONNECT message establishes the presence of a remote system. +The version is used to ensure protocol compatibility and maxdata +declares the maximum message body size that the remote system +is willing to accept. + +Currently, version=0x01000000 and maxdata=4096 + +Both sides send a CONNECT message when the connection between them is +established. Until a CONNECT message is received no other messages may +be sent. Any messages received before a CONNECT message MUST be ignored. + +If a CONNECT message is received with an unknown version or insufficiently +large maxdata value, the connection with the other side must be closed. + +The system identity string should be "<systemtype>:<serialno>:<banner>" +where systemtype is "bootloader", "device", or "host", serialno is some +kind of unique ID (or empty), and banner is a human-readable version +or identifier string (informational only). + + +--- OPEN(local-id, 0, "destination") ----------------------------------- + +The OPEN message informs the recipient that the sender has a stream +identified by local-id that it wishes to connect to the named +destination in the message payload. The local-id may not be zero. + +The OPEN message MUST result in either a READY message indicating that +the connection has been established (and identifying the other end) or +a CLOSE message, indicating failure. An OPEN message also implies +a READY message sent at the same time. + +Common destination naming conventions include: + +* "tcp:<host>:<port>" - host may be omitted to indicate localhost +* "udp:<host>:<port>" - host may be omitted to indicate localhost +* "local-dgram:<identifier>" +* "local-stream:<identifier>" +* "shell" - local shell service +* "upload" - service for pushing files across (like aproto's /sync) +* "fs-bridge" - FUSE protocol filesystem bridge + + +--- READY(local-id, remote-id, "") ------------------------------------- + +The READY message informs the recipient that the sender's stream +identified by local-id is ready for write messages and that it is +connected to the recipient's stream identified by remote-id. + +Neither the local-id nor the remote-id may be zero. + +A READY message containing a remote-id which does not map to an open +stream on the recipient's side is ignored. The stream may have been +closed while this message was in-flight. + +The local-id is ignored on all but the first READY message (where it +is used to establish the connection). Nonetheless, the local-id MUST +not change on later READY messages sent to the same stream. + + + +--- WRITE(0, remote-id, "data") ---------------------------------------- + +The WRITE message sends data to the recipient's stream identified by +remote-id. The payload MUST be <= maxdata in length. + +A WRITE message containing a remote-id which does not map to an open +stream on the recipient's side is ignored. The stream may have been +closed while this message was in-flight. + +A WRITE message may not be sent until a READY message is received. +Once a WRITE message is sent, an additional WRITE message may not be +sent until another READY message has been received. Recipients of +a WRITE message that is in violation of this requirement will CLOSE +the connection. + + +--- CLOSE(local-id, remote-id, "") ------------------------------------- + +The CLOSE message informs recipient that the connection between the +sender's stream (local-id) and the recipient's stream (remote-id) is +broken. The remote-id MUST not be zero, but the local-id MAY be zero +if this CLOSE indicates a failed OPEN. + +A CLOSE message containing a remote-id which does not map to an open +stream on the recipient's side is ignored. The stream may have +already been closed by the recipient while this message was in-flight. + +The recipient should not respond to a CLOSE message in any way. The +recipient should cancel pending WRITEs or CLOSEs, but this is not a +requirement, since they will be ignored. + + +--- SYNC(online, sequence, "") ----------------------------------------- + +The SYNC message is used by the io pump to make sure that stale +outbound messages are discarded when the connection to the remote side +is broken. It is only used internally to the bridge and never valid +to send across the wire. + +* when the connection to the remote side goes offline, the io pump + sends a SYNC(0, 0) and starts discarding all messages +* when the connection to the remote side is established, the io pump + sends a SYNC(1, token) and continues to discard messages +* when the io pump receives a matching SYNC(1, token), it once again + starts accepting messages to forward to the remote side + + +--- message command constants ------------------------------------------ + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 + + + +--- implementation details --------------------------------------------- + +The core of the bridge program will use three threads. One thread +will be a select/epoll loop to handle io between various inbound and +outbound connections and the connection to the remote side. + +The remote side connection will be implemented as two threads (one for +reading, one for writing) and a datagram socketpair to provide the +channel between the main select/epoll thread and the remote connection +threadpair. The reason for this is that for usb connections, the +kernel interface on linux and osx does not allow you to do meaningful +nonblocking IO. + +The endian swapping for the message headers will happen (as needed) in +the remote connection threadpair and that the rest of the program will +always treat message header values as native-endian. + +The bridge program will be able to have a number of mini-servers +compiled in. They will be published under known names (examples +"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a +service, the bridge program will create a stream socketpair and spawn +a thread or subprocess to handle the io. + + +--- simplified / embedded implementation ------------------------------- + +For limited environments, like the bootloader, it is allowable to +support a smaller, fixed number of channels using pre-assigned channel +ID numbers such that only one stream may be connected to a bootloader +endpoint at any given time. The protocol remains unchanged, but the +"embedded" version of it is less dynamic. + +The bootloader will support two streams. A "bootloader:debug" stream, +which may be opened to get debug messages from the bootloader and a +"bootloader:control", stream which will support the set of basic +bootloader commands. + +Example command stream dialogues: + "flash_kernel,2515049,........\n" "okay\n" + "flash_ramdisk,5038,........\n" "fail,flash write error\n" + "bogus_command......" <CLOSE> + + +--- future expansion --------------------------------------------------- + +I plan on providing either a message or a special control stream so that +the client device could ask the host computer to setup inbound socket +translations on the fly on behalf of the client device. + + +The initial design does handshaking to provide flow control, with a +message flow that looks like: + + >OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE + +The far side may choose to issue the READY message as soon as it receives +a WRITE or it may defer the READY until the write to the local stream +succeeds. A future version may want to do some level of windowing where +multiple WRITEs may be sent without requiring individual READY acks. + +------------------------------------------------------------------------ + +--- smartsockets ------------------------------------------------------- + +Port 5037 is used for smart sockets which allow a client on the host +side to request access to a service in the host adb daemon or in the +remote (device) daemon. The service is requested by ascii name, +preceeded by a 4 digit hex length. Upon successful connection an +"OKAY" response is sent, otherwise a "FAIL" message is returned. Once +connected the client is talking to that (remote or local) service. + +client: <hex4> <service-name> +server: "OKAY" + +client: <hex4> <service-name> +server: "FAIL" <hex4> <reason> + diff --git a/adb/remount_service.c b/adb/remount_service.c new file mode 100644 index 0000000..26bc841 --- /dev/null +++ b/adb/remount_service.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb.h" + + +static int system_ro = 1; + +/* Returns the mount number of the requested partition from /proc/mtd */ +static int find_mount(const char *findme) +{ + int fd; + int res; + int size; + char *token = NULL; + const char delims[] = "\n"; + char buf[1024]; + + fd = unix_open("/proc/mtd", O_RDONLY); + if (fd < 0) + return -errno; + + buf[sizeof(buf) - 1] = '\0'; + size = adb_read(fd, buf, sizeof(buf) - 1); + adb_close(fd); + + token = strtok(buf, delims); + + while (token) { + char mtdname[16]; + int mtdnum, mtdsize, mtderasesize; + + res = sscanf(token, "mtd%d: %x %x %15s", + &mtdnum, &mtdsize, &mtderasesize, mtdname); + + if (res == 4 && !strcmp(mtdname, findme)) + return mtdnum; + + token = strtok(NULL, delims); + } + return -1; +} + +/* Init mounts /system as read only, remount to enable writes. */ +static int remount_system() +{ + int num; + char source[64]; + if (system_ro == 0) { + return 0; + } + if ((num = find_mount("\"system\"")) < 0) + return -1; + + snprintf(source, sizeof source, "/dev/block/mtdblock%d", num); + system_ro = mount(source, "/system", "yaffs2", MS_REMOUNT, NULL); + return system_ro; +} + +static void write_string(int fd, const char* str) +{ + writex(fd, str, strlen(str)); +} + +void remount_service(int fd, void *cookie) +{ + int ret = remount_system(); + + if (!ret) + write_string(fd, "remount succeeded\n"); + else { + char buffer[200]; + snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno)); + write_string(fd, buffer); + } + + adb_close(fd); +} + diff --git a/adb/services.c b/adb/services.c new file mode 100644 index 0000000..e686949 --- /dev/null +++ b/adb/services.c @@ -0,0 +1,367 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb.h" +#include "file_sync_service.h" + +#if ADB_HOST +# ifndef HAVE_WINSOCK +# include <netinet/in.h> +# include <netdb.h> +# endif +#endif + +typedef struct stinfo stinfo; + +struct stinfo { + void (*func)(int fd, void *cookie); + int fd; + void *cookie; +}; + + +void *service_bootstrap_func(void *x) +{ + stinfo *sti = x; + sti->func(sti->fd, sti->cookie); + free(sti); + return 0; +} + +#if ADB_HOST +ADB_MUTEX_DEFINE( dns_lock ); + +static void dns_service(int fd, void *cookie) +{ + char *hostname = cookie; + struct hostent *hp; + unsigned zero = 0; + + adb_mutex_lock(&dns_lock); + hp = gethostbyname(hostname); + if(hp == 0) { + writex(fd, &zero, 4); + } else { + writex(fd, hp->h_addr, 4); + } + adb_mutex_unlock(&dns_lock); + adb_close(fd); +} +#else +extern int recovery_mode; + +static void recover_service(int s, void *cookie) +{ + unsigned char buf[4096]; + unsigned count = (unsigned) cookie; + int fd; + + fd = adb_creat("/tmp/update", 0644); + if(fd < 0) { + adb_close(s); + return; + } + + while(count > 0) { + unsigned xfer = (count > 4096) ? 4096 : count; + if(readx(s, buf, xfer)) break; + if(writex(fd, buf, xfer)) break; + count -= xfer; + } + + if(count == 0) { + writex(s, "OKAY", 4); + } else { + writex(s, "FAIL", 4); + } + adb_close(fd); + adb_close(s); + + fd = adb_creat("/tmp/update.begin", 0644); + adb_close(fd); +} + +#endif + +#if 0 +static void echo_service(int fd, void *cookie) +{ + char buf[4096]; + int r; + char *p; + int c; + + for(;;) { + r = read(fd, buf, 4096); + if(r == 0) goto done; + if(r < 0) { + if(errno == EINTR) continue; + else goto done; + } + + c = r; + p = buf; + while(c > 0) { + r = write(fd, p, c); + if(r > 0) { + c -= r; + p += r; + continue; + } + if((r < 0) && (errno == EINTR)) continue; + goto done; + } + } +done: + close(fd); +} +#endif + +static int create_service_thread(void (*func)(int, void *), void *cookie) +{ + stinfo *sti; + adb_thread_t t; + int s[2]; + + if(adb_socketpair(s)) { + printf("cannot create service socket pair\n"); + return -1; + } + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = func; + sti->cookie = cookie; + sti->fd = s[1]; + + if(adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(s[0]); + adb_close(s[1]); + printf("cannot create service thread\n"); + return -1; + } + + D("service thread started, %d:%d\n",s[0], s[1]); + return s[0]; +} + +static int create_subprocess(const char *cmd, const char *arg0, const char *arg1) +{ +#ifdef HAVE_WIN32_PROC + fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); + return -1; +#else /* !HAVE_WIN32_PROC */ + char *devname; + int ptm; + pid_t pid; + + ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY); + if(ptm < 0){ + printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno)); + return -1; + } + fcntl(ptm, F_SETFD, FD_CLOEXEC); + + if(grantpt(ptm) || unlockpt(ptm) || + ((devname = (char*) ptsname(ptm)) == 0)){ + printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno)); + return -1; + } + + pid = fork(); + if(pid < 0) { + printf("- fork failed: %s -\n", strerror(errno)); + return -1; + } + + if(pid == 0){ + int pts; + + setsid(); + + pts = unix_open(devname, O_RDWR); + if(pts < 0) exit(-1); + + dup2(pts, 0); + dup2(pts, 1); + dup2(pts, 2); + + adb_close(ptm); + + execl(cmd, cmd, arg0, arg1, NULL); + fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", + cmd, strerror(errno), errno); + exit(-1); + } else { + return ptm; + } +#endif /* !HAVE_WIN32_PROC */ +} + +#if ADB_HOST +#define SHELL_COMMAND "/bin/sh" +#else +#define SHELL_COMMAND "/system/bin/sh" +#endif + +int service_to_fd(const char *name) +{ + int ret = -1; + + if(!strncmp(name, "tcp:", 4)) { + int port = atoi(name + 4); + name = strchr(name + 4, ':'); + if(name == 0) { + ret = socket_loopback_client(port, SOCK_STREAM); + if (ret >= 0) + disable_tcp_nagle(ret); + } else { +#if ADB_HOST + adb_mutex_lock(&dns_lock); + ret = socket_network_client(name + 1, port, SOCK_STREAM); + adb_mutex_unlock(&dns_lock); +#else + return -1; +#endif + } +#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */ + } else if(!strncmp(name, "local:", 6)) { + ret = socket_local_client(name + 6, + ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); + } else if(!strncmp(name, "localreserved:", 14)) { + ret = socket_local_client(name + 14, + ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); + } else if(!strncmp(name, "localabstract:", 14)) { + ret = socket_local_client(name + 14, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localfilesystem:", 16)) { + ret = socket_local_client(name + 16, + ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM); +#endif +#if ADB_HOST + } else if(!strncmp("dns:", name, 4)){ + char *n = strdup(name + 4); + if(n == 0) return -1; + ret = create_service_thread(dns_service, n); +#else /* !ADB_HOST */ + } else if(!strncmp("dev:", name, 4)) { + ret = unix_open(name + 4, O_RDWR); + } else if(!strncmp(name, "framebuffer:", 12)) { + ret = create_service_thread(framebuffer_service, 0); + } else if(recovery_mode && !strncmp(name, "recover:", 8)) { + ret = create_service_thread(recover_service, (void*) atoi(name + 8)); + } else if (!strncmp(name, "jdwp:", 5)) { + ret = create_jdwp_connection_fd(atoi(name+5)); + } else if (!strncmp(name, "log:", 4)) { + ret = create_service_thread(log_service, get_log_file_path(name + 4)); +#endif + } else if(!HOST && !strncmp(name, "shell:", 6)) { + if(name[6]) { + ret = create_subprocess(SHELL_COMMAND, "-c", name + 6); + } else { + ret = create_subprocess(SHELL_COMMAND, "-", 0); + } +#if !ADB_HOST + } else if(!strncmp(name, "sync:", 5)) { + ret = create_service_thread(file_sync_service, NULL); + } else if(!strncmp(name, "remount:", 8)) { + ret = create_service_thread(remount_service, NULL); +#endif +#if 0 + } else if(!strncmp(name, "echo:", 5)){ + ret = create_service_thread(echo_service, 0); +#endif + } + if (ret >= 0) { + close_on_exec(ret); + } + return ret; +} + +#if ADB_HOST +struct state_info { + transport_type transport; + char* serial; + int state; +}; + +static void wait_for_state(int fd, void* cookie) +{ + struct state_info* sinfo = cookie; + char* err = "unknown error"; + + D("wait_for_state %d\n", sinfo->state); + + atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err); + if(t != 0) { + writex(fd, "OKAY", 4); + } else { + sendfailmsg(fd, err); + } + + if (sinfo->serial) + free(sinfo->serial); + free(sinfo); + adb_close(fd); + D("wait_for_state is done\n"); +} +#endif + +#if ADB_HOST +asocket* host_service_to_socket(const char* name, const char *serial) +{ + if (!strcmp(name,"track-devices")) { + return create_device_tracker(); + } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) { + struct state_info* sinfo = malloc(sizeof(struct state_info)); + + if (serial) + sinfo->serial = strdup(serial); + else + sinfo->serial = NULL; + + name += strlen("wait-for-"); + + if (!strncmp(name, "local", strlen("local"))) { + sinfo->transport = kTransportLocal; + sinfo->state = CS_DEVICE; + } else if (!strncmp(name, "usb", strlen("usb"))) { + sinfo->transport = kTransportUsb; + sinfo->state = CS_DEVICE; + } else if (!strncmp(name, "any", strlen("any"))) { + sinfo->transport = kTransportAny; + sinfo->state = CS_DEVICE; + } else { + free(sinfo); + return NULL; + } + + int fd = create_service_thread(wait_for_state, sinfo); + return create_local_socket(fd); + } + return NULL; +} +#endif /* ADB_HOST */ diff --git a/adb/shlist.c b/adb/shlist.c new file mode 100755 index 0000000..44919ef --- /dev/null +++ b/adb/shlist.c @@ -0,0 +1,185 @@ +/*-------------------------------------------------------------------*/ +/* List Functionality */ +/*-------------------------------------------------------------------*/ +/* #define SH_LIST_DEBUG */ +/*-------------------------------------------------------------------*/ +#include <stdio.h> +#include <stdlib.h> +#include "shlist.h" +/*-------------------------------------------------------------------*/ +void shListInitList( SHLIST *listPtr ) +{ + listPtr->data = (void *)0L; + listPtr->next = listPtr; + listPtr->prev = listPtr; +} + +SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func ) +{ + SHLIST *item; + + for(item=head->next;( item != head );item=item->next) + if( func ) { + if( func( val, item->data ) ) { + return( item ); + } + } + else { + if( item->data == val ) { + return( item ); + } + } + return( NULL ); +} + +SHLIST *shListGetLastItem( SHLIST *head ) +{ + if( head->prev != head ) + return( head->prev ); + return( NULL ); +} + +SHLIST *shListGetFirstItem( SHLIST *head ) +{ + if( head->next != head ) + return( head->next ); + return( NULL ); +} + +SHLIST *shListGetNItem( SHLIST *head, unsigned long num ) +{ + SHLIST *item; + unsigned long i; + + for(i=0,item=head->next;( (i < num) && (item != head) );i++,item=item->next); + if( item != head ) + return( item ); + return( NULL ); +} + +SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item ) +{ + if( item == NULL ) + return( NULL ); + if( item->next != head ) + return( item->next ); + return( NULL ); +} + +SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item ) +{ + if( item == NULL ) + return( NULL ); + if( item->prev != head ) + return( item->prev ); + return( NULL ); +} + +void shListDelItem( SHLIST *head, SHLIST *item, shListFree func ) +{ + if( item == NULL ) + return; +#ifdef SH_LIST_DEBUG + fprintf(stderr, "Del %lx\n", (unsigned long)(item->data)); +#endif + (item->prev)->next = item->next; + (item->next)->prev = item->prev; + if( func && item->data ) { + func( (void *)(item->data) ); + } + free( item ); + head->data = (void *)((unsigned long)(head->data) - 1); +} + +void shListInsFirstItem( SHLIST *head, void *val ) +{ /* Insert to the beginning of the list */ + SHLIST *item; + + item = (SHLIST *)malloc( sizeof(SHLIST) ); + if( item == NULL ) + return; + item->data = val; + item->next = head->next; + item->prev = head; + (head->next)->prev = item; + head->next = item; +#ifdef SH_LIST_DEBUG + fprintf(stderr, "Ins First %lx\n", (unsigned long)(item->data)); +#endif + head->data = (void *)((unsigned long)(head->data) + 1); +} + +void shListInsLastItem( SHLIST *head, void *val ) +{ /* Insert to the end of the list */ + SHLIST *item; + + item = (SHLIST *)malloc( sizeof(SHLIST) ); + if( item == NULL ) + return; + item->data = val; + item->next = head; + item->prev = head->prev; + (head->prev)->next = item; + head->prev = item; +#ifdef SH_LIST_DEBUG + fprintf(stderr, "Ins Last %lx\n", (unsigned long)(item->data)); +#endif + head->data = (void *)((unsigned long)(head->data) + 1); +} + +void shListInsBeforeItem( SHLIST *head, void *val, void *etal, + shListCmp func ) +{ + SHLIST *item, *iptr; + + if( func == NULL ) + shListInsFirstItem( head, val ); + else { + item = (SHLIST *)malloc( sizeof(SHLIST) ); + if( item == NULL ) + return; + item->data = val; + for(iptr=head->next;( iptr != head );iptr=iptr->next) + if( func( val, iptr->data, etal ) ) + break; + item->next = iptr; + item->prev = iptr->prev; + (iptr->prev)->next = item; + iptr->prev = item; +#ifdef SH_LIST_DEBUG + fprintf(stderr, "Ins Before %lx\n", (unsigned long)(item->data)); +#endif + head->data = (void *)((unsigned long)(head->data) + 1); + } +} + +void shListDelAllItems( SHLIST *head, shListFree func ) +{ + SHLIST *item; + + for(item=head->next;( item != head );) { + shListDelItem( head, item, func ); + item = head->next; + } + head->data = (void *)0L; +} + +void shListPrintAllItems( SHLIST *head, shListPrint func ) +{ +#ifdef SH_LIST_DEBUG + SHLIST *item; + + for(item=head->next;( item != head );item=item->next) + if( func ) { + func(item->data); + } + else { + fprintf(stderr, "Item: %lx\n",(unsigned long)(item->data)); + } +#endif +} + +unsigned long shListGetCount( SHLIST *head ) +{ + return( (unsigned long)(head->data) ); +} diff --git a/adb/shlist.h b/adb/shlist.h new file mode 100755 index 0000000..0a9b07b --- /dev/null +++ b/adb/shlist.h @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------*/ +/* List Functionality */ +/*-------------------------------------------------------------------*/ +#ifndef _SHLIST_H_ +#define _SHLIST_H_ + +typedef struct SHLIST_STRUC { + void *data; + struct SHLIST_STRUC *next; + struct SHLIST_STRUC *prev; +} SHLIST; + +typedef int (*shListCmp)( void *valo, void *valn, void *etalon ); +typedef int (*shListPrint)( void *val ); +typedef void (*shListFree)( void *val ); +typedef int (*shListEqual)( void *val, void *idata ); + +void shListInitList( SHLIST *listPtr ); +SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func ); +SHLIST *shListGetFirstItem( SHLIST *head ); +SHLIST *shListGetNItem( SHLIST *head, unsigned long num ); +SHLIST *shListGetLastItem( SHLIST *head ); +SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item ); +SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item ); +void shListDelItem( SHLIST *head, SHLIST *item, shListFree func ); +void shListInsFirstItem( SHLIST *head, void *val ); +void shListInsBeforeItem( SHLIST *head, void *val, void *etalon, + shListCmp func ); +void shListInsLastItem( SHLIST *head, void *val ); +void shListDelAllItems( SHLIST *head, shListFree func ); +void shListPrintAllItems( SHLIST *head, shListPrint func ); +unsigned long shListGetCount( SHLIST *head ); + +#endif diff --git a/adb/sockets.c b/adb/sockets.c new file mode 100644 index 0000000..9f1b598 --- /dev/null +++ b/adb/sockets.c @@ -0,0 +1,787 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SOCKETS +#include "adb.h" + +ADB_MUTEX_DEFINE( socket_list_lock ); + +static void local_socket_close_locked(asocket *s); + +int sendfailmsg(int fd, const char *reason) +{ + char buf[9]; + int len; + len = strlen(reason); + if(len > 0xffff) len = 0xffff; + snprintf(buf, sizeof buf, "FAIL%04x", len); + if(writex(fd, buf, 8)) return -1; + return writex(fd, reason, len); +} + +//extern int online; + +static unsigned local_socket_next_id = 1; + +static asocket local_socket_list = { + .next = &local_socket_list, + .prev = &local_socket_list, +}; + +/* the the list of currently closing local sockets. +** these have no peer anymore, but still packets to +** write to their fd. +*/ +static asocket local_socket_closing_list = { + .next = &local_socket_closing_list, + .prev = &local_socket_closing_list, +}; + +asocket *find_local_socket(unsigned id) +{ + asocket *s; + asocket *result = NULL; + + adb_mutex_lock(&socket_list_lock); + for(s = local_socket_list.next; s != &local_socket_list && !result; s = s->next) { + if(s->id == id) result = s; + } + adb_mutex_unlock(&socket_list_lock); + + return result; +} + +static void +insert_local_socket(asocket* s, asocket* list) +{ + s->next = list; + s->prev = s->next->prev; + s->prev->next = s; + s->next->prev = s; +} + + +void install_local_socket(asocket *s) +{ + adb_mutex_lock(&socket_list_lock); + + s->id = local_socket_next_id++; + insert_local_socket(s, &local_socket_list); + + adb_mutex_unlock(&socket_list_lock); +} + +void remove_socket(asocket *s) +{ + // socket_list_lock should already be held + if (s->prev && s->next) + { + s->prev->next = s->next; + s->next->prev = s->prev; + s->next = 0; + s->prev = 0; + s->id = 0; + } +} + +void close_all_sockets(atransport *t) +{ + asocket *s; + + /* this is a little gross, but since s->close() *will* modify + ** the list out from under you, your options are limited. + */ + adb_mutex_lock(&socket_list_lock); +restart: + for(s = local_socket_list.next; s != &local_socket_list; s = s->next){ + if(s->transport == t || (s->peer && s->peer->transport == t)) { + local_socket_close_locked(s); + goto restart; + } + } + adb_mutex_unlock(&socket_list_lock); +} + +static int local_socket_enqueue(asocket *s, apacket *p) +{ + D("LS(%d): enqueue %d\n", s->id, p->len); + + p->ptr = p->data; + + /* if there is already data queue'd, we will receive + ** events when it's time to write. just add this to + ** the tail + */ + if(s->pkt_first) { + goto enqueue; + } + + /* write as much as we can, until we + ** would block or there is an error/eof + */ + while(p->len > 0) { + int r = adb_write(s->fd, p->ptr, p->len); + if(r > 0) { + p->len -= r; + p->ptr += r; + continue; + } + if((r == 0) || (errno != EAGAIN)) { + D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) ); + s->close(s); + return 1; /* not ready (error) */ + } else { + break; + } + } + + if(p->len == 0) { + put_apacket(p); + return 0; /* ready for more data */ + } + +enqueue: + p->next = 0; + if(s->pkt_first) { + s->pkt_last->next = p; + } else { + s->pkt_first = p; + } + s->pkt_last = p; + + /* make sure we are notified when we can drain the queue */ + fdevent_add(&s->fde, FDE_WRITE); + + return 1; /* not ready (backlog) */ +} + +static void local_socket_ready(asocket *s) +{ + /* far side is ready for data, pay attention to + readable events */ + fdevent_add(&s->fde, FDE_READ); +// D("LS(%d): ready()\n", s->id); +} + +static void local_socket_close(asocket *s) +{ + adb_mutex_lock(&socket_list_lock); + local_socket_close_locked(s); + adb_mutex_unlock(&socket_list_lock); +} + +// be sure to hold the socket list lock when calling this +static void local_socket_destroy(asocket *s) +{ + apacket *p, *n; + + /* IMPORTANT: the remove closes the fd + ** that belongs to this socket + */ + fdevent_remove(&s->fde); + + /* dispose of any unwritten data */ + for(p = s->pkt_first; p; p = n) { + D("LS(%d): discarding %d bytes\n", s->id, p->len); + n = p->next; + put_apacket(p); + } + remove_socket(s); + free(s); +} + + +static void local_socket_close_locked(asocket *s) +{ + if(s->peer) { + s->peer->peer = 0; + // tweak to avoid deadlock + if (s->peer->close == local_socket_close) + local_socket_close_locked(s->peer); + else + s->peer->close(s->peer); + } + + /* If we are already closing, or if there are no + ** pending packets, destroy immediately + */ + if (s->closing || s->pkt_first == NULL) { + int id = s->id; + local_socket_destroy(s); + D("LS(%d): closed\n", id); + return; + } + + /* otherwise, put on the closing list + */ + D("LS(%d): closing\n", s->id); + s->closing = 1; + fdevent_del(&s->fde, FDE_READ); + remove_socket(s); + insert_local_socket(s, &local_socket_closing_list); +} + +static void local_socket_event_func(int fd, unsigned ev, void *_s) +{ + asocket *s = _s; + + /* put the FDE_WRITE processing before the FDE_READ + ** in order to simplify the code. + */ + if(ev & FDE_WRITE){ + apacket *p; + + while((p = s->pkt_first) != 0) { + while(p->len > 0) { + int r = adb_write(fd, p->ptr, p->len); + if(r > 0) { + p->ptr += r; + p->len -= r; + continue; + } + if(r < 0) { + /* returning here is ok because FDE_READ will + ** be processed in the next iteration loop + */ + if(errno == EAGAIN) return; + if(errno == EINTR) continue; + } + s->close(s); + return; + } + + if(p->len == 0) { + s->pkt_first = p->next; + if(s->pkt_first == 0) s->pkt_last = 0; + put_apacket(p); + } + } + + /* if we sent the last packet of a closing socket, + ** we can now destroy it. + */ + if (s->closing) { + s->close(s); + return; + } + + /* no more packets queued, so we can ignore + ** writable events again and tell our peer + ** to resume writing + */ + fdevent_del(&s->fde, FDE_WRITE); + s->peer->ready(s->peer); + } + + + if(ev & FDE_READ){ + apacket *p = get_apacket(); + unsigned char *x = p->data; + size_t avail = MAX_PAYLOAD; + int r; + int is_eof = 0; + + while(avail > 0) { + r = adb_read(fd, x, avail); + if(r > 0) { + avail -= r; + x += r; + continue; + } + if(r < 0) { + if(errno == EAGAIN) break; + if(errno == EINTR) continue; + } + + /* r = 0 or unhandled error */ + is_eof = 1; + break; + } + + if((avail == MAX_PAYLOAD) || (s->peer == 0)) { + put_apacket(p); + } else { + p->len = MAX_PAYLOAD - avail; + + r = s->peer->enqueue(s->peer, p); + + if(r < 0) { + /* error return means they closed us as a side-effect + ** and we must return immediately. + ** + ** note that if we still have buffered packets, the + ** socket will be placed on the closing socket list. + ** this handler function will be called again + ** to process FDE_WRITE events. + */ + return; + } + + if(r > 0) { + /* if the remote cannot accept further events, + ** we disable notification of READs. They'll + ** be enabled again when we get a call to ready() + */ + fdevent_del(&s->fde, FDE_READ); + } + } + + if(is_eof) { + s->close(s); + } + } + + if(ev & FDE_ERROR){ + /* this should be caught be the next read or write + ** catching it here means we may skip the last few + ** bytes of readable data. + */ +// s->close(s); + return; + } +} + +asocket *create_local_socket(int fd) +{ + asocket *s = calloc(1, sizeof(asocket)); + if(s == 0) fatal("cannot allocate socket"); + install_local_socket(s); + s->fd = fd; + s->enqueue = local_socket_enqueue; + s->ready = local_socket_ready; + s->close = local_socket_close; + + fdevent_install(&s->fde, fd, local_socket_event_func, s); +/* fdevent_add(&s->fde, FDE_ERROR); */ + //fprintf(stderr, "Created local socket in create_local_socket \n"); + D("LS(%d): created (fd=%d)\n", s->id, s->fd); + return s; +} + +asocket *create_local_service_socket(const char *name) +{ + asocket *s; + int fd; + +#if !ADB_HOST + if (!strcmp(name,"jdwp")) { + return create_jdwp_service_socket(); + } + if (!strcmp(name,"track-jdwp")) { + return create_jdwp_tracker_service_socket(); + } +#endif + fd = service_to_fd(name); + if(fd < 0) return 0; + + s = create_local_socket(fd); + D("LS(%d): bound to '%s'\n", s->id, name); + return s; +} + +#if ADB_HOST +static asocket *create_host_service_socket(const char *name, const char* serial) +{ + asocket *s; + + s = host_service_to_socket(name, serial); + + if (s != NULL) { + D("LS(%d) bound to '%s'\n", s->id, name); + return s; + } + + return s; +} +#endif /* ADB_HOST */ + +/* a Remote socket is used to send/receive data to/from a given transport object +** it needs to be closed when the transport is forcibly destroyed by the user +*/ +typedef struct aremotesocket { + asocket socket; + adisconnect disconnect; +} aremotesocket; + +static int remote_socket_enqueue(asocket *s, apacket *p) +{ + D("Calling remote_socket_enqueue\n"); + p->msg.command = A_WRTE; + p->msg.arg0 = s->peer->id; + p->msg.arg1 = s->id; + p->msg.data_length = p->len; + send_packet(p, s->transport); + return 1; +} + +static void remote_socket_ready(asocket *s) +{ + D("Calling remote_socket_ready\n"); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = s->peer->id; + p->msg.arg1 = s->id; + send_packet(p, s->transport); +} + +static void remote_socket_close(asocket *s) +{ + D("Calling remote_socket_close\n"); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + if(s->peer) { + p->msg.arg0 = s->peer->id; + s->peer->peer = 0; + s->peer->close(s->peer); + } + p->msg.arg1 = s->id; + send_packet(p, s->transport); + D("RS(%d): closed\n", s->id); + remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); + free(s); +} + +static void remote_socket_disconnect(void* _s, atransport* t) +{ + asocket* s = _s; + asocket* peer = s->peer; + + D("remote_socket_disconnect RS(%d)\n", s->id); + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); + free(s); +} + +asocket *create_remote_socket(unsigned id, atransport *t) +{ + asocket *s = calloc(1, sizeof(aremotesocket)); + adisconnect* dis = &((aremotesocket*)s)->disconnect; + + if(s == 0) fatal("cannot allocate socket"); + s->id = id; + s->enqueue = remote_socket_enqueue; + s->ready = remote_socket_ready; + s->close = remote_socket_close; + s->transport = t; + + dis->func = remote_socket_disconnect; + dis->opaque = s; + add_transport_disconnect( t, dis ); + D("RS(%d): created\n", s->id); + return s; +} + +void connect_to_remote(asocket *s, const char *destination) +{ + D("Connect_to_remote call \n"); + apacket *p = get_apacket(); + int len = strlen(destination) + 1; + + if(len > (MAX_PAYLOAD-1)) { + fatal("destination oversized"); + } + + D("LS(%d): connect('%s')\n", s->id, destination); + p->msg.command = A_OPEN; + p->msg.arg0 = s->id; + p->msg.data_length = len; + strcpy((char*) p->data, destination); + send_packet(p, s->transport); +} + + +/* this is used by magic sockets to rig local sockets to + send the go-ahead message when they connect */ +static void local_socket_ready_notify(asocket *s) +{ + s->ready = local_socket_ready; + s->close = local_socket_close; + adb_write(s->fd, "OKAY", 4); + s->ready(s); +} + +/* this is used by magic sockets to rig local sockets to + send the failure message if they are closed before + connected (to avoid closing them without a status message) */ +static void local_socket_close_notify(asocket *s) +{ + s->ready = local_socket_ready; + s->close = local_socket_close; + sendfailmsg(s->fd, "closed"); + s->close(s); +} + +unsigned unhex(unsigned char *s, int len) +{ + unsigned n = 0, c; + + while(len-- > 0) { + switch((c = *s++)) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': + c -= '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + c = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + c = c - 'A' + 10; + break; + default: + return 0xffffffff; + } + + n = (n << 4) | c; + } + + return n; +} + +static int smart_socket_enqueue(asocket *s, apacket *p) +{ + unsigned len; +#if ADB_HOST + char *service = NULL; + char* serial = NULL; + transport_type ttype = kTransportAny; +#endif + + D("SS(%d): enqueue %d\n", s->id, p->len); + + if(s->pkt_first == 0) { + s->pkt_first = p; + s->pkt_last = p; + } else { + if((s->pkt_first->len + p->len) > MAX_PAYLOAD) { + D("SS(%d): overflow\n", s->id); + put_apacket(p); + goto fail; + } + + memcpy(s->pkt_first->data + s->pkt_first->len, + p->data, p->len); + s->pkt_first->len += p->len; + put_apacket(p); + + p = s->pkt_first; + } + + /* don't bother if we can't decode the length */ + if(p->len < 4) return 0; + + len = unhex(p->data, 4); + if((len < 1) || (len > 1024)) { + D("SS(%d): bad size (%d)\n", s->id, len); + goto fail; + } + + D("SS(%d): len is %d\n", s->id, len ); + /* can't do anything until we have the full header */ + if((len + 4) > p->len) { + D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len); + return 0; + } + + p->data[len + 4] = 0; + + D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4)); + +#if ADB_HOST + service = (char *)p->data + 4; + if(!strncmp(service, "host-serial:", strlen("host-serial:"))) { + char* serial_end; + service += strlen("host-serial:"); + + // serial number should follow "host:" + serial_end = strchr(service, ':'); + if (serial_end) { + *serial_end = 0; // terminate string + serial = service; + service = serial_end + 1; + } + } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) { + ttype = kTransportUsb; + service += strlen("host-usb:"); + } else if (!strncmp(service, "host-local:", strlen("host-local:"))) { + ttype = kTransportLocal; + service += strlen("host-local:"); + } else if (!strncmp(service, "host:", strlen("host:"))) { + ttype = kTransportAny; + service += strlen("host:"); + } else { + service = NULL; + } + + if (service) { + asocket *s2; + + /* some requests are handled immediately -- in that + ** case the handle_host_request() routine has sent + ** the OKAY or FAIL message and all we have to do + ** is clean up. + */ + if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) { + /* XXX fail message? */ + D( "SS(%d): handled host service '%s'\n", s->id, service ); + goto fail; + } + if (!strncmp(service, "transport", strlen("transport"))) { + D( "SS(%d): okay transport\n", s->id ); + p->len = 0; + return 0; + } + + /* try to find a local service with this name. + ** if no such service exists, we'll fail out + ** and tear down here. + */ + s2 = create_host_service_socket(service, serial); + if(s2 == 0) { + D( "SS(%d): couldn't create host service '%s'\n", s->id, service ); + sendfailmsg(s->peer->fd, "unknown host service"); + goto fail; + } + + /* we've connected to a local host service, + ** so we make our peer back into a regular + ** local socket and bind it to the new local + ** service socket, acknowledge the successful + ** connection, and close this smart socket now + ** that its work is done. + */ + adb_write(s->peer->fd, "OKAY", 4); + + s->peer->ready = local_socket_ready; + s->peer->close = local_socket_close; + s->peer->peer = s2; + s2->peer = s->peer; + s->peer = 0; + D( "SS(%d): okay\n", s->id ); + s->close(s); + + /* initial state is "ready" */ + s2->ready(s2); + return 0; + } +#else /* !ADB_HOST */ + if (s->transport == NULL) { + char* error_string = "unknown failure"; + s->transport = acquire_one_transport (CS_ANY, + kTransportAny, NULL, &error_string); + + if (s->transport == NULL) { + sendfailmsg(s->peer->fd, error_string); + goto fail; + } + } +#endif + + if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) { + /* if there's no remote we fail the connection + ** right here and terminate it + */ + sendfailmsg(s->peer->fd, "device offline (x)"); + goto fail; + } + + + /* instrument our peer to pass the success or fail + ** message back once it connects or closes, then + ** detach from it, request the connection, and + ** tear down + */ + s->peer->ready = local_socket_ready_notify; + s->peer->close = local_socket_close_notify; + s->peer->peer = 0; + /* give him our transport and upref it */ + s->peer->transport = s->transport; + + connect_to_remote(s->peer, (char*) (p->data + 4)); + s->peer = 0; + s->close(s); + return 1; + +fail: + /* we're going to close our peer as a side-effect, so + ** return -1 to signal that state to the local socket + ** who is enqueueing against us + */ + s->close(s); + return -1; +} + +static void smart_socket_ready(asocket *s) +{ + D("SS(%d): ready\n", s->id); +} + +static void smart_socket_close(asocket *s) +{ + D("SS(%d): closed\n", s->id); + if(s->pkt_first){ + put_apacket(s->pkt_first); + } + if(s->peer) { + s->peer->peer = 0; + s->peer->close(s->peer); + } + free(s); +} + +asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act)) +{ + D("Creating smart socket \n"); + asocket *s = calloc(1, sizeof(asocket)); + if(s == 0) fatal("cannot allocate socket"); + s->id = 0; + s->enqueue = smart_socket_enqueue; + s->ready = smart_socket_ready; + s->close = smart_socket_close; + s->extra = action_cb; + + D("SS(%d): created %p\n", s->id, action_cb); + return s; +} + +void smart_socket_action(asocket *s, const char *act) +{ + +} + +void connect_to_smartsocket(asocket *s) +{ + D("Connecting to smart socket \n"); + asocket *ss = create_smart_socket(smart_socket_action); + s->peer = ss; + ss->peer = s; + s->ready(s); +} diff --git a/adb/sockets.dia b/adb/sockets.dia Binary files differnew file mode 100644 index 0000000..c626f20 --- /dev/null +++ b/adb/sockets.dia diff --git a/adb/sysdeps.h b/adb/sysdeps.h new file mode 100644 index 0000000..e5d17a8 --- /dev/null +++ b/adb/sysdeps.h @@ -0,0 +1,473 @@ +/* + * 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 file contains system-dependent definitions used by ADB + * they're related to threads, sockets and file descriptors + */ +#ifndef _ADB_SYSDEPS_H +#define _ADB_SYSDEPS_H + +#ifdef __CYGWIN__ +# undef _WIN32 +#endif + +#ifdef _WIN32 + +#include <windows.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#include <process.h> +#include <fcntl.h> +#include <io.h> +#include <sys/stat.h> +#include <errno.h> +#include <ctype.h> + +#define OS_PATH_SEPARATOR '\\' +#define OS_PATH_SEPARATOR_STR "\\" + +typedef CRITICAL_SECTION adb_mutex_t; + +#define ADB_MUTEX_DEFINE(x) adb_mutex_t x + +/* declare all mutexes */ +#define ADB_MUTEX(x) extern adb_mutex_t x; +#include "mutex_list.h" + +extern void adb_sysdeps_init(void); + +static __inline__ void adb_mutex_lock( adb_mutex_t* lock ) +{ + EnterCriticalSection( lock ); +} + +static __inline__ void adb_mutex_unlock( adb_mutex_t* lock ) +{ + LeaveCriticalSection( lock ); +} + +typedef struct { unsigned tid; } adb_thread_t; + +typedef void* (*adb_thread_func_t)(void* arg); + +typedef void (*win_thread_func_t)(void* arg); + +static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg) +{ + thread->tid = _beginthread( (win_thread_func_t)func, 0, arg ); + if (thread->tid == (unsigned)-1L) { + return -1; + } + return 0; +} + +static __inline__ void close_on_exec(int fd) +{ + /* nothing really */ +} + +extern void disable_tcp_nagle(int fd); + +#define lstat stat /* no symlinks on Win32 */ + +#define S_ISLNK(m) 0 /* no symlinks on Win32 */ + +static __inline__ int adb_unlink(const char* path) +{ + int rc = unlink(path); + + if (rc == -1 && errno == EACCES) { + /* unlink returns EACCES when the file is read-only, so we first */ + /* try to make it writable, then unlink again... */ + rc = chmod(path, _S_IREAD|_S_IWRITE ); + if (rc == 0) + rc = unlink(path); + } + return rc; +} +#undef unlink +#define unlink ___xxx_unlink + +static __inline__ int adb_mkdir(const char* path, int mode) +{ + return _mkdir(path); +} +#undef mkdir +#define mkdir ___xxx_mkdir + +extern int adb_open(const char* path, int options); +extern int adb_creat(const char* path, int mode); +extern int adb_read(int fd, void* buf, int len); +extern int adb_write(int fd, const void* buf, int len); +extern int adb_lseek(int fd, int pos, int where); +extern int adb_close(int fd); + +static __inline__ int unix_close(int fd) +{ + return close(fd); +} +#undef close +#define close ____xxx_close + +static __inline__ int unix_read(int fd, void* buf, size_t len) +{ + return read(fd, buf, len); +} +#undef read +#define read ___xxx_read + +static __inline__ int unix_write(int fd, const void* buf, size_t len) +{ + return write(fd, buf, len); +} +#undef write +#define write ___xxx_write + +static __inline__ int adb_open_mode(const char* path, int options, int mode) +{ + return adb_open(path, options); +} + +static __inline__ int unix_open(const char* path, int options,...) +{ + if ((options & O_CREAT) == 0) + { + return open(path, options); + } + else + { + int mode; + va_list args; + va_start( args, options ); + mode = va_arg( args, int ); + va_end( args ); + return open(path, options, mode); + } +} +#define open ___xxx_unix_open + + +/* normally provided by <cutils/misc.h> */ +extern void* load_file(const char* pathname, unsigned* psize); + +/* normally provided by <cutils/sockets.h> */ +extern int socket_loopback_client(int port, int type); +extern int socket_network_client(const char *host, int port, int type); +extern int socket_loopback_server(int port, int type); +extern int socket_inaddr_any_server(int port, int type); + +/* normally provided by <cutils/fdevent.h> */ + +#define FDE_READ 0x0001 +#define FDE_WRITE 0x0002 +#define FDE_ERROR 0x0004 +#define FDE_DONT_CLOSE 0x0080 + +typedef struct fdevent fdevent; + +typedef void (*fd_func)(int fd, unsigned events, void *userdata); + +fdevent *fdevent_create(int fd, fd_func func, void *arg); +void fdevent_destroy(fdevent *fde); +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg); +void fdevent_remove(fdevent *item); +void fdevent_set(fdevent *fde, unsigned events); +void fdevent_add(fdevent *fde, unsigned events); +void fdevent_del(fdevent *fde, unsigned events); +void fdevent_loop(); + +struct fdevent { + fdevent *next; + fdevent *prev; + + int fd; + unsigned short state; + unsigned short events; + + fd_func func; + void *arg; +}; + +static __inline__ void adb_sleep_ms( int mseconds ) +{ + Sleep( mseconds ); +} + +extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen); + +#undef accept +#define accept ___xxx_accept + +static __inline__ int adb_socket_setbufsize( int fd, int bufsize ) +{ + int opt = bufsize; + return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt)); +} + +extern int adb_socketpair( int sv[2] ); + +static __inline__ char* adb_dirstart( const char* path ) +{ + char* p = strchr(path, '/'); + char* p2 = strchr(path, '\\'); + + if ( !p ) + p = p2; + else if ( p2 && p2 > p ) + p = p2; + + return p; +} + +static __inline__ char* adb_dirstop( const char* path ) +{ + char* p = strrchr(path, '/'); + char* p2 = strrchr(path, '\\'); + + if ( !p ) + p = p2; + else if ( p2 && p2 > p ) + p = p2; + + return p; +} + +static __inline__ int adb_is_absolute_host_path( const char* path ) +{ + return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; +} + +#else /* !_WIN32 a.k.a. Unix */ + +#include <cutils/fdevent.h> +#include <cutils/sockets.h> +#include <cutils/properties.h> +#include <cutils/misc.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <pthread.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <string.h> + +#define OS_PATH_SEPARATOR '/' +#define OS_PATH_SEPARATOR_STR "/" + +typedef pthread_mutex_t adb_mutex_t; +#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define adb_mutex_init pthread_mutex_init +#define adb_mutex_lock pthread_mutex_lock +#define adb_mutex_unlock pthread_mutex_unlock +#define adb_mutex_destroy pthread_mutex_destroy + +#define ADB_MUTEX_DEFINE(m) static adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER + +#define adb_cond_t pthread_cond_t +#define adb_cond_init pthread_cond_init +#define adb_cond_wait pthread_cond_wait +#define adb_cond_broadcast pthread_cond_broadcast +#define adb_cond_signal pthread_cond_signal +#define adb_cond_destroy pthread_cond_destroy + +static __inline__ void close_on_exec(int fd) +{ + fcntl( fd, F_SETFD, FD_CLOEXEC ); +} + +static __inline__ int unix_open(const char* path, int options,...) +{ + if ((options & O_CREAT) == 0) + { + return open(path, options); + } + else + { + int mode; + va_list args; + va_start( args, options ); + mode = va_arg( args, int ); + va_end( args ); + return open(path, options, mode); + } +} + +static __inline__ int adb_open_mode( const char* pathname, int options, int mode ) +{ + return open( pathname, options, mode ); +} + + +static __inline__ int adb_open( const char* pathname, int options ) +{ + int fd = open( pathname, options ); + if (fd < 0) + return -1; + close_on_exec( fd ); + return fd; +} +#undef open +#define open ___xxx_open + +static __inline__ int adb_close(int fd) +{ + return close(fd); +} +#undef close +#define close ____xxx_close + + +static __inline__ int adb_read(int fd, void* buf, size_t len) +{ + return read(fd, buf, len); +} + +#undef read +#define read ___xxx_read + +static __inline__ int adb_write(int fd, const void* buf, size_t len) +{ + return write(fd, buf, len); +} +#undef write +#define write ___xxx_write + +static __inline__ int adb_lseek(int fd, int pos, int where) +{ + return lseek(fd, pos, where); +} +#undef lseek +#define lseek ___xxx_lseek + +static __inline__ int adb_unlink(const char* path) +{ + return unlink(path); +} +#undef unlink +#define unlink ___xxx_unlink + +static __inline__ int adb_creat(const char* path, int mode) +{ + int fd = creat(path, mode); + + if ( fd < 0 ) + return -1; + + close_on_exec(fd); + return fd; +} +#undef creat +#define creat ___xxx_creat + +static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) +{ + return accept( serverfd, addr, addrlen ); +} + +#undef accept +#define accept ___xxx_accept + +#define unix_read adb_read +#define unix_write adb_write +#define unix_close adb_close + +typedef pthread_t adb_thread_t; + +typedef void* (*adb_thread_func_t)( void* arg ); + +static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg ) +{ + pthread_attr_t attr; + + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + return pthread_create( pthread, &attr, start, arg ); +} + +static __inline__ int adb_socket_setbufsize( int fd, int bufsize ) +{ + int opt = bufsize; + return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); +} + +static __inline__ void disable_tcp_nagle(int fd) +{ + int on = 1; + setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) ); +} + + +static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] ) +{ + return socketpair( d, type, protocol, sv ); +} + +static __inline__ int adb_socketpair( int sv[2] ) +{ + int rc; + + rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv ); + if (rc < 0) + return -1; + + close_on_exec( sv[0] ); + close_on_exec( sv[1] ); + return 0; +} + +#undef socketpair +#define socketpair ___xxx_socketpair + +static __inline__ void adb_sleep_ms( int mseconds ) +{ + usleep( mseconds*1000 ); +} + +static __inline__ int adb_mkdir(const char* path, int mode) +{ + return mkdir(path, mode); +} +#undef mkdir +#define mkdir ___xxx_mkdir + +static __inline__ void adb_sysdeps_init(void) +{ +} + +static __inline__ char* adb_dirstart(const char* path) +{ + return strchr(path, '/'); +} + +static __inline__ char* adb_dirstop(const char* path) +{ + return strrchr(path, '/'); +} + +static __inline__ int adb_is_absolute_host_path( const char* path ) +{ + return path[0] == '/'; +} + +#endif /* !_WIN32 */ + +#endif /* _ADB_SYSDEPS_H */ diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c new file mode 100644 index 0000000..c2a9a98 --- /dev/null +++ b/adb/sysdeps_win32.c @@ -0,0 +1,1953 @@ +#include "sysdeps.h" +#include <windows.h> +#include <winsock2.h> +#include <stdio.h> +#include <errno.h> +#define TRACE_TAG TRACE_SYSDEPS +#include "adb.h" + +extern void fatal(const char *fmt, ...); + +#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0) + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** replaces libs/cutils/load_file.c *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +void *load_file(const char *fn, unsigned *_sz) +{ + HANDLE file; + char *data; + DWORD file_size; + + file = CreateFile( fn, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (file == INVALID_HANDLE_VALUE) + return NULL; + + file_size = GetFileSize( file, NULL ); + data = NULL; + + if (file_size > 0) { + data = (char*) malloc( file_size + 1 ); + if (data == NULL) { + D("load_file: could not allocate %ld bytes\n", file_size ); + file_size = 0; + } else { + DWORD out_bytes; + + if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) || + out_bytes != file_size ) + { + D("load_file: could not read %ld bytes from '%s'\n", file_size, fn); + free(data); + data = NULL; + file_size = 0; + } + } + } + CloseHandle( file ); + + *_sz = (unsigned) file_size; + return data; +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** common file descriptor handling *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +typedef const struct FHClassRec_* FHClass; + +typedef struct FHRec_* FH; + +typedef struct EventHookRec_* EventHook; + +typedef struct FHClassRec_ +{ + void (*_fh_init) ( FH f ); + int (*_fh_close)( FH f ); + int (*_fh_lseek)( FH f, int pos, int origin ); + int (*_fh_read) ( FH f, void* buf, int len ); + int (*_fh_write)( FH f, const void* buf, int len ); + void (*_fh_hook) ( FH f, int events, EventHook hook ); + +} FHClassRec; + +/* used to emulate unix-domain socket pairs */ +typedef struct SocketPairRec_* SocketPair; + +typedef struct FHRec_ +{ + FHClass clazz; + int used; + int eof; + union { + HANDLE handle; + SOCKET socket; + SocketPair pair; + } u; + + HANDLE event; + int mask; + + char name[32]; + +} FHRec; + +#define fh_handle u.handle +#define fh_socket u.socket +#define fh_pair u.pair + +#define WIN32_FH_BASE 100 + +#define WIN32_MAX_FHS 128 + +static adb_mutex_t _win32_lock; +static FHRec _win32_fhs[ WIN32_MAX_FHS ]; +static int _win32_fh_count; + +static FH +_fh_from_int( int fd ) +{ + FH f; + + fd -= WIN32_FH_BASE; + + if (fd < 0 || fd >= _win32_fh_count) { + D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE ); + errno = EBADF; + return NULL; + } + + f = &_win32_fhs[fd]; + + if (f->used == 0) { + D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE ); + errno = EBADF; + return NULL; + } + + return f; +} + + +static int +_fh_to_int( FH f ) +{ + if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS) + return (int)(f - _win32_fhs) + WIN32_FH_BASE; + + return -1; +} + +static FH +_fh_alloc( FHClass clazz ) +{ + int nn; + FH f = NULL; + + adb_mutex_lock( &_win32_lock ); + + if (_win32_fh_count < WIN32_MAX_FHS) { + f = &_win32_fhs[ _win32_fh_count++ ]; + goto Exit; + } + + for (nn = 0; nn < WIN32_MAX_FHS; nn++) { + if ( _win32_fhs[nn].clazz == NULL) { + f = &_win32_fhs[nn]; + goto Exit; + } + } + D( "_fh_alloc: no more free file descriptors\n" ); +Exit: + if (f) { + f->clazz = clazz; + f->used = 1; + f->eof = 0; + clazz->_fh_init(f); + } + adb_mutex_unlock( &_win32_lock ); + return f; +} + + +static int +_fh_close( FH f ) +{ + if ( f->used ) { + f->clazz->_fh_close( f ); + f->used = 0; + f->eof = 0; + f->clazz = NULL; + } + return 0; +} + +/* forward definitions */ +static const FHClassRec _fh_file_class; +static const FHClassRec _fh_socket_class; + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** file-based descriptor handling *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +static void +_fh_file_init( FH f ) +{ + f->fh_handle = INVALID_HANDLE_VALUE; +} + +static int +_fh_file_close( FH f ) +{ + CloseHandle( f->fh_handle ); + f->fh_handle = INVALID_HANDLE_VALUE; + return 0; +} + +static int +_fh_file_read( FH f, void* buf, int len ) +{ + DWORD read_bytes; + + if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) { + D( "adb_read: could not read %d bytes from %s\n", len, f->name ); + errno = EIO; + return -1; + } else if (read_bytes < (DWORD)len) { + f->eof = 1; + } + return (int)read_bytes; +} + +static int +_fh_file_write( FH f, const void* buf, int len ) +{ + DWORD wrote_bytes; + + if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) { + D( "adb_file_write: could not write %d bytes from %s\n", len, f->name ); + errno = EIO; + return -1; + } else if (wrote_bytes < (DWORD)len) { + f->eof = 1; + } + return (int)wrote_bytes; +} + +static int +_fh_file_lseek( FH f, int pos, int origin ) +{ + DWORD method; + DWORD result; + + switch (origin) + { + case SEEK_SET: method = FILE_BEGIN; break; + case SEEK_CUR: method = FILE_CURRENT; break; + case SEEK_END: method = FILE_END; break; + default: + errno = EINVAL; + return -1; + } + + result = SetFilePointer( f->fh_handle, pos, NULL, method ); + if (result == INVALID_SET_FILE_POINTER) { + errno = EIO; + return -1; + } else { + f->eof = 0; + } + return (int)result; +} + +static void _fh_file_hook( FH f, int event, EventHook eventhook ); /* forward */ + +static const FHClassRec _fh_file_class = +{ + _fh_file_init, + _fh_file_close, + _fh_file_lseek, + _fh_file_read, + _fh_file_write, + _fh_file_hook +}; + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** file-based descriptor handling *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +int adb_open(const char* path, int options) +{ + FH f; + + DWORD desiredAccess = 0; + DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + switch (options) { + case O_RDONLY: + desiredAccess = GENERIC_READ; + break; + case O_WRONLY: + desiredAccess = GENERIC_WRITE; + break; + case O_RDWR: + desiredAccess = GENERIC_READ | GENERIC_WRITE; + break; + default: + D("adb_open: invalid options (0x%0x)\n", options); + errno = EINVAL; + return -1; + } + + f = _fh_alloc( &_fh_file_class ); + if ( !f ) { + errno = ENOMEM; + return -1; + } + + f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING, + 0, NULL ); + + if ( f->fh_handle == INVALID_HANDLE_VALUE ) { + _fh_close(f); + D( "adb_open: could not open '%s':", path ); + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + D( "file not found\n" ); + errno = ENOENT; + return -1; + + case ERROR_PATH_NOT_FOUND: + D( "path not found\n" ); + errno = ENOTDIR; + return -1; + + default: + D( "unknown error\n" ); + errno = ENOENT; + return -1; + } + } + + snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path ); + D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) ); + return _fh_to_int(f); +} + +/* ignore mode on Win32 */ +int adb_creat(const char* path, int mode) +{ + FH f; + + f = _fh_alloc( &_fh_file_class ); + if ( !f ) { + errno = ENOMEM; + return -1; + } + + f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL ); + + if ( f->fh_handle == INVALID_HANDLE_VALUE ) { + _fh_close(f); + D( "adb_creat: could not open '%s':", path ); + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + D( "file not found\n" ); + errno = ENOENT; + return -1; + + case ERROR_PATH_NOT_FOUND: + D( "path not found\n" ); + errno = ENOTDIR; + return -1; + + default: + D( "unknown error\n" ); + errno = ENOENT; + return -1; + } + } + snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path ); + D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) ); + return _fh_to_int(f); +} + + +int adb_read(int fd, void* buf, int len) +{ + FH f = _fh_from_int(fd); + + if (f == NULL) { + return -1; + } + + return f->clazz->_fh_read( f, buf, len ); +} + + +int adb_write(int fd, const void* buf, int len) +{ + FH f = _fh_from_int(fd); + + if (f == NULL) { + return -1; + } + + return f->clazz->_fh_write(f, buf, len); +} + + +int adb_lseek(int fd, int pos, int where) +{ + FH f = _fh_from_int(fd); + + if (!f) { + return -1; + } + + return f->clazz->_fh_lseek(f, pos, where); +} + + +int adb_close(int fd) +{ + FH f = _fh_from_int(fd); + + if (!f) { + return -1; + } + + D( "adb_close: %s\n", f->name); + _fh_close(f); + return 0; +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** socket-based file descriptors *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +static void +_socket_set_errno( void ) +{ + switch (WSAGetLastError()) { + case 0: errno = 0; break; + case WSAEWOULDBLOCK: errno = EAGAIN; break; + case WSAEINTR: errno = EINTR; break; + default: + D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() ); + errno = EINVAL; + } +} + +static void +_fh_socket_init( FH f ) +{ + f->fh_socket = INVALID_SOCKET; + f->event = WSACreateEvent(); + f->mask = 0; +} + +static int +_fh_socket_close( FH f ) +{ + /* gently tell any peer that we're closing the socket */ + shutdown( f->fh_socket, SD_BOTH ); + closesocket( f->fh_socket ); + f->fh_socket = INVALID_SOCKET; + CloseHandle( f->event ); + f->mask = 0; + return 0; +} + +static int +_fh_socket_lseek( FH f, int pos, int origin ) +{ + errno = EPIPE; + return -1; +} + +static int +_fh_socket_read( FH f, void* buf, int len ) +{ + int result = recv( f->fh_socket, buf, len, 0 ); + if (result == SOCKET_ERROR) { + _socket_set_errno(); + result = -1; + } + return result; +} + +static int +_fh_socket_write( FH f, const void* buf, int len ) +{ + int result = send( f->fh_socket, buf, len, 0 ); + if (result == SOCKET_ERROR) { + _socket_set_errno(); + result = -1; + } + return result; +} + +static void _fh_socket_hook( FH f, int event, EventHook hook ); /* forward */ + +static const FHClassRec _fh_socket_class = +{ + _fh_socket_init, + _fh_socket_close, + _fh_socket_lseek, + _fh_socket_read, + _fh_socket_write, + _fh_socket_hook +}; + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** replacement for libs/cutils/socket_xxxx.c *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +#include <winsock2.h> + +static int _winsock_init; + +static void +_cleanup_winsock( void ) +{ + WSACleanup(); +} + +static void +_init_winsock( void ) +{ + if (!_winsock_init) { + WSADATA wsaData; + int rc = WSAStartup( MAKEWORD(2,2), &wsaData); + if (rc != 0) { + fatal( "adb: could not initialize Winsock\n" ); + } + atexit( _cleanup_winsock ); + _winsock_init = 1; + } +} + +int socket_loopback_client(int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct sockaddr_in addr; + SOCKET s; + + if (!f) + return -1; + + if (!_winsock_init) + _init_winsock(); + + 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 == INVALID_SOCKET) { + D("socket_loopback_client: could not create socket\n" ); + _fh_close(f); + return -1; + } + + f->fh_socket = s; + if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port ); + _fh_close(f); + return -1; + } + snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + +#define LISTEN_BACKLOG 4 + +int socket_loopback_server(int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct sockaddr_in addr; + SOCKET s; + int n; + + if (!f) { + return -1; + } + + if (!_winsock_init) + _init_winsock(); + + 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 == INVALID_SOCKET) return -1; + + f->fh_socket = s; + + n = 1; + setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + _fh_close(f); + return -1; + } + if (type == SOCK_STREAM) { + int ret; + + ret = listen(s, LISTEN_BACKLOG); + if (ret < 0) { + _fh_close(f); + return -1; + } + } + snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + + +int socket_network_client(const char *host, int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct hostent *hp; + struct sockaddr_in addr; + SOCKET s; + + if (!f) + return -1; + + if (!_winsock_init) + _init_winsock(); + + hp = gethostbyname(host); + if(hp == 0) { + _fh_close(f); + 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 == INVALID_SOCKET) { + _fh_close(f); + return -1; + } + f->fh_socket = s; + + if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + _fh_close(f); + return -1; + } + + snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + + +int socket_inaddr_any_server(int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct sockaddr_in addr; + SOCKET s; + int n; + + if (!f) + return -1; + + if (!_winsock_init) + _init_winsock(); + + 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 == INVALID_SOCKET) { + _fh_close(f); + return -1; + } + + f->fh_socket = s; + n = 1; + setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + _fh_close(f); + return -1; + } + + if (type == SOCK_STREAM) { + int ret; + + ret = listen(s, LISTEN_BACKLOG); + if (ret < 0) { + _fh_close(f); + return -1; + } + } + snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + +#undef accept +int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) +{ + FH serverfh = _fh_from_int(serverfd); + FH fh; + + if ( !serverfh || serverfh->clazz != &_fh_socket_class ) { + D( "adb_socket_accept: invalid fd %d\n", serverfd ); + return -1; + } + + fh = _fh_alloc( &_fh_socket_class ); + if (!fh) { + D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" ); + return -1; + } + + fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen ); + if (fh->fh_socket == INVALID_SOCKET) { + _fh_close( fh ); + D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() ); + return -1; + } + + snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name ); + D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) ); + return _fh_to_int(fh); +} + + +void disable_tcp_nagle(int fd) +{ + FH fh = _fh_from_int(fd); + int on; + + if ( !fh || fh->clazz != &_fh_socket_class ) + return; + + setsockopt( fh->fh_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on) ); +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** emulated socketpairs *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +/* we implement socketpairs directly in use space for the following reasons: + * - it avoids copying data from/to the Nt kernel + * - it allows us to implement fdevent hooks easily and cheaply, something + * that is not possible with standard Win32 pipes !! + * + * basically, we use two circular buffers, each one corresponding to a given + * direction. + * + * each buffer is implemented as two regions: + * + * region A which is (a_start,a_end) + * region B which is (0, b_end) with b_end <= a_start + * + * an empty buffer has: a_start = a_end = b_end = 0 + * + * a_start is the pointer where we start reading data + * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE, + * then you start writing at b_end + * + * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE + * + * there is room when b_end < a_start || a_end < BUFER_SIZE + * + * when reading, a_start is incremented, it a_start meets a_end, then + * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on.. + */ + +#define BIP_BUFFER_SIZE 4096 + +#if 0 +#include <stdio.h> +# define BIPD(x) D x +# define BIPDUMP bip_dump_hex + +static void bip_dump_hex( const unsigned char* ptr, size_t len ) +{ + int nn, len2 = len; + + if (len2 > 8) len2 = 8; + + for (nn = 0; nn < len2; nn++) + printf("%02x", ptr[nn]); + printf(" "); + + for (nn = 0; nn < len2; nn++) { + int c = ptr[nn]; + if (c < 32 || c > 127) + c = '.'; + printf("%c", c); + } + printf("\n"); + fflush(stdout); +} + +#else +# define BIPD(x) do {} while (0) +# define BIPDUMP(p,l) BIPD(p) +#endif + +typedef struct BipBufferRec_ +{ + int a_start; + int a_end; + int b_end; + int fdin; + int fdout; + int closed; + int can_write; /* boolean */ + HANDLE evt_write; /* event signaled when one can write to a buffer */ + int can_read; /* boolean */ + HANDLE evt_read; /* event signaled when one can read from a buffer */ + CRITICAL_SECTION lock; + unsigned char buff[ BIP_BUFFER_SIZE ]; + +} BipBufferRec, *BipBuffer; + +static void +bip_buffer_init( BipBuffer buffer ) +{ + D( "bit_buffer_init %p\n", buffer ); + buffer->a_start = 0; + buffer->a_end = 0; + buffer->b_end = 0; + buffer->can_write = 1; + buffer->can_read = 0; + buffer->fdin = 0; + buffer->fdout = 0; + buffer->closed = 0; + buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL ); + buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL ); + InitializeCriticalSection( &buffer->lock ); +} + +static void +bip_buffer_close( BipBuffer bip ) +{ + bip->closed = 1; + + if (!bip->can_read) { + SetEvent( bip->evt_read ); + } + if (!bip->can_write) { + SetEvent( bip->evt_write ); + } +} + +static void +bip_buffer_done( BipBuffer bip ) +{ + BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout )); + CloseHandle( bip->evt_read ); + CloseHandle( bip->evt_write ); + DeleteCriticalSection( &bip->lock ); +} + +static int +bip_buffer_write( BipBuffer bip, const void* src, int len ) +{ + int avail, count = 0; + + if (len <= 0) + return 0; + + BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len )); + BIPDUMP( src, len ); + + EnterCriticalSection( &bip->lock ); + + while (!bip->can_write) { + int ret; + LeaveCriticalSection( &bip->lock ); + + if (bip->closed) { + errno = EPIPE; + return -1; + } + /* spinlocking here is probably unfair, but let's live with it */ + ret = WaitForSingleObject( bip->evt_write, INFINITE ); + if (ret != WAIT_OBJECT_0) { /* buffer probably closed */ + D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() ); + return 0; + } + if (bip->closed) { + errno = EPIPE; + return -1; + } + EnterCriticalSection( &bip->lock ); + } + + BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len )); + + avail = BIP_BUFFER_SIZE - bip->a_end; + if (avail > 0) + { + /* we can append to region A */ + if (avail > len) + avail = len; + + memcpy( bip->buff + bip->a_end, src, avail ); + src += avail; + count += avail; + len -= avail; + + bip->a_end += avail; + if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) { + bip->can_write = 0; + ResetEvent( bip->evt_write ); + goto Exit; + } + } + + if (len == 0) + goto Exit; + + avail = bip->a_start - bip->b_end; + assert( avail > 0 ); /* since can_write is TRUE */ + + if (avail > len) + avail = len; + + memcpy( bip->buff + bip->b_end, src, avail ); + count += avail; + bip->b_end += avail; + + if (bip->b_end == bip->a_start) { + bip->can_write = 0; + ResetEvent( bip->evt_write ); + } + +Exit: + assert( count > 0 ); + + if ( !bip->can_read ) { + bip->can_read = 1; + SetEvent( bip->evt_read ); + } + + BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", + bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read )); + LeaveCriticalSection( &bip->lock ); + + return count; + } + +static int +bip_buffer_read( BipBuffer bip, void* dst, int len ) +{ + int avail, count = 0; + + if (len <= 0) + return 0; + + BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len )); + + EnterCriticalSection( &bip->lock ); + while ( !bip->can_read ) + { +#if 0 + LeaveCriticalSection( &bip->lock ); + errno = EAGAIN; + return -1; +#else + int ret; + LeaveCriticalSection( &bip->lock ); + + if (bip->closed) { + errno = EPIPE; + return -1; + } + + ret = WaitForSingleObject( bip->evt_read, INFINITE ); + if (ret != WAIT_OBJECT_0) { /* probably closed buffer */ + D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError()); + return 0; + } + if (bip->closed) { + errno = EPIPE; + return -1; + } + EnterCriticalSection( &bip->lock ); +#endif + } + + BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len )); + + avail = bip->a_end - bip->a_start; + assert( avail > 0 ); /* since can_read is TRUE */ + + if (avail > len) + avail = len; + + memcpy( dst, bip->buff + bip->a_start, avail ); + dst += avail; + count += avail; + len -= avail; + + bip->a_start += avail; + if (bip->a_start < bip->a_end) + goto Exit; + + bip->a_start = 0; + bip->a_end = bip->b_end; + bip->b_end = 0; + + avail = bip->a_end; + if (avail > 0) { + if (avail > len) + avail = len; + memcpy( dst, bip->buff, avail ); + count += avail; + bip->a_start += avail; + + if ( bip->a_start < bip->a_end ) + goto Exit; + + bip->a_start = bip->a_end = 0; + } + + bip->can_read = 0; + ResetEvent( bip->evt_read ); + +Exit: + assert( count > 0 ); + + if (!bip->can_write ) { + bip->can_write = 1; + SetEvent( bip->evt_write ); + } + + BIPDUMP( (const unsigned char*)dst - count, count ); + BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", + bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read )); + LeaveCriticalSection( &bip->lock ); + + return count; +} + +typedef struct SocketPairRec_ +{ + BipBufferRec a2b_bip; + BipBufferRec b2a_bip; + FH a_fd; + int used; + +} SocketPairRec; + +void _fh_socketpair_init( FH f ) +{ + f->fh_pair = NULL; +} + +static int +_fh_socketpair_close( FH f ) +{ + if ( f->fh_pair ) { + SocketPair pair = f->fh_pair; + + if ( f == pair->a_fd ) { + pair->a_fd = NULL; + } + + bip_buffer_close( &pair->b2a_bip ); + bip_buffer_close( &pair->a2b_bip ); + + if ( --pair->used == 0 ) { + bip_buffer_done( &pair->b2a_bip ); + bip_buffer_done( &pair->a2b_bip ); + free( pair ); + } + f->fh_pair = NULL; + } + return 0; +} + +static int +_fh_socketpair_lseek( FH f, int pos, int origin ) +{ + errno = ESPIPE; + return -1; +} + +static int +_fh_socketpair_read( FH f, void* buf, int len ) +{ + SocketPair pair = f->fh_pair; + BipBuffer bip; + + if (!pair) + return -1; + + if ( f == pair->a_fd ) + bip = &pair->b2a_bip; + else + bip = &pair->a2b_bip; + + return bip_buffer_read( bip, buf, len ); +} + +static int +_fh_socketpair_write( FH f, const void* buf, int len ) +{ + SocketPair pair = f->fh_pair; + BipBuffer bip; + + if (!pair) + return -1; + + if ( f == pair->a_fd ) + bip = &pair->a2b_bip; + else + bip = &pair->b2a_bip; + + return bip_buffer_write( bip, buf, len ); +} + + +static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */ + +static const FHClassRec _fh_socketpair_class = +{ + _fh_socketpair_init, + _fh_socketpair_close, + _fh_socketpair_lseek, + _fh_socketpair_read, + _fh_socketpair_write, + _fh_socketpair_hook +}; + + +int adb_socketpair( int sv[2] ) +{ + FH fa, fb; + SocketPair pair; + + fa = _fh_alloc( &_fh_socketpair_class ); + fb = _fh_alloc( &_fh_socketpair_class ); + + if (!fa || !fb) + goto Fail; + + pair = malloc( sizeof(*pair) ); + if (pair == NULL) { + D("adb_socketpair: not enough memory to allocate pipes\n" ); + goto Fail; + } + + bip_buffer_init( &pair->a2b_bip ); + bip_buffer_init( &pair->b2a_bip ); + + fa->fh_pair = pair; + fb->fh_pair = pair; + pair->used = 2; + pair->a_fd = fa; + + sv[0] = _fh_to_int(fa); + sv[1] = _fh_to_int(fb); + + pair->a2b_bip.fdin = sv[0]; + pair->a2b_bip.fdout = sv[1]; + pair->b2a_bip.fdin = sv[1]; + pair->b2a_bip.fdout = sv[0]; + + snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] ); + snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] ); + D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] ); + return 0; + +Fail: + _fh_close(fb); + _fh_close(fa); + return -1; +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** fdevents emulation *****/ +/***** *****/ +/***** this is a very simple implementation, we rely on the fact *****/ +/***** that ADB doesn't use FDE_ERROR. *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +#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; + +typedef struct EventLooperRec_* EventLooper; + +typedef struct EventHookRec_ +{ + EventHook next; + FH fh; + HANDLE h; + int wanted; /* wanted event flags */ + int ready; /* ready event flags */ + void* aux; + void (*prepare)( EventHook hook ); + int (*start) ( EventHook hook ); + void (*stop) ( EventHook hook ); + int (*check) ( EventHook hook ); + int (*peek) ( EventHook hook ); +} EventHookRec; + +static EventHook _free_hooks; + +static EventHook +event_hook_alloc( FH fh ) +{ + EventHook hook = _free_hooks; + if (hook != NULL) + _free_hooks = hook->next; + else { + hook = malloc( sizeof(*hook) ); + if (hook == NULL) + fatal( "could not allocate event hook\n" ); + } + hook->next = NULL; + hook->fh = fh; + hook->wanted = 0; + hook->ready = 0; + hook->h = INVALID_HANDLE_VALUE; + hook->aux = NULL; + + hook->prepare = NULL; + hook->start = NULL; + hook->stop = NULL; + hook->check = NULL; + hook->peek = NULL; + + return hook; +} + +static void +event_hook_free( EventHook hook ) +{ + hook->fh = NULL; + hook->wanted = 0; + hook->ready = 0; + hook->next = _free_hooks; + _free_hooks = hook; +} + + +static void +event_hook_signal( EventHook hook ) +{ + FH f = hook->fh; + int fd = _fh_to_int(f); + fdevent* fde = fd_table[ fd - WIN32_FH_BASE ]; + + if (fde != NULL && fde->fd == fd) { + if ((fde->state & FDE_PENDING) == 0) { + fde->state |= FDE_PENDING; + fdevent_plist_enqueue( fde ); + } + fde->events |= hook->wanted; + } +} + + +#define MAX_LOOPER_HANDLES WIN32_MAX_FHS + +typedef struct EventLooperRec_ +{ + EventHook hooks; + HANDLE htab[ MAX_LOOPER_HANDLES ]; + int htab_count; + +} EventLooperRec; + +static EventHook* +event_looper_find_p( EventLooper looper, FH fh ) +{ + EventHook *pnode = &looper->hooks; + EventHook node = *pnode; + for (;;) { + if ( node == NULL || node->fh == fh ) + break; + pnode = &node->next; + node = *pnode; + } + return pnode; +} + +static void +event_looper_hook( EventLooper looper, int fd, int events ) +{ + FH f = _fh_from_int(fd); + EventHook *pnode; + EventHook node; + + if (f == NULL) /* invalid arg */ { + D("event_looper_hook: invalid fd=%d\n", fd); + return; + } + + pnode = event_looper_find_p( looper, f ); + node = *pnode; + if ( node == NULL ) { + node = event_hook_alloc( f ); + node->next = *pnode; + *pnode = node; + } + + if ( (node->wanted & events) != events ) { + /* this should update start/stop/check/peek */ + D("event_looper_hook: call hook for %d (new=%x, old=%x)\n", + fd, node->wanted, events); + f->clazz->_fh_hook( f, events & ~node->wanted, node ); + node->wanted |= events; + } else { + D("event_looper_hook: ignoring events %x for %d wanted=%x)\n", + events, fd, node->wanted); + } +} + +static void +event_looper_unhook( EventLooper looper, int fd, int events ) +{ + FH fh = _fh_from_int(fd); + EventHook *pnode = event_looper_find_p( looper, fh ); + EventHook node = *pnode; + + if (node != NULL) { + int events2 = events & node->wanted; + if ( events2 == 0 ) { + D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd ); + return; + } + node->wanted &= ~events2; + if (!node->wanted) { + *pnode = node->next; + event_hook_free( node ); + } + } +} + +static EventLooperRec win32_looper; + +static void fdevent_init(void) +{ + win32_looper.htab_count = 0; + win32_looper.hooks = NULL; +} + +static void fdevent_connect(fdevent *fde) +{ + EventLooper looper = &win32_looper; + int events = fde->state & FDE_EVENTMASK; + + if (events != 0) + event_looper_hook( looper, fde->fd, events ); +} + +static void fdevent_disconnect(fdevent *fde) +{ + EventLooper looper = &win32_looper; + int events = fde->state & FDE_EVENTMASK; + + if (events != 0) + event_looper_unhook( looper, fde->fd, events ); +} + +static void fdevent_update(fdevent *fde, unsigned events) +{ + EventLooper looper = &win32_looper; + unsigned events0 = fde->state & FDE_EVENTMASK; + + if (events != events0) { + int removes = events0 & ~events; + int adds = events & ~events0; + if (removes) { + D("fdevent_update: remove %x from %d\n", removes, fde->fd); + event_looper_unhook( looper, fde->fd, removes ); + } + if (adds) { + D("fdevent_update: add %x to %d\n", adds, fde->fd); + event_looper_hook ( looper, fde->fd, adds ); + } + } +} + +static void fdevent_process() +{ + EventLooper looper = &win32_looper; + EventHook hook; + int gotone = 0; + + /* if we have at least one ready hook, execute it/them */ + for (hook = looper->hooks; hook; hook = hook->next) { + hook->ready = 0; + if (hook->prepare) { + hook->prepare(hook); + if (hook->ready != 0) { + event_hook_signal( hook ); + gotone = 1; + } + } + } + + /* nothing's ready yet, so wait for something to happen */ + if (!gotone) + { + looper->htab_count = 0; + + for (hook = looper->hooks; hook; hook = hook->next) + { + if (hook->start && !hook->start(hook)) { + D( "fdevent_process: error when starting a hook\n" ); + return; + } + if (hook->h != INVALID_HANDLE_VALUE) { + int nn; + + for (nn = 0; nn < looper->htab_count; nn++) + { + if ( looper->htab[nn] == hook->h ) + goto DontAdd; + } + looper->htab[ looper->htab_count++ ] = hook->h; + DontAdd: + ; + } + } + + if (looper->htab_count == 0) { + D( "fdevent_process: nothing to wait for !!\n" ); + return; + } + + do + { + int wait_ret; + + D( "adb_win32: waiting for %d events\n", looper->htab_count ); + if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) { + D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count); + abort(); + } + wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE ); + if (wait_ret == (int)WAIT_FAILED) { + D( "adb_win32: wait failed, error %ld\n", GetLastError() ); + } else { + D( "adb_win32: got one (index %d)\n", wait_ret ); + + /* according to Cygwin, some objects like consoles wake up on "inappropriate" events + * like mouse movements. we need to filter these with the "check" function + */ + if ((unsigned)wait_ret < (unsigned)looper->htab_count) + { + for (hook = looper->hooks; hook; hook = hook->next) + { + if ( looper->htab[wait_ret] == hook->h && + (!hook->check || hook->check(hook)) ) + { + D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready ); + event_hook_signal( hook ); + gotone = 1; + break; + } + } + } + } + } + while (!gotone); + + for (hook = looper->hooks; hook; hook = hook->next) { + if (hook->stop) + hook->stop( hook ); + } + } + + for (hook = looper->hooks; hook; hook = hook->next) { + if (hook->peek && hook->peek(hook)) + event_hook_signal( hook ); + } +} + + +static void fdevent_register(fdevent *fde) +{ + int fd = fde->fd - WIN32_FH_BASE; + + if(fd < 0) { + FATAL("bogus negative fd (%d)\n", fde->fd); + } + + if(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 <= 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[fd] = fde; +} + +static void fdevent_unregister(fdevent *fde) +{ + int fd = fde->fd - WIN32_FH_BASE; + + if((fd < 0) || (fd >= fd_table_max)) { + FATAL("fd out of range (%d)\n", fde->fd); + } + + if(fd_table[fd] != fde) { + FATAL("fd_table out of sync"); + } + + fd_table[fd] = 0; + + if(!(fde->state & FDE_DONT_CLOSE)) { + dump_fde(fde, "close"); + adb_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; + + 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) == (int)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); + } + } +} + +/** FILE EVENT HOOKS + **/ + +static void _event_file_prepare( EventHook hook ) +{ + if (hook->wanted & (FDE_READ|FDE_WRITE)) { + /* we can always read/write */ + hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE); + } +} + +static int _event_file_peek( EventHook hook ) +{ + return (hook->wanted & (FDE_READ|FDE_WRITE)); +} + +static void _fh_file_hook( FH f, int events, EventHook hook ) +{ + hook->h = f->fh_handle; + hook->prepare = _event_file_prepare; + hook->peek = _event_file_peek; +} + +/** SOCKET EVENT HOOKS + **/ + +static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts ) +{ + if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) { + if (hook->wanted & FDE_READ) + hook->ready |= FDE_READ; + if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR) + hook->ready |= FDE_ERROR; + } + if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) { + if (hook->wanted & FDE_WRITE) + hook->ready |= FDE_WRITE; + if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR) + hook->ready |= FDE_ERROR; + } + if ( evts->lNetworkEvents & FD_OOB ) { + if (hook->wanted & FDE_ERROR) + hook->ready |= FDE_ERROR; + } +} + +static void _event_socket_prepare( EventHook hook ) +{ + WSANETWORKEVENTS evts; + + /* look if some of the events we want already happened ? */ + if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts )) + _event_socket_verify( hook, &evts ); +} + +static int _socket_wanted_to_flags( int wanted ) +{ + int flags = 0; + if (wanted & FDE_READ) + flags |= FD_READ | FD_ACCEPT | FD_CLOSE; + + if (wanted & FDE_WRITE) + flags |= FD_WRITE | FD_CONNECT | FD_CLOSE; + + if (wanted & FDE_ERROR) + flags |= FD_OOB; + + return flags; +} + +static int _event_socket_start( EventHook hook ) +{ + /* create an event which we're going to wait for */ + FH fh = hook->fh; + long flags = _socket_wanted_to_flags( hook->wanted ); + + hook->h = fh->event; + if (hook->h == INVALID_HANDLE_VALUE) { + D( "_event_socket_start: no event for %s\n", fh->name ); + return 0; + } + + if ( flags != fh->mask ) { + D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags ); + if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) { + D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() ); + CloseHandle( hook->h ); + hook->h = INVALID_HANDLE_VALUE; + exit(1); + return 0; + } + fh->mask = flags; + } + return 1; +} + +static void _event_socket_stop( EventHook hook ) +{ + hook->h = INVALID_HANDLE_VALUE; +} + +static int _event_socket_check( EventHook hook ) +{ + int result = 0; + FH fh = hook->fh; + WSANETWORKEVENTS evts; + + if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) { + _event_socket_verify( hook, &evts ); + result = (hook->ready != 0); + if (result) { + ResetEvent( hook->h ); + } + } + D( "_event_socket_check %s returns %d\n", fh->name, result ); + return result; +} + +static int _event_socket_peek( EventHook hook ) +{ + WSANETWORKEVENTS evts; + FH fh = hook->fh; + + /* look if some of the events we want already happened ? */ + if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) { + _event_socket_verify( hook, &evts ); + if (hook->ready) + ResetEvent( hook->h ); + } + + return hook->ready != 0; +} + + + +static void _fh_socket_hook( FH f, int events, EventHook hook ) +{ + hook->prepare = _event_socket_prepare; + hook->start = _event_socket_start; + hook->stop = _event_socket_stop; + hook->check = _event_socket_check; + hook->peek = _event_socket_peek; + + _event_socket_start( hook ); +} + +/** SOCKETPAIR EVENT HOOKS + **/ + +static void _event_socketpair_prepare( EventHook hook ) +{ + FH fh = hook->fh; + SocketPair pair = fh->fh_pair; + BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip; + BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip; + + if (hook->wanted & FDE_READ && rbip->can_read) + hook->ready |= FDE_READ; + + if (hook->wanted & FDE_WRITE && wbip->can_write) + hook->ready |= FDE_WRITE; + } + + static int _event_socketpair_start( EventHook hook ) + { + FH fh = hook->fh; + SocketPair pair = fh->fh_pair; + BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip; + BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip; + + if (hook->wanted == FDE_READ) + hook->h = rbip->evt_read; + + else if (hook->wanted == FDE_WRITE) + hook->h = wbip->evt_write; + + else { + D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" ); + return 0; + } + D( "_event_socketpair_start: hook %s for %x wanted=%x\n", + hook->fh->name, _fh_to_int(fh), hook->wanted); + return 1; +} + +static int _event_socketpair_peek( EventHook hook ) +{ + _event_socketpair_prepare( hook ); + return hook->ready != 0; +} + +static void _fh_socketpair_hook( FH fh, int events, EventHook hook ) +{ + hook->prepare = _event_socketpair_prepare; + hook->start = _event_socketpair_start; + hook->peek = _event_socketpair_peek; +} + + +void +adb_sysdeps_init( void ) +{ +#define ADB_MUTEX(x) InitializeCriticalSection( & x ); +#include "mutex_list.h" + InitializeCriticalSection( &_win32_lock ); +} + diff --git a/adb/test_track_devices.c b/adb/test_track_devices.c new file mode 100644 index 0000000..77b3ad9 --- /dev/null +++ b/adb/test_track_devices.c @@ -0,0 +1,97 @@ +/* a simple test program, connects to ADB server, and opens a track-devices session */ +#include <netdb.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <memory.h> + +static void +panic( const char* msg ) +{ + fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno)); + exit(1); +} + +static int +unix_write( int fd, const char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = write(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + +static int +unix_read( int fd, char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = read(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + + +int main( void ) +{ + int ret, s; + struct sockaddr_in server; + char buffer[1024]; + const char* request = "host:track-devices"; + int len; + + memset( &server, 0, sizeof(server) ); + server.sin_family = AF_INET; + server.sin_port = htons(5037); + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + s = socket( PF_INET, SOCK_STREAM, 0 ); + ret = connect( s, (struct sockaddr*) &server, sizeof(server) ); + if (ret < 0) panic( "could not connect to server" ); + + /* send the request */ + len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request ); + if (unix_write(s, buffer, len) < 0) + panic( "could not send request" ); + + /* read the OKAY answer */ + if (unix_read(s, buffer, 4) != 4) + panic( "could not read request" ); + + printf( "server answer: %.*s\n", 4, buffer ); + + /* now loop */ + for (;;) { + char head[5] = "0000"; + + if (unix_read(s, head, 4) < 0) + panic("could not read length"); + + if ( sscanf( head, "%04x", &len ) != 1 ) + panic("could not decode length"); + + if (unix_read(s, buffer, len) != len) + panic("could not read data"); + + printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer ); + } + close(s); +} diff --git a/adb/test_track_jdwp.c b/adb/test_track_jdwp.c new file mode 100644 index 0000000..8ecc6b8 --- /dev/null +++ b/adb/test_track_jdwp.c @@ -0,0 +1,97 @@ +/* a simple test program, connects to ADB server, and opens a track-devices session */ +#include <netdb.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <memory.h> + +static void +panic( const char* msg ) +{ + fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno)); + exit(1); +} + +static int +unix_write( int fd, const char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = write(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + +static int +unix_read( int fd, char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = read(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + + +int main( void ) +{ + int ret, s; + struct sockaddr_in server; + char buffer[1024]; + const char* request = "track-jdwp"; + int len; + + memset( &server, 0, sizeof(server) ); + server.sin_family = AF_INET; + server.sin_port = htons(5037); + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + s = socket( PF_INET, SOCK_STREAM, 0 ); + ret = connect( s, (struct sockaddr*) &server, sizeof(server) ); + if (ret < 0) panic( "could not connect to server" ); + + /* send the request */ + len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request ); + if (unix_write(s, buffer, len) < 0) + panic( "could not send request" ); + + /* read the OKAY answer */ + if (unix_read(s, buffer, 4) != 4) + panic( "could not read request" ); + + printf( "server answer: %.*s\n", 4, buffer ); + + /* now loop */ + for (;;) { + char head[5] = "0000"; + + if (unix_read(s, head, 4) < 0) + panic("could not read length"); + + if ( sscanf( head, "%04x", &len ) != 1 ) + panic("could not decode length"); + + if (unix_read(s, buffer, len) != len) + panic("could not read data"); + + printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer ); + } + close(s); +} diff --git a/adb/transport.c b/adb/transport.c new file mode 100644 index 0000000..c76f1a5 --- /dev/null +++ b/adb/transport.c @@ -0,0 +1,958 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +static void transport_unref(atransport *t); + +static atransport transport_list = { + .next = &transport_list, + .prev = &transport_list, +}; + +ADB_MUTEX_DEFINE( transport_lock ); + +#if ADB_TRACE +static void dump_hex( const unsigned char* ptr, size_t len ) +{ + int nn, len2 = len; + + if (len2 > 16) len2 = 16; + + for (nn = 0; nn < len2; nn++) + D("%02x", ptr[nn]); + D(" "); + + for (nn = 0; nn < len2; nn++) { + int c = ptr[nn]; + if (c < 32 || c > 127) + c = '.'; + D("%c", c); + } + D("\n"); + fflush(stdout); +} +#endif + +void +kick_transport(atransport* t) +{ + if (t && !t->kicked) + { + int kicked; + + adb_mutex_lock(&transport_lock); + kicked = t->kicked; + if (!kicked) + t->kicked = 1; + adb_mutex_unlock(&transport_lock); + + if (!kicked) + t->kick(t); + } +} + +void +run_transport_disconnects(atransport* t) +{ + adisconnect* dis = t->disconnects.next; + + D("run_transport_disconnects: %p (%s)\n", t, t->serial ? t->serial : "unknown" ); + while (dis != &t->disconnects) { + adisconnect* next = dis->next; + dis->func( dis->opaque, t ); + dis = next; + } +} + +static int +read_packet(int fd, apacket** ppacket) +{ + char *p = (char*)ppacket; /* really read a packet address */ + int r; + int len = sizeof(*ppacket); + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + D("read_packet: %d error %d %d\n", fd, r, errno); + if((r < 0) && (errno == EINTR)) continue; + return -1; + } + } + +#if ADB_TRACE + if (ADB_TRACING) + { + unsigned command = (*ppacket)->msg.command; + int len = (*ppacket)->msg.data_length; + char cmd[5]; + int n; + + for (n = 0; n < 4; n++) { + int b = (command >> (n*8)) & 255; + if (b >= 32 && b < 127) + cmd[n] = (char)b; + else + cmd[n] = '.'; + } + cmd[4] = 0; + + D("read_packet: %d ok: [%08x %s] %08x %08x (%d) ", + fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len); + dump_hex((*ppacket)->data, len); + } +#endif + return 0; +} + +static int +write_packet(int fd, apacket** ppacket) +{ + char *p = (char*) ppacket; /* we really write the packet address */ + int r, len = sizeof(ppacket); + +#if ADB_TRACE + if (ADB_TRACING) + { + unsigned command = (*ppacket)->msg.command; + int len = (*ppacket)->msg.data_length; + char cmd[5]; + int n; + + for (n = 0; n < 4; n++) { + int b = (command >> (n*8)) & 255; + if (b >= 32 && b < 127) + cmd[n] = (char)b; + else + cmd[n] = '.'; + } + cmd[4] = 0; + + D("write_packet: %d [%08x %s] %08x %08x (%d) ", + fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len); + dump_hex((*ppacket)->data, len); + } +#endif + len = sizeof(ppacket); + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + D("write_packet: %d error %d %d\n", fd, r, errno); + if((r < 0) && (errno == EINTR)) continue; + return -1; + } + } + return 0; +} + +static void transport_socket_events(int fd, unsigned events, void *_t) +{ + if(events & FDE_READ){ + apacket *p = 0; + if(read_packet(fd, &p)){ + D("failed to read packet from transport socket on fd %d\n", fd); + } else { + handle_packet(p, (atransport *) _t); + } + } +} + +void send_packet(apacket *p, atransport *t) +{ + unsigned char *x; + unsigned sum; + unsigned count; + + p->msg.magic = p->msg.command ^ 0xffffffff; + + count = p->msg.data_length; + x = (unsigned char *) p->data; + sum = 0; + while(count-- > 0){ + sum += *x++; + } + p->msg.data_check = sum; + + print_packet("send", p); + + if (t == NULL) { + fatal_errno("Transport is null"); + D("Transport is null \n"); + } + + if(write_packet(t->transport_socket, &p)){ + fatal_errno("cannot enqueue packet on transport socket"); + } +} + +/* The transport is opened by transport_register_func before +** the input and output threads are started. +** +** The output thread issues a SYNC(1, token) message to let +** the input thread know to start things up. In the event +** of transport IO failure, the output thread will post a +** SYNC(0,0) message to ensure shutdown. +** +** The transport will not actually be closed until both +** threads exit, but the input thread will kick the transport +** on its way out to disconnect the underlying device. +*/ + +static void *output_thread(void *_t) +{ + atransport *t = _t; + apacket *p; + + D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd ); + + D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1); + p = get_apacket(); + p->msg.command = A_SYNC; + p->msg.arg0 = 1; + p->msg.arg1 = ++(t->sync_token); + p->msg.magic = A_SYNC ^ 0xffffffff; + if(write_packet(t->fd, &p)) { + put_apacket(p); + D("from_remote: failed to write SYNC apacket to transport %p", t); + goto oops; + } + + D("from_remote: data pump for transport %p\n", t); + for(;;) { + p = get_apacket(); + + if(t->read_from_remote(p, t) == 0){ + D("from_remote: received remote packet, sending to transport %p\n", + t); + if(write_packet(t->fd, &p)){ + put_apacket(p); + D("from_remote: failed to write apacket to transport %p", t); + goto oops; + } + } else { + D("from_remote: remote read failed for transport %p\n", p); + put_apacket(p); + break; + } + } + + D("from_remote: SYNC offline for transport %p\n", t); + p = get_apacket(); + p->msg.command = A_SYNC; + p->msg.arg0 = 0; + p->msg.arg1 = 0; + p->msg.magic = A_SYNC ^ 0xffffffff; + if(write_packet(t->fd, &p)) { + put_apacket(p); + D("from_remote: failed to write SYNC apacket to transport %p", t); + } + +oops: + D("from_remote: thread is exiting for transport %p\n", t); + kick_transport(t); + transport_unref(t); + return 0; +} + +static void *input_thread(void *_t) +{ + atransport *t = _t; + apacket *p; + int active = 0; + + D("to_remote: starting input_thread for %p, reading from fd %d\n", + t, t->fd); + + for(;;){ + if(read_packet(t->fd, &p)) { + D("to_remote: failed to read apacket from transport %p on fd %d\n", + t, t->fd ); + break; + } + if(p->msg.command == A_SYNC){ + if(p->msg.arg0 == 0) { + D("to_remote: transport %p SYNC offline\n", t); + put_apacket(p); + break; + } else { + if(p->msg.arg1 == t->sync_token) { + D("to_remote: transport %p SYNC online\n", t); + active = 1; + } else { + D("to_remote: trandport %p ignoring SYNC %d != %d\n", + t, p->msg.arg1, t->sync_token); + } + } + } else { + if(active) { + D("to_remote: transport %p got packet, sending to remote\n", t); + t->write_to_remote(p, t); + } else { + D("to_remote: transport %p ignoring packet while offline\n", t); + } + } + + put_apacket(p); + } + + // this is necessary to avoid a race condition that occured when a transport closes + // while a client socket is still active. + close_all_sockets(t); + + D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd); + kick_transport(t); + transport_unref(t); + return 0; +} + + +static int transport_registration_send = -1; +static int transport_registration_recv = -1; +static fdevent transport_registration_fde; + + +#if ADB_HOST +static int list_transports_msg(char* buffer, size_t bufferlen) +{ + char head[5]; + int len; + + len = list_transports(buffer+4, bufferlen-4); + snprintf(head, sizeof(head), "%04x", len); + memcpy(buffer, head, 4); + len += 4; + return len; +} + +/* this adds support required by the 'track-devices' service. + * this is used to send the content of "list_transport" to any + * number of client connections that want it through a single + * live TCP connection + */ +typedef struct device_tracker device_tracker; +struct device_tracker { + asocket socket; + int update_needed; + device_tracker* next; +}; + +/* linked list of all device trackers */ +static device_tracker* device_tracker_list; + +static void +device_tracker_remove( device_tracker* tracker ) +{ + device_tracker** pnode = &device_tracker_list; + device_tracker* node = *pnode; + + adb_mutex_lock( &transport_lock ); + while (node) { + if (node == tracker) { + *pnode = node->next; + break; + } + pnode = &node->next; + node = *pnode; + } + adb_mutex_unlock( &transport_lock ); +} + +static void +device_tracker_close( asocket* socket ) +{ + device_tracker* tracker = (device_tracker*) socket; + asocket* peer = socket->peer; + + D( "device tracker %p removed\n", tracker); + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + device_tracker_remove(tracker); + free(tracker); +} + +static int +device_tracker_enqueue( asocket* socket, apacket* p ) +{ + /* you can't read from a device tracker, close immediately */ + put_apacket(p); + device_tracker_close(socket); + return -1; +} + +static int +device_tracker_send( device_tracker* tracker, + const char* buffer, + int len ) +{ + apacket* p = get_apacket(); + asocket* peer = tracker->socket.peer; + + memcpy(p->data, buffer, len); + p->len = len; + return peer->enqueue( peer, p ); +} + + +static void +device_tracker_ready( asocket* socket ) +{ + device_tracker* tracker = (device_tracker*) socket; + + /* we want to send the device list when the tracker connects + * for the first time, even if no update occured */ + if (tracker->update_needed > 0) { + char buffer[1024]; + int len; + + tracker->update_needed = 0; + + len = list_transports_msg(buffer, sizeof(buffer)); + device_tracker_send(tracker, buffer, len); + } +} + + +asocket* +create_device_tracker(void) +{ + device_tracker* tracker = calloc(1,sizeof(*tracker)); + + if(tracker == 0) fatal("cannot allocate device tracker"); + + D( "device tracker %p created\n", tracker); + + tracker->socket.enqueue = device_tracker_enqueue; + tracker->socket.ready = device_tracker_ready; + tracker->socket.close = device_tracker_close; + tracker->update_needed = 1; + + tracker->next = device_tracker_list; + device_tracker_list = tracker; + + return &tracker->socket; +} + + +/* call this function each time the transport list has changed */ +void update_transports(void) +{ + char buffer[1024]; + int len; + device_tracker* tracker; + + len = list_transports_msg(buffer, sizeof(buffer)); + + tracker = device_tracker_list; + while (tracker != NULL) { + device_tracker* next = tracker->next; + /* note: this may destroy the tracker if the connection is closed */ + device_tracker_send(tracker, buffer, len); + tracker = next; + } +} +#else +void update_transports(void) +{ + // nothing to do on the device side +} +#endif // ADB_HOST + +typedef struct tmsg tmsg; +struct tmsg +{ + atransport *transport; + int action; +}; + +static int +transport_read_action(int fd, struct tmsg* m) +{ + char *p = (char*)m; + int len = sizeof(*m); + int r; + + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if((r < 0) && (errno == EINTR)) continue; + D("transport_read_action: on fd %d, error %d: %s\n", + fd, errno, strerror(errno)); + return -1; + } + } + return 0; +} + +static int +transport_write_action(int fd, struct tmsg* m) +{ + char *p = (char*)m; + int len = sizeof(*m); + int r; + + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if((r < 0) && (errno == EINTR)) continue; + D("transport_write_action: on fd %d, error %d: %s\n", + fd, errno, strerror(errno)); + return -1; + } + } + return 0; +} + +static void transport_registration_func(int _fd, unsigned ev, void *data) +{ + tmsg m; + adb_thread_t output_thread_ptr; + adb_thread_t input_thread_ptr; + int s[2]; + atransport *t; + + if(!(ev & FDE_READ)) { + return; + } + + if(transport_read_action(_fd, &m)) { + fatal_errno("cannot read transport registration socket"); + } + + t = m.transport; + + if(m.action == 0){ + D("transport: %p removing and free'ing %d\n", t, t->transport_socket); + + /* IMPORTANT: the remove closes one half of the + ** socket pair. The close closes the other half. + */ + fdevent_remove(&(t->transport_fde)); + adb_close(t->fd); + + adb_mutex_lock(&transport_lock); + t->next->prev = t->prev; + t->prev->next = t->next; + adb_mutex_unlock(&transport_lock); + + run_transport_disconnects(t); + + if (t->product) + free(t->product); + if (t->serial) + free(t->serial); + + memset(t,0xee,sizeof(atransport)); + free(t); + + update_transports(); + return; + } + + /* initial references are the two threads */ + t->ref_count = 2; + + if(adb_socketpair(s)) { + fatal_errno("cannot open transport socketpair"); + } + + D("transport: %p (%d,%d) starting\n", t, s[0], s[1]); + + t->transport_socket = s[0]; + t->fd = s[1]; + + /* put us on the master device list */ + adb_mutex_lock(&transport_lock); + t->next = &transport_list; + t->prev = transport_list.prev; + t->next->prev = t; + t->prev->next = t; + adb_mutex_unlock(&transport_lock); + + D("transport: %p install %d\n", t, t->transport_socket ); + fdevent_install(&(t->transport_fde), + t->transport_socket, + transport_socket_events, + t); + + fdevent_set(&(t->transport_fde), FDE_READ); + + if(adb_thread_create(&input_thread_ptr, input_thread, t)){ + fatal_errno("cannot create input thread"); + } + + if(adb_thread_create(&output_thread_ptr, output_thread, t)){ + fatal_errno("cannot create output thread"); + } + + t->disconnects.next = t->disconnects.prev = &t->disconnects; + + update_transports(); +} + +void init_transport_registration(void) +{ + int s[2]; + + if(adb_socketpair(s)){ + fatal_errno("cannot open transport registration socketpair"); + } + + transport_registration_send = s[0]; + transport_registration_recv = s[1]; + + fdevent_install(&transport_registration_fde, + transport_registration_recv, + transport_registration_func, + 0); + + fdevent_set(&transport_registration_fde, FDE_READ); +} + +/* the fdevent select pump is single threaded */ +static void register_transport(atransport *transport) +{ + tmsg m; + m.transport = transport; + m.action = 1; + D("transport: %p registered\n", transport); + if(transport_write_action(transport_registration_send, &m)) { + fatal_errno("cannot write transport registration socket\n"); + } +} + +static void remove_transport(atransport *transport) +{ + tmsg m; + m.transport = transport; + m.action = 0; + D("transport: %p removed\n", transport); + if(transport_write_action(transport_registration_send, &m)) { + fatal_errno("cannot write transport registration socket\n"); + } +} + + +static void transport_unref(atransport *t) +{ + if (t) { + adb_mutex_lock(&transport_lock); + t->ref_count--; + D("transport: %p R- (ref=%d)\n", t, t->ref_count); + if (t->ref_count == 0) { + D("transport: %p kicking and closing\n", t); + if (!t->kicked) { + t->kicked = 1; + t->kick(t); + } + t->close(t); + remove_transport(t); + } + adb_mutex_unlock(&transport_lock); + } +} + +void add_transport_disconnect(atransport* t, adisconnect* dis) +{ + adb_mutex_lock(&transport_lock); + dis->next = &t->disconnects; + dis->prev = dis->next->prev; + dis->prev->next = dis; + dis->next->prev = dis; + adb_mutex_unlock(&transport_lock); +} + +void remove_transport_disconnect(atransport* t, adisconnect* dis) +{ + dis->prev->next = dis->next; + dis->next->prev = dis->prev; + dis->next = dis->prev = dis; +} + + +atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out) +{ + atransport *t; + atransport *result = NULL; + int ambiguous = 0; + +retry: + if (error_out) + *error_out = "device not found"; + + adb_mutex_lock(&transport_lock); + for (t = transport_list.next; t != &transport_list; t = t->next) { + /* check for matching serial number */ + if (serial) { + if (t->serial && !strcmp(serial, t->serial)) { + result = t; + break; + } + } else { + if (ttype == kTransportUsb && t->type == kTransportUsb) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } else if (ttype == kTransportLocal && t->type == kTransportLocal) { + if (result) { + if (error_out) + *error_out = "more than one emulator"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } else if (ttype == kTransportAny) { + if (result) { + if (error_out) + *error_out = "more than one device and emulator"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } + } + } + adb_mutex_unlock(&transport_lock); + + if (result) { + /* offline devices are ignored -- they are either being born or dying */ + if (result && result->connection_state == CS_OFFLINE) { + if (error_out) + *error_out = "device offline"; + result = NULL; + } + + /* check for required connection state */ + if (result && state != CS_ANY && result->connection_state != state) { + if (error_out) + *error_out = "invalid device state"; + result = NULL; + } + } + + if (result) { + /* found one that we can take */ + if (error_out) + *error_out = NULL; + } else if (state != CS_ANY && (serial || !ambiguous)) { + adb_sleep_ms(1000); + goto retry; + } + + return result; +} + +#if ADB_HOST +static const char *statename(atransport *t) +{ + switch(t->connection_state){ + case CS_OFFLINE: return "offline"; + case CS_BOOTLOADER: return "bootloader"; + case CS_DEVICE: return "device"; + case CS_HOST: return "host"; + case CS_RECOVERY: return "recovery"; + default: return "unknown"; + } +} + +int list_transports(char *buf, size_t bufsize) +{ + char* p = buf; + char* end = buf + bufsize; + int len; + atransport *t; + + /* XXX OVERRUN PROBLEMS XXX */ + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + len = snprintf(p, end - p, "%s\t%s\n", + t->serial ? t->serial : "", + statename(t)); + + if (p + len >= end) { + /* discard last line if buffer is too short */ + break; + } + p += len; + } + p[0] = 0; + adb_mutex_unlock(&transport_lock); + return p - buf; +} + + +/* hack for osx */ +void close_usb_devices() +{ + atransport *t; + + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + if ( !t->kicked ) { + t->kicked = 1; + t->kick(t); + } + } + adb_mutex_unlock(&transport_lock); +} +#endif // ADB_HOST + +void register_socket_transport(int s, const char *serial, int port) +{ + atransport *t = calloc(1, sizeof(atransport)); + D("transport: %p init'ing for socket %d, on port %d\n", t, s, port); + if ( init_socket_transport(t, s, port) < 0 ) { + adb_close(s); + free(t); + return; + } + if(serial) { + t->serial = strdup(serial); + } + register_transport(t); +} + +void register_usb_transport(usb_handle *usb, const char *serial) +{ + atransport *t = calloc(1, sizeof(atransport)); + D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, + serial ? serial : ""); + init_usb_transport(t, usb); + if(serial) { + t->serial = strdup(serial); + } + register_transport(t); +} + + +#undef TRACE_TAG +#define TRACE_TAG TRACE_RWX + +int readx(int fd, void *ptr, size_t len) +{ + char *p = ptr; + int r; +#if ADB_TRACE + int len0 = len; +#endif + D("readx: %d %p %d\n", fd, ptr, (int)len); + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + D("readx: %d %d %s\n", fd, r, strerror(errno)); + if((r < 0) && (errno == EINTR)) continue; + return -1; + } + } + +#if ADB_TRACE + D("readx: %d ok: ", fd); + dump_hex( ptr, len0 ); +#endif + return 0; +} + +int writex(int fd, const void *ptr, size_t len) +{ + char *p = (char*) ptr; + int r; + +#if ADB_TRACE + D("writex: %d %p %d: ", fd, ptr, (int)len); + dump_hex( ptr, len ); +#endif + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + D("writex: %d %d %s\n", fd, r, strerror(errno)); + if((r < 0) && (errno == EINTR)) continue; + return -1; + } + } + + D("writex: %d ok\n", fd); + return 0; +} + +int check_header(apacket *p) +{ + if(p->msg.magic != (p->msg.command ^ 0xffffffff)) { + D("check_header(): invalid magic\n"); + return -1; + } + + if(p->msg.data_length > MAX_PAYLOAD) { + D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length); + return -1; + } + + return 0; +} + +int check_data(apacket *p) +{ + unsigned count, sum; + unsigned char *x; + + count = p->msg.data_length; + x = p->data; + sum = 0; + while(count-- > 0) { + sum += *x++; + } + + if(sum != p->msg.data_check) { + return -1; + } else { + return 0; + } +} + diff --git a/adb/transport_local.c b/adb/transport_local.c new file mode 100644 index 0000000..be01f29 --- /dev/null +++ b/adb/transport_local.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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "sysdeps.h" +#include <sys/types.h> + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +#ifdef __ppc__ +#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) +static inline void fix_endians(apacket *p) +{ + p->msg.command = H4(p->msg.command); + p->msg.arg0 = H4(p->msg.arg0); + p->msg.arg1 = H4(p->msg.arg1); + p->msg.data_length = H4(p->msg.data_length); + p->msg.data_check = H4(p->msg.data_check); + p->msg.magic = H4(p->msg.magic); +} +#else +#define fix_endians(p) do {} while (0) +#endif + +#if ADB_HOST +/* we keep a list of opened transports, transport 0 is bound to 5555, + * transport 1 to 5557, .. transport n to 5555 + n*2. the list is used + * to detect when we're trying to connect twice to a given local transport + */ +#define ADB_LOCAL_TRANSPORT_MAX 16 + +ADB_MUTEX_DEFINE( local_transports_lock ); + +static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ]; +#endif /* ADB_HOST */ + +static int remote_read(apacket *p, atransport *t) +{ + if(readx(t->sfd, &p->msg, sizeof(amessage))){ + D("remote local: read terminated (message)\n"); + return -1; + } + + fix_endians(p); + +#if 0 && defined __ppc__ + D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n", + p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic); +#endif + if(check_header(p)) { + D("bad header: terminated (data)\n"); + return -1; + } + + if(readx(t->sfd, p->data, p->msg.data_length)){ + D("remote local: terminated (data)\n"); + return -1; + } + + if(check_data(p)) { + D("bad data: terminated (data)\n"); + return -1; + } + + return 0; +} + +static int remote_write(apacket *p, atransport *t) +{ + int length = p->msg.data_length; + + fix_endians(p); + +#if 0 && defined __ppc__ + D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n", + p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic); +#endif + if(writex(t->sfd, &p->msg, sizeof(amessage) + length)) { + D("remote local: write terminated\n"); + return -1; + } + + return 0; +} + + +int local_connect(int port) +{ + char buf[64]; + int fd = -1; + +#if ADB_HOST + const char *host = getenv("ADBHOST"); + if (host) { + fd = socket_network_client(host, port, SOCK_STREAM); + } +#endif + if (fd < 0) { + fd = socket_loopback_client(port, SOCK_STREAM); + } + + if (fd >= 0) { + D("client: connected on remote on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, port - 1); + register_socket_transport(fd, buf, port); + return 0; + } + return -1; +} + + +static void *client_socket_thread(void *x) +{ +#if ADB_HOST + int port = ADB_LOCAL_TRANSPORT_PORT; + int count = ADB_LOCAL_TRANSPORT_MAX; + + D("transport: client_socket_thread() starting\n"); + + /* try to connect to any number of running emulator instances */ + /* this is only done when ADB starts up. later, each new emulator */ + /* will send a message to ADB to indicate that is is starting up */ + for ( ; count > 0; count--, port += 2 ) { + (void) local_connect(port); + } +#endif + return 0; +} + +static void *server_socket_thread(void *x) +{ + int serverfd, fd; + struct sockaddr addr; + socklen_t alen; + + D("transport: server_socket_thread() starting\n"); + serverfd = -1; + for(;;) { + if(serverfd == -1) { + serverfd = socket_inaddr_any_server(ADB_LOCAL_TRANSPORT_PORT, SOCK_STREAM); + if(serverfd < 0) { + D("server: cannot bind socket yet\n"); + adb_sleep_ms(1000); + continue; + } + close_on_exec(serverfd); + } + + alen = sizeof(addr); + D("server: trying to get new connection from %d\n", ADB_LOCAL_TRANSPORT_PORT); + fd = adb_socket_accept(serverfd, &addr, &alen); + if(fd >= 0) { + D("server: new connection on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + register_socket_transport(fd,"host",ADB_LOCAL_TRANSPORT_PORT); + } + } + D("transport: server_socket_thread() exiting\n"); + return 0; +} + +void local_init(void) +{ + adb_thread_t thr; + void* (*func)(void *); + + if(HOST) { + func = client_socket_thread; + } else { + func = server_socket_thread; + } + + D("transport: local %s init\n", HOST ? "client" : "server"); + + if(adb_thread_create(&thr, func, 0)) { + fatal_errno("cannot create local socket %s thread", + HOST ? "client" : "server"); + } +} + +static void remote_kick(atransport *t) +{ + int fd = t->sfd; + t->sfd = -1; + adb_close(fd); + +#if ADB_HOST + if(HOST) { + int nn; + adb_mutex_lock( &local_transports_lock ); + for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) { + if (local_transports[nn] == t) { + local_transports[nn] = NULL; + break; + } + } + adb_mutex_unlock( &local_transports_lock ); + } +#endif +} + +static void remote_close(atransport *t) +{ + adb_close(t->fd); +} + +int init_socket_transport(atransport *t, int s, int port) +{ + int fail = 0; + + t->kick = remote_kick; + t->close = remote_close; + t->read_from_remote = remote_read; + t->write_to_remote = remote_write; + t->sfd = s; + t->sync_token = 1; + t->connection_state = CS_OFFLINE; + t->type = kTransportLocal; + +#if ADB_HOST + if (HOST) { + adb_mutex_lock( &local_transports_lock ); + { + int index = (port - ADB_LOCAL_TRANSPORT_PORT)/2; + + if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) { + D("bad local transport port number: %d\n", port); + fail = -1; + } + else if (local_transports[index] != NULL) { + D("local transport for port %d already registered (%p)?\n", + port, local_transports[index]); + fail = -1; + } + else + local_transports[index] = t; + } + adb_mutex_unlock( &local_transports_lock ); + } +#endif + return fail; +} diff --git a/adb/transport_usb.c b/adb/transport_usb.c new file mode 100644 index 0000000..01c4a7e --- /dev/null +++ b/adb/transport_usb.c @@ -0,0 +1,147 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sysdeps.h> + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +/* XXX better define? */ +#ifdef __ppc__ +#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) +static inline void fix_endians(apacket *p) +{ + p->msg.command = H4(p->msg.command); + p->msg.arg0 = H4(p->msg.arg0); + p->msg.arg1 = H4(p->msg.arg1); + p->msg.data_length = H4(p->msg.data_length); + p->msg.data_check = H4(p->msg.data_check); + p->msg.magic = H4(p->msg.magic); +} +unsigned host_to_le32(unsigned n) +{ + return H4(n); +} +#else +#define fix_endians(p) do {} while (0) +unsigned host_to_le32(unsigned n) +{ + return n; +} +#endif + +static int remote_read(apacket *p, atransport *t) +{ + if(usb_read(t->usb, &p->msg, sizeof(amessage))){ + D("remote usb: read terminated (message)\n"); + return -1; + } + + fix_endians(p); + + if(check_header(p)) { + D("remote usb: check_header failed\n"); + return -1; + } + + if(p->msg.data_length) { + if(usb_read(t->usb, p->data, p->msg.data_length)){ + D("remote usb: terminated (data)\n"); + return -1; + } + } + + if(check_data(p)) { + D("remote usb: check_data failed\n"); + return -1; + } + + return 0; +} + +static int remote_write(apacket *p, atransport *t) +{ + unsigned size = p->msg.data_length; + + fix_endians(p); + + if(usb_write(t->usb, &p->msg, sizeof(amessage))) { + D("remote usb: 1 - write terminated\n"); + return -1; + } + if(p->msg.data_length == 0) return 0; + if(usb_write(t->usb, &p->data, size)) { + D("remote usb: 2 - write terminated\n"); + return -1; + } + + return 0; +} + +static void remote_close(atransport *t) +{ + usb_close(t->usb); + t->usb = 0; +} + +static void remote_kick(atransport *t) +{ + usb_kick(t->usb); +} + +void init_usb_transport(atransport *t, usb_handle *h) +{ + D("transport: usb\n"); + t->close = remote_close; + t->kick = remote_kick; + t->read_from_remote = remote_read; + t->write_to_remote = remote_write; + t->sync_token = 1; + t->connection_state = CS_OFFLINE; + t->type = kTransportUsb; + t->usb = h; + +#if ADB_HOST + HOST = 1; +#else + HOST = 0; +#endif +} + +int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol) +{ + if (vid == VENDOR_ID_GOOGLE) { + /* might support adb */ + } else if (vid == VENDOR_ID_HTC) { + /* might support adb */ + } else { + /* not supported */ + return 0; + } + + /* class:vendor (0xff) subclass:android (0x42) proto:adb (0x01) */ + if(usb_class == 0xff) { + if((usb_subclass == 0x42) && (usb_protocol == 0x01)) { + return 1; + } + } + + return 0; +} diff --git a/adb/usb_linux.c b/adb/usb_linux.c new file mode 100644 index 0000000..32ce0a9 --- /dev/null +++ b/adb/usb_linux.c @@ -0,0 +1,654 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> + +#include <linux/usbdevice_fs.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include <linux/usb/ch9.h> +#else +#include <linux/usb_ch9.h> +#endif +#include <asm/byteorder.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + + +/* usb scan debugging is waaaay too verbose */ +#define DBGX(x...) + +static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER; + +struct usb_handle +{ + usb_handle *prev; + usb_handle *next; + + char fname[64]; + int desc; + unsigned char ep_in; + unsigned char ep_out; + + unsigned zero_mask; + + struct usbdevfs_urb urb_in; + struct usbdevfs_urb urb_out; + + int urb_in_busy; + int urb_out_busy; + int dead; + + adb_cond_t notify; + adb_mutex_t lock; + + // for garbage collecting disconnected devices + int mark; + + // ID of thread currently in REAPURB + pthread_t reaper_thread; +}; + +static usb_handle handle_list = { + .prev = &handle_list, + .next = &handle_list, +}; + +static int known_device(const char *dev_name) +{ + usb_handle *usb; + + adb_mutex_lock(&usb_lock); + for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ + if(!strcmp(usb->fname, dev_name)) { + // set mark flag to indicate this device is still alive + usb->mark = 1; + adb_mutex_unlock(&usb_lock); + return 1; + } + } + adb_mutex_unlock(&usb_lock); + return 0; +} + +static void kick_disconnected_devices() +{ + usb_handle *usb; + + adb_mutex_lock(&usb_lock); + // kick any devices in the device list that were not found in the device scan + for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ + if (usb->mark == 0) { + usb_kick(usb); + } else { + usb->mark = 0; + } + } + adb_mutex_unlock(&usb_lock); + +} + +static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out, + int ifc, const char *serial, unsigned zero_mask); + +static inline int badname(const char *name) +{ + while(*name) { + if(!isdigit(*name++)) return 1; + } + return 0; +} + +static int find_usb_device(const char *base, + void (*register_device_callback) (const char *, unsigned char, unsigned char, int, const char *, unsigned)) +{ + char busname[32], devname[32]; + unsigned char local_ep_in, local_ep_out; + DIR *busdir , *devdir ; + struct dirent *de; + int fd ; + int found_device = 0; + char serial[256]; + + busdir = opendir(base); + if(busdir == 0) return 0; + + while((de = readdir(busdir)) != 0) { + if(badname(de->d_name)) continue; + + snprintf(busname, sizeof busname, "%s/%s", base, de->d_name); + devdir = opendir(busname); + if(devdir == 0) continue; + +// DBGX("[ scanning %s ]\n", busname); + while((de = readdir(devdir))) { + unsigned char devdesc[256]; + unsigned char* bufptr = devdesc; + struct usb_device_descriptor* device; + struct usb_config_descriptor* config; + struct usb_interface_descriptor* interface; + struct usb_endpoint_descriptor *ep1, *ep2; + unsigned zero_mask = 0; + unsigned vid, pid; + int i, interfaces; + size_t desclength; + + if(badname(de->d_name)) continue; + snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); + + if(known_device(devname)) { + DBGX("skipping %s\n", devname); + continue; + } + +// DBGX("[ scanning %s ]\n", devname); + if((fd = unix_open(devname, O_RDWR)) < 0) { + continue; + } + + desclength = adb_read(fd, devdesc, sizeof(devdesc)); + + // should have device and configuration descriptors, and atleast two endpoints + if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) { + D("desclength %d is too small\n", desclength); + adb_close(fd); + continue; + } + + device = (struct usb_device_descriptor*)bufptr; + bufptr += USB_DT_DEVICE_SIZE; + + if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) { + adb_close(fd); + continue; + } + + vid = __le16_to_cpu(device->idVendor); + pid = __le16_to_cpu(device->idProduct); + pid = devdesc[10] | (devdesc[11] << 8); + DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid); + + // should have config descriptor next + config = (struct usb_config_descriptor *)bufptr; + bufptr += USB_DT_CONFIG_SIZE; + if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) { + D("usb_config_descriptor not found\n"); + adb_close(fd); + continue; + } + + // loop through all the interfaces and look for the ADB interface + interfaces = config->bNumInterfaces; + for (i = 0; i < interfaces; i++) { + if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength) + break; + + interface = (struct usb_interface_descriptor *)bufptr; + bufptr += USB_DT_INTERFACE_SIZE; + if (interface->bLength != USB_DT_INTERFACE_SIZE || + interface->bDescriptorType != USB_DT_INTERFACE) { + D("usb_interface_descriptor not found\n"); + break; + } + + DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d," + "bInterfaceProtocol: %d, bNumEndpoints: %d\n", + interface->bInterfaceClass, interface->bInterfaceSubClass, + interface->bInterfaceProtocol, interface->bNumEndpoints); + + if (interface->bNumEndpoints == 2 && + is_adb_interface(vid, pid, interface->bInterfaceClass, + interface->bInterfaceSubClass, interface->bInterfaceProtocol)) { + + DBGX("looking for bulk endpoints\n"); + // looks like ADB... + ep1 = (struct usb_endpoint_descriptor *)bufptr; + bufptr += USB_DT_ENDPOINT_SIZE; + ep2 = (struct usb_endpoint_descriptor *)bufptr; + bufptr += USB_DT_ENDPOINT_SIZE; + + if (bufptr > devdesc + desclength || + ep1->bLength != USB_DT_ENDPOINT_SIZE || + ep1->bDescriptorType != USB_DT_ENDPOINT || + ep2->bLength != USB_DT_ENDPOINT_SIZE || + ep2->bDescriptorType != USB_DT_ENDPOINT) { + D("endpoints not found\n"); + break; + } + + // both endpoints should be bulk + if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK || + ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) { + D("bulk endpoints not found\n"); + continue; + } + + /* aproto 01 needs 0 termination */ + if(interface->bInterfaceProtocol == 0x01) { + zero_mask = ep1->wMaxPacketSize - 1; + } + + // we have a match. now we just need to figure out which is in and which is out. + if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + local_ep_in = ep1->bEndpointAddress; + local_ep_out = ep2->bEndpointAddress; + } else { + local_ep_in = ep2->bEndpointAddress; + local_ep_out = ep1->bEndpointAddress; + } + + // read the device's serial number + serial[0] = 0; + memset(serial, 0, sizeof(serial)); + if (device->iSerialNumber) { + struct usbdevfs_ctrltransfer ctrl; + __u16 buffer[128]; + int result; + + memset(buffer, 0, sizeof(buffer)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; + ctrl.wValue = (USB_DT_STRING << 8) | device->iSerialNumber; + ctrl.wIndex = 0; + ctrl.wLength = sizeof(buffer); + ctrl.data = buffer; + + result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + if (result > 0) { + int i; + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + result /= 2; + for (i = 1; i < result; i++) + serial[i - 1] = buffer[i]; + serial[i - 1] = 0; + } + } + + register_device_callback(devname, local_ep_in, local_ep_out, + interface->bInterfaceNumber, serial, zero_mask); + + found_device = 1; + break; + } else { + // skip to next interface + bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE); + } + } // end of for + + adb_close(fd); + } // end of devdir while + closedir(devdir); + } //end of busdir while + closedir(busdir); + + return found_device; +} + +void usb_cleanup() +{ +} + +static int usb_bulk_write(usb_handle *h, const void *data, int len) +{ + struct usbdevfs_urb *urb = &h->urb_out; + int res; + + memset(urb, 0, sizeof(*urb)); + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->endpoint = h->ep_out; + urb->status = -1; + urb->buffer = (void*) data; + urb->buffer_length = len; + + D("++ write ++\n"); + + adb_mutex_lock(&h->lock); + if(h->dead) { + res = -1; + goto fail; + } + do { + res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb); + } while((res < 0) && (errno == EINTR)); + + if(res < 0) { + goto fail; + } + + res = -1; + h->urb_out_busy = 1; + for(;;) { + adb_cond_wait(&h->notify, &h->lock); + if(h->dead) { + break; + } + if(h->urb_out_busy == 0) { + if(urb->status == 0) { + res = urb->actual_length; + } + break; + } + } +fail: + adb_mutex_unlock(&h->lock); + D("-- write --\n"); + return res; +} + +static int usb_bulk_read(usb_handle *h, void *data, int len) +{ + struct usbdevfs_urb *urb = &h->urb_in; + struct usbdevfs_urb *out = NULL; + int res; + + memset(urb, 0, sizeof(*urb)); + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->endpoint = h->ep_in; + urb->status = -1; + urb->buffer = data; + urb->buffer_length = len; + + + adb_mutex_lock(&h->lock); + if(h->dead) { + res = -1; + goto fail; + } + do { + res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb); + } while((res < 0) && (errno == EINTR)); + + if(res < 0) { + goto fail; + } + + h->urb_in_busy = 1; + for(;;) { + D("[ reap urb - wait ]\n"); + h->reaper_thread = pthread_self(); + adb_mutex_unlock(&h->lock); + res = ioctl(h->desc, USBDEVFS_REAPURB, &out); + adb_mutex_lock(&h->lock); + h->reaper_thread = 0; + if(h->dead) { + res = -1; + break; + } + if(res < 0) { + if(errno == EINTR) { + continue; + } + D("[ reap urb - error ]\n"); + break; + } + D("[ urb @%p status = %d, actual = %d ]\n", + out, out->status, out->actual_length); + + if(out == &h->urb_in) { + D("[ reap urb - IN complete ]\n"); + h->urb_in_busy = 0; + if(urb->status == 0) { + res = urb->actual_length; + } else { + res = -1; + } + break; + } + if(out == &h->urb_out) { + D("[ reap urb - OUT compelete ]\n"); + h->urb_out_busy = 0; + adb_cond_broadcast(&h->notify); + } + } +fail: + adb_mutex_unlock(&h->lock); + return res; +} + + +int usb_write(usb_handle *h, const void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + int need_zero = 0; + + if(h->zero_mask) { + /* if we need 0-markers and our transfer + ** is an even multiple of the packet size, + ** we make note of it + */ + if(!(len & h->zero_mask)) { + need_zero = 1; + } + } + + while(len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + n = usb_bulk_write(h, data, xfer); + if(n != xfer) { + D("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + + len -= xfer; + data += xfer; + } + + if(need_zero){ + n = usb_bulk_write(h, _data, 0); + return n; + } + + return 0; +} + +int usb_read(usb_handle *h, void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + + D("++ usb_read ++\n"); + while(len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname); + n = usb_bulk_read(h, data, xfer); + D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname); + if(n != xfer) { + if((errno == ETIMEDOUT) && (h->desc != -1)) { + D("[ timeout ]\n"); + if(n > 0){ + data += n; + len -= n; + } + continue; + } + D("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + + len -= xfer; + data += xfer; + } + + D("-- usb_read --\n"); + return 0; +} + +void usb_kick(usb_handle *h) +{ + D("[ kicking %p (fd = %d) ]\n", h, h->desc); + adb_mutex_lock(&h->lock); + if(h->dead == 0) { + h->dead = 1; + + /* HACK ALERT! + ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB). + ** This is a workaround for that problem. + */ + if (h->reaper_thread) { + pthread_kill(h->reaper_thread, SIGALRM); + } + + /* cancel any pending transactions + ** these will quietly fail if the txns are not active, + ** but this ensures that a reader blocked on REAPURB + ** will get unblocked + */ + ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in); + ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out); + h->urb_in.status = -ENODEV; + h->urb_out.status = -ENODEV; + h->urb_in_busy = 0; + h->urb_out_busy = 0; + adb_cond_broadcast(&h->notify); + } + adb_mutex_unlock(&h->lock); +} + +int usb_close(usb_handle *h) +{ + D("[ usb close ... ]\n"); + adb_mutex_lock(&usb_lock); + h->next->prev = h->prev; + h->prev->next = h->next; + h->prev = 0; + h->next = 0; + + adb_close(h->desc); + D("[ usb closed %p (fd = %d) ]\n", h, h->desc); + adb_mutex_unlock(&usb_lock); + + free(h); + return 0; +} + +static void register_device(const char *dev_name, + unsigned char ep_in, unsigned char ep_out, + int interface, + const char *serial, unsigned zero_mask) +{ + usb_handle* usb = 0; + int n = 0; + + /* Since Linux will not reassign the device ID (and dev_name) + ** as long as the device is open, we can add to the list here + ** once we open it and remove from the list when we're finally + ** closed and everything will work out fine. + ** + ** If we have a usb_handle on the list 'o handles with a matching + ** name, we have no further work to do. + */ + adb_mutex_lock(&usb_lock); + for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ + if(!strcmp(usb->fname, dev_name)) { + adb_mutex_unlock(&usb_lock); + return; + } + } + adb_mutex_unlock(&usb_lock); + + D("[ usb located new device %s (%d/%d/%d) ]\n", + dev_name, ep_in, ep_out, interface); + usb = calloc(1, sizeof(usb_handle)); + strcpy(usb->fname, dev_name); + usb->ep_in = ep_in; + usb->ep_out = ep_out; + usb->zero_mask = zero_mask; + + adb_cond_init(&usb->notify, 0); + adb_mutex_init(&usb->lock, 0); + /* initialize mark to 1 so we don't get garbage collected after the device scan */ + usb->mark = 1; + usb->reaper_thread = 0; + + usb->desc = unix_open(usb->fname, O_RDWR); + if(usb->desc < 0) goto fail; + D("[ usb open %s fd = %d]\n", usb->fname, usb->desc); + n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface); + if(n != 0) goto fail; + + /* add to the end of the active handles */ + adb_mutex_lock(&usb_lock); + usb->next = &handle_list; + usb->prev = handle_list.prev; + usb->prev->next = usb; + usb->next->prev = usb; + adb_mutex_unlock(&usb_lock); + + register_usb_transport(usb, serial); + return; + +fail: + D("[ usb open %s error=%d, err_str = %s]\n", + usb->fname, errno, strerror(errno)); + if(usb->desc >= 0) { + adb_close(usb->desc); + } + free(usb); +} + +void* device_poll_thread(void* unused) +{ + D("Created device thread\n"); + for(;;) { + /* XXX use inotify */ + find_usb_device("/dev/bus/usb", register_device); + kick_disconnected_devices(); + sleep(1); + } + return NULL; +} + +static void sigalrm_handler(int signo) +{ + // don't need to do anything here +} + +void usb_init() +{ + adb_thread_t tid; + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = sigalrm_handler; + sigaction(SIGALRM,& actions, NULL); + + if(adb_thread_create(&tid, device_poll_thread, NULL)){ + fatal_errno("cannot create input thread"); + } +} + diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c new file mode 100644 index 0000000..530bd04 --- /dev/null +++ b/adb/usb_linux_client.c @@ -0,0 +1,156 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + + +struct usb_handle +{ + int fd; + adb_cond_t notify; + adb_mutex_t lock; +}; + +void usb_cleanup() +{ + // nothing to do here +} + +static void *usb_open_thread(void *x) +{ + struct usb_handle *usb = (struct usb_handle *)x; + int fd; + + while (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->fd != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + D("[ usb_thread - opening device ]\n"); + do { + /* XXX use inotify? */ + fd = unix_open("/dev/android_adb", O_RDWR); + if (fd < 0) { + // to support older kernels + fd = unix_open("/dev/android", O_RDWR); + } + if (fd < 0) { + adb_sleep_ms(1000); + } + } while (fd < 0); + D("[ opening device succeeded ]\n"); + + close_on_exec(fd); + usb->fd = fd; + + D("[ usb_thread - registering device ]\n"); + register_usb_transport(usb, 0); + } + + // never gets here + return 0; +} + +int usb_write(usb_handle *h, const void *data, int len) +{ + int n; + + D("[ write %d ]\n", len); + n = adb_write(h->fd, data, len); + if(n != len) { + D("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + D("[ done ]\n"); + return 0; +} + +int usb_read(usb_handle *h, void *data, int len) +{ + int n; + + D("[ read %d ]\n", len); + n = adb_read(h->fd, data, len); + if(n != len) { + D("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + return 0; +} + +void usb_init() +{ + usb_handle *h; + adb_thread_t tid; + int fd; + + h = calloc(1, sizeof(usb_handle)); + h->fd = -1; + adb_cond_init(&h->notify, 0); + adb_mutex_init(&h->lock, 0); + + // Open the file /dev/android_adb_enable to trigger + // the enabling of the adb USB function in the kernel. + // We never touch this file again - just leave it open + // indefinitely so the kernel will know when we are running + // and when we are not. + fd = unix_open("/dev/android_adb_enable", O_RDWR); + if (fd < 0) { + D("failed to open /dev/android_adb_enable\n"); + } else { + close_on_exec(fd); + } + + D("[ usb_init - starting thread ]\n"); + if(adb_thread_create(&tid, usb_open_thread, h)){ + fatal_errno("cannot create usb thread"); + } +} + +void usb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_close(h->fd); + h->fd = -1; + + // notify usb_open_thread that we are disconnected + adb_cond_signal(&h->notify); + adb_mutex_unlock(&h->lock); +} + +int usb_close(usb_handle *h) +{ + // nothing to do here + return 0; +} diff --git a/adb/usb_osx.c b/adb/usb_osx.c new file mode 100644 index 0000000..49e1eef --- /dev/null +++ b/adb/usb_osx.c @@ -0,0 +1,536 @@ +/* + * 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 <CoreFoundation/CoreFoundation.h> + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/usb/IOUSBLib.h> +#include <IOKit/IOMessage.h> +#include <mach/mach_port.h> + +#include "sysdeps.h" + +#include <stdio.h> + +#define TRACE_TAG TRACE_USB +#include "adb.h" + +#define DBG D + +typedef struct { + int vid; + int pid; +} VendorProduct; + +#define kSupportedDeviceCount 4 +VendorProduct kSupportedDevices[kSupportedDeviceCount] = { + { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER }, + { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER_COMP }, + { VENDOR_ID_HTC, PRODUCT_ID_DREAM }, + { VENDOR_ID_HTC, PRODUCT_ID_DREAM_COMP }, +}; + +static IONotificationPortRef notificationPort = 0; +static io_iterator_t notificationIterators[kSupportedDeviceCount]; + +struct usb_handle +{ + UInt8 bulkIn; + UInt8 bulkOut; + IOUSBInterfaceInterface **interface; + io_object_t usbNotification; + unsigned int zero_mask; +}; + +static CFRunLoopRef currentRunLoop = 0; +static pthread_mutex_t start_lock; +static pthread_cond_t start_cond; + + +static void AndroidDeviceAdded(void *refCon, io_iterator_t iterator); +static void AndroidDeviceNotify(void *refCon, io_iterator_t iterator, natural_t messageType, void *messageArgument); +static usb_handle* FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product); + +static int +InitUSB() +{ + CFMutableDictionaryRef matchingDict; + CFRunLoopSourceRef runLoopSource; + SInt32 vendor, product; + int i; + + //* To set up asynchronous notifications, create a notification port and + //* add its run loop event source to the program's run loop + notificationPort = IONotificationPortCreate(kIOMasterPortDefault); + runLoopSource = IONotificationPortGetRunLoopSource(notificationPort); + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode); + + memset(notificationIterators, 0, sizeof(notificationIterators)); + + //* loop through all supported vendor/product pairs + for (i = 0; i < kSupportedDeviceCount; i++) { + //* Create our matching dictionary to find the Android device + //* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + if (!matchingDict) { + DBG("ERR: Couldn't create USB matching dictionary.\n"); + return -1; + } + + //* Set up two matching dictionaries, one for each product ID we support. + //* This will cause the kernel to notify us only if the vendor and product IDs match. + vendor = kSupportedDevices[i].vid; + product = kSupportedDevices[i].pid; + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor)); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product)); + + //* Now set up two notifications: one to be called when a raw device + //* is first matched by the I/O Kit and another to be called when the + //* device is terminated. + //* we need to do this with each matching dictionary. + IOServiceAddMatchingNotification( + notificationPort, + kIOFirstMatchNotification, + matchingDict, + AndroidDeviceAdded, + NULL, + ¬ificationIterators[i]); + + //* Iterate over set of matching devices to access already-present devices + //* and to arm the notification + AndroidDeviceAdded(NULL, notificationIterators[i]); + } + + return 0; +} + +static void +AndroidDeviceAdded(void *refCon, io_iterator_t iterator) +{ + kern_return_t kr; + io_service_t usbDevice; + IOCFPlugInInterface **plugInInterface = NULL; + IOUSBDeviceInterface182 **dev = NULL; + HRESULT result; + SInt32 score; + UInt16 vendor; + UInt16 product; + UInt8 serialIndex; + char serial[256]; + + while ((usbDevice = IOIteratorNext(iterator))) { + //* Create an intermediate plugin + kr = IOCreatePlugInInterfaceForService(usbDevice, + kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, &score); + + if ((kIOReturnSuccess != kr) || (!plugInInterface)) { + DBG("ERR: Unable to create a plug-in (%08x)\n", kr); + goto continue1; + } + + //* Now create the device interface + result = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev); + + if (result || !dev) { + DBG("ERR: Couldn't create a device interface (%08x)\n", (int) result); + goto continue2; + } + + //* Check the device to see if it's ours + kr = (*dev)->GetDeviceVendor(dev, &vendor); + kr = (*dev)->GetDeviceProduct(dev, &product); + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); + + if (serialIndex > 0) { + IOUSBDevRequest req; + UInt16 buffer[256]; + + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = (kUSBStringDesc << 8) | serialIndex; + req.wIndex = 0; + req.pData = buffer; + req.wLength = sizeof(buffer); + kr = (*dev)->DeviceRequest(dev, &req); + + if (kr == kIOReturnSuccess && req.wLenDone > 0) { + int i, count; + + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + count = (req.wLenDone - 1) / 2; + for (i = 0; i < count; i++) + serial[i] = buffer[i + 1]; + serial[i] = 0; + } + } + + usb_handle* handle = NULL; + + //* Open the device + kr = (*dev)->USBDeviceOpen(dev); + + if (kr != kIOReturnSuccess) { + DBG("ERR: Could not open device: %08x\n", kr); + goto continue3; + } else { + //* Find an interface for the device + handle = FindDeviceInterface((IOUSBDeviceInterface**)dev, vendor, product); + } + + if (handle == NULL) { + DBG("ERR: Could not find device interface: %08x\n", kr); + (*dev)->USBDeviceClose(dev); + goto continue3; + } + + DBG("AndroidDeviceAdded calling register_usb_transport\n"); + register_usb_transport(handle, (serial[0] ? serial : NULL)); + + // Register for an interest notification of this device being removed. Pass the reference to our + // private data as the refCon for the notification. + kr = IOServiceAddInterestNotification(notificationPort, + usbDevice, + kIOGeneralInterest, + AndroidDeviceNotify, + handle, + &handle->usbNotification); + if (kIOReturnSuccess != kr) { + DBG("ERR: Unable to create interest notification (%08x)\n", kr); + } + +continue3: + (void)(*dev)->Release(dev); +continue2: + IODestroyPlugInInterface(plugInInterface); +continue1: + IOObjectRelease(usbDevice); + } +} + +static void +AndroidDeviceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) +{ + usb_handle *handle = (usb_handle *)refCon; + + if (messageType == kIOMessageServiceIsTerminated) { + DBG("AndroidDeviceNotify\n"); + IOObjectRelease(handle->usbNotification); + usb_kick(handle); + } +} + +static usb_handle* +FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product) +{ + usb_handle* handle = NULL; + IOReturn kr; + IOUSBFindInterfaceRequest request; + io_iterator_t iterator; + io_service_t usbInterface; + IOCFPlugInInterface **plugInInterface; + IOUSBInterfaceInterface **interface = NULL; + HRESULT result; + SInt32 score; + UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol; + UInt8 endpoint, configuration; + + //* Placing the constant KIOUSBFindInterfaceDontCare into the following + //* fields of the IOUSBFindInterfaceRequest structure will allow us to + //* find all of the interfaces + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + //* SetConfiguration will kill an existing UMS connection, so let's not do this if not necessary. + configuration = 0; + (*dev)->GetConfiguration(dev, &configuration); + if (configuration != 1) + (*dev)->SetConfiguration(dev, 1); + + //* Get an iterator for the interfaces on the device + kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator); + + if (kr != kIOReturnSuccess) { + DBG("ERR: Couldn't create a device interface iterator: (%08x)\n", kr); + return NULL; + } + + while ((usbInterface = IOIteratorNext(iterator))) { + //* Create an intermediate plugin + kr = IOCreatePlugInInterfaceForService( + usbInterface, + kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score); + + //* No longer need the usbInterface object now that we have the plugin + (void) IOObjectRelease(usbInterface); + + if ((kr != kIOReturnSuccess) || (!plugInInterface)) { + DBG("ERR: Unable to create plugin (%08x)\n", kr); + break; + } + + //* Now create the interface interface for the interface + result = (*plugInInterface)->QueryInterface( + plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID) &interface); + + //* No longer need the intermediate plugin + (*plugInInterface)->Release(plugInInterface); + + if (result || !interface) { + DBG("ERR: Couldn't create interface interface: (%08x)\n", + (unsigned int) result); + break; + } + + //* Now open the interface. This will cause the pipes associated with + //* the endpoints in the interface descriptor to be instantiated + kr = (*interface)->USBInterfaceOpen(interface); + + if (kr != kIOReturnSuccess) + { + DBG("ERR: Could not open interface: (%08x)\n", kr); + (void) (*interface)->Release(interface); + //* continue so we can try the next interface + continue; + } + + //* Get the number of endpoints associated with this interface + kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints); + + if (kr != kIOReturnSuccess) { + DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr); + goto next_interface; + } + + //* Get interface class, subclass and protocol + if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess || + (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess || + (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) + { + DBG("ERR: Unable to get interface class, subclass and protocol\n"); + goto next_interface; + } + + //* check to make sure interface class, subclass and protocol match ADB + //* avoid opening mass storage endpoints + if (is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) { + handle = calloc(1, sizeof(usb_handle)); + + //* Iterate over the endpoints for this interface and find the first + //* bulk in/out pipes available. These will be our read/write pipes. + for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) { + UInt8 transferType; + UInt16 maxPacketSize; + UInt8 interval; + UInt8 number; + UInt8 direction; + + kr = (*interface)->GetPipeProperties(interface, endpoint, &direction, + &number, &transferType, &maxPacketSize, &interval); + + if (kIOReturnSuccess == kr) { + if (kUSBBulk != transferType) + continue; + + if (kUSBIn == direction) + handle->bulkIn = endpoint; + + if (kUSBOut == direction) + handle->bulkOut = endpoint; + + if (interfaceProtocol == 0x01) { + handle->zero_mask = maxPacketSize - 1; + } + + } else { + DBG("ERR: FindDeviceInterface - could not get pipe properties\n"); + } + } + + handle->interface = interface; + break; + } + +next_interface: + (*interface)->USBInterfaceClose(interface); + (*interface)->Release(interface); + } + + return handle; +} + + +void* RunLoopThread(void* unused) +{ + int i; + + InitUSB(); + + currentRunLoop = CFRunLoopGetCurrent(); + + // Signal the parent that we are running + adb_mutex_lock(&start_lock); + adb_cond_signal(&start_cond); + adb_mutex_unlock(&start_lock); + + CFRunLoopRun(); + currentRunLoop = 0; + + for (i = 0; i < kSupportedDeviceCount; i++) { + IOObjectRelease(notificationIterators[i]); + } + IONotificationPortDestroy(notificationPort); + + DBG("RunLoopThread done\n"); + return NULL; +} + + +static int initialized = 0; +void usb_init() +{ + if (!initialized) + { + adb_thread_t tid; + + adb_mutex_init(&start_lock, NULL); + adb_cond_init(&start_cond, NULL); + + if(adb_thread_create(&tid, RunLoopThread, NULL)) + fatal_errno("cannot create input thread"); + + // Wait for initialization to finish + adb_mutex_lock(&start_lock); + adb_cond_wait(&start_cond, &start_lock); + adb_mutex_unlock(&start_lock); + + adb_mutex_destroy(&start_lock); + adb_cond_destroy(&start_cond); + + initialized = 1; + } +} + +void usb_cleanup() +{ + DBG("usb_cleanup\n"); + close_usb_devices(); + if (currentRunLoop) + CFRunLoopStop(currentRunLoop); +} + +int usb_write(usb_handle *handle, const void *buf, int len) +{ + IOReturn result; + + if (!len) + return 0; + + if (!handle) + return -1; + + if (NULL == handle->interface) { + DBG("ERR: usb_write interface was null\n"); + return -1; + } + + if (0 == handle->bulkOut) { + DBG("ERR: bulkOut endpoint not assigned\n"); + return -1; + } + + result = + (*handle->interface)->WritePipe( + handle->interface, handle->bulkOut, (void *)buf, len); + + if ((result == 0) && (handle->zero_mask)) { + /* we need 0-markers and our transfer */ + if(!(len & handle->zero_mask)) { + result = + (*handle->interface)->WritePipe( + handle->interface, handle->bulkOut, (void *)buf, 0); + } + } + + if (0 == result) + return 0; + + DBG("ERR: usb_write failed with status %d\n", result); + return -1; +} + +int usb_read(usb_handle *handle, void *buf, int len) +{ + IOReturn result; + UInt32 numBytes = len; + + if (!len) { + return 0; + } + + if (!handle) { + return -1; + } + + if (NULL == handle->interface) { + DBG("ERR: usb_read interface was null\n"); + return -1; + } + + if (0 == handle->bulkIn) { + DBG("ERR: bulkIn endpoint not assigned\n"); + return -1; + } + + result = + (*handle->interface)->ReadPipe(handle->interface, + handle->bulkIn, buf, &numBytes); + + if (0 == result) + return 0; + else { + DBG("ERR: usb_read failed with status %d\n", result); + } + + return -1; +} + +int usb_close(usb_handle *handle) +{ + return 0; +} + +void usb_kick(usb_handle *handle) +{ + /* release the interface */ + if (handle->interface) + { + (*handle->interface)->USBInterfaceClose(handle->interface); + (*handle->interface)->Release(handle->interface); + handle->interface = 0; + } +} diff --git a/adb/usb_windows.c b/adb/usb_windows.c new file mode 100644 index 0000000..7ddaa0c --- /dev/null +++ b/adb/usb_windows.c @@ -0,0 +1,513 @@ +/* + * 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 <windows.h> +#include <winerror.h> +#include <errno.h> +#include <usb100.h> +#include <adb_api.h> +#include <stdio.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + +/** Structure usb_handle describes our connection to the usb device via + AdbWinApi.dll. This structure is returned from usb_open() routine and + is expected in each subsequent call that is accessing the device. +*/ +struct usb_handle { + /// Previous entry in the list of opened usb handles + usb_handle *prev; + + /// Next entry in the list of opened usb handles + usb_handle *next; + + /// Handle to USB interface + ADBAPIHANDLE adb_interface; + + /// Handle to USB read pipe (endpoint) + ADBAPIHANDLE adb_read_pipe; + + /// Handle to USB write pipe (endpoint) + ADBAPIHANDLE adb_write_pipe; + + /// Interface name + char* interface_name; + + /// Mask for determining when to use zero length packets + unsigned zero_mask; +}; + +/// Class ID assigned to the device by androidusb.sys +static const GUID usb_class_id = ANDROID_USB_CLASS_ID; + +/// List of opened usb handles +static usb_handle handle_list = { + .prev = &handle_list, + .next = &handle_list, +}; + +/// Locker for the list of opened usb handles +ADB_MUTEX_DEFINE( usb_lock ); + +/// Checks if there is opened usb handle in handle_list for this device. +int known_device(const char* dev_name); + +/// Checks if there is opened usb handle in handle_list for this device. +/// usb_lock mutex must be held before calling this routine. +int known_device_locked(const char* dev_name); + +/// Registers opened usb handle (adds it to handle_list). +int register_new_device(usb_handle* handle); + +/// Checks if interface (device) matches certain criteria +int recognized_device(usb_handle* handle); + +/// Enumerates present and available interfaces (devices), opens new ones and +/// registers usb transport for them. +void find_devices(); + +/// Entry point for thread that polls (every second) for new usb interfaces. +/// This routine calls find_devices in infinite loop. +void* device_poll_thread(void* unused); + +/// Initializes this module +void usb_init(); + +/// Cleans up this module +void usb_cleanup(); + +/// Opens usb interface (device) by interface (device) name. +usb_handle* do_usb_open(const wchar_t* interface_name); + +/// Writes data to the opened usb handle +int usb_write(usb_handle* handle, const void* data, int len); + +/// Reads data using the opened usb handle +int usb_read(usb_handle *handle, void* data, int len); + +/// Cleans up opened usb handle +void usb_cleanup_handle(usb_handle* handle); + +/// Cleans up (but don't close) opened usb handle +void usb_kick(usb_handle* handle); + +/// Closes opened usb handle +int usb_close(usb_handle* handle); + +/// Gets interface (device) name for an opened usb handle +const char *usb_name(usb_handle* handle); + +int known_device_locked(const char* dev_name) { + usb_handle* usb; + + if (NULL != dev_name) { + // Iterate through the list looking for the name match. + for(usb = handle_list.next; usb != &handle_list; usb = usb->next) { + // In Windows names are not case sensetive! + if((NULL != usb->interface_name) && + (0 == stricmp(usb->interface_name, dev_name))) { + return 1; + } + } + } + + return 0; +} + +int known_device(const char* dev_name) { + int ret = 0; + + if (NULL != dev_name) { + adb_mutex_lock(&usb_lock); + ret = known_device_locked(dev_name); + adb_mutex_unlock(&usb_lock); + } + + return ret; +} + +int register_new_device(usb_handle* handle) { + if (NULL == handle) + return 0; + + adb_mutex_lock(&usb_lock); + + // Check if device is already in the list + if (known_device_locked(handle->interface_name)) { + adb_mutex_unlock(&usb_lock); + return 0; + } + + // Not in the list. Add this handle to the list. + handle->next = &handle_list; + handle->prev = handle_list.prev; + handle->prev->next = handle; + handle->next->prev = handle; + + adb_mutex_unlock(&usb_lock); + + return 1; +} + +void* device_poll_thread(void* unused) { + D("Created device thread\n"); + + while(1) { + find_devices(); + adb_sleep_ms(1000); + } + + return NULL; +} + +void usb_init() { + adb_thread_t tid; + + if(adb_thread_create(&tid, device_poll_thread, NULL)) { + fatal_errno("cannot create input thread"); + } +} + +void usb_cleanup() { +} + +usb_handle* do_usb_open(const wchar_t* interface_name) { + // Allocate our handle + usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle)); + if (NULL == ret) + return NULL; + + // Set linkers back to the handle + ret->next = ret; + ret->prev = ret; + + // Create interface. + ret->adb_interface = AdbCreateInterfaceByName(interface_name); + + if (NULL == ret->adb_interface) { + free(ret); + errno = GetLastError(); + return NULL; + } + + // Open read pipe (endpoint) + ret->adb_read_pipe = + AdbOpenDefaultBulkReadEndpoint(ret->adb_interface, + AdbOpenAccessTypeReadWrite, + AdbOpenSharingModeReadWrite); + if (NULL != ret->adb_read_pipe) { + // Open write pipe (endpoint) + ret->adb_write_pipe = + AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface, + AdbOpenAccessTypeReadWrite, + AdbOpenSharingModeReadWrite); + if (NULL != ret->adb_write_pipe) { + // Save interface name + unsigned long name_len = 0; + + // First get expected name length + AdbGetInterfaceName(ret->adb_interface, + NULL, + &name_len, + true); + if (0 != name_len) { + ret->interface_name = (char*)malloc(name_len); + + if (NULL != ret->interface_name) { + // Now save the name + if (AdbGetInterfaceName(ret->adb_interface, + ret->interface_name, + &name_len, + true)) { + // We're done at this point + return ret; + } + } else { + SetLastError(ERROR_OUTOFMEMORY); + } + } + } + } + + // Something went wrong. + errno = GetLastError(); + usb_cleanup_handle(ret); + free(ret); + SetLastError(errno); + + return NULL; +} + +int usb_write(usb_handle* handle, const void* data, int len) { + unsigned long time_out = 500 + len * 8; + unsigned long written = 0; + int ret; + + D("usb_write %d\n", len); + if (NULL != handle) { + // Perform write + ret = AdbWriteEndpointSync(handle->adb_write_pipe, + (void*)data, + (unsigned long)len, + &written, + time_out); + errno = GetLastError(); + + if (ret) { + // Make sure that we've written what we were asked to write + D("usb_write got: %ld, expected: %d\n", written, len); + if (written == (unsigned long)len) { + if(handle->zero_mask && (len & handle->zero_mask) == 0) { + // Send a zero length packet + AdbWriteEndpointSync(handle->adb_write_pipe, + (void*)data, + 0, + &written, + time_out); + } + return 0; + } + } else { + // assume ERROR_INVALID_HANDLE indicates we are disconnected + if (errno == ERROR_INVALID_HANDLE) + usb_kick(handle); + } + } else { + D("usb_write NULL handle\n"); + SetLastError(ERROR_INVALID_HANDLE); + } + + D("usb_write failed: %d\n", errno); + + return -1; +} + +int usb_read(usb_handle *handle, void* data, int len) { + unsigned long time_out = 500 + len * 8; + unsigned long read = 0; + int ret; + + D("usb_read %d\n", len); + if (NULL != handle) { + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + ret = AdbReadEndpointSync(handle->adb_read_pipe, + (void*)data, + (unsigned long)xfer, + &read, + time_out); + errno = GetLastError(); + D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno); + if (ret) { + data += read; + len -= read; + + if (len == 0) + return 0; + } else if (errno != ERROR_SEM_TIMEOUT) { + // assume ERROR_INVALID_HANDLE indicates we are disconnected + if (errno == ERROR_INVALID_HANDLE) + usb_kick(handle); + break; + } + } + } else { + D("usb_read NULL handle\n"); + SetLastError(ERROR_INVALID_HANDLE); + } + + D("usb_read failed: %d\n", errno); + + return -1; +} + +void usb_cleanup_handle(usb_handle* handle) { + if (NULL != handle) { + if (NULL != handle->interface_name) + free(handle->interface_name); + if (NULL != handle->adb_write_pipe) + AdbCloseHandle(handle->adb_write_pipe); + if (NULL != handle->adb_read_pipe) + AdbCloseHandle(handle->adb_read_pipe); + if (NULL != handle->adb_interface) + AdbCloseHandle(handle->adb_interface); + + handle->interface_name = NULL; + handle->adb_write_pipe = NULL; + handle->adb_read_pipe = NULL; + handle->adb_interface = NULL; + } +} + +void usb_kick(usb_handle* handle) { + if (NULL != handle) { + adb_mutex_lock(&usb_lock); + + usb_cleanup_handle(handle); + + adb_mutex_unlock(&usb_lock); + } else { + SetLastError(ERROR_INVALID_HANDLE); + errno = ERROR_INVALID_HANDLE; + } +} + +int usb_close(usb_handle* handle) { + D("usb_close\n"); + + if (NULL != handle) { + // Remove handle from the list + adb_mutex_lock(&usb_lock); + + if ((handle->next != handle) && (handle->prev != handle)) { + handle->next->prev = handle->prev; + handle->prev->next = handle->next; + handle->prev = handle; + handle->next = handle; + } + + adb_mutex_unlock(&usb_lock); + + // Cleanup handle + usb_cleanup_handle(handle); + free(handle); + } + + return 0; +} + +const char *usb_name(usb_handle* handle) { + if (NULL == handle) { + SetLastError(ERROR_INVALID_HANDLE); + errno = ERROR_INVALID_HANDLE; + return NULL; + } + + return (const char*)handle->interface_name; +} + +int recognized_device(usb_handle* handle) { + if (NULL == handle) + return 0; + + // Check vendor and product id first + USB_DEVICE_DESCRIPTOR device_desc; + + if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, + &device_desc)) { + return 0; + } + + // Then check interface properties + USB_INTERFACE_DESCRIPTOR interf_desc; + + if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, + &interf_desc)) { + return 0; + } + + // Must have two endpoints + if (2 != interf_desc.bNumEndpoints) { + return 0; + } + + if (is_adb_interface(device_desc.idVendor, device_desc.idProduct, + interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) { + + if(interf_desc.bInterfaceProtocol == 0x01) { + AdbEndpointInformation endpoint_info; + // assuming zero is a valid bulk endpoint ID + if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) { + handle->zero_mask = endpoint_info.max_packet_size - 1; + } + } + + return 1; + } + + return 0; +} + +void find_devices() { + usb_handle* handle = NULL; + char entry_buffer[2048]; + char interf_name[2048]; + AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]); + unsigned long entry_buffer_size = sizeof(entry_buffer); + char* copy_name; + + // Enumerate all present and active interfaces. + ADBAPIHANDLE enum_handle = + AdbEnumInterfaces(usb_class_id, true, true, true); + + if (NULL == enum_handle) + return; + + while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) { + // TODO: FIXME - temp hack converting wchar_t into char. + // It would be better to change AdbNextInterface so it will return + // interface name as single char string. + const wchar_t* wchar_name = next_interface->device_name; + for(copy_name = interf_name; + L'\0' != *wchar_name; + wchar_name++, copy_name++) { + *copy_name = (char)(*wchar_name); + } + *copy_name = '\0'; + + // Lets see if we already have this device in the list + if (!known_device(interf_name)) { + // This seems to be a new device. Open it! + handle = do_usb_open(next_interface->device_name); + if (NULL != handle) { + // Lets see if this interface (device) belongs to us + if (recognized_device(handle)) { + D("adding a new device %s\n", interf_name); + char serial_number[512]; + unsigned long serial_number_len = sizeof(serial_number); + if (AdbGetSerialNumber(handle->adb_interface, + serial_number, + &serial_number_len, + true)) { + // Lets make sure that we don't duplicate this device + if (register_new_device(handle)) { + register_usb_transport(handle, serial_number); + } else { + D("register_new_device failed for %s\n", interf_name); + usb_cleanup_handle(handle); + free(handle); + } + } else { + D("cannot get serial number\n"); + usb_cleanup_handle(handle); + free(handle); + } + } else { + usb_cleanup_handle(handle); + free(handle); + } + } + } + + entry_buffer_size = sizeof(entry_buffer); + } + + AdbCloseHandle(enum_handle); +} diff --git a/adb/utils.c b/adb/utils.c new file mode 100644 index 0000000..91518ba --- /dev/null +++ b/adb/utils.c @@ -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. + */ +#include "utils.h" +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +char* +buff_addc (char* buff, char* buffEnd, int c) +{ + int avail = buffEnd - buff; + + if (avail <= 0) /* already in overflow mode */ + return buff; + + if (avail == 1) { /* overflowing, the last byte is reserved for zero */ + buff[0] = 0; + return buff + 1; + } + + buff[0] = (char) c; /* add char and terminating zero */ + buff[1] = 0; + return buff + 1; +} + +char* +buff_adds (char* buff, char* buffEnd, const char* s) +{ + int slen = strlen(s); + + return buff_addb(buff, buffEnd, s, slen); +} + +char* +buff_addb (char* buff, char* buffEnd, const void* data, int len) +{ + int avail = (buffEnd - buff); + + if (avail <= 0 || len <= 0) /* already overflowing */ + return buff; + + if (len > avail) + len = avail; + + memcpy(buff, data, len); + + buff += len; + + /* ensure there is a terminating zero */ + if (buff >= buffEnd) { /* overflow */ + buff[-1] = 0; + } else + buff[0] = 0; + + return buff; +} + +char* +buff_add (char* buff, char* buffEnd, const char* format, ... ) +{ + int avail; + + avail = (buffEnd - buff); + + if (avail > 0) { + va_list args; + int nn; + + va_start(args, format); + nn = vsnprintf( buff, avail, format, args); + va_end(args); + + if (nn < 0) { + /* some C libraries return -1 in case of overflow, + * but they will also do that if the format spec is + * invalid. We assume ADB is not buggy enough to + * trigger that last case. */ + nn = avail; + } + else if (nn > avail) { + nn = avail; + } + + buff += nn; + + /* ensure that there is a terminating zero */ + if (buff >= buffEnd) + buff[-1] = 0; + else + buff[0] = 0; + } + return buff; +} diff --git a/adb/utils.h b/adb/utils.h new file mode 100644 index 0000000..f70ecd2 --- /dev/null +++ b/adb/utils.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _ADB_UTILS_H +#define _ADB_UTILS_H + +/* bounded buffer functions */ + +/* all these functions are used to append data to a bounded buffer. + * + * after each operation, the buffer is guaranteed to be zero-terminated, + * even in the case of an overflow. they all return the new buffer position + * which allows one to use them in succession, only checking for overflows + * at the end. For example: + * + * BUFF_DECL(temp,p,end,1024); + * char* p; + * + * p = buff_addc(temp, end, '"'); + * p = buff_adds(temp, end, string); + * p = buff_addc(temp, end, '"'); + * + * if (p >= end) { + * overflow detected. note that 'temp' is + * zero-terminated for safety. + * } + * return strdup(temp); + */ + +/* tries to add a character to the buffer, in case of overflow + * this will only write a terminating zero and return buffEnd. + */ +char* buff_addc (char* buff, char* buffEnd, int c); + +/* tries to add a string to the buffer */ +char* buff_adds (char* buff, char* buffEnd, const char* s); + +/* tries to add a bytes to the buffer. the input can contain zero bytes, + * but a terminating zero will always be appended at the end anyway + */ +char* buff_addb (char* buff, char* buffEnd, const void* data, int len); + +/* tries to add a formatted string to a bounded buffer */ +char* buff_add (char* buff, char* buffEnd, const char* format, ... ); + +/* convenience macro used to define a bounded buffer, as well as + * a 'cursor' and 'end' variables all in one go. + * + * note: this doesn't place an initial terminating zero in the buffer, + * you need to use one of the buff_ functions for this. or simply + * do _cursor[0] = 0 manually. + */ +#define BUFF_DECL(_buff,_cursor,_end,_size) \ + char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size) + +#endif /* _ADB_UTILS_H */ diff --git a/cpio/Android.mk b/cpio/Android.mk new file mode 100644 index 0000000..8d01852 --- /dev/null +++ b/cpio/Android.mk @@ -0,0 +1,13 @@ +# Copyright 2005 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + mkbootfs.c + +LOCAL_MODULE := mkbootfs + +include $(BUILD_HOST_EXECUTABLE) + +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c new file mode 100644 index 0000000..f672336 --- /dev/null +++ b/cpio/mkbootfs.c @@ -0,0 +1,261 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> + +#include <stdarg.h> +#include <fcntl.h> + +#include <private/android_filesystem_config.h> + +/* NOTES +** +** - see buffer-format.txt from the linux kernel docs for +** an explanation of this file format +** - dotfiles are ignored +** - directories named 'root' are ignored +** - device notes, pipes, etc are not supported (error) +*/ + +void die(const char *why, ...) +{ + va_list ap; + + va_start(ap, why); + fprintf(stderr,"error: "); + vfprintf(stderr, why, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} + +static int verbose = 0; +static int total_size = 0; + +static void fix_stat(const char *path, struct stat *s) +{ + fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode); +} + +static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize) +{ + // Nothing is special about this value, just picked something in the + // approximate range that was being used already, and avoiding small + // values which may be special. + static unsigned next_inode = 300000; + + while(total_size & 3) { + total_size++; + putchar(0); + } + + fix_stat(out, s); +// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode); + + printf("%06x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x%08x%08x%08x%08x%s%c", + 0x070701, + next_inode++, // s.st_ino, + s->st_mode, + 0, // s.st_uid, + 0, // s.st_gid, + 1, // s.st_nlink, + 0, // s.st_mtime, + datasize, + 0, // volmajor + 0, // volminor + 0, // devmajor + 0, // devminor, + olen + 1, + 0, + out, + 0 + ); + + total_size += 6 + 8*13 + olen + 1; + + if(strlen(out) != olen) die("ACK!"); + + while(total_size & 3) { + total_size++; + putchar(0); + } + + if(datasize) { + fwrite(data, datasize, 1, stdout); + total_size += datasize; + } +} + +static void _eject_trailer() +{ + struct stat s; + memset(&s, 0, sizeof(s)); + _eject(&s, "TRAILER!!!", 10, 0, 0); + + while(total_size & 0xff) { + total_size++; + putchar(0); + } +} + +static void _archive(char *in, char *out, int ilen, int olen); + +static int compare(const void* a, const void* b) { + return strcmp(*(const char**)a, *(const char**)b); +} + +static void _archive_dir(char *in, char *out, int ilen, int olen) +{ + int i, t; + DIR *d; + struct dirent *de; + + if(verbose) { + fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n", + in, out, ilen, olen); + } + + d = opendir(in); + if(d == 0) die("cannot open directory '%s'", in); + + int size = 32; + int entries = 0; + char** names = malloc(size * sizeof(char*)); + if (names == NULL) { + fprintf(stderr, "failed to allocate dir names array (size %d)\n", size); + exit(1); + } + + while((de = readdir(d)) != 0){ + /* xxx: feature? maybe some dotfiles are okay */ + if(de->d_name[0] == '.') continue; + + /* xxx: hack. use a real exclude list */ + if(!strcmp(de->d_name, "root")) continue; + + if (entries >= size) { + size *= 2; + names = realloc(names, size * sizeof(char*)); + if (names == NULL) { + fprintf(stderr, "failed to reallocate dir names array (size %d)\n", + size); + exit(1); + } + } + names[entries] = strdup(de->d_name); + if (names[entries] == NULL) { + fprintf(stderr, "failed to strdup name \"%s\"\n", + de->d_name); + exit(1); + } + ++entries; + } + + qsort(names, entries, sizeof(char*), compare); + + for (i = 0; i < entries; ++i) { + t = strlen(names[i]); + in[ilen] = '/'; + memcpy(in + ilen + 1, names[i], t + 1); + + if(olen > 0) { + out[olen] = '/'; + memcpy(out + olen + 1, names[i], t + 1); + _archive(in, out, ilen + t + 1, olen + t + 1); + } else { + memcpy(out, names[i], t + 1); + _archive(in, out, ilen + t + 1, t); + } + + in[ilen] = 0; + out[olen] = 0; + + free(names[i]); + } + free(names); +} + +static void _archive(char *in, char *out, int ilen, int olen) +{ + struct stat s; + + if(verbose) { + fprintf(stderr,"_archive('%s','%s',%d,%d)\n", + in, out, ilen, olen); + } + + if(lstat(in, &s)) die("could not stat '%s'\n", in); + + if(S_ISREG(s.st_mode)){ + char *tmp; + int fd; + + fd = open(in, O_RDONLY); + if(fd < 0) die("cannot open '%s' for read", in); + + tmp = (char*) malloc(s.st_size); + if(tmp == 0) die("cannot allocate %d bytes", s.st_size); + + if(read(fd, tmp, s.st_size) != s.st_size) { + die("cannot read %d bytes", s.st_size); + } + + _eject(&s, out, olen, tmp, s.st_size); + + free(tmp); + close(fd); + } else if(S_ISDIR(s.st_mode)) { + _eject(&s, out, olen, 0, 0); + _archive_dir(in, out, ilen, olen); + } else if(S_ISLNK(s.st_mode)) { + char buf[1024]; + int size; + size = readlink(in, buf, 1024); + if(size < 0) die("cannot read symlink '%s'", in); + _eject(&s, out, olen, buf, size); + } else { + die("Unknown '%s' (mode %d)?\n", in, s.st_mode); + } +} + +void archive(const char *start, const char *prefix) +{ + char in[8192]; + char out[8192]; + + strcpy(in, start); + strcpy(out, prefix); + + _archive_dir(in, out, strlen(in), strlen(out)); +} + +int main(int argc, char *argv[]) +{ + argc--; + argv++; + + if(argc == 0) die("no directories to process?!"); + + while(argc-- > 0){ + char *x = strchr(*argv, '='); + if(x != 0) { + *x++ = 0; + } else { + x = ""; + } + + archive(*argv, x); + + argv++; + } + + _eject_trailer(); + + return 0; +} diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk new file mode 100644 index 0000000..b22e1a8 --- /dev/null +++ b/debuggerd/Android.mk @@ -0,0 +1,26 @@ +# Copyright 2005 The Android Open Source Project + +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c +LOCAL_CFLAGS := -Wall +LOCAL_MODULE := debuggerd + +LOCAL_STATIC_LIBRARIES := libcutils libc + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := crasher.c +LOCAL_SRC_FILES += crashglue.S +LOCAL_MODULE := crasher +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := eng +#LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SHARED_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm diff --git a/debuggerd/MODULE_LICENSE_APACHE2 b/debuggerd/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/debuggerd/MODULE_LICENSE_APACHE2 diff --git a/debuggerd/NOTICE b/debuggerd/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/debuggerd/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/debuggerd/crasher.c b/debuggerd/crasher.c new file mode 100644 index 0000000..f4a5a62 --- /dev/null +++ b/debuggerd/crasher.c @@ -0,0 +1,105 @@ + +//#include <cutils/misc.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sched.h> +#include <errno.h> + +#include <signal.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <sys/socket.h> + +#include <pthread.h> + +#include <cutils/sockets.h> + +void crash1(void); +void crashnostack(void); + +static void debuggerd_connect() +{ + char tmp[1]; + int s; + sprintf(tmp, "%d", gettid()); + s = socket_local_client("android:debuggerd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if(s >= 0) { + read(s, tmp, 1); + close(s); + } +} + +void test_call1() +{ + *((int*) 32) = 1; +} + +void *test_thread(void *x) +{ + printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid()); + + sleep(1); + test_call1(); + printf("goodbye\n"); + + return 0; +} + +void *noisy(void *x) +{ + char c = (unsigned) x; + for(;;) { + usleep(250*1000); + write(2, &c, 1); + if(c == 'C') *((unsigned*) 0) = 42; + } + return 0; +} + +int ctest() +{ + pthread_t thr; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thr, &attr, noisy, (void*) 'A'); + pthread_create(&thr, &attr, noisy, (void*) 'B'); + pthread_create(&thr, &attr, noisy, (void*) 'C'); + for(;;) ; + return 0; +} + +int main(int argc, char **argv) +{ + pthread_t thr; + pthread_attr_t attr; + + fprintf(stderr,"crasher: " __TIME__ "!@\n"); + fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid()); + + if(argc > 1) { + if(!strcmp(argv[1],"nostack")) crashnostack(); + if(!strcmp(argv[1],"ctest")) return ctest(); + if(!strcmp(argv[1],"exit")) exit(1); + if(!strcmp(argv[1],"abort")) maybeabort(); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thr, &attr, test_thread, 0); + while(1) sleep(1); + } else { + crash1(); +// *((int*) 0) = 42; + } + + return 0; +} + +void maybeabort() +{ + if(time(0) != 42) abort(); +} diff --git a/debuggerd/crashglue.S b/debuggerd/crashglue.S new file mode 100644 index 0000000..888951b --- /dev/null +++ b/debuggerd/crashglue.S @@ -0,0 +1,28 @@ +.globl crash1 +.globl crashnostack + +crash1: + ldr r0, =0xa5a50000 + ldr r1, =0xa5a50001 + ldr r2, =0xa5a50002 + ldr r3, =0xa5a50003 + ldr r4, =0xa5a50004 + ldr r5, =0xa5a50005 + ldr r6, =0xa5a50006 + ldr r7, =0xa5a50007 + ldr r8, =0xa5a50008 + ldr r9, =0xa5a50009 + ldr r10, =0xa5a50010 + ldr r11, =0xa5a50011 + ldr r12, =0xa5a50012 + + mov lr, #0 + ldr lr, [lr] + b . + + +crashnostack: + mov sp, #0 + mov r0, #0 + ldr r0, [r0] + b .
\ No newline at end of file diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c new file mode 100644 index 0000000..9394e1c --- /dev/null +++ b/debuggerd/debuggerd.c @@ -0,0 +1,852 @@ +/* system/debuggerd/debuggerd.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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <pthread.h> +#include <stdarg.h> +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> + +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <sys/exec_elf.h> +#include <sys/stat.h> + +#include <cutils/sockets.h> +#include <cutils/logd.h> +#include <cutils/sockets.h> +#include <cutils/properties.h> + +#include <linux/input.h> + +#include <private/android_filesystem_config.h> + +#include "utility.h" + +/* Main entry point to get the backtrace from the crashing process */ +extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], + int *frame0_pc_sane, + bool at_fault); + +static char **process_name_ptr; + +static int logsocket = -1; + +#define ANDROID_LOG_INFO 4 + +/* Log information onto the tombstone */ +void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) +{ + char buf[128]; + + va_list ap; + va_start(ap, fmt); + + if (tfd >= 0) { + int len; + vsnprintf(buf, sizeof(buf), fmt, ap); + len = strlen(buf); + if(tfd >= 0) write(tfd, buf, len); + } + + if (!in_tombstone_only) + __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); +} + +#define LOG(fmt...) _LOG(-1, 0, fmt) +#if 0 +#define XLOG(fmt...) _LOG(-1, 0, fmt) +#else +#define XLOG(fmt...) do {} while(0) +#endif + +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 + +mapinfo *parse_maps_line(char *line) +{ + mapinfo *mi; + int len = strlen(line); + + if(len < 1) return 0; + line[--len] = 0; + + if(len < 50) return 0; + if(line[20] != 'x') return 0; + + mi = malloc(sizeof(mapinfo) + (len - 47)); + if(mi == 0) return 0; + + mi->start = strtoul(line, 0, 16); + mi->end = strtoul(line + 9, 0, 16); + /* To be filled in parse_exidx_info if the mapped section starts with + * elf_header + */ + mi->exidx_start = mi->exidx_end = 0; + mi->next = 0; + strcpy(mi->name, line + 49); + + return mi; +} + +void dump_build_info(int tfd) +{ + char fingerprint[PROPERTY_VALUE_MAX]; + + property_get("ro.build.fingerprint", fingerprint, "unknown"); + + _LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint); +} + + +void dump_stack_and_code(int tfd, int pid, mapinfo *map, + int unwind_depth, unsigned int sp_list[], + int frame0_pc_sane, bool at_fault) +{ + unsigned int sp, pc, p, end, data; + struct pt_regs r; + int sp_depth; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; + sp = r.ARM_sp; + pc = r.ARM_pc; + + /* Died because calling the weeds - dump + * the code around the PC in the next frame instead. + */ + if (frame0_pc_sane == 0) { + pc = r.ARM_lr; + } + + _LOG(tfd, true, "code%s:\n", frame0_pc_sane ? "" : " (around frame #01)"); + + end = p = pc & ~3; + p -= 16; + + /* Dump the code as: + * PC contents + * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c + * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 + */ + while (p <= end) { + int i; + + _LOG(tfd, true, " %08x ", p); + for (i = 0; i < 4; i++) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + _LOG(tfd, true, " %08x", data); + p += 4; + } + _LOG(tfd, true, "\n", p); + } + + p = sp - 64; + p &= ~3; + if (unwind_depth != 0) { + if (unwind_depth < STACK_CONTENT_DEPTH) { + end = sp_list[unwind_depth-1]; + } + else { + end = sp_list[STACK_CONTENT_DEPTH-1]; + } + } + else { + end = sp | 0x000000ff; + end += 0xff; + } + + _LOG(tfd, only_in_tombstone, "stack:\n"); + + /* If the crash is due to PC == 0, there will be two frames that + * have identical SP value. + */ + if (sp_list[0] == sp_list[1]) { + sp_depth = 1; + } + else { + sp_depth = 0; + } + + while (p <= end) { + char *prompt; + char level[16]; + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + if (p == sp_list[sp_depth]) { + sprintf(level, "#%02d", sp_depth++); + prompt = level; + } + else { + prompt = " "; + } + + /* Print the stack content in the log for the first 3 frames. For the + * rest only print them in the tombstone file. + */ + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + "%s %08x %08x %s\n", prompt, p, data, + map_to_name(map, data, "")); + p += 4; + } + /* print another 64-byte of stack data after the last frame */ + + end = p+64; + while (p <= end) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + " %08x %08x %s\n", p, data, + map_to_name(map, data, "")); + p += 4; + } +} + +void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, + bool at_fault) +{ + struct pt_regs r; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, !at_fault, "tid %d not responding!\n", pid); + return; + } + + if (unwound_level == 0) { + _LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc, + map_to_name(map, r.ARM_pc, "<unknown>")); + } + _LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr, + map_to_name(map, r.ARM_lr, "<unknown>")); +} + +void dump_registers(int tfd, int pid, bool at_fault) +{ + struct pt_regs r; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3); + _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7); + _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n", + r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp); + _LOG(tfd, only_in_tombstone, + " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr); +} + +const char *get_signame(int sig) +{ + switch(sig) { + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGSEGV: return "SIGSEGV"; + case SIGSTKFLT: return "SIGSTKFLT"; + default: return "?"; + } +} + +void dump_fault_addr(int tfd, int pid, int sig) +{ + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ + _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); + } else { + _LOG(tfd, false, "signal %d (%s), fault addr %08x\n", + sig, get_signame(sig), si.si_addr); + } +} + +void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig) +{ + char data[1024]; + char *x = 0; + FILE *fp; + + sprintf(data, "/proc/%d/cmdline", pid); + fp = fopen(data, "r"); + if(fp) { + x = fgets(data, 1024, fp); + fclose(fp); + } + + _LOG(tfd, false, + "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + dump_build_info(tfd); + _LOG(tfd, false, "pid: %d, tid: %d >>> %s <<<\n", + pid, tid, x ? x : "UNKNOWN"); + + if(sig) dump_fault_addr(tfd, tid, sig); +} + +static void parse_exidx_info(mapinfo *milist, pid_t pid) +{ + mapinfo *mi; + for (mi = milist; mi != NULL; mi = mi->next) { + Elf32_Ehdr ehdr; + + memset(&ehdr, 0, sizeof(Elf32_Ehdr)); + /* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of + * mapped section. + */ + get_remote_struct(pid, (void *) (mi->start), &ehdr, + sizeof(Elf32_Ehdr)); + /* Check if it has the matching magic words */ + if (IS_ELF(ehdr)) { + Elf32_Phdr phdr; + Elf32_Phdr *ptr; + int i; + + ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff); + for (i = 0; i < ehdr.e_phnum; i++) { + /* Parse the program header */ + get_remote_struct(pid, (void *) ptr+i, &phdr, + sizeof(Elf32_Phdr)); + /* Found a EXIDX segment? */ + if (phdr.p_type == PT_ARM_EXIDX) { + mi->exidx_start = mi->start + phdr.p_offset; + mi->exidx_end = mi->exidx_start + phdr.p_filesz; + break; + } + } + } + } +} + +void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) +{ + char data[1024]; + FILE *fp; + mapinfo *milist = 0; + unsigned int sp_list[STACK_CONTENT_DEPTH]; + int stack_depth; + int frame0_pc_sane = 1; + + if (!at_fault) { + _LOG(tfd, true, + "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + _LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid); + } + + dump_registers(tfd, tid, at_fault); + + /* Clear stack pointer records */ + memset(sp_list, 0, sizeof(sp_list)); + + sprintf(data, "/proc/%d/maps", pid); + fp = fopen(data, "r"); + if(fp) { + while(fgets(data, 1024, fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + + parse_exidx_info(milist, tid); + + /* If stack unwinder fails, use the default solution to dump the stack + * content. + */ + stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list, + &frame0_pc_sane, at_fault); + + /* The stack unwinder should at least unwind two levels of stack. If less + * level is seen we make sure at lease pc and lr are dumped. + */ + if (stack_depth < 2) { + dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault); + } + + dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, frame0_pc_sane, + at_fault); + + while(milist) { + mapinfo *next = milist->next; + free(milist); + milist = next; + } +} + +/* FIXME: unused: use it or lose it*/ +#if 0 +static +void start_gdbserver_vs(int pid, int port) +{ + pid_t p; + char *args[5]; + char commspec[16]; + char pidspec[16]; + + p = fork(); + if(p < 0) { + LOG("could not fork()\n"); + return; + } + + if(p == 0) { + sprintf(commspec, ":%d", port); + sprintf(pidspec, "%d", pid); + args[0] = "/system/bin/gdbserver"; + args[1] = commspec; + args[2] = "--attach"; + args[3] = pidspec; + args[4] = 0; + exit(execv(args[0], args)); + } else { + LOG("gdbserver pid=%d port=%d targetpid=%d\n", + p, port, pid); + + sleep(5); + } +} +#endif + +#define MAX_TOMBSTONES 10 + +#define typecheck(x,y) { \ + typeof(x) __dummy1; \ + typeof(y) __dummy2; \ + (void)(&__dummy1 == &__dummy2); } + +#define TOMBSTONE_DIR "/data/tombstones" + +/* + * find_and_open_tombstone - find an available tombstone slot, if any, of the + * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no + * file is available, we reuse the least-recently-modified file. + */ +static int find_and_open_tombstone(void) +{ + unsigned long mtime = ULONG_MAX; + struct stat sb; + char path[128]; + int fd, i, oldest = 0; + + /* + * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought + * to, our logic breaks. This check will generate a warning if that happens. + */ + typecheck(mtime, sb.st_mtime); + + /* + * In a single wolf-like pass, find an available slot and, in case none + * exist, find and record the least-recently-modified file. + */ + for (i = 0; i < MAX_TOMBSTONES; i++) { + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); + + if (!stat(path, &sb)) { + if (sb.st_mtime < mtime) { + oldest = i; + mtime = sb.st_mtime; + } + continue; + } + if (errno != ENOENT) + continue; + + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); + if (fd < 0) + continue; /* raced ? */ + + fchown(fd, AID_SYSTEM, AID_SYSTEM); + return fd; + } + + /* we didn't find an available file, so we clobber the oldest one */ + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); + fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + fchown(fd, AID_SYSTEM, AID_SYSTEM); + + return fd; +} + +/* Return true if some thread is not detached cleanly */ +static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) +{ + char task_path[1024]; + + sprintf(task_path, "/proc/%d/task", pid); + DIR *d; + struct dirent *de; + int need_cleanup = 0; + + d = opendir(task_path); + /* Bail early if cannot open the task directory */ + if (d == NULL) { + XLOG("Cannot open /proc/%d/task\n", pid); + return false; + } + while ((de = readdir(d)) != NULL) { + unsigned new_tid; + /* Ignore "." and ".." */ + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + new_tid = atoi(de->d_name); + /* The main thread at fault has been handled individually */ + if (new_tid == tid) + continue; + + /* Skip this thread if cannot ptrace it */ + if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) + continue; + + dump_crash_report(tfd, pid, new_tid, false); + need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0); + } + closedir(d); + return need_cleanup != 0; +} + +/* Return true if some thread is not detached cleanly */ +static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, + int signal) +{ + int fd; + bool need_cleanup = false; + + mkdir(TOMBSTONE_DIR, 0755); + chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); + + fd = find_and_open_tombstone(); + if (fd < 0) + return need_cleanup; + + dump_crash_banner(fd, pid, tid, signal); + dump_crash_report(fd, pid, tid, true); + /* + * If the user has requested to attach gdb, don't collect the per-thread + * information as it increases the chance to lose track of the process. + */ + if ((signed)pid > debug_uid) { + need_cleanup = dump_sibling_thread_report(fd, pid, tid); + } + + close(fd); + return need_cleanup; +} + +static int +write_string(const char* file, const char* string) +{ + int len; + int fd; + ssize_t amt; + fd = open(file, O_RDWR); + len = strlen(string); + if (fd < 0) + return -errno; + amt = write(fd, string, len); + close(fd); + return amt >= 0 ? 0 : -errno; +} + +static +void init_debug_led(void) +{ + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + write_string("/sys/class/leds/green/brightness", "0"); + write_string("/sys/class/leds/blue/brightness", "0"); + write_string("/sys/class/leds/red/device/blink", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); +} + +static +void enable_debug_led(void) +{ + // trout leds + write_string("/sys/class/leds/red/brightness", "255"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "1,0"); +} + +static +void disable_debug_led(void) +{ + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); +} + +extern int init_getevent(); +extern void uninit_getevent(); +extern int get_event(struct input_event* event, int timeout); + +static void wait_for_user_action(unsigned tid, struct ucred* cr) +{ + (void)tid; + /* First log a helpful message */ + LOG( "********************************************************\n" + "* process %d crashed. debuggerd waiting for gdbserver \n" + "* \n" + "* adb shell gdbserver :port --attach %d & \n" + "* \n" + "* and press the HOME key. \n" + "********************************************************\n", + cr->pid, cr->pid); + + /* wait for HOME key */ + if (init_getevent() == 0) { + int ms = 1200 / 10; + int dit = 1; + int dah = 3*dit; + int _ = -dit; + int ___ = 3*_; + int _______ = 7*_; + const signed char codes[] = { + dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ + }; + size_t s = 0; + struct input_event e; + int home = 0; + init_debug_led(); + enable_debug_led(); + do { + int timeout = abs((int)(codes[s])) * ms; + int res = get_event(&e, timeout); + if (res == 0) { + if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0) + home = 1; + } else if (res == 1) { + if (++s >= sizeof(codes)/sizeof(*codes)) + s = 0; + if (codes[s] > 0) { + enable_debug_led(); + } else { + disable_debug_led(); + } + } + } while (!home); + uninit_getevent(); + } + + /* don't forget to turn debug led off */ + disable_debug_led(); + + /* close filedescriptor */ + LOG("debuggerd resuming process %d", cr->pid); + } + +static void handle_crashing_process(int fd) +{ + char buf[64]; + struct stat s; + unsigned tid; + struct ucred cr; + int n, len, status; + int tid_attach_status = -1; + unsigned retry = 30; + bool need_cleanup = false; + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.db.uid", value, "-1"); + int debug_uid = atoi(value); + + XLOG("handle_crashing_process(%d)\n", fd); + + len = sizeof(cr); + n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + if(n != 0) { + LOG("cannot get credentials\n"); + goto done; + } + + XLOG("reading tid\n"); + fcntl(fd, F_SETFL, O_NONBLOCK); + while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) { + if(errno == EINTR) continue; + if(errno == EWOULDBLOCK) { + if(retry-- > 0) { + usleep(100 * 1000); + continue; + } + LOG("timed out reading tid\n"); + goto done; + } + LOG("read failure? %s\n", strerror(errno)); + goto done; + } + + sprintf(buf,"/proc/%d/task/%d", cr.pid, tid); + if(stat(buf, &s)) { + LOG("tid %d does not exist in pid %d. ignorning debug request\n", + tid, cr.pid); + close(fd); + return; + } + + XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); + + tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); + if(tid_attach_status < 0) { + LOG("ptrace attach failed: %s\n", strerror(errno)); + goto done; + } + + close(fd); + fd = -1; + + for(;;) { + n = waitpid(tid, &status, __WALL); + + if(n < 0) { + if(errno == EAGAIN) continue; + LOG("waitpid failed: %s\n", strerror(errno)); + goto done; + } + + XLOG("waitpid: n=%d status=%08x\n", n, status); + + if(WIFSTOPPED(status)){ + n = WSTOPSIG(status); + switch(n) { + case SIGSTOP: + XLOG("stopped -- continuing\n"); + n = ptrace(PTRACE_CONT, tid, 0, 0); + if(n) { + LOG("ptrace failed: %s\n", strerror(errno)); + goto done; + } + continue; + + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGSTKFLT: { + XLOG("stopped -- fatal signal\n"); + need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n); + kill(tid, SIGSTOP); + goto done; + } + + default: + XLOG("stopped -- unexpected signal\n"); + goto done; + } + } else { + XLOG("unexpected waitpid response\n"); + goto done; + } + } + +done: + XLOG("detaching\n"); + + /* stop the process so we can debug */ + kill(cr.pid, SIGSTOP); + + /* + * If a thread has been attached by ptrace, make sure it is detached + * successfully otherwise we will get a zombie. + */ + if (tid_attach_status == 0) { + int detach_status; + /* detach so we can attach gdbserver */ + detach_status = ptrace(PTRACE_DETACH, tid, 0, 0); + need_cleanup |= (detach_status != 0); + } + + /* + * if debug.db.uid is set, its value indicates if we should wait + * for user action for the crashing process. + * in this case, we log a message and turn the debug LED on + * waiting for a gdb connection (for instance) + */ + + if ((signed)cr.uid <= debug_uid) { + wait_for_user_action(tid, &cr); + } + + /* resume stopped process (so it can crash in peace) */ + kill(cr.pid, SIGCONT); + + if (need_cleanup) { + LOG("debuggerd committing suicide to free the zombie!\n"); + kill(getpid(), SIGKILL); + } + + if(fd != -1) close(fd); +} + +int main(int argc, char **argv) +{ + int s; + struct sigaction act; + + process_name_ptr = argv; + + logsocket = socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); + if(logsocket < 0) { + logsocket = -1; + } else { + fcntl(logsocket, F_SETFD, FD_CLOEXEC); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,SIGCHLD); + act.sa_flags = SA_NOCLDWAIT; + sigaction(SIGCHLD, &act, 0); + + s = socket_local_server("android:debuggerd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if(s < 0) return -1; + fcntl(s, F_SETFD, FD_CLOEXEC); + + LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); + + for(;;) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = accept(s, &addr, &alen); + if(fd < 0) continue; + + fcntl(fd, F_SETFD, FD_CLOEXEC); + + handle_crashing_process(fd); + } + return 0; +} diff --git a/debuggerd/getevent.c b/debuggerd/getevent.c new file mode 100644 index 0000000..ebd070c --- /dev/null +++ b/debuggerd/getevent.c @@ -0,0 +1,219 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <sys/limits.h> +#include <sys/poll.h> +#include <linux/input.h> +#include <errno.h> +#include <cutils/log.h> + +static struct pollfd *ufds; +static char **device_names; +static int nfds; + +static int open_device(const char *device) +{ + int version; + int fd; + struct pollfd *new_ufds; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if(fd < 0) { + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); + idstr[0] = '\0'; + } + + new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); + if(new_ufds == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + ufds = new_ufds; + new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1)); + if(new_device_names == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + device_names = new_device_names; + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; +} + +int close_device(const char *device) +{ + int i; + for(i = 1; i < nfds; i++) { + if(strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + return -1; +} + +static int read_notify(const char *dirname, int nfd) +{ + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname); + } + else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +static int scan_dir(const char *dirname) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + +int init_getevent() +{ + int res; + const char *device_path = "/dev/input"; + + nfds = 1; + ufds = calloc(1, sizeof(ufds[0])); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + return 1; + } + res = scan_dir(device_path); + if(res < 0) { + return 1; + } + return 0; +} + +void uninit_getevent() +{ + int i; + for(i = 0; i < nfds; i++) { + close(ufds[i].fd); + } + free(ufds); + ufds = 0; + nfds = 0; +} + +int get_event(struct input_event* event, int timeout) +{ + int res; + int i; + int pollres; + const char *device_path = "/dev/input"; + while(1) { + pollres = poll(ufds, nfds, timeout); + if (pollres == 0) { + return 1; + } + if(ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd); + } + for(i = 1; i < nfds; i++) { + if(ufds[i].revents) { + if(ufds[i].revents & POLLIN) { + res = read(ufds[i].fd, event, sizeof(*event)); + if(res < (int)sizeof(event)) { + fprintf(stderr, "could not get event\n"); + return -1; + } + return 0; + } + } + } + } + return 0; +} diff --git a/debuggerd/pr-support.c b/debuggerd/pr-support.c new file mode 100644 index 0000000..358d9b7 --- /dev/null +++ b/debuggerd/pr-support.c @@ -0,0 +1,345 @@ +/* ARM EABI compliant unwinding routines + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Paul Brook + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + In addition to the permissions in the GNU General Public License, the + Free Software Foundation gives you unlimited permission to link the + compiled version of this file into combinations with other programs, + and to distribute those combinations without any restriction coming + from the use of this file. (The General Public License restrictions + do apply in other respects; for example, they cover modification of + the file, and distribution when not linked into a combine + executable.) + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/**************************************************************************** + * The functions here are derived from gcc/config/arm/pr-support.c from the + * 4.3.x release. The main changes here involve the use of ptrace to retrieve + * memory/processor states from a remote process. + ****************************************************************************/ + +#include <sys/types.h> +#include <unwind.h> + +#include "utility.h" + +/* We add a prototype for abort here to avoid creating a dependency on + target headers. */ +extern void abort (void); + +/* Derived from _Unwind_VRS_Pop to use ptrace */ +extern _Unwind_VRS_Result +unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + _uw discriminator, + _Unwind_VRS_DataRepresentation representation, + pid_t pid); + +typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ + +/* Misc constants. */ +#define R_IP 12 +#define R_SP 13 +#define R_LR 14 +#define R_PC 15 + +#define uint32_highbit (((_uw) 1) << 31) + +void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); + +/* Unwind descriptors. */ + +typedef struct +{ + _uw16 length; + _uw16 offset; +} EHT16; + +typedef struct +{ + _uw length; + _uw offset; +} EHT32; + +/* Personality routine helper functions. */ + +#define CODE_FINISH (0xb0) + +/* Derived from next_unwind_byte to use ptrace */ +/* Return the next byte of unwinding information, or CODE_FINISH if there is + no data remaining. */ +static inline _uw8 +next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid) +{ + _uw8 b; + + if (uws->bytes_left == 0) + { + /* Load another word */ + if (uws->words_left == 0) + return CODE_FINISH; /* Nothing left. */ + uws->words_left--; + uws->data = get_remote_word(pid, uws->next); + uws->next++; + uws->bytes_left = 3; + } + else + uws->bytes_left--; + + /* Extract the most significant byte. */ + b = (uws->data >> 24) & 0xff; + uws->data <<= 8; + return b; +} + +/* Execute the unwinding instructions described by UWS. */ +_Unwind_Reason_Code +unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, + pid_t pid) +{ + _uw op; + int set_pc; + _uw reg; + + set_pc = 0; + for (;;) + { + op = next_unwind_byte_with_ptrace (uws, pid); + if (op == CODE_FINISH) + { + /* If we haven't already set pc then copy it from lr. */ + if (!set_pc) + { + _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, + ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, + ®); + set_pc = 1; + } + /* Drop out of the loop. */ + break; + } + if ((op & 0x80) == 0) + { + /* vsp = vsp +- (imm6 << 2 + 4). */ + _uw offset; + + offset = ((op & 0x3f) << 2) + 4; + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + if (op & 0x40) + reg -= offset; + else + reg += offset; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + + if ((op & 0xf0) == 0x80) + { + op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid); + if (op == 0x8000) + { + /* Refuse to unwind. */ + return _URC_FAILURE; + } + /* Pop r4-r15 under mask. */ + op = (op << 4) & 0xfff0; + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + if (op & (1 << R_PC)) + set_pc = 1; + continue; + } + if ((op & 0xf0) == 0x90) + { + op &= 0xf; + if (op == 13 || op == 15) + /* Reserved. */ + return _URC_FAILURE; + /* vsp = r[nnnn]. */ + _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + if ((op & 0xf0) == 0xa0) + { + /* Pop r4-r[4+nnn], [lr]. */ + _uw mask; + + mask = (0xff0 >> (7 - (op & 7))) & 0xff0; + if (op & 8) + mask |= (1 << R_LR); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xf0) == 0xb0) + { + /* op == 0xb0 already handled. */ + if (op == 0xb1) + { + op = next_unwind_byte_with_ptrace (uws, pid); + if (op == 0 || ((op & 0xf0) != 0)) + /* Spare. */ + return _URC_FAILURE; + /* Pop r0-r4 under mask. */ + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, + _UVRSD_UINT32, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if (op == 0xb2) + { + /* vsp = vsp + 0x204 + (uleb128 << 2). */ + int shift; + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, + ®); + op = next_unwind_byte_with_ptrace (uws, pid); + shift = 2; + while (op & 0x80) + { + reg += ((op & 0x7f) << shift); + shift += 7; + op = next_unwind_byte_with_ptrace (uws, pid); + } + reg += ((op & 0x7f) << shift) + 0x204; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, + ®); + continue; + } + if (op == 0xb3) + { + /* Pop VFP registers with fldmx. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xfc) == 0xb4) + { + /* Pop FPA E[4]-E[4+nn]. */ + op = 0x40000 | ((op & 3) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + /* op & 0xf8 == 0xb8. */ + /* Pop VFP D[8]-D[8+nnn] with fldmx. */ + op = 0x80000 | ((op & 7) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xf0) == 0xc0) + { + if (op == 0xc6) + { + /* Pop iWMMXt D registers. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, + _UVRSD_UINT64, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if (op == 0xc7) + { + op = next_unwind_byte_with_ptrace (uws, pid); + if (op == 0 || (op & 0xf0) != 0) + /* Spare. */ + return _URC_FAILURE; + /* Pop iWMMXt wCGR{3,2,1,0} under mask. */ + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op, + _UVRSD_UINT32, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xf8) == 0xc0) + { + /* Pop iWMMXt wR[10]-wR[10+nnn]. */ + op = 0xa0000 | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, + _UVRSD_UINT64, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if (op == 0xc8) + { +#ifndef __VFP_FP__ + /* Pop FPA registers. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; +#else + /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, + _UVRSD_DOUBLE, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; +#endif + } + if (op == 0xc9) + { + /* Pop VFP registers with fldmd. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, + _UVRSD_DOUBLE, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + /* Spare. */ + return _URC_FAILURE; + } + if ((op & 0xf8) == 0xd0) + { + /* Pop VFP D[8]-D[8+nnn] with fldmd. */ + op = 0x80000 | ((op & 7) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + /* Spare. */ + return _URC_FAILURE; + } + return _URC_OK; +} diff --git a/debuggerd/unwind-arm.c b/debuggerd/unwind-arm.c new file mode 100644 index 0000000..9642d2e --- /dev/null +++ b/debuggerd/unwind-arm.c @@ -0,0 +1,654 @@ +/* ARM EABI compliant unwinding routines. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Paul Brook + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + In addition to the permissions in the GNU General Public License, the + Free Software Foundation gives you unlimited permission to link the + compiled version of this file into combinations with other programs, + and to distribute those combinations without any restriction coming + from the use of this file. (The General Public License restrictions + do apply in other respects; for example, they cover modification of + the file, and distribution when not linked into a combine + executable.) + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/**************************************************************************** + * The functions here are derived from gcc/config/arm/unwind-arm.c from the + * 4.3.x release. The main changes here involve the use of ptrace to retrieve + * memory/processor states from a remote process. + ****************************************************************************/ + +#include <cutils/logd.h> +#include <sys/ptrace.h> +#include <unwind.h> +#include "utility.h" + +typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ + +void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); +bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp); +bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp, + const type_info *rttip, + bool is_reference, + void **matched_object); + +/* Misc constants. */ +#define R_IP 12 +#define R_SP 13 +#define R_LR 14 +#define R_PC 15 + +#define EXIDX_CANTUNWIND 1 +#define uint32_highbit (((_uw) 1) << 31) + +#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1) +#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2) +#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3) +#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4) + +struct core_regs +{ + _uw r[16]; +}; + +/* We use normal integer types here to avoid the compiler generating + coprocessor instructions. */ +struct vfp_regs +{ + _uw64 d[16]; + _uw pad; +}; + +struct vfpv3_regs +{ + /* Always populated via VSTM, so no need for the "pad" field from + vfp_regs (which is used to store the format word for FSTMX). */ + _uw64 d[16]; +}; + +struct fpa_reg +{ + _uw w[3]; +}; + +struct fpa_regs +{ + struct fpa_reg f[8]; +}; + +struct wmmxd_regs +{ + _uw64 wd[16]; +}; + +struct wmmxc_regs +{ + _uw wc[4]; +}; + +/* Unwind descriptors. */ + +typedef struct +{ + _uw16 length; + _uw16 offset; +} EHT16; + +typedef struct +{ + _uw length; + _uw offset; +} EHT32; + +/* The ABI specifies that the unwind routines may only use core registers, + except when actually manipulating coprocessor state. This allows + us to write one implementation that works on all platforms by + demand-saving coprocessor registers. + + During unwinding we hold the coprocessor state in the actual hardware + registers and allocate demand-save areas for use during phase1 + unwinding. */ + +typedef struct +{ + /* The first fields must be the same as a phase2_vrs. */ + _uw demand_save_flags; + struct core_regs core; + _uw prev_sp; /* Only valid during forced unwinding. */ + struct vfp_regs vfp; + struct vfpv3_regs vfp_regs_16_to_31; + struct fpa_regs fpa; + struct wmmxd_regs wmmxd; + struct wmmxc_regs wmmxc; +} phase1_vrs; + +/* This must match the structure created by the assembly wrappers. */ +typedef struct +{ + _uw demand_save_flags; + struct core_regs core; +} phase2_vrs; + + +/* An exception index table entry. */ + +typedef struct __EIT_entry +{ + _uw fnoffset; + _uw content; +} __EIT_entry; + +/* Derived version to use ptrace */ +typedef _Unwind_Reason_Code (*personality_routine_with_ptrace) + (_Unwind_State, + _Unwind_Control_Block *, + _Unwind_Context *, + pid_t); + +/* Derived version to use ptrace */ +/* ABI defined personality routines. */ +static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); +static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); +static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); + +/* Execute the unwinding instructions described by UWS. */ +extern _Unwind_Reason_Code +unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, + pid_t pid); + +/* Derived version to use ptrace. Only handles core registers. Disregards + * FP and others. + */ +/* ABI defined function to pop registers off the stack. */ + +_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + _uw discriminator, + _Unwind_VRS_DataRepresentation representation, + pid_t pid) +{ + phase1_vrs *vrs = (phase1_vrs *) context; + + switch (regclass) + { + case _UVRSC_CORE: + { + _uw *ptr; + _uw mask; + int i; + + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + + mask = discriminator & 0xffff; + ptr = (_uw *) vrs->core.r[R_SP]; + /* Pop the requested registers. */ + for (i = 0; i < 16; i++) + { + if (mask & (1 << i)) { + vrs->core.r[i] = get_remote_word(pid, ptr); + ptr++; + } + } + /* Writeback the stack pointer value if it wasn't restored. */ + if ((mask & (1 << R_SP)) == 0) + vrs->core.r[R_SP] = (_uw) ptr; + } + return _UVRSR_OK; + + default: + return _UVRSR_FAILED; + } +} + +/* Core unwinding functions. */ + +/* Calculate the address encoded by a 31-bit self-relative offset at address + P. */ +static inline _uw +selfrel_offset31 (const _uw *p, pid_t pid) +{ + _uw offset = get_remote_word(pid, (void*)p); + + //offset = *p; + /* Sign extend to 32 bits. */ + if (offset & (1 << 30)) + offset |= 1u << 31; + else + offset &= ~(1u << 31); + + return offset + (_uw) p; +} + + +/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains + NREC entries. */ + +static const __EIT_entry * +search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address, + pid_t pid) +{ + _uw next_fn; + _uw this_fn; + int n, left, right; + + if (nrec == 0) + return (__EIT_entry *) 0; + + left = 0; + right = nrec - 1; + + while (1) + { + n = (left + right) / 2; + this_fn = selfrel_offset31 (&table[n].fnoffset, pid); + if (n != nrec - 1) + next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1; + else + next_fn = (_uw)0 - 1; + + if (return_address < this_fn) + { + if (n == left) + return (__EIT_entry *) 0; + right = n - 1; + } + else if (return_address <= next_fn) + return &table[n]; + else + left = n + 1; + } +} + +/* Find the exception index table eintry for the given address. */ +static const __EIT_entry* +get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map) +{ + const __EIT_entry *eitp = NULL; + int nrec; + mapinfo *mi; + + /* The return address is the address of the instruction following the + call instruction (plus one in thumb mode). If this was the last + instruction in the function the address will lie in the following + function. Subtract 2 from the address so that it points within the call + instruction itself. */ + if (return_address >= 2) + return_address -= 2; + + for (mi = map; mi != NULL; mi = mi->next) { + if (return_address >= mi->start && return_address <= mi->end) break; + } + + if (mi) { + if (containing_map) *containing_map = mi; + eitp = (__EIT_entry *) mi->exidx_start; + nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry); + eitp = search_EIT_table (eitp, nrec, return_address, pid); + } + return eitp; +} + +/* Find the exception index table eintry for the given address. + Fill in the relevant fields of the UCB. + Returns _URC_FAILURE if an error occurred, _URC_OK on success. */ + +static _Unwind_Reason_Code +get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid, + mapinfo *map, mapinfo **containing_map) +{ + const __EIT_entry *eitp; + + eitp = get_eitp(return_address, pid, map, containing_map); + + if (!eitp) + { + UCB_PR_ADDR (ucbp) = 0; + return _URC_FAILURE; + } + ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid); + + _uw eitp_content = get_remote_word(pid, (void *)&eitp->content); + + /* Can this frame be unwound at all? */ + if (eitp_content == EXIDX_CANTUNWIND) + { + UCB_PR_ADDR (ucbp) = 0; + return _URC_END_OF_STACK; + } + + /* Obtain the address of the "real" __EHT_Header word. */ + + if (eitp_content & uint32_highbit) + { + /* It is immediate data. */ + ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content; + ucbp->pr_cache.additional = 1; + } + else + { + /* The low 31 bits of the content field are a self-relative + offset to an _Unwind_EHT_Entry structure. */ + ucbp->pr_cache.ehtp = + (_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid); + ucbp->pr_cache.additional = 0; + } + + /* Discover the personality routine address. */ + if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31)) + { + /* One of the predefined standard routines. */ + _uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf; + if (idx == 0) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace; + else if (idx == 1) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace; + else if (idx == 2) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace; + else + { /* Failed */ + UCB_PR_ADDR (ucbp) = 0; + return _URC_FAILURE; + } + } + else + { + /* Execute region offset to PR */ + UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid); + /* Since we are unwinding the stack from a different process, it is + * impossible to execute the personality routine in debuggerd. Punt here. + */ + return _URC_FAILURE; + } + return _URC_OK; +} + +/* Print out the current call level, pc, and module name in the crash log */ +static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, + int tfd, + int stack_level, + mapinfo *map, + unsigned int sp_list[], + bool at_fault) +{ + _uw pc; + _uw rel_pc; + phase2_vrs *vrs = (phase2_vrs*) context; + const mapinfo *mi; + bool only_in_tombstone = !at_fault; + + if (stack_level < STACK_CONTENT_DEPTH) { + sp_list[stack_level] = vrs->core.r[R_SP]; + } + pc = vrs->core.r[R_PC]; + + // Top level frame + if (stack_level == 0) { + pc &= ~1; + } + // For deeper framers, rollback pc by one instruction + else { + pc = vrs->core.r[R_PC]; + /* Thumb mode - need to check whether the bl(x) has long offset or not. + * Examples: + * + * arm blx in the middle of thumb: + * 187ae: 2300 movs r3, #0 + * 187b0: f7fe ee1c blx 173ec + * 187b4: 2c00 cmp r4, #0 + * + * arm bl in the middle of thumb: + * 187d8: 1c20 adds r0, r4, #0 + * 187da: f136 fd15 bl 14f208 + * 187de: 2800 cmp r0, #0 + * + * pure thumb: + * 18894: 189b adds r3, r3, r2 + * 18896: 4798 blx r3 + * 18898: b001 add sp, #4 + */ + if (pc & 1) { + _uw prev_word; + pc = (pc & ~1); + prev_word = get_remote_word(pid, (void *) pc-4); + // Long offset + if ((prev_word & 0xf0000000) == 0xf0000000 && + (prev_word & 0x0000e000) == 0x0000e000) { + pc -= 4; + } + else { + pc -= 2; + } + } + else { + pc -= 4; + } + } + + /* We used to print the absolute PC in the back trace, and mask out the top + * 3 bits to guesstimate the offset in the .so file. This is not working for + * non-prelinked libraries since the starting offset may not be aligned on + * 1MB boundaries, and the library may be larger than 1MB. So for .so + * addresses we print the relative offset in back trace. + */ + rel_pc = pc; + mi = pc_to_mapinfo(map, pc, &rel_pc); + + _LOG(tfd, only_in_tombstone, + " #%02d pc %08x %s\n", stack_level, rel_pc, + mi ? mi->name : ""); + + return _URC_NO_REASON; +} + +/* Derived from __gnu_Unwind_Backtrace to use ptrace */ +/* Perform stack backtrace through unwind data. Return the level of stack it + * unwinds. + */ +int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], int *frame0_pc_sane, + bool at_fault) +{ + phase1_vrs saved_vrs; + _Unwind_Reason_Code code = _URC_OK; + struct pt_regs r; + int i; + int stack_level = 0; + + _Unwind_Control_Block ucb; + _Unwind_Control_Block *ucbp = &ucb; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0; + + for (i = 0; i < 16; i++) { + saved_vrs.core.r[i] = r.uregs[i]; + /* + _LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]); + */ + } + + /* Set demand-save flags. */ + saved_vrs.demand_save_flags = ~(_uw) 0; + + /* + * If the app crashes because of calling the weeds, we cannot pass the PC + * to the usual unwinding code as the EXIDX mapping will fail. + * Instead, we simply print out the 0 as the top frame, and resume the + * unwinding process with the value stored in LR. + */ + if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) { + *frame0_pc_sane = 0; + log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, + map, sp_list, at_fault); + saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR]; + stack_level++; + } + + do { + mapinfo *this_map = NULL; + /* Find the entry for this routine. */ + if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map) + != _URC_OK) { + /* Uncomment the code below to study why the unwinder failed */ +#if 0 + /* Shed more debugging info for stack unwinder improvement */ + if (this_map) { + _LOG(tfd, 1, + "Relative PC=%#x from %s not contained in EXIDX\n", + saved_vrs.core.r[R_PC] - this_map->start, this_map->name); + } + _LOG(tfd, 1, "PC=%#x SP=%#x\n", + saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]); +#endif + code = _URC_FAILURE; + break; + } + + /* The dwarf unwinder assumes the context structure holds things + like the function and LSDA pointers. The ARM implementation + caches these in the exception header (UCB). To avoid + rewriting everything we make the virtual IP register point at + the UCB. */ + _Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp); + + /* Call log function. */ + if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, + map, sp_list, at_fault) != _URC_NO_REASON) { + code = _URC_FAILURE; + break; + } + stack_level++; + + /* Call the pr to decide what to do. */ + code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))( + _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp, + (void *) &saved_vrs, pid); + /* + * In theory the unwinding process will stop when the end of stack is + * reached or there is no unwinding information for the code address. + * To add another level of guarantee that the unwinding process + * will terminate we will stop it when the STACK_CONTENT_DEPTH is reached. + */ + } while (code != _URC_END_OF_STACK && code != _URC_FAILURE && + stack_level < STACK_CONTENT_DEPTH); + return stack_level; +} + + +/* Derived version to use ptrace */ +/* Common implementation for ARM ABI defined personality routines. + ID is the index of the personality routine, other arguments are as defined + by __aeabi_unwind_cpp_pr{0,1,2}. */ + +static _Unwind_Reason_Code +unwind_pr_common_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + int id, + pid_t pid) +{ + __gnu_unwind_state uws; + _uw *data; + int phase2_call_unexpected_after_unwind = 0; + + state &= _US_ACTION_MASK; + + data = (_uw *) ucbp->pr_cache.ehtp; + uws.data = get_remote_word(pid, data); + data++; + uws.next = data; + if (id == 0) + { + uws.data <<= 8; + uws.words_left = 0; + uws.bytes_left = 3; + } + else + { + uws.words_left = (uws.data >> 16) & 0xff; + uws.data <<= 16; + uws.bytes_left = 2; + data += uws.words_left; + } + + /* Restore the saved pointer. */ + if (state == _US_UNWIND_FRAME_RESUME) + data = (_uw *) ucbp->cleanup_cache.bitpattern[0]; + + if ((ucbp->pr_cache.additional & 1) == 0) + { + /* Process descriptors. */ + while (get_remote_word(pid, data)) { + /********************************************************************** + * The original code here seems to deal with exceptions that are not + * applicable in our toolchain, thus there is no way to test it for now. + * Instead of leaving it here and causing potential instability in + * debuggerd, we'd better punt here and leave the stack unwound. + * In the future when we discover cases where the stack should be unwound + * further but is not, we can revisit the code here. + **********************************************************************/ + return _URC_FAILURE; + } + /* Finished processing this descriptor. */ + } + + if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK) + return _URC_FAILURE; + + if (phase2_call_unexpected_after_unwind) + { + /* Enter __cxa_unexpected as if called from the call site. */ + _Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC)); + _Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected); + return _URC_INSTALL_CONTEXT; + } + + return _URC_CONTINUE_UNWIND; +} + + +/* ABI defined personality routine entry points. */ + +static _Unwind_Reason_Code +unwind_cpp_pr0_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid); +} + +static _Unwind_Reason_Code +unwind_cpp_pr1_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid); +} + +static _Unwind_Reason_Code +unwind_cpp_pr2_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid); +} diff --git a/debuggerd/utility.c b/debuggerd/utility.c new file mode 100644 index 0000000..8f3931c --- /dev/null +++ b/debuggerd/utility.c @@ -0,0 +1,83 @@ +/* system/debuggerd/utility.c +** +** Copyright 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 <sys/ptrace.h> +#include <sys/exec_elf.h> +#include <assert.h> +#include <string.h> +#include <errno.h> + +#include "utility.h" + +/* Get a word from pid using ptrace. The result is the return value. */ +int get_remote_word(int pid, void *src) +{ + return ptrace(PTRACE_PEEKTEXT, pid, src, NULL); +} + + +/* Handy routine to read aggregated data from pid using ptrace. The read + * values are written to the dest locations directly. + */ +void get_remote_struct(int pid, void *src, void *dst, size_t size) +{ + unsigned int i; + + for (i = 0; i+4 <= size; i+=4) { + *(int *)(dst+i) = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL); + } + + if (i < size) { + int val; + + assert((size - i) < 4); + val = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL); + while (i < size) { + ((unsigned char *)dst)[i] = val & 0xff; + i++; + val >>= 8; + } + } +} + +/* Map a pc address to the name of the containing ELF file */ +const char *map_to_name(mapinfo *mi, unsigned pc, const char* def) +{ + while(mi) { + if((pc >= mi->start) && (pc < mi->end)){ + return mi->name; + } + mi = mi->next; + } + return def; +} + +/* Find the containing map info for the pc */ +const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) +{ + while(mi) { + if((pc >= mi->start) && (pc < mi->end)){ + // Only calculate the relative offset for shared libraries + if (strstr(mi->name, ".so")) { + *rel_pc = pc - mi->start; + } + return mi; + } + mi = mi->next; + } + return NULL; +} diff --git a/debuggerd/utility.h b/debuggerd/utility.h new file mode 100644 index 0000000..49f5951 --- /dev/null +++ b/debuggerd/utility.h @@ -0,0 +1,56 @@ +/* system/debuggerd/utility.h +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __utility_h +#define __utility_h + +#include <stddef.h> +#include <stdbool.h> + +#ifndef PT_ARM_EXIDX +#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ +#endif + +#define STACK_CONTENT_DEPTH 32 + +typedef struct mapinfo { + struct mapinfo *next; + unsigned start; + unsigned end; + unsigned exidx_start; + unsigned exidx_end; + char name[]; +} mapinfo; + +/* Get a word from pid using ptrace. The result is the return value. */ +extern int get_remote_word(int pid, void *src); + +/* Handy routine to read aggregated data from pid using ptrace. The read + * values are written to the dest locations directly. + */ +extern void get_remote_struct(int pid, void *src, void *dst, size_t size); + +/* Find the containing map for the pc */ +const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc, unsigned *rel_pc); + +/* Map a pc address to the name of the containing ELF file */ +const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); + +/* Log information onto the tombstone */ +extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); + +#endif diff --git a/fastboot/Android.mk b/fastboot/Android.mk new file mode 100644 index 0000000..7a9d35f --- /dev/null +++ b/fastboot/Android.mk @@ -0,0 +1,57 @@ +# Copyright (C) 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg +LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c +LOCAL_MODULE := fastboot + +ifeq ($(HOST_OS),linux) + LOCAL_SRC_FILES += usb_linux.c util_linux.c +endif + +ifeq ($(HOST_OS),darwin) + LOCAL_SRC_FILES += usb_osx.c util_osx.c + LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \ + -framework Carbon +endif + +ifeq ($(HOST_OS),windows) + LOCAL_SRC_FILES += usb_windows.c util_windows.c + EXTRA_STATIC_LIBS := AdbWinApi + LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api + ifeq ($(strip $(USE_CYGWIN)),) + LOCAL_LDLIBS += -lws2_32 + USE_SYSDEPS_WIN32 := 1 + endif +endif + +LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz + +include $(BUILD_HOST_EXECUTABLE) +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) + +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := usbtest.c usb_linux.c +LOCAL_MODULE := usbtest +include $(BUILD_HOST_EXECUTABLE) +endif + +ifeq ($(HOST_OS),windows) +$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll +endif diff --git a/fastboot/bootimg.c b/fastboot/bootimg.c new file mode 100644 index 0000000..1d77b3c --- /dev/null +++ b/fastboot/bootimg.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <bootimg.h> + +void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline) +{ + strcpy((char*) h->cmdline, cmdline); +} + +boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, + void *ramdisk, unsigned ramdisk_size, + void *second, unsigned second_size, + unsigned page_size, + unsigned *bootimg_size) +{ + unsigned kernel_actual; + unsigned ramdisk_actual; + unsigned second_actual; + unsigned page_mask; + boot_img_hdr *hdr; + + page_mask = page_size - 1; + + kernel_actual = (kernel_size + page_mask) & (~page_mask); + ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask); + second_actual = (second_size + page_mask) & (~page_mask); + + *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual; + + hdr = calloc(*bootimg_size, 1); + + if(hdr == 0) { + return hdr; + } + + memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); + + hdr->kernel_size = kernel_size; + hdr->kernel_addr = 0x10008000; + hdr->ramdisk_size = ramdisk_size; + hdr->ramdisk_addr = 0x11000000; + hdr->second_size = second_size; + hdr->second_addr = 0x10F00000; + + hdr->tags_addr = 0x10000100; + hdr->page_size = page_size; + + memcpy(hdr->magic + page_size, + kernel, kernel_size); + memcpy(hdr->magic + page_size + kernel_actual, + ramdisk, ramdisk_size); + memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, + second, second_size); + return hdr; +} diff --git a/fastboot/engine.c b/fastboot/engine.c new file mode 100644 index 0000000..4c7e197 --- /dev/null +++ b/fastboot/engine.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include "fastboot.h" + +char *mkmsg(const char *fmt, ...) +{ + char buf[256]; + char *s; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + s = strdup(buf); + if (s == 0) die("out of memory"); + return s; +} + +#define OP_DOWNLOAD 1 +#define OP_COMMAND 2 +#define OP_QUERY 3 +#define OP_NOTICE 4 + +typedef struct Action Action; + +struct Action +{ + unsigned op; + Action *next; + + char cmd[64]; + void *data; + unsigned size; + + const char *msg; + int (*func)(Action *a, int status, char *resp); +}; + +static Action *action_list = 0; +static Action *action_last = 0; + +static int cb_default(Action *a, int status, char *resp) +{ + if (status) { + fprintf(stderr,"FAILED (%s)\n", resp); + } else { + fprintf(stderr,"OKAY\n"); + } + return status; +} + +static Action *queue_action(unsigned op, const char *fmt, ...) +{ + Action *a; + va_list ap; + + a = calloc(1, sizeof(Action)); + if (a == 0) die("out of memory"); + + va_start(ap, fmt); + vsprintf(a->cmd, fmt, ap); + va_end(ap); + + if (action_last) { + action_last->next = a; + } else { + action_list = a; + } + action_last = a; + a->op = op; + a->func = cb_default; + return a; +} + +void fb_queue_erase(const char *ptn) +{ + Action *a; + a = queue_action(OP_COMMAND, "erase:%s", ptn); + a->msg = mkmsg("erasing '%s'", ptn); +} + +void fb_queue_flash(const char *ptn, void *data, unsigned sz) +{ + Action *a; + + a = queue_action(OP_DOWNLOAD, ""); + a->data = data; + a->size = sz; + a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024); + + a = queue_action(OP_COMMAND, "flash:%s", ptn); + a->msg = mkmsg("writing '%s'", ptn); +} + +static int match(char *str, const char **value, unsigned count) +{ + const char *val; + unsigned n; + int len; + + for (n = 0; n < count; n++) { + const char *val = value[n]; + int len = strlen(val); + int match; + + if ((len > 1) && (val[len-1] == '*')) { + len--; + match = !strncmp(val, str, len); + } else { + match = !strcmp(val, str); + } + + if (match) return 1; + } + + return 0; +} + + + +static int cb_check(Action *a, int status, char *resp, int invert) +{ + const char **value = a->data; + unsigned count = a->size; + unsigned n; + int yes; + + if (status) { + fprintf(stderr,"FAILED (%s)\n", resp); + return status; + } + + yes = match(resp, value, count); + if (invert) yes = !yes; + + if (yes) { + fprintf(stderr,"OKAY\n"); + return 0; + } + + fprintf(stderr,"FAILED\n\n"); + fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp); + fprintf(stderr,"Update %s '%s'", + invert ? "rejects" : "requires", value[0]); + for (n = 1; n < count; n++) { + fprintf(stderr," or '%s'", value[n]); + } + fprintf(stderr,".\n\n"); + return -1; +} + +static int cb_require(Action *a, int status, char *resp) +{ + return cb_check(a, status, resp, 0); +} + +static int cb_reject(Action *a, int status, char *resp) +{ + return cb_check(a, status, resp, 1); +} + +void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value) +{ + Action *a; + a = queue_action(OP_QUERY, "getvar:%s", var); + a->data = value; + a->size = nvalues; + a->msg = mkmsg("checking %s", var); + a->func = invert ? cb_reject : cb_require; + if (a->data == 0) die("out of memory"); +} + +static int cb_display(Action *a, int status, char *resp) +{ + if (status) { + fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp); + return status; + } + fprintf(stderr, "%s: %s\n", (char*) a->data, resp); + return 0; +} + +void fb_queue_display(const char *var, const char *prettyname) +{ + Action *a; + a = queue_action(OP_QUERY, "getvar:%s", var); + a->data = strdup(prettyname); + if (a->data == 0) die("out of memory"); + a->func = cb_display; +} + +static int cb_do_nothing(Action *a, int status, char *resp) +{ + fprintf(stderr,"\n"); + return 0; +} + +void fb_queue_reboot(void) +{ + Action *a = queue_action(OP_COMMAND, "reboot"); + a->func = cb_do_nothing; + a->msg = "rebooting"; +} + +void fb_queue_command(const char *cmd, const char *msg) +{ + Action *a = queue_action(OP_COMMAND, cmd); + a->msg = msg; +} + +void fb_queue_download(const char *name, void *data, unsigned size) +{ + Action *a = queue_action(OP_DOWNLOAD, ""); + a->data = data; + a->size = size; + a->msg = mkmsg("downloading '%s'", name); +} + +void fb_queue_notice(const char *notice) +{ + Action *a = queue_action(OP_NOTICE, ""); + a->data = (void*) notice; +} + +void fb_execute_queue(usb_handle *usb) +{ + Action *a; + char resp[FB_RESPONSE_SZ+1]; + int status; + + a = action_list; + resp[FB_RESPONSE_SZ] = 0; + + for (a = action_list; a; a = a->next) { + if (a->msg) { + fprintf(stderr,"%s... ",a->msg); + } + if (a->op == OP_DOWNLOAD) { + status = fb_download_data(usb, a->data, a->size); + status = a->func(a, status, status ? fb_get_error() : ""); + if (status) break; + } else if (a->op == OP_COMMAND) { + status = fb_command(usb, a->cmd); + status = a->func(a, status, status ? fb_get_error() : ""); + if (status) break; + } else if (a->op == OP_QUERY) { + status = fb_command_response(usb, a->cmd, resp); + status = a->func(a, status, status ? fb_get_error() : resp); + if (status) break; + } else if (a->op == OP_NOTICE) { + fprintf(stderr,"%s\n",(char*)a->data); + } else { + die("bogus action"); + } + } +} + diff --git a/fastboot/engineering_key.p12 b/fastboot/engineering_key.p12 Binary files differnew file mode 100644 index 0000000..d8183b0 --- /dev/null +++ b/fastboot/engineering_key.p12 diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c new file mode 100644 index 0000000..e220dbe --- /dev/null +++ b/fastboot/fastboot.c @@ -0,0 +1,673 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <ctype.h> + +#include <sys/time.h> +#include <bootimg.h> +#include <zipfile/zipfile.h> + +#include "fastboot.h" + +static usb_handle *usb = 0; +static const char *serial = 0; +static const char *product = 0; +static const char *cmdline = 0; +static int wipe_data = 0; +static unsigned short vendor_id = 0; + +void die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr,"error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} + +void get_my_path(char *path); + +char *find_item(const char *item, const char *product) +{ + char *dir; + char *fn; + char path[PATH_MAX + 128]; + + if(!strcmp(item,"boot")) { + fn = "boot.img"; + } else if(!strcmp(item,"recovery")) { + fn = "recovery.img"; + } else if(!strcmp(item,"system")) { + fn = "system.img"; + } else if(!strcmp(item,"userdata")) { + fn = "userdata.img"; + } else if(!strcmp(item,"info")) { + fn = "android-info.txt"; + } else { + fprintf(stderr,"unknown partition '%s'\n", item); + return 0; + } + + if(product) { + get_my_path(path); + sprintf(path + strlen(path), + "../../../target/product/%s/%s", product, fn); + return strdup(path); + } + + dir = getenv("ANDROID_PRODUCT_OUT"); + if((dir == 0) || (dir[0] == 0)) { + die("neither -p product specified nor ANDROID_PRODUCT_OUT set"); + return 0; + } + + sprintf(path, "%s/%s", dir, fn); + return strdup(path); +} + +#ifdef _WIN32 +void *load_file(const char *fn, unsigned *_sz); +#else +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); + if(data == 0) goto oops; + + if(read(fd, data, sz) != sz) goto oops; + close(fd); + + if(_sz) *_sz = sz; + return data; + +oops: + close(fd); + if(data != 0) free(data); + return 0; +} +#endif + +int match_fastboot(usb_ifc_info *info) +{ + if(!(vendor_id && (info->dev_vendor == vendor_id)) && + (info->dev_vendor != 0x18d1) && + (info->dev_vendor != 0x0bb4)) return -1; + if(info->ifc_class != 0xff) return -1; + if(info->ifc_subclass != 0x42) return -1; + if(info->ifc_protocol != 0x03) return -1; + // require matching serial number if a serial number is specified + // at the command line with the -s option. + if (serial && strcmp(serial, info->serial_number) != 0) return -1; + return 0; +} + +int list_devices_callback(usb_ifc_info *info) +{ + if (match_fastboot(info) == 0) { + char* serial = info->serial_number; + if (!serial[0]) { + serial = "????????????"; + } + // output compatible with "adb devices" + printf("%s\tfastboot\n", serial); + } + + return -1; +} + +usb_handle *open_device(void) +{ + static usb_handle *usb = 0; + int announce = 1; + + if(usb) return usb; + + for(;;) { + usb = usb_open(match_fastboot); + if(usb) return usb; + if(announce) { + announce = 0; + fprintf(stderr,"< waiting for device >\n"); + } + sleep(1); + } +} + +void list_devices(void) { + // We don't actually open a USB device here, + // just getting our callback called so we can + // list all the connected devices. + usb_open(list_devices_callback); +} + +void usage(void) +{ + fprintf(stderr, +/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */ + "usage: fastboot [ <option> ] <command>\n" + "\n" + "commands:\n" + " update <filename> reflash device from update.zip\n" + " flashall flash boot + recovery + system\n" + " flash <partition> [ <filename> ] write a file to a flash partition\n" + " erase <partition> erase a flash partition\n" + " getvar <variable> display a bootloader variable\n" + " boot <kernel> [ <ramdisk> ] download and boot kernel\n" + " flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n" + " devices list all connected devices\n" + " reboot reboot device normally\n" + " reboot-bootloader reboot device into bootloader\n" + "\n" + "options:\n" + " -w erase userdata and cache\n" + " -s <serial number> specify device serial number\n" + " -p <product> specify product name\n" + " -c <cmdline> override kernel commandline\n" + " -i <vendor id> specify a custom USB vendor id\n" + ); + exit(1); +} + +void *load_bootable_image(const char *kernel, const char *ramdisk, + unsigned *sz, const char *cmdline) +{ + void *kdata = 0, *rdata = 0; + unsigned ksize = 0, rsize = 0; + void *bdata; + unsigned bsize; + + if(kernel == 0) { + fprintf(stderr, "no image specified\n"); + return 0; + } + + kdata = load_file(kernel, &ksize); + if(kdata == 0) { + fprintf(stderr, "cannot load '%s'\n", kernel); + return 0; + } + + /* is this actually a boot image? */ + if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline); + + if(ramdisk) { + fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n"); + return 0; + } + + *sz = ksize; + return kdata; + } + + if(ramdisk) { + rdata = load_file(ramdisk, &rsize); + if(rdata == 0) { + fprintf(stderr,"cannot load '%s'\n", ramdisk); + return 0; + } + } + + fprintf(stderr,"creating boot image...\n"); + bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, &bsize); + if(bdata == 0) { + fprintf(stderr,"failed to create boot.img\n"); + return 0; + } + if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline); + fprintf(stderr,"creating boot image - %d bytes\n", bsize); + *sz = bsize; + + return bdata; +} + +void *unzip_file(zipfile_t zip, const char *name, unsigned *sz) +{ + void *data; + zipentry_t entry; + unsigned datasz; + + entry = lookup_zipentry(zip, name); + if (entry == NULL) { + fprintf(stderr, "archive does not contain '%s'\n", name); + return 0; + } + + *sz = get_zipentry_size(entry); + + datasz = *sz * 1.001; + data = malloc(datasz); + + if(data == 0) { + fprintf(stderr, "failed to allocate %d bytes\n", *sz); + return 0; + } + + if (decompress_zipentry(entry, data, datasz)) { + fprintf(stderr, "failed to unzip '%s' from archive\n", name); + free(data); + return 0; + } + + return data; +} + +static char *strip(char *s) +{ + int n; + while(*s && isspace(*s)) s++; + n = strlen(s); + while(n-- > 0) { + if(!isspace(s[n])) break; + s[n] = 0; + } + return s; +} + +#define MAX_OPTIONS 32 +static int setup_requirement_line(char *name) +{ + char *val[MAX_OPTIONS]; + const char **out; + unsigned n, count; + char *x; + int invert = 0; + + if (!strncmp(name, "reject ", 7)) { + name += 7; + invert = 1; + } else if (!strncmp(name, "require ", 8)) { + name += 8; + invert = 0; + } + + x = strchr(name, '='); + if (x == 0) return 0; + *x = 0; + val[0] = x + 1; + + for(count = 1; count < MAX_OPTIONS; count++) { + x = strchr(val[count - 1],'|'); + if (x == 0) break; + *x = 0; + val[count] = x + 1; + } + + name = strip(name); + for(n = 0; n < count; n++) val[n] = strip(val[n]); + + name = strip(name); + if (name == 0) return -1; + + /* work around an unfortunate name mismatch */ + if (!strcmp(name,"board")) name = "product"; + + out = malloc(sizeof(char*) * count); + if (out == 0) return -1; + + for(n = 0; n < count; n++) { + out[n] = strdup(strip(val[n])); + if (out[n] == 0) return -1; + } + + fb_queue_require(name, invert, n, out); + return 0; +} + +static void setup_requirements(char *data, unsigned sz) +{ + char *s; + + s = data; + while (sz-- > 0) { + if(*s == '\n') { + *s++ = 0; + if (setup_requirement_line(data)) { + die("out of memory"); + } + data = s; + } else { + s++; + } + } +} + +void queue_info_dump(void) +{ + fb_queue_notice("--------------------------------------------"); + fb_queue_display("version-bootloader", "Bootloader Version..."); + fb_queue_display("version-baseband", "Baseband Version....."); + fb_queue_display("serialno", "Serial Number........"); + fb_queue_notice("--------------------------------------------"); +} + +void do_update_signature(zipfile_t zip, char *fn) +{ + void *data; + unsigned sz; + data = unzip_file(zip, fn, &sz); + if (data == 0) return; + fb_queue_download("signature", data, sz); + fb_queue_command("signature", "installing signature"); +} + +void do_update(char *fn) +{ + void *zdata; + unsigned zsize; + void *data; + unsigned sz; + zipfile_t zip; + + queue_info_dump(); + + zdata = load_file(fn, &zsize); + if (zdata == 0) die("failed to load '%s'", fn); + + zip = init_zipfile(zdata, zsize); + if(zip == 0) die("failed to access zipdata in '%s'"); + + data = unzip_file(zip, "android-info.txt", &sz); + if (data == 0) { + char *tmp; + /* fallback for older zipfiles */ + data = unzip_file(zip, "android-product.txt", &sz); + if ((data == 0) || (sz < 1)) { + die("update package has no android-info.txt or android-product.txt"); + } + tmp = malloc(sz + 128); + if (tmp == 0) die("out of memory"); + sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data); + data = tmp; + sz = strlen(tmp); + } + + setup_requirements(data, sz); + + data = unzip_file(zip, "boot.img", &sz); + if (data == 0) die("update package missing boot.img"); + do_update_signature(zip, "boot.sig"); + fb_queue_flash("boot", data, sz); + + data = unzip_file(zip, "recovery.img", &sz); + if (data != 0) { + do_update_signature(zip, "recovery.sig"); + fb_queue_flash("recovery", data, sz); + } + + data = unzip_file(zip, "system.img", &sz); + if (data == 0) die("update package missing system.img"); + do_update_signature(zip, "system.sig"); + fb_queue_flash("system", data, sz); +} + +void do_send_signature(char *fn) +{ + void *data; + unsigned sz; + char *xtn; + + xtn = strrchr(fn, '.'); + if (!xtn) return; + if (strcmp(xtn, ".img")) return; + + strcpy(xtn,".sig"); + data = load_file(fn, &sz); + strcpy(xtn,".img"); + if (data == 0) return; + fb_queue_download("signature", data, sz); + fb_queue_command("signature", "installing signature"); +} + +void do_flashall(void) +{ + char *fname; + void *data; + unsigned sz; + + queue_info_dump(); + + fname = find_item("info", product); + if (fname == 0) die("cannot find android-info.txt"); + data = load_file(fname, &sz); + if (data == 0) die("could not load android-info.txt"); + setup_requirements(data, sz); + + fname = find_item("boot", product); + data = load_file(fname, &sz); + if (data == 0) die("could not load boot.img"); + do_send_signature(fname); + fb_queue_flash("boot", data, sz); + + fname = find_item("recovery", product); + data = load_file(fname, &sz); + if (data != 0) { + do_send_signature(fname); + fb_queue_flash("recovery", data, sz); + } + + fname = find_item("system", product); + data = load_file(fname, &sz); + if (data == 0) die("could not load system.img"); + do_send_signature(fname); + fb_queue_flash("system", data, sz); +} + +#define skip(n) do { argc -= (n); argv += (n); } while (0) +#define require(n) do { if (argc < (n)) usage(); } while (0) + +int do_oem_command(int argc, char **argv) +{ + int i; + char command[256]; + if (argc <= 1) return 0; + + command[0] = 0; + while(1) { + strcat(command,*argv); + skip(1); + if(argc == 0) break; + strcat(command," "); + } + + fb_queue_command(command,""); + return 0; +} + +int main(int argc, char **argv) +{ + int wants_wipe = 0; + int wants_reboot = 0; + int wants_reboot_bootloader = 0; + void *data; + unsigned sz; + + skip(1); + if (argc == 0) { + usage(); + return 0; + } + + if (!strcmp(*argv, "devices")) { + list_devices(); + return 0; + } + + while (argc > 0) { + if(!strcmp(*argv, "-w")) { + wants_wipe = 1; + skip(1); + } else if(!strcmp(*argv, "-s")) { + require(2); + serial = argv[1]; + skip(2); + } else if(!strcmp(*argv, "-p")) { + require(2); + product = argv[1]; + skip(2); + } else if(!strcmp(*argv, "-c")) { + require(2); + cmdline = argv[1]; + skip(2); + } else if(!strcmp(*argv, "-i")) { + char *endptr = NULL; + unsigned long val; + + require(2); + val = strtoul(argv[1], &endptr, 0); + if (!endptr || *endptr != '\0' || (val & ~0xffff)) + die("invalid vendor id '%s'", argv[1]); + vendor_id = (unsigned short)val; + skip(2); + } else if(!strcmp(*argv, "getvar")) { + require(2); + fb_queue_display(argv[1], argv[1]); + skip(2); + } else if(!strcmp(*argv, "erase")) { + require(2); + fb_queue_erase(argv[1]); + skip(2); + } else if(!strcmp(*argv, "signature")) { + require(2); + data = load_file(argv[1], &sz); + if (data == 0) die("could not load '%s'", argv[1]); + if (sz != 256) die("signature must be 256 bytes"); + fb_queue_download("signature", data, sz); + fb_queue_command("signature", "installing signature"); + skip(2); + } else if(!strcmp(*argv, "reboot")) { + wants_reboot = 1; + skip(1); + } else if(!strcmp(*argv, "reboot-bootloader")) { + wants_reboot_bootloader = 1; + skip(1); + } else if (!strcmp(*argv, "continue")) { + fb_queue_command("continue", "resuming boot"); + skip(1); + } else if(!strcmp(*argv, "boot")) { + char *kname = 0; + char *rname = 0; + skip(1); + if (argc > 0) { + kname = argv[0]; + skip(1); + } + if (argc > 0) { + rname = argv[0]; + skip(1); + } + data = load_bootable_image(kname, rname, &sz, cmdline); + if (data == 0) return 1; + fb_queue_download("boot.img", data, sz); + fb_queue_command("boot", "booting"); + } else if(!strcmp(*argv, "flash")) { + char *pname = argv[1]; + char *fname = 0; + require(2); + if (argc > 2) { + fname = argv[2]; + skip(3); + } else { + fname = find_item(pname, product); + skip(2); + } + if (fname == 0) die("cannot determine image filename for '%s'", pname); + data = load_file(fname, &sz); + if (data == 0) die("cannot load '%s'\n", fname); + fb_queue_flash(pname, data, sz); + } else if(!strcmp(*argv, "flash:raw")) { + char *pname = argv[1]; + char *kname = argv[2]; + char *rname = 0; + require(3); + if(argc > 3) { + rname = argv[3]; + skip(4); + } else { + skip(3); + } + data = load_bootable_image(kname, rname, &sz, cmdline); + if (data == 0) die("cannot load bootable image"); + fb_queue_flash(pname, data, sz); + } else if(!strcmp(*argv, "flashall")) { + skip(1); + do_flashall(); + wants_reboot = 1; + } else if(!strcmp(*argv, "update")) { + if (argc > 1) { + do_update(argv[1]); + skip(2); + } else { + do_update("update.zip"); + skip(1); + } + wants_reboot = 1; + } else if(!strcmp(*argv, "oem")) { + argc = do_oem_command(argc, argv); + } else { + usage(); + } + } + + if (wants_wipe) { + fb_queue_erase("userdata"); + fb_queue_erase("cache"); + } + if (wants_reboot) { + fb_queue_reboot(); + } else if (wants_reboot_bootloader) { + fb_queue_command("reboot-bootloader", "rebooting into bootloader"); + } + + usb = open_device(); + + fb_execute_queue(usb); + return 0; +} diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h new file mode 100644 index 0000000..a36c569 --- /dev/null +++ b/fastboot/fastboot.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOT_H_ +#define _FASTBOOT_H_ + +#include "usb.h" + +/* protocol.c - fastboot protocol */ +int fb_command(usb_handle *usb, const char *cmd); +int fb_command_response(usb_handle *usb, const char *cmd, char *response); +int fb_download_data(usb_handle *usb, const void *data, unsigned size); +char *fb_get_error(void); + +#define FB_COMMAND_SZ 64 +#define FB_RESPONSE_SZ 64 + +/* engine.c - high level command queue engine */ +void fb_queue_flash(const char *ptn, void *data, unsigned sz);; +void fb_queue_erase(const char *ptn); +void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value); +void fb_queue_display(const char *var, const char *prettyname); +void fb_queue_reboot(void); +void fb_queue_command(const char *cmd, const char *msg); +void fb_queue_download(const char *name, void *data, unsigned size); +void fb_queue_notice(const char *notice); +void fb_execute_queue(usb_handle *usb); + +/* util stuff */ +void die(const char *fmt, ...); + +#endif diff --git a/fastboot/genkey.sh b/fastboot/genkey.sh new file mode 100755 index 0000000..011e902 --- /dev/null +++ b/fastboot/genkey.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +if [ $# -ne 2 ] +then + echo "Usage: $0 alias \"pass phrase\"" + exit -1 +fi + +# Generate a 2048 bit RSA key with public exponent 3. +# Encrypt private key with provided password. +openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048 + +# Create a self-signed cert for this key. +openssl req -new -x509 -key $1.pem -passin pass:"$2" \ + -out $1-cert.pem \ + -batch -days 10000 + +# Create a PKCS12 store containing the generated private key. +# Protect the keystore and the private key with the provided password. +openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \ + -out $1.p12 -name $1 -passout pass:"$2" + +rm $1.pem +rm $1-cert.pem + diff --git a/fastboot/p12topem.sh b/fastboot/p12topem.sh new file mode 100755 index 0000000..f081eb5 --- /dev/null +++ b/fastboot/p12topem.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ $# -ne 2 ] +then + echo "Usage: $0 alias passphrase" + exit -1 +fi + +openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem diff --git a/fastboot/protocol.c b/fastboot/protocol.c new file mode 100644 index 0000000..c788a12 --- /dev/null +++ b/fastboot/protocol.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "fastboot.h" + +static char ERROR[128]; + +char *fb_get_error(void) +{ + return ERROR; +} + +static int check_response(usb_handle *usb, unsigned size, + unsigned data_okay, char *response) +{ + unsigned char status[65]; + int r; + + for(;;) { + r = usb_read(usb, status, 64); + if(r < 0) { + sprintf(ERROR, "status read failed (%s)", strerror(errno)); + usb_close(usb); + return -1; + } + status[r] = 0; + + if(r < 4) { + sprintf(ERROR, "status malformed (%d bytes)", r); + usb_close(usb); + return -1; + } + + if(!memcmp(status, "INFO", 4)) { + fprintf(stderr,"%s\n", status); + continue; + } + + if(!memcmp(status, "OKAY", 4)) { + if(response) { + strcpy(response, (char*) status + 4); + } + return 0; + } + + if(!memcmp(status, "FAIL", 4)) { + if(r > 4) { + sprintf(ERROR, "remote: %s", status + 4); + } else { + strcpy(ERROR, "remote failure"); + } + return -1; + } + + if(!memcmp(status, "DATA", 4) && data_okay){ + unsigned dsize = strtoul((char*) status + 4, 0, 16); + if(dsize > size) { + strcpy(ERROR, "data size too large"); + usb_close(usb); + return -1; + } + return dsize; + } + + strcpy(ERROR,"unknown status code"); + usb_close(usb); + break; + } + + return -1; +} + +static int _command_send(usb_handle *usb, const char *cmd, + const void *data, unsigned size, + char *response) +{ + int cmdsize = strlen(cmd); + int r; + + if(response) { + response[0] = 0; + } + + if(cmdsize > 64) { + sprintf(ERROR,"command too large"); + return -1; + } + + if(usb_write(usb, cmd, cmdsize) != cmdsize) { + sprintf(ERROR,"command write failed (%s)", strerror(errno)); + usb_close(usb); + return -1; + } + + if(data == 0) { + return check_response(usb, size, 0, response); + } + + r = check_response(usb, size, 1, 0); + if(r < 0) { + return -1; + } + size = r; + + if(size) { + r = usb_write(usb, data, size); + if(r < 0) { + sprintf(ERROR, "data transfer failure (%s)", strerror(errno)); + usb_close(usb); + return -1; + } + if(r != ((int) size)) { + sprintf(ERROR, "data transfer failure (short transfer)"); + usb_close(usb); + return -1; + } + } + + r = check_response(usb, 0, 0, 0); + if(r < 0) { + return -1; + } else { + return size; + } +} + +int fb_command(usb_handle *usb, const char *cmd) +{ + return _command_send(usb, cmd, 0, 0, 0); +} + +int fb_command_response(usb_handle *usb, const char *cmd, char *response) +{ + return _command_send(usb, cmd, 0, 0, response); +} + +int fb_download_data(usb_handle *usb, const void *data, unsigned size) +{ + char cmd[64]; + int r; + + sprintf(cmd, "download:%08x", size); + r = _command_send(usb, cmd, data, size, 0); + + if(r < 0) { + return -1; + } else { + return 0; + } +} + diff --git a/fastboot/signfile.sh b/fastboot/signfile.sh new file mode 100755 index 0000000..3188d2d --- /dev/null +++ b/fastboot/signfile.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ $# -ne 3 ] +then + echo "Usage: $0 alias filename passpharse" + exit -1 +fi + +openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign + diff --git a/fastboot/usb.h b/fastboot/usb.h new file mode 100644 index 0000000..f3ec5bf --- /dev/null +++ b/fastboot/usb.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _USB_H_ +#define _USB_H_ + +typedef struct usb_handle usb_handle; + +typedef struct usb_ifc_info usb_ifc_info; + +struct usb_ifc_info +{ + /* from device descriptor */ + unsigned short dev_vendor; + unsigned short dev_product; + + unsigned char dev_class; + unsigned char dev_subclass; + unsigned char dev_protocol; + + unsigned char ifc_class; + unsigned char ifc_subclass; + unsigned char ifc_protocol; + + unsigned char has_bulk_in; + unsigned char has_bulk_out; + + char serial_number[256]; +}; + +typedef int (*ifc_match_func)(usb_ifc_info *ifc); + +usb_handle *usb_open(ifc_match_func callback); +int usb_close(usb_handle *h); +int usb_read(usb_handle *h, void *_data, int len); +int usb_write(usb_handle *h, const void *_data, int len); + + +#endif diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c new file mode 100644 index 0000000..06c62b8 --- /dev/null +++ b/fastboot/usb_linux.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <ctype.h> + +#include <linux/usbdevice_fs.h> +#include <linux/usbdevice_fs.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include <linux/usb/ch9.h> +#else +#include <linux/usb_ch9.h> +#endif +#include <asm/byteorder.h> + +#include "usb.h" + +#if TRACE_USB +#define DBG1(x...) fprintf(stderr, x) +#define DBG(x...) fprintf(stderr, x) +#else +#define DBG(x...) +#define DBG1(x...) +#endif + +struct usb_handle +{ + char fname[64]; + int desc; + unsigned char ep_in; + unsigned char ep_out; +}; + +static inline int badname(const char *name) +{ + while(*name) { + if(!isdigit(*name++)) return 1; + } + return 0; +} + +static int check(void *_desc, int len, unsigned type, int size) +{ + unsigned char *desc = _desc; + + if(len < size) return -1; + if(desc[0] < size) return -1; + if(desc[0] > len) return -1; + if(desc[1] != type) return -1; + + return 0; +} + +static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback, + int *ept_in_id, int *ept_out_id, int *ifc_id) +{ + struct usb_device_descriptor *dev; + struct usb_config_descriptor *cfg; + struct usb_interface_descriptor *ifc; + struct usb_endpoint_descriptor *ept; + struct usb_ifc_info info; + + int in, out; + unsigned i; + unsigned e; + + if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) + return -1; + dev = (void*) ptr; + len -= dev->bLength; + ptr += dev->bLength; + + if(check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE)) + return -1; + cfg = (void*) ptr; + len -= cfg->bLength; + ptr += cfg->bLength; + + info.dev_vendor = dev->idVendor; + info.dev_product = dev->idProduct; + info.dev_class = dev->bDeviceClass; + info.dev_subclass = dev->bDeviceSubClass; + info.dev_protocol = dev->bDeviceProtocol; + + // read device serial number (if there is one) + info.serial_number[0] = 0; + if (dev->iSerialNumber) { + struct usbdevfs_ctrltransfer ctrl; + __u16 buffer[128]; + int result; + + memset(buffer, 0, sizeof(buffer)); + + ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; + ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber; + ctrl.wIndex = 0; + ctrl.wLength = sizeof(buffer); + ctrl.data = buffer; + + result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + if (result > 0) { + int i; + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + result /= 2; + for (i = 1; i < result; i++) + info.serial_number[i - 1] = buffer[i]; + info.serial_number[i - 1] = 0; + } + } + + for(i = 0; i < cfg->bNumInterfaces; i++) { + if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE)) + return -1; + ifc = (void*) ptr; + len -= ifc->bLength; + ptr += ifc->bLength; + + in = -1; + out = -1; + info.ifc_class = ifc->bInterfaceClass; + info.ifc_subclass = ifc->bInterfaceSubClass; + info.ifc_protocol = ifc->bInterfaceProtocol; + + for(e = 0; e < ifc->bNumEndpoints; e++) { + if(check(ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE)) + return -1; + ept = (void*) ptr; + len -= ept->bLength; + ptr += ept->bLength; + + if((ept->bmAttributes & 0x03) != 0x02) + continue; + + if(ept->bEndpointAddress & 0x80) { + in = ept->bEndpointAddress; + } else { + out = ept->bEndpointAddress; + } + } + + info.has_bulk_in = (in != -1); + info.has_bulk_out = (out != -1); + + if(callback(&info) == 0) { + *ept_in_id = in; + *ept_out_id = out; + *ifc_id = ifc->bInterfaceNumber; + return 0; + } + } + + return -1; +} + +static usb_handle *find_usb_device(const char *base, ifc_match_func callback) +{ + usb_handle *usb = 0; + char busname[64], devname[64]; + char desc[1024]; + int n, in, out, ifc; + + DIR *busdir, *devdir; + struct dirent *de; + int fd; + + busdir = opendir(base); + if(busdir == 0) return 0; + + while((de = readdir(busdir)) && (usb == 0)) { + if(badname(de->d_name)) continue; + + sprintf(busname, "%s/%s", base, de->d_name); + devdir = opendir(busname); + if(devdir == 0) continue; + +// DBG("[ scanning %s ]\n", busname); + while((de = readdir(devdir)) && (usb == 0)) { + + if(badname(de->d_name)) continue; + sprintf(devname, "%s/%s", busname, de->d_name); + +// DBG("[ scanning %s ]\n", devname); + if((fd = open(devname, O_RDWR)) < 0) { + continue; + } + + n = read(fd, desc, sizeof(desc)); + + if(filter_usb_device(fd, desc, n, callback, &in, &out, &ifc) == 0){ + usb = calloc(1, sizeof(usb_handle)); + strcpy(usb->fname, devname); + usb->ep_in = in; + usb->ep_out = out; + usb->desc = fd; + + n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc); + if(n != 0) { + close(fd); + free(usb); + usb = 0; + continue; + } + } else { + close(fd); + } + } + closedir(devdir); + } + closedir(busdir); + + return usb; +} + +int usb_write(usb_handle *h, const void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + unsigned count = 0; + struct usbdevfs_bulktransfer bulk; + int n; + + if(h->ep_out == 0) { + return -1; + } + + if(len == 0) { + bulk.ep = h->ep_out; + bulk.len = 0; + bulk.data = data; + bulk.timeout = 0; + + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); + if(n != 0) { + fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + return 0; + } + + while(len > 0) { + int xfer; + xfer = (len > 4096) ? 4096 : len; + + bulk.ep = h->ep_out; + bulk.len = xfer; + bulk.data = data; + bulk.timeout = 0; + + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); + if(n != xfer) { + DBG("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + + count += xfer; + len -= xfer; + data += xfer; + } + + return count; +} + +int usb_read(usb_handle *h, void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + unsigned count = 0; + struct usbdevfs_bulktransfer bulk; + int n; + + if(h->ep_in == 0) { + return -1; + } + + while(len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + bulk.ep = h->ep_in; + bulk.len = xfer; + bulk.data = data; + bulk.timeout = 0; + + DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname); + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); + DBG("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname); + + if(n < 0) { + DBG1("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + + count += n; + len -= n; + data += n; + + if(n < xfer) { + break; + } + } + + return count; +} + +void usb_kick(usb_handle *h) +{ + int fd; + + fd = h->desc; + h->desc = -1; + if(fd >= 0) { + close(fd); + DBG("[ usb closed %d ]\n", fd); + } +} + +int usb_close(usb_handle *h) +{ + int fd; + + fd = h->desc; + h->desc = -1; + if(fd >= 0) { + close(fd); + DBG("[ usb closed %d ]\n", fd); + } + + return 0; +} + +usb_handle *usb_open(ifc_match_func callback) +{ + return find_usb_device("/dev/bus/usb", callback); +} + + diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c new file mode 100644 index 0000000..d6a8260 --- /dev/null +++ b/fastboot/usb_osx.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/usb/IOUSBLib.h> +#include <IOKit/IOMessage.h> +#include <mach/mach_port.h> + +#include "usb.h" + + +/* + * Internal helper functions and associated definitions. + */ + +#if TRACE_USB +#define WARN(x...) fprintf(stderr, x) +#else +#define WARN(x...) +#endif + +#define ERR(x...) fprintf(stderr, "ERROR: " x) + +/** An open usb device */ +struct usb_handle +{ + int success; + ifc_match_func callback; + usb_ifc_info info; + + UInt8 bulkIn; + UInt8 bulkOut; + IOUSBInterfaceInterface190 **interface; + unsigned int zero_mask; +}; + +/** Try out all the interfaces and see if there's a match. Returns 0 on + * success, -1 on failure. */ +static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) { + IOReturn kr; + IOUSBFindInterfaceRequest request; + io_iterator_t iterator; + io_service_t usbInterface; + IOCFPlugInInterface **plugInInterface; + IOUSBInterfaceInterface190 **interface = NULL; + HRESULT result; + SInt32 score; + UInt8 interfaceNumEndpoints; + UInt8 endpoint; + UInt8 configuration; + + // Placing the constant KIOUSBFindInterfaceDontCare into the following + // fields of the IOUSBFindInterfaceRequest structure will allow us to + // find all of the interfaces + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + // SetConfiguration will kill an existing UMS connection, so let's + // not do this if not necessary. + configuration = 0; + (*dev)->GetConfiguration(dev, &configuration); + if (configuration != 1) + (*dev)->SetConfiguration(dev, 1); + + // Get an iterator for the interfaces on the device + kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator); + + if (kr != 0) { + ERR("Couldn't create a device interface iterator: (%08x)\n", kr); + return -1; + } + + while ((usbInterface = IOIteratorNext(iterator))) { + // Create an intermediate plugin + kr = IOCreatePlugInInterfaceForService( + usbInterface, + kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score); + + // No longer need the usbInterface object now that we have the plugin + (void) IOObjectRelease(usbInterface); + + if ((kr != 0) || (!plugInInterface)) { + WARN("Unable to create plugin (%08x)\n", kr); + continue; + } + + // Now create the interface interface for the interface + result = (*plugInInterface)->QueryInterface( + plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID) &interface); + + // No longer need the intermediate plugin + (*plugInInterface)->Release(plugInInterface); + + if (result || !interface) { + ERR("Couldn't create interface interface: (%08x)\n", + (unsigned int) result); + // continue so we can try the next interface + continue; + } + + /* + * Now open the interface. This will cause the pipes + * associated with the endpoints in the interface descriptor + * to be instantiated. + */ + + /* + * TODO: Earlier comments here indicated that it was a bad + * idea to just open any interface, because opening "mass + * storage endpoints" is bad. However, the only way to find + * out if an interface does bulk in or out is to open it, and + * the framework in this application wants to be told about + * bulk in / out before deciding whether it actually wants to + * use the interface. Maybe something needs to be done about + * this situation. + */ + + kr = (*interface)->USBInterfaceOpen(interface); + + if (kr != 0) { + WARN("Could not open interface: (%08x)\n", kr); + (void) (*interface)->Release(interface); + // continue so we can try the next interface + continue; + } + + // Get the number of endpoints associated with this interface. + kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints); + + if (kr != 0) { + ERR("Unable to get number of endpoints: (%08x)\n", kr); + goto next_interface; + } + + // Get interface class, subclass and protocol + if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 || + (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 || + (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0) + { + ERR("Unable to get interface class, subclass and protocol\n"); + goto next_interface; + } + + handle->info.has_bulk_in = 0; + handle->info.has_bulk_out = 0; + + // Iterate over the endpoints for this interface and see if there + // are any that do bulk in/out. + for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) { + UInt8 transferType; + UInt16 maxPacketSize; + UInt8 interval; + UInt8 number; + UInt8 direction; + + kr = (*interface)->GetPipeProperties(interface, endpoint, + &direction, + &number, &transferType, &maxPacketSize, &interval); + + if (kr == 0) { + if (transferType != kUSBBulk) { + continue; + } + + if (direction == kUSBIn) { + handle->info.has_bulk_in = 1; + handle->bulkIn = endpoint; + } else if (direction == kUSBOut) { + handle->info.has_bulk_out = 1; + handle->bulkOut = endpoint; + } + + if (handle->info.ifc_protocol == 0x01) { + handle->zero_mask = maxPacketSize - 1; + } + } else { + ERR("could not get pipe properties\n"); + } + + if (handle->info.has_bulk_in && handle->info.has_bulk_out) { + break; + } + } + + if (handle->callback(&handle->info) == 0) { + handle->interface = interface; + handle->success = 1; + + /* + * Clear both the endpoints, because it has been observed + * that the Mac may otherwise (incorrectly) start out with + * them in bad state. + */ + + if (handle->info.has_bulk_in) { + kr = (*interface)->ClearPipeStallBothEnds(interface, + handle->bulkIn); + if (kr != 0) { + ERR("could not clear input pipe; result %d", kr); + return -1; + } + } + + if (handle->info.has_bulk_out) { + kr = (*interface)->ClearPipeStallBothEnds(interface, + handle->bulkOut); + if (kr != 0) { + ERR("could not clear output pipe; result %d", kr); + return -1; + } + } + + return 0; + } + +next_interface: + (*interface)->USBInterfaceClose(interface); + (*interface)->Release(interface); + } + + return 0; +} + +/** Try out the given device and see if there's a match. Returns 0 on + * success, -1 on failure. + */ +static int try_device(io_service_t device, usb_handle *handle) { + kern_return_t kr; + IOCFPlugInInterface **plugin = NULL; + IOUSBDeviceInterface182 **dev = NULL; + SInt32 score; + HRESULT result; + UInt8 serialIndex; + + // Create an intermediate plugin. + kr = IOCreatePlugInInterfaceForService(device, + kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugin, &score); + + if ((kr != 0) || (plugin == NULL)) { + ERR("Unable to create a plug-in (%08x)\n", kr); + goto error; + } + + // Now create the device interface. + result = (*plugin)->QueryInterface(plugin, + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev); + if ((result != 0) || (dev == NULL)) { + ERR("Couldn't create a device interface (%08x)\n", (int) result); + goto error; + } + + /* + * We don't need the intermediate interface after the device interface + * is created. + */ + IODestroyPlugInInterface(plugin); + + // So, we have a device, finally. Grab its vitals. + + kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor); + if (kr != 0) { + ERR("GetDeviceVendor"); + goto error; + } + + kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product); + if (kr != 0) { + ERR("GetDeviceProduct"); + goto error; + } + + kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class); + if (kr != 0) { + ERR("GetDeviceClass"); + goto error; + } + + kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass); + if (kr != 0) { + ERR("GetDeviceSubClass"); + goto error; + } + + kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol); + if (kr != 0) { + ERR("GetDeviceProtocol"); + goto error; + } + + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); + + if (serialIndex > 0) { + IOUSBDevRequest req; + UInt16 buffer[256]; + + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = (kUSBStringDesc << 8) | serialIndex; + req.wIndex = 0; + req.pData = buffer; + req.wLength = sizeof(buffer); + kr = (*dev)->DeviceRequest(dev, &req); + + if (kr == kIOReturnSuccess && req.wLenDone > 0) { + int i, count; + + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + count = (req.wLenDone - 1) / 2; + for (i = 0; i < count; i++) + handle->info.serial_number[i] = buffer[i + 1]; + handle->info.serial_number[i] = 0; + } + } else { + // device has no serial number + handle->info.serial_number[0] = 0; + } + + if (try_interfaces(dev, handle)) { + goto error; + } + + (*dev)->Release(dev); + return 0; + + error: + + if (dev != NULL) { + (*dev)->Release(dev); + } + + return -1; +} + + +/** Initializes the USB system. Returns 0 on success, -1 on error. */ +static int init_usb(ifc_match_func callback, usb_handle **handle) { + int ret = -1; + CFMutableDictionaryRef matchingDict; + kern_return_t result; + io_iterator_t iterator; + usb_handle h; + + h.success = 0; + h.callback = callback; + + /* + * Create our matching dictionary to find appropriate devices. + * IOServiceAddMatchingNotification consumes the reference, so we + * do not need to release it. + */ + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + if (matchingDict == NULL) { + ERR("Couldn't create USB matching dictionary.\n"); + return -1; + } + + result = IOServiceGetMatchingServices( + kIOMasterPortDefault, matchingDict, &iterator); + + if (result != 0) { + ERR("Could not create iterator."); + return -1; + } + + for (;;) { + if (! IOIteratorIsValid(iterator)) { + /* + * Apple documentation advises resetting the iterator if + * it should become invalid during iteration. + */ + IOIteratorReset(iterator); + continue; + } + + io_service_t device = IOIteratorNext(iterator); + + if (device == 0) { + break; + } + + usb_ifc_info info; + + if (try_device(device, &h) != 0) { + IOObjectRelease(device); + ret = -1; + break; + } + + if (h.success) { + *handle = calloc(1, sizeof(usb_handle)); + memcpy(*handle, &h, sizeof(usb_handle)); + ret = 0; + break; + } + + IOObjectRelease(device); + } + + IOObjectRelease(iterator); + + return ret; +} + + + +/* + * Definitions of this file's public functions. + */ + +usb_handle *usb_open(ifc_match_func callback) { + usb_handle *handle = NULL; + + if (init_usb(callback, &handle) < 0) { + /* Something went wrong initializing USB. */ + return NULL; + } + + return handle; +} + +int usb_close(usb_handle *h) { + /* TODO: Something better here? */ + return 0; +} + +int usb_read(usb_handle *h, void *data, int len) { + IOReturn result; + UInt32 numBytes = len; + + if (len == 0) { + return 0; + } + + if (h == NULL) { + return -1; + } + + if (h->interface == NULL) { + ERR("usb_read interface was null\n"); + return -1; + } + + if (h->bulkIn == 0) { + ERR("bulkIn endpoint not assigned\n"); + return -1; + } + + result = (*h->interface)->ReadPipe( + h->interface, h->bulkIn, data, &numBytes); + + if (result == 0) { + return (int) numBytes; + } else { + ERR("usb_read failed with status %x\n", result); + } + + return -1; +} + +int usb_write(usb_handle *h, const void *data, int len) { + IOReturn result; + + if (len == 0) { + return 0; + } + + if (h == NULL) { + return -1; + } + + if (h->interface == NULL) { + ERR("usb_write interface was null\n"); + return -1; + } + + if (h->bulkOut == 0) { + ERR("bulkOut endpoint not assigned\n"); + return -1; + } + + result = (*h->interface)->WritePipe( + h->interface, h->bulkOut, (void *)data, len); + + #if 0 + if ((result == 0) && (h->zero_mask)) { + /* we need 0-markers and our transfer */ + if(!(len & h->zero_mask)) { + result = (*h->interface)->WritePipe( + h->interface, h->bulkOut, (void *)data, 0); + } + } + #endif + + if (result != 0) { + ERR("usb_write failed with status %x\n", result); + return -1; + } + + return len; +} diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c new file mode 100644 index 0000000..9c0a9cb --- /dev/null +++ b/fastboot/usb_windows.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <windows.h> +#include <winerror.h> +#include <errno.h> +#include <usb100.h> +#include <adb_api.h> +#include <stdio.h> + +#include "usb.h" + +//#define TRACE_USB 1 +#if TRACE_USB +#define DBG(x...) fprintf(stderr, x) +#else +#define DBG(x...) +#endif + + +/** Structure usb_handle describes our connection to the usb device via + AdbWinApi.dll. This structure is returned from usb_open() routine and + is expected in each subsequent call that is accessing the device. +*/ +struct usb_handle { + /// Handle to USB interface + ADBAPIHANDLE adb_interface; + + /// Handle to USB read pipe (endpoint) + ADBAPIHANDLE adb_read_pipe; + + /// Handle to USB write pipe (endpoint) + ADBAPIHANDLE adb_write_pipe; + + /// Interface name + char* interface_name; +}; + +/// Class ID assigned to the device by androidusb.sys +static const GUID usb_class_id = ANDROID_USB_CLASS_ID; + + +/// Checks if interface (device) matches certain criteria +int recognized_device(usb_handle* handle, ifc_match_func callback); + +/// Opens usb interface (device) by interface (device) name. +usb_handle* do_usb_open(const wchar_t* interface_name); + +/// Writes data to the opened usb handle +int usb_write(usb_handle* handle, const void* data, int len); + +/// Reads data using the opened usb handle +int usb_read(usb_handle *handle, void* data, int len); + +/// Cleans up opened usb handle +void usb_cleanup_handle(usb_handle* handle); + +/// Cleans up (but don't close) opened usb handle +void usb_kick(usb_handle* handle); + +/// Closes opened usb handle +int usb_close(usb_handle* handle); + + +usb_handle* do_usb_open(const wchar_t* interface_name) { + // Allocate our handle + usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle)); + if (NULL == ret) + return NULL; + + // Create interface. + ret->adb_interface = AdbCreateInterfaceByName(interface_name); + + if (NULL == ret->adb_interface) { + free(ret); + errno = GetLastError(); + return NULL; + } + + // Open read pipe (endpoint) + ret->adb_read_pipe = + AdbOpenDefaultBulkReadEndpoint(ret->adb_interface, + AdbOpenAccessTypeReadWrite, + AdbOpenSharingModeReadWrite); + if (NULL != ret->adb_read_pipe) { + // Open write pipe (endpoint) + ret->adb_write_pipe = + AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface, + AdbOpenAccessTypeReadWrite, + AdbOpenSharingModeReadWrite); + if (NULL != ret->adb_write_pipe) { + // Save interface name + unsigned long name_len = 0; + + // First get expected name length + AdbGetInterfaceName(ret->adb_interface, + NULL, + &name_len, + true); + if (0 != name_len) { + ret->interface_name = (char*)malloc(name_len); + + if (NULL != ret->interface_name) { + // Now save the name + if (AdbGetInterfaceName(ret->adb_interface, + ret->interface_name, + &name_len, + true)) { + // We're done at this point + return ret; + } + } else { + SetLastError(ERROR_OUTOFMEMORY); + } + } + } + } + + // Something went wrong. + errno = GetLastError(); + usb_cleanup_handle(ret); + free(ret); + SetLastError(errno); + + return NULL; +} + +int usb_write(usb_handle* handle, const void* data, int len) { + unsigned long time_out = 500 + len * 8; + unsigned long written = 0; + unsigned count = 0; + int ret; + + DBG("usb_write %d\n", len); + if (NULL != handle) { + // Perform write + while(len > 0) { + int xfer = (len > 4096) ? 4096 : len; + ret = AdbWriteEndpointSync(handle->adb_write_pipe, + (void*)data, + (unsigned long)xfer, + &written, + time_out); + errno = GetLastError(); + DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno); + if (ret == 0) { + // assume ERROR_INVALID_HANDLE indicates we are disconnected + if (errno == ERROR_INVALID_HANDLE) + usb_kick(handle); + return -1; + } + + count += written; + len -= written; + data += written; + + if (len == 0) + return count; + } + } else { + DBG("usb_write NULL handle\n"); + SetLastError(ERROR_INVALID_HANDLE); + } + + DBG("usb_write failed: %d\n", errno); + + return -1; +} + +int usb_read(usb_handle *handle, void* data, int len) { + unsigned long time_out = 500 + len * 8; + unsigned long read = 0; + int ret; + + DBG("usb_read %d\n", len); + if (NULL != handle) { + while (1) { + int xfer = (len > 4096) ? 4096 : len; + + ret = AdbReadEndpointSync(handle->adb_read_pipe, + (void*)data, + (unsigned long)xfer, + &read, + time_out); + errno = GetLastError(); + DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno); + if (ret) { + return read; + } else if (errno != ERROR_SEM_TIMEOUT) { + // assume ERROR_INVALID_HANDLE indicates we are disconnected + if (errno == ERROR_INVALID_HANDLE) + usb_kick(handle); + break; + } + // else we timed out - try again + } + } else { + DBG("usb_read NULL handle\n"); + SetLastError(ERROR_INVALID_HANDLE); + } + + DBG("usb_read failed: %d\n", errno); + + return -1; +} + +void usb_cleanup_handle(usb_handle* handle) { + if (NULL != handle) { + if (NULL != handle->interface_name) + free(handle->interface_name); + if (NULL != handle->adb_write_pipe) + AdbCloseHandle(handle->adb_write_pipe); + if (NULL != handle->adb_read_pipe) + AdbCloseHandle(handle->adb_read_pipe); + if (NULL != handle->adb_interface) + AdbCloseHandle(handle->adb_interface); + + handle->interface_name = NULL; + handle->adb_write_pipe = NULL; + handle->adb_read_pipe = NULL; + handle->adb_interface = NULL; + } +} + +void usb_kick(usb_handle* handle) { + if (NULL != handle) { + usb_cleanup_handle(handle); + } else { + SetLastError(ERROR_INVALID_HANDLE); + errno = ERROR_INVALID_HANDLE; + } +} + +int usb_close(usb_handle* handle) { + DBG("usb_close\n"); + + if (NULL != handle) { + // Cleanup handle + usb_cleanup_handle(handle); + free(handle); + } + + return 0; +} + +int recognized_device(usb_handle* handle, ifc_match_func callback) { + struct usb_ifc_info info; + USB_DEVICE_DESCRIPTOR device_desc; + USB_INTERFACE_DESCRIPTOR interf_desc; + + if (NULL == handle) + return 0; + + // Check vendor and product id first + if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, + &device_desc)) { + return 0; + } + + // Then check interface properties + if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, + &interf_desc)) { + return 0; + } + + // Must have two endpoints + if (2 != interf_desc.bNumEndpoints) { + return 0; + } + + info.dev_vendor = device_desc.idVendor; + info.dev_product = device_desc.idProduct; + info.dev_class = device_desc.bDeviceClass; + info.dev_subclass = device_desc.bDeviceSubClass; + info.dev_protocol = device_desc.bDeviceProtocol; + info.ifc_class = interf_desc.bInterfaceClass; + info.ifc_subclass = interf_desc.bInterfaceSubClass; + info.ifc_protocol = interf_desc.bInterfaceProtocol; + + // read serial number (if there is one) + unsigned long serial_number_len = sizeof(info.serial_number); + if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number, + &serial_number_len, true)) { + info.serial_number[0] = 0; + } + + if (callback(&info) == 0) { + return 1; + } + + return 0; +} + +static usb_handle *find_usb_device(ifc_match_func callback) { + usb_handle* handle = NULL; + char entry_buffer[2048]; + char interf_name[2048]; + AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]); + unsigned long entry_buffer_size = sizeof(entry_buffer); + char* copy_name; + + // Enumerate all present and active interfaces. + ADBAPIHANDLE enum_handle = + AdbEnumInterfaces(usb_class_id, true, true, true); + + if (NULL == enum_handle) + return NULL; + + while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) { + // TODO(vchtchetkine): FIXME - temp hack converting wchar_t into char. + // It would be better to change AdbNextInterface so it will return + // interface name as single char string. + const wchar_t* wchar_name = next_interface->device_name; + for(copy_name = interf_name; + L'\0' != *wchar_name; + wchar_name++, copy_name++) { + *copy_name = (char)(*wchar_name); + } + *copy_name = '\0'; + + handle = do_usb_open(next_interface->device_name); + if (NULL != handle) { + // Lets see if this interface (device) belongs to us + if (recognized_device(handle, callback)) { + // found it! + break; + } else { + usb_cleanup_handle(handle); + free(handle); + handle = NULL; + } + } + + entry_buffer_size = sizeof(entry_buffer); + } + + AdbCloseHandle(enum_handle); + return handle; +} + +usb_handle *usb_open(ifc_match_func callback) +{ + return find_usb_device(callback); +} + +// called from fastboot.c +void sleep(int seconds) +{ + Sleep(seconds * 1000); +} diff --git a/fastboot/usbtest.c b/fastboot/usbtest.c new file mode 100644 index 0000000..e34d7e6 --- /dev/null +++ b/fastboot/usbtest.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/time.h> + +#include "usb.h" + +static unsigned arg_size = 4096; +static unsigned arg_count = 4096; + +long long NOW(void) +{ + struct timeval tv; + gettimeofday(&tv, 0); + + return (((long long) tv.tv_sec) * ((long long) 1000000)) + + (((long long) tv.tv_usec)); +} + +int printifc(usb_ifc_info *info) +{ + printf("dev: csp=%02x/%02x/%02x v=%04x p=%04x ", + info->dev_class, info->dev_subclass, info->dev_protocol, + info->dev_vendor, info->dev_product); + printf("ifc: csp=%02x/%02x/%02x%s%s\n", + info->ifc_class, info->ifc_subclass, info->ifc_protocol, + info->has_bulk_in ? " in" : "", + info->has_bulk_out ? " out" : ""); + return -1; +} + +int match_null(usb_ifc_info *info) +{ + if(info->dev_vendor != 0x18d1) return -1; + if(info->ifc_class != 0xff) return -1; + if(info->ifc_subclass != 0xfe) return -1; + if(info->ifc_protocol != 0x01) return -1; + return 0; +} + +int match_zero(usb_ifc_info *info) +{ + if(info->dev_vendor != 0x18d1) return -1; + if(info->ifc_class != 0xff) return -1; + if(info->ifc_subclass != 0xfe) return -1; + if(info->ifc_protocol != 0x02) return -1; + return 0; +} + +int match_loop(usb_ifc_info *info) +{ + if(info->dev_vendor != 0x18d1) return -1; + if(info->ifc_class != 0xff) return -1; + if(info->ifc_subclass != 0xfe) return -1; + if(info->ifc_protocol != 0x03) return -1; + return 0; +} + +int test_null(usb_handle *usb) +{ + int i; + unsigned char buf[4096]; + memset(buf, 0xee, 4096); + long long t0, t1; + + t0 = NOW(); + for(i = 0; i < arg_count; i++) { + if(usb_write(usb, buf, arg_size) != arg_size) { + fprintf(stderr,"write failed (%s)\n", strerror(errno)); + return -1; + } + } + t1 = NOW(); + fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0)); + return 0; +} + +int test_zero(usb_handle *usb) +{ + int i; + unsigned char buf[4096]; + long long t0, t1; + + t0 = NOW(); + for(i = 0; i < arg_count; i++) { + if(usb_read(usb, buf, arg_size) != arg_size) { + fprintf(stderr,"read failed (%s)\n", strerror(errno)); + return -1; + } + } + t1 = NOW(); + fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0)); + return 0; +} + +struct +{ + const char *cmd; + ifc_match_func match; + int (*test)(usb_handle *usb); + const char *help; +} tests[] = { + { "list", printifc, 0, "list interfaces" }, + { "send", match_null, test_null, "send to null interface" }, + { "recv", match_zero, test_zero, "recv from zero interface" }, + { "loop", match_loop, 0, "exercise loopback interface" }, + {}, +}; + +int usage(void) +{ + int i; + + fprintf(stderr,"usage: usbtest <testname>\n\navailable tests:\n"); + for(i = 0; tests[i].cmd; i++) { + fprintf(stderr," %-8s %s\n", tests[i].cmd, tests[i].help); + } + return -1; +} + +int process_args(int argc, char **argv) +{ + while(argc-- > 0) { + char *arg = *argv++; + if(!strncmp(arg,"count=",6)) { + arg_count = atoi(arg + 6); + } else if(!strncmp(arg,"size=",5)) { + arg_size = atoi(arg + 5); + } else { + fprintf(stderr,"unknown argument: %s\n", arg); + return -1; + } + } + + if(arg_count == 0) { + fprintf(stderr,"count may not be zero\n"); + return -1; + } + + if(arg_size > 4096) { + fprintf(stderr,"size may not be greater than 4096\n"); + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + usb_handle *usb; + int i; + + if(argc < 2) + return usage(); + + if(argc > 2) { + if(process_args(argc - 2, argv + 2)) + return -1; + } + + for(i = 0; tests[i].cmd; i++) { + if(!strcmp(argv[1], tests[i].cmd)) { + usb = usb_open(tests[i].match); + if(tests[i].test) { + if(usb == 0) { + fprintf(stderr,"usbtest: %s: could not find interface\n", + tests[i].cmd); + return -1; + } + if(tests[i].test(usb)) { + fprintf(stderr,"usbtest: %s: FAIL\n", tests[i].cmd); + return -1; + } else { + fprintf(stderr,"usbtest: %s: OKAY\n", tests[i].cmd); + } + } + return 0; + } + } + + return usage(); +} diff --git a/fastboot/util_linux.c b/fastboot/util_linux.c new file mode 100644 index 0000000..912e16f --- /dev/null +++ b/fastboot/util_linux.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> + +void get_my_path(char *path) +{ + char proc[64]; + char *x; + + sprintf(proc, "/proc/%d/exe", getpid()); + int err = readlink(proc, path, PATH_MAX - 1); + + if(err <= 0) { + path[0] = 0; + } else { + path[err] = 0; + x = strrchr(path,'/'); + if(x) x[1] = 0; + } +} diff --git a/fastboot/util_osx.c b/fastboot/util_osx.c new file mode 100644 index 0000000..068241c --- /dev/null +++ b/fastboot/util_osx.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <utils/executablepath.h> +#import <Carbon/Carbon.h> +#include <unistd.h> + +void get_my_path(char s[PATH_MAX]) +{ + char *x; + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8); + x = strrchr(s, '/'); + if(x) x[1] = 0; +} + + diff --git a/fastboot/util_windows.c b/fastboot/util_windows.c new file mode 100644 index 0000000..37077a4 --- /dev/null +++ b/fastboot/util_windows.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> + +#include <windows.h> + +void get_my_path(char exe[PATH_MAX]) +{ + char* r; + + GetModuleFileName( NULL, exe, PATH_MAX-1 ); + exe[PATH_MAX-1] = 0; + r = strrchr( exe, '\\' ); + if (r) + *r = 0; +} + + +void *load_file(const char *fn, unsigned *_sz) +{ + HANDLE file; + char *data; + DWORD file_size; + + file = CreateFile( fn, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (file == INVALID_HANDLE_VALUE) + return NULL; + + file_size = GetFileSize( file, NULL ); + data = NULL; + + if (file_size > 0) { + data = (char*) malloc( file_size ); + if (data == NULL) { + fprintf(stderr, "load_file: could not allocate %ld bytes\n", file_size ); + file_size = 0; + } else { + DWORD out_bytes; + + if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) || + out_bytes != file_size ) + { + fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", file_size, fn); + free(data); + data = NULL; + file_size = 0; + } + } + } + CloseHandle( file ); + + *_sz = (unsigned) file_size; + return data; +} diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h new file mode 100644 index 0000000..49f04e5 --- /dev/null +++ b/include/arch/darwin-x86/AndroidConfig.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android config -- "Darwin". Used for PPC Mac OS X. + * + * TODO: split this into "x86" and "ppc" versions + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have the futex syscall? + */ + +/* #define HAVE_FUTEX */ + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory + * badness adjustment. + */ +/* #define HAVE_OOM_ADJ */ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_MACOSX_IPC + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP + +/* + * Define this if you have <termio.h> + */ +#define HAVE_TERMIO_H + +/* + * Define this if you build against MSVCRT.DLL + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS + +/* + * Define this if we have localtime_r(). + */ +#define HAVE_LOCALTIME_R + +/* + * Define this if we have gethostbyname_r(). + */ +/* #define HAVE_GETHOSTBYNAME_R */ + +/* + * Define this if we have ioctl(). + */ +/* #define HAVE_IOCTL */ + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + */ +/* #define HAVE_POSIX_CLOCKS */ + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +/* #define HAVE_TIMEDWAIT_MONOTONIC */ + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#if (defined(__ppc__) || defined(__ppc64__)) +# define HAVE_BIG_ENDIAN +#elif defined(__i386__) +# define HAVE_LITTLE_ENDIAN +#endif + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 0 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 0 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +/* #define HAVE_GETTID */ + + +/* + * Add any extra platform-specific defines here. + */ +#define _THREAD_SAFE + +/* + * Define if we have <malloc.h> header + */ +/* #define HAVE_MALLOC_H */ + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if we have madvise() in <sys/mman.h> + */ +#define HAVE_MADVISE 1 + +/* + * Define if we include <sys/mount.h> for statfs() + */ +#define INCLUDE_SYS_MOUNT_FOR_STATFS 1 + +/* + * What CPU architecture does this platform use? + */ +#if (defined(__ppc__) || defined(__ppc64__)) +# define ARCH_PPC +#elif defined(__i386__) +# define ARCH_X86 +#endif + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.dylib" + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE char * + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + * + * For tools apps, we'll treat is as not case sensitive. + */ +/* #define OS_CASE_SENSITIVE */ + +/* + * Define if <sys/socket.h> exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +#define HAVE_STRLCPY 1 + +/* + * Define if writev() exists + */ +#define HAVE_WRITEV 1 + +#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h new file mode 100644 index 0000000..f322127 --- /dev/null +++ b/include/arch/linux-arm/AndroidConfig.h @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android config -- "android-arm". Used for ARM device builds. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have the futex syscall? + */ + +#define HAVE_FUTEX + +/* + * Define if we already have the futex wrapper functions defined. Yes if + * compiling against bionic. + */ +#define HAVE_FUTEX_WRAPPERS 1 + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory + * badness adjustment. + */ +#define HAVE_OOM_ADJ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_ANDROID_IPC + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP + +/* + * Define this if you have <termio.h> + */ +#define HAVE_TERMIO_H + +/* + * Define this if you build against MSVCRT.DLL + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS + +/* + * Define this if we have localtime_r(). + */ +/* #define HAVE_LOCALTIME_R */ + +/* + * Define this if we have gethostbyname_r(). + */ +/* #define HAVE_GETHOSTBYNAME_R */ + +/* + * Define this if we have ioctl(). + */ +#define HAVE_IOCTL + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + */ +#define HAVE_POSIX_CLOCKS + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +#define HAVE_TIMEDWAIT_MONOTONIC + +/* + * Define this if we have linux style epoll() + */ +#define HAVE_EPOLL + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#define HAVE_ENDIAN_H +#define HAVE_LITTLE_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +/* #define _FILE_OFFSET_BITS 64 */ +/* #define _LARGEFILE_SOURCE 1 */ + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 0 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 0 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +#define HAVE_GETTID + +/* + * Defined if we have the sched_setscheduler() call + */ +#define HAVE_SCHED_SETSCHEDULER + +/* + * Add any extra platform-specific defines here. + */ +#define __linux__ + +/* + * Define if we have <malloc.h> header + */ +#define HAVE_MALLOC_H + +/* + * Define if we're running on *our* linux on device or emulator. + */ +#define HAVE_ANDROID_OS 1 + +/* + * Define if we have Linux-style non-filesystem Unix Domain Sockets + */ +#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 + +/* + * Define if we have Linux's inotify in <sys/inotify.h>. + */ +#define HAVE_INOTIFY 1 + +/* + * Define if we have madvise() in <sys/mman.h> + */ +#define HAVE_MADVISE 1 + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if libc includes Android system properties implementation. + */ +#define HAVE_LIBC_SYSTEM_PROPERTIES 1 + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +/* #define HAVE_SYSTEM_PROPERTY_SERVER */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_ARM + +/* + * Define if the size of enums is as short as possible, + */ +/* #define HAVE_SHORT_ENUMS */ + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" + +/* + * Do we have __memcmp16()? + */ +#define HAVE__MEMCMP16 1 + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * Do we have the sigaction flag SA_NOCLDWAIT? + */ +#define HAVE_SA_NOCLDWAIT + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + */ +#define OS_CASE_SENSITIVE + +/* + * Define if <sys/socket.h> exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +#define HAVE_STRLCPY 1 + +/* + * Define if prctl() exists + */ +#define HAVE_PRCTL 1 + +/* + * Define if writev() exists + */ +#define HAVE_WRITEV 1 + +#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h new file mode 100644 index 0000000..6de75f8 --- /dev/null +++ b/include/arch/linux-x86/AndroidConfig.h @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android config -- "Linux". Used for desktop x86 Linux. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have the futex syscall? + */ + +#define HAVE_FUTEX + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory + * badness adjustment. + */ +#define HAVE_OOM_ADJ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_SYSV_IPC + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP + +/* + * Define this if you have <termio.h> + */ +#define HAVE_TERMIO_H + +/* + * Define this if you build against MSVCRT.DLL + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS + +/* + * Define this if we have localtime_r(). + */ +#define HAVE_LOCALTIME_R + +/* + * Define this if we have gethostbyname_r(). + */ +#define HAVE_GETHOSTBYNAME_R + +/* + * Define this if we have ioctl(). + */ +#define HAVE_IOCTL + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + * + * Desktop Linux has this in librt, but it's broken in goobuntu, yielding + * mildly or wildly inaccurate results. + */ +/*#define HAVE_POSIX_CLOCKS*/ + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +/* #define HAVE_TIMEDWAIT_MONOTONIC */ + +/* + * Define this if we have linux style epoll() + */ +#define HAVE_EPOLL + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#define HAVE_ENDIAN_H +#define HAVE_LITTLE_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 1 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 1 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +/* #define HAVE_GETTID */ + +/* + * Defined if we have the sched_setscheduler() call + */ +#define HAVE_SCHED_SETSCHEDULER + +/* + * Add any extra platform-specific defines here. + */ + +/* + * Define if we have <malloc.h> header + */ +#define HAVE_MALLOC_H + +/* + * Define if we have Linux-style non-filesystem Unix Domain Sockets + */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_X86 + + +/* + * Define if we have Linux's inotify in <sys/inotify.h>. + */ +/*#define HAVE_INOTIFY 1*/ + +/* + * Define if we have madvise() in <sys/mman.h> + */ +#define HAVE_MADVISE 1 + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if libc includes Android system properties implementation. + */ +/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +#define HAVE_SYSTEM_PROPERTY_SERVER + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * Do we have the sigaction flag SA_NOCLDWAIT? + */ +#define HAVE_SA_NOCLDWAIT + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + */ +#define OS_CASE_SENSITIVE + +/* + * Define if <sys/socket.h> exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +/* #define HAVE_STRLCPY 1 */ + +/* + * Define if prctl() exists + */ +#define HAVE_PRCTL 1 + +/* + * Define if writev() exists + */ +#define HAVE_WRITEV 1 + +#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h new file mode 100644 index 0000000..4aa44f8 --- /dev/null +++ b/include/arch/target_linux-x86/AndroidConfig.h @@ -0,0 +1,296 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Android config -- "target_linux-x86". Used for x86 linux target devices. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have the futex syscall? + */ + +#define HAVE_FUTEX + +/* + * Define if we already have the futex wrapper functions defined. Yes if + * compiling against bionic. + */ +#define HAVE_FUTEX_WRAPPERS 1 + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory + * badness adjustment. + */ +#define HAVE_OOM_ADJ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_ANDROID_IPC 1 + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP 1 + +/* + * Define this if you have <termio.h> + */ +#define HAVE_TERMIO_H 1 + +/* + * Define this if you build against have Microsoft C runtime (MSVCRT.DLL) + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H 1 + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS 1 + +/* + * Define this if we have localtime_r(). + */ +/* #define HAVE_LOCALTIME_R */ + +/* + * Define this if we have gethostbyname_r(). + */ +/* #define HAVE_GETHOSTBYNAME_R */ + +/* + * Define this if we have ioctl(). + */ +#define HAVE_IOCTL + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + * + */ +#define HAVE_POSIX_CLOCKS + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +#define HAVE_TIMEDWAIT_MONOTONIC + +/* + * Define this if we have linux style epoll() + */ +#define HAVE_EPOLL + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#define HAVE_ENDIAN_H +#define HAVE_LITTLE_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +/* + * #define _FILE_OFFSET_BITS 64 + * #define _LARGEFILE_SOURCE 1 + */ + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 0 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 0 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +#define HAVE_GETTID + +/* + * Defined if we have the sched_setscheduler() call + */ +#define HAVE_SCHED_SETSCHEDULER + +/* + * Add any extra platform-specific defines here. + */ +#ifndef __linux__ +#define __linux__ +#endif + +/* + * Define if we have <malloc.h> header + */ +#define HAVE_MALLOC_H + +/* + * Define if we're running on *our* linux on device or emulator. + */ +#define HAVE_ANDROID_OS 1 + +/* + * Define if we have Linux-style non-filesystem Unix Domain Sockets + */ +#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 + +/* + * Define if we have Linux's inotify in <sys/inotify.h>. + */ +#define HAVE_INOTIFY 1 + +/* + * Define if we have madvise() in <sys/mman.h> + */ +#define HAVE_MADVISE 1 + +/* + * Define if we have Linux's dbus + */ +#define HAVE_DBUS 1 + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if libc includes Android system properties implementation. + */ +#define HAVE_LIBC_SYSTEM_PROPERTIES 1 + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +/* #define HAVE_SYSTEM_PROPERTY_SERVER */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_X86 + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" + +/* + * Do we have __memcmp16()? + */ +/* #define HAVE__MEMCMP16 1 */ + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * Do we have the sigaction flag SA_NOCLDWAIT? + */ +#define HAVE_SA_NOCLDWAIT + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + */ +#define OS_CASE_SENSITIVE + +/* + * Define if <sys/socket.h> exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +#define HAVE_STRLCPY 1 + +/* + * Define if prctl() exists + */ +#define HAVE_PRCTL 1 + +/* + * Whether or not _Unwind_Context is defined as a struct. + */ +#define HAVE_UNWIND_CONTEXT_STRUCT + +#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h new file mode 100644 index 0000000..c3c6ff1 --- /dev/null +++ b/include/arch/windows/AndroidConfig.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android config -- "CYGWIN_NT-5.1". + * + * Cygwin has pthreads, but GDB seems to get confused if you use it to + * create threads. By "confused", I mean it freezes up the first time the + * debugged process creates a thread, even if you use CreateThread. The + * mere presence of pthreads linkage seems to cause problems. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + */ +#define HAVE_WIN32_THREADS + +/* + * Do we have the futex syscall? + */ + +/* #define HAVE_FUTEX */ + + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#ifdef __CYGWIN__ +# define HAVE_FORKEXEC +#else +# define HAVE_WIN32_PROC +#endif + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory + * badness adjustment. + */ +/* #define HAVE_OOM_ADJ */ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_WIN32_IPC + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#ifdef __CYGWIN__ +#define HAVE_POSIX_FILEMAP +#else +#define HAVE_WIN32_FILEMAP +#endif + +/* + * Define this if you have <termio.h> + */ +#ifdef __CYGWIN__ +# define HAVE_TERMIO_H +#endif + +/* + * Define this if you build against MSVCRT.DLL + */ +#ifndef __CYGWIN__ +# define HAVE_MS_C_RUNTIME +#endif + +/* + * Define this if you have sys/uio.h + */ +#ifdef __CYGWIN__ +#define HAVE_SYS_UIO_H +#endif + + +/* + * Define this if we have localtime_r(). + */ +/* #define HAVE_LOCALTIME_R */ + +/* + * Define this if we have gethostbyname_r(). + */ +/* #define HAVE_GETHOSTBYNAME_R */ + +/* + * Define this if we have ioctl(). + */ +/* #define HAVE_IOCTL */ + +/* + * Define this if we want to use WinSock. + */ +#ifndef __CYGWIN__ +#define HAVE_WINSOCK +#endif + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +/* #define HAVE_SYMLINKS */ + +/* + * Define this if have clock_gettime() and friends + */ +/* #define HAVE_POSIX_CLOCKS */ + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#ifdef __CYGWIN__ +#define HAVE_ENDIAN_H +#endif + +#define HAVE_LITTLE_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 0 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 0 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Define if tm struct has tm_gmtoff field + */ +/* #define HAVE_TM_GMTOFF 1 */ + +/* + * Define if dirent struct has d_type field + */ +/* #define HAVE_DIRENT_D_TYPE 1 */ + +/* + * Define if libc includes Android system properties implementation. + */ +/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +/* #define HAVE_SYSTEM_PROPERTY_SERVER */ + +/* + * Define if we have madvise() in <sys/mman.h> + */ +/*#define HAVE_MADVISE 1*/ + +/* + * Add any extra platform-specific defines here. + */ +#define WIN32 1 /* stock Cygwin doesn't define these */ +#define _WIN32 1 +#define _WIN32_WINNT 0x0500 /* admit to using >= Win2K */ + +#define HAVE_WINDOWS_PATHS /* needed by simulator */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_X86 + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.dll" + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '\\' + +/* + * Is the filesystem case sensitive? + */ +/* #define OS_CASE_SENSITIVE */ + +/* + * Define if <sys/socket.h> exists. + * Cygwin has it, but not MinGW. + */ +#ifdef USE_MINGW +/* #define HAVE_SYS_SOCKET_H */ +#else +#define HAVE_SYS_SOCKET_H 1 +#endif + +/* + * Define if the strlcpy() function exists on the system. + */ +/* #define HAVE_STRLCPY 1 */ + +/* + * Define if <winsock2.h> exists. + * Only MinGW has it. + */ +#ifdef USE_MINGW +#define HAVE_WINSOCK2_H 1 +#else +/* #define HAVE_WINSOCK2_H */ +#endif + +/* + * Various definitions missing in MinGW + */ +#ifdef USE_MINGW +#define S_IRGRP 0 +#define sleep _sleep +#endif + +/* + * Define if writev() exists. + */ +/* #define HAVE_WRITEV */ + +#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/ctest/ctest.h b/include/ctest/ctest.h new file mode 100644 index 0000000..1a83b20 --- /dev/null +++ b/include/ctest/ctest.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +/** + * Very simple unit testing framework. + */ + +#ifndef __CUTILS_TEST_H +#define __CUTILS_TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Adds a test to the test suite. + */ +#define addTest(test) addNamedTest(#test, &test) + +/** + * Asserts that a condition is true. The test fails if it isn't. + */ +#define assertTrue(value, message) assertTrueWithSource(value, __FILE__, __LINE__, message); + +/** + * Asserts that a condition is false. The test fails if the value is true. + */ +#define assertFalse(value, message) assertTrueWithSource(!value, __FILE__, __LINE__, message); + +/** Fails a test with the given message. */ +#define fail(message) assertTrueWithSource(0, __FILE__, __LINE__, message); + +/** + * Asserts that two values are ==. + */ +#define assertSame(a, b) assertTrueWithSource(a == b, __FILE__, __LINE__, "Expected same value."); + +/** + * Asserts that two values are !=. + */ +#define assertNotSame(a, b) assertTrueWithSource(a != b, __FILE__, __LINE__,\ + "Expected different values"); + +/** + * Runs a test suite. + */ +void runTests(void); + +// Do not call these functions directly. Use macros above instead. +void addNamedTest(const char* name, void (*test)(void)); +void assertTrueWithSource(int value, const char* file, int line, char* message); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_TEST_H */ diff --git a/include/cutils/adb_networking.h b/include/cutils/adb_networking.h new file mode 100755 index 0000000..409d577 --- /dev/null +++ b/include/cutils/adb_networking.h @@ -0,0 +1,35 @@ +/* + * 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 _ADB_NETWORKING_H +#define _ADB_NETWORKING_H 1 +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address); +extern int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr); + +#ifdef __cplusplus +} +#endif + +#endif /*_ADB_NETWORKING_H*/ + diff --git a/include/cutils/array.h b/include/cutils/array.h new file mode 100644 index 0000000..c97ff34 --- /dev/null +++ b/include/cutils/array.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +/** + * A pointer array which intelligently expands its capacity ad needed. + */ + +#ifndef __ARRAY_H +#define __ARRAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> + +/** An array. */ +typedef struct Array Array; + +/** Constructs a new array. Returns NULL if we ran out of memory. */ +Array* arrayCreate(); + +/** Frees an array. Does not free elements themselves. */ +void arrayFree(Array* array); + +/** Adds a pointer. Returns 0 is successful, < 0 otherwise. */ +int arrayAdd(Array* array, void* pointer); + +/** Gets the pointer at the specified index. */ +void* arrayGet(Array* array, int index); + +/** Removes the pointer at the given index and returns it. */ +void* arrayRemove(Array* array, int index); + +/** Sets pointer at the given index. Returns old pointer. */ +void* arraySet(Array* array, int index, void* pointer); + +/** Sets the array size. Sets new pointers to NULL. Returns 0 if successful, < 0 otherwise . */ +int arraySetSize(Array* array, int size); + +/** Returns the size of the given array. */ +int arraySize(Array* array); + +/** + * Returns a pointer to a C-style array which will be valid until this array + * changes. + */ +const void** arrayUnwrap(Array* array); + +#ifdef __cplusplus +} +#endif + +#endif /* __ARRAY_H */ diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h new file mode 100644 index 0000000..fd71352 --- /dev/null +++ b/include/cutils/ashmem.h @@ -0,0 +1,42 @@ +/* cutils/ashmem.h + ** + ** Copyright 2008 The Android Open Source Project + ** + ** This file is dual licensed. It may be redistributed and/or modified + ** under the terms of the Apache 2.0 License OR version 2 of the GNU + ** General Public License. + */ + +#ifndef _CUTILS_ASHMEM_H +#define _CUTILS_ASHMEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +int ashmem_create_region(const char *name, size_t size); +int ashmem_set_prot_region(int fd, int prot); +int ashmem_pin_region(int fd, size_t offset, size_t len); +int ashmem_unpin_region(int fd, size_t offset, size_t len); + +#ifdef __cplusplus +} +#endif + +#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */ + +#define ASHMEM_NAME_LEN 256 + +#define ASHMEM_NAME_DEF "dev/ashmem" + +/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ +#define ASHMEM_NOT_PURGED 0 +#define ASHMEM_WAS_PURGED 1 + +/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ +#define ASHMEM_IS_UNPINNED 0 +#define ASHMEM_IS_PINNED 1 + +#endif /* ! __ASHMEMIOC */ + +#endif /* _CUTILS_ASHMEM_H */ diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h new file mode 100644 index 0000000..5694d66 --- /dev/null +++ b/include/cutils/atomic.h @@ -0,0 +1,79 @@ +/* + * 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 ANDROID_CUTILS_ATOMIC_H +#define ANDROID_CUTILS_ATOMIC_H + +#include <stdint.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * NOTE: memory shared between threads is synchronized by all atomic operations + * below, this means that no explicit memory barrier is required: all reads or + * writes issued before android_atomic_* operations are guaranteed to complete + * before the atomic operation takes place. + */ + +void android_atomic_write(int32_t value, volatile int32_t* addr); + +/* + * all these atomic operations return the previous value + */ + + +int32_t android_atomic_inc(volatile int32_t* addr); +int32_t android_atomic_dec(volatile int32_t* addr); + +int32_t android_atomic_add(int32_t value, volatile int32_t* addr); +int32_t android_atomic_and(int32_t value, volatile int32_t* addr); +int32_t android_atomic_or(int32_t value, volatile int32_t* addr); + +int32_t android_atomic_swap(int32_t value, volatile int32_t* addr); + +/* + * NOTE: Two "quasiatomic" operations on the exact same memory address + * are guaranteed to operate atomically with respect to each other, + * but no guarantees are made about quasiatomic operations mixed with + * non-quasiatomic operations on the same address, nor about + * quasiatomic operations that are performed on partially-overlapping + * memory. + */ + +int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr); +int64_t android_quasiatomic_read_64(volatile int64_t* addr); + +/* + * cmpxchg return a non zero value if the exchange was NOT performed, + * in other words if oldvalue != *addr + */ + +int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr); + +int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, + volatile int64_t* addr); + + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_CUTILS_ATOMIC_H diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h new file mode 100644 index 0000000..f3fb370 --- /dev/null +++ b/include/cutils/config_utils.h @@ -0,0 +1,61 @@ +/* + * 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 __CUTILS_CONFIG_UTILS_H +#define __CUTILS_CONFIG_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct cnode cnode; + + +struct cnode +{ + cnode *next; + cnode *first_child; + cnode *last_child; + const char *name; + const char *value; +}; + +/* parse a text string into a config node tree */ +void config_load(cnode *root, char *data); + +/* parse a file into a config node tree */ +void config_load_file(cnode *root, const char *fn); + +/* create a single config node */ +cnode* config_node(const char *name, const char *value); + +/* locate a named child of a config node */ +cnode* config_find(cnode *root, const char *name); + +/* look up a child by name and return the boolean value */ +int config_bool(cnode *root, const char *name, int _default); + +/* look up a child by name and return the string value */ +const char* config_str(cnode *root, const char *name, const char *_default); + +/* add a named child to a config node (or modify it if it already exists) */ +void config_set(cnode *root, const char *name, const char *value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/cutils/cpu_info.h b/include/cutils/cpu_info.h new file mode 100644 index 0000000..78c1884 --- /dev/null +++ b/include/cutils/cpu_info.h @@ -0,0 +1,34 @@ +/* + * 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 __CUTILS_CPU_INFO_H +#define __CUTILS_CPU_INFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* returns a string contiaining an ASCII representation of the CPU serial number, +** or NULL if cpu info not available. +** The string is a static variable, so don't call free() on it. +*/ +extern const char* get_cpu_serial_number(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_CPU_INFO_H */ diff --git a/include/cutils/dir_hash.h b/include/cutils/dir_hash.h new file mode 100644 index 0000000..fbb4d02 --- /dev/null +++ b/include/cutils/dir_hash.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +typedef enum { + SHA_1, +} HashAlgorithm; + +int get_file_hash(HashAlgorithm algorithm, const char *path, + char *output_string, size_t max_output_string); + +int get_recursive_hash_manifest(HashAlgorithm algorithm, + const char *directory_path, + char **output_string); diff --git a/include/cutils/event_tag_map.h b/include/cutils/event_tag_map.h new file mode 100644 index 0000000..1653c61 --- /dev/null +++ b/include/cutils/event_tag_map.h @@ -0,0 +1,50 @@ +/* + * 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 _LIBS_CUTILS_EVENTTAGMAP_H +#define _LIBS_CUTILS_EVENTTAGMAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags" + +struct EventTagMap; +typedef struct EventTagMap EventTagMap; + +/* + * Open the specified file as an event log tag map. + * + * Returns NULL on failure. + */ +EventTagMap* android_openEventTagMap(const char* fileName); + +/* + * Close the map. + */ +void android_closeEventTagMap(EventTagMap* map); + +/* + * Look up a tag by index. Returns the tag string, or NULL if not found. + */ +const char* android_lookupEventTag(const EventTagMap* map, int tag); + +#ifdef __cplusplus +} +#endif + +#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/ diff --git a/include/cutils/fdevent.h b/include/cutils/fdevent.h new file mode 100644 index 0000000..7a442d4 --- /dev/null +++ b/include/cutils/fdevent.h @@ -0,0 +1,74 @@ +/* + * 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 __FDEVENT_H +#define __FDEVENT_H + +/* events that may be observed */ +#define FDE_READ 0x0001 +#define FDE_WRITE 0x0002 +#define FDE_ERROR 0x0004 + +/* features that may be set (via the events set/add/del interface) */ +#define FDE_DONT_CLOSE 0x0080 + +typedef struct fdevent fdevent; + +typedef void (*fd_func)(int fd, unsigned events, void *userdata); + +/* Allocate and initialize a new fdevent object +*/ +fdevent *fdevent_create(int fd, fd_func func, void *arg); + +/* Uninitialize and deallocate an fdevent object that was +** created by fdevent_create() +*/ +void fdevent_destroy(fdevent *fde); + +/* Initialize an fdevent object that was externally allocated +*/ +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg); + +/* Uninitialize an fdevent object that was initialized by +** fdevent_install() +*/ +void fdevent_remove(fdevent *item); + +/* Change which events should cause notifications +*/ +void fdevent_set(fdevent *fde, unsigned events); +void fdevent_add(fdevent *fde, unsigned events); +void fdevent_del(fdevent *fde, unsigned events); + +/* loop forever, handling events. +*/ +void fdevent_loop(); + +struct fdevent +{ + fdevent *next; + fdevent *prev; + + int fd; + unsigned short state; + unsigned short events; + + fd_func func; + void *arg; +}; + + +#endif diff --git a/include/cutils/hashmap.h b/include/cutils/hashmap.h new file mode 100644 index 0000000..5cb344c --- /dev/null +++ b/include/cutils/hashmap.h @@ -0,0 +1,150 @@ +/* + * 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. + */ + +/** + * Hash map. + */ + +#ifndef __HASHMAP_H +#define __HASHMAP_H + +#include <stdbool.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** A hash map. */ +typedef struct Hashmap Hashmap; + +/** + * Creates a new hash map. Returns NULL if memory allocation fails. + * + * @param initialCapacity number of expected entries + * @param hash function which hashes keys + * @param equals function which compares keys for equality + */ +Hashmap* hashmapCreate(size_t initialCapacity, + int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)); + +/** + * Frees the hash map. Does not free the keys or values themselves. + */ +void hashmapFree(Hashmap* map); + +/** + * Hashes the memory pointed to by key with the given size. Useful for + * implementing hash functions. + */ +int hashmapHash(void* key, size_t keySize); + +/** + * Puts value for the given key in the map. Returns pre-existing value if + * any. + * + * If memory allocation fails, this function returns NULL, the map's size + * does not increase, and errno is set to ENOMEM. + */ +void* hashmapPut(Hashmap* map, void* key, void* value); + +/** + * Gets a value from the map. Returns NULL if no entry for the given key is + * found or if the value itself is NULL. + */ +void* hashmapGet(Hashmap* map, void* key); + +/** + * Returns true if the map contains an entry for the given key. + */ +bool hashmapContainsKey(Hashmap* map, void* key); + +/** + * Gets the value for a key. If a value is not found, this function gets a + * value and creates an entry using the given callback. + * + * If memory allocation fails, the callback is not called, this function + * returns NULL, and errno is set to ENOMEM. + */ +void* hashmapMemoize(Hashmap* map, void* key, + void* (*initialValue)(void* key, void* context), void* context); + +/** + * Removes an entry from the map. Returns the removed value or NULL if no + * entry was present. + */ +void* hashmapRemove(Hashmap* map, void* key); + +/** + * Gets the number of entries in this map. + */ +size_t hashmapSize(Hashmap* map); + +/** + * Invokes the given callback on each entry in the map. Stops iterating if + * the callback returns false. + */ +void hashmapForEach(Hashmap* map, + bool (*callback)(void* key, void* value, void* context), + void* context); + +/** + * Concurrency support. + */ + +/** + * Locks the hash map so only the current thread can access it. + */ +void hashmapLock(Hashmap* map); + +/** + * Unlocks the hash map so other threads can access it. + */ +void hashmapUnlock(Hashmap* map); + +/** + * Key utilities. + */ + +/** + * Hashes int keys. 'key' is a pointer to int. + */ +int hashmapIntHash(void* key); + +/** + * Compares two int keys for equality. + */ +bool hashmapIntEquals(void* keyA, void* keyB); + +/** + * For debugging. + */ + +/** + * Gets current capacity. + */ +size_t hashmapCurrentCapacity(Hashmap* map); + +/** + * Counts the number of entry collisions. + */ +size_t hashmapCountCollisions(Hashmap* map); + +#ifdef __cplusplus +} +#endif + +#endif /* __HASHMAP_H */ diff --git a/include/cutils/jstring.h b/include/cutils/jstring.h new file mode 100644 index 0000000..ee0018f --- /dev/null +++ b/include/cutils/jstring.h @@ -0,0 +1,43 @@ +/* + * 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 __CUTILS_STRING16_H +#define __CUTILS_STRING16_H + +#include <stdint.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t char16_t; + +extern char * strndup16to8 (const char16_t* s, size_t n); +extern size_t strnlen16to8 (const char16_t* s, size_t n); +extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n); + +extern char16_t * strdup8to16 (const char* s, size_t *out_len); +extern size_t strlen8to16 (const char* utf8Str); +extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len); +extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length, + size_t *out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_STRING16_H */ diff --git a/include/cutils/log.h b/include/cutils/log.h new file mode 100644 index 0000000..ec3cac8 --- /dev/null +++ b/include/cutils/log.h @@ -0,0 +1,346 @@ +/* + * 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// +#ifndef _LIBS_CUTILS_LOG_H +#define _LIBS_CUTILS_LOG_H + +#include <stdio.h> +#include <time.h> +#include <sys/types.h> +#include <unistd.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <stdarg.h> + +#include <cutils/uio.h> +#include <cutils/logd.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------- + +/* + * Normally we strip LOGV (VERBOSE messages) from release builds. + * You can modify this (for example with "#define LOG_NDEBUG 0" + * at the top of your source file) to change that behavior. + */ +#ifndef LOG_NDEBUG +#ifdef NDEBUG +#define LOG_NDEBUG 1 +#else +#define LOG_NDEBUG 0 +#endif +#endif + +/* + * This is the local tag used for the following simplified + * logging macros. You can change this preprocessor definition + * before using the other macros to change the tag. + */ +#ifndef LOG_TAG +#define LOG_TAG NULL +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose log message using the current LOG_TAG. + */ +#ifndef LOGV +#if LOG_NDEBUG +#define LOGV(...) ((void)0) +#else +#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef LOGV_IF +#if LOG_NDEBUG +#define LOGV_IF(cond, ...) ((void)0) +#else +#define LOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug log message using the current LOG_TAG. + */ +#ifndef LOGD +#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef LOGD_IF +#define LOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info log message using the current LOG_TAG. + */ +#ifndef LOGI +#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef LOGI_IF +#define LOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning log message using the current LOG_TAG. + */ +#ifndef LOGW +#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef LOGW_IF +#define LOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error log message using the current LOG_TAG. + */ +#ifndef LOGE +#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef LOGE_IF +#define LOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * verbose priority. + */ +#ifndef IF_LOGV +#if LOG_NDEBUG +#define IF_LOGV() if (false) +#else +#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG) +#endif +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * debug priority. + */ +#ifndef IF_LOGD +#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * info priority. + */ +#ifndef IF_LOGI +#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * warn priority. + */ +#ifndef IF_LOGW +#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * error priority. + */ +#ifndef IF_LOGE +#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG) +#endif + +// --------------------------------------------------------------------- + +/* + * Log a fatal error. If the given condition fails, this stops program + * execution like a normal assertion, but also generating the given message. + * It is NOT stripped from release builds. Note that the condition test + * is -inverted- from the normal assert() semantics. + */ +#define LOG_ALWAYS_FATAL_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) + +#define LOG_ALWAYS_FATAL(...) \ + ( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) ) + +/* + * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that + * are stripped out of release builds. + */ +#if LOG_NDEBUG + +#define LOG_FATAL_IF(cond, ...) ((void)0) +#define LOG_FATAL(...) ((void)0) + +#else + +#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__) +#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) + +#endif + +/* + * Assertion that generates a log message when the assertion fails. + * Stripped out of release builds. Uses the current LOG_TAG. + */ +#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__) +//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) + +// --------------------------------------------------------------------- + +/* + * Basic log message macro. + * + * Example: + * LOG(LOG_WARN, NULL, "Failed with error %d", errno); + * + * The second argument may be NULL or "" to indicate the "global" tag. + */ +#ifndef LOG +#define LOG(priority, tag, ...) \ + LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to specify a number for the priority. + */ +#ifndef LOG_PRI +#define LOG_PRI(priority, tag, ...) \ + android_printLog(priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to pass in a varargs ("args" is a va_list). + */ +#ifndef LOG_PRI_VA +#define LOG_PRI_VA(priority, tag, fmt, args) \ + android_vprintLog(priority, NULL, tag, fmt, args) +#endif + +/* + * Conditional given a desired logging priority and tag. + */ +#ifndef IF_LOG +#define IF_LOG(priority, tag) \ + if (android_testLog(ANDROID_##priority, tag)) +#endif + +// --------------------------------------------------------------------- + +/* + * Event logging. + */ + +/* + * Event log entry types. These must match up with the declarations in + * java/android/android/util/EventLog.java. + */ +typedef enum { + EVENT_TYPE_INT = 0, + EVENT_TYPE_LONG = 1, + EVENT_TYPE_STRING = 2, + EVENT_TYPE_LIST = 3, +} AndroidEventLogType; + + +#define LOG_EVENT_INT(_tag, _value) { \ + int intBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ + sizeof(intBuf)); \ + } +#define LOG_EVENT_LONG(_tag, _value) { \ + long long longBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ + sizeof(longBuf)); \ + } +#define LOG_EVENT_STRING(_tag, _value) \ + ((void) 0) /* not implemented -- must combine len with string */ +/* TODO: something for LIST */ + +/* + * =========================================================================== + * + * The stuff in the rest of this file should not be used directly. + */ + +#define android_printLog(prio, tag, fmt...) \ + __android_log_print(prio, tag, fmt) + +#define android_vprintLog(prio, cond, tag, fmt...) \ + __android_log_vprint(prio, tag, fmt) + +#define android_printAssert(cond, tag, fmt...) \ + __android_log_assert(cond, tag, fmt) + +#define android_writeLog(prio, tag, text) \ + __android_log_write(prio, tag, text) + +#define android_bWriteLog(tag, payload, len) \ + __android_log_bwrite(tag, payload, len) +#define android_btWriteLog(tag, type, payload, len) \ + __android_log_btwrite(tag, type, payload, len) + +// TODO: remove these prototypes and their users +#define android_testLog(prio, tag) (1) +#define android_writevLog(vec,num) do{}while(0) +#define android_write1Log(str,len) do{}while (0) +#define android_setMinPriority(tag, prio) do{}while(0) +//#define android_logToCallback(func) do{}while(0) +#define android_logToFile(tag, file) (0) +#define android_logToFd(tag, fd) (0) + + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_CUTILS_LOG_H diff --git a/include/cutils/logd.h b/include/cutils/logd.h new file mode 100644 index 0000000..a1cb012 --- /dev/null +++ b/include/cutils/logd.h @@ -0,0 +1,78 @@ +/* + * 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 _ANDROID_CUTILS_LOGD_H +#define _ANDROID_CUTILS_LOGD_H + +#include <time.h> +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/types.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <cutils/uio.h> +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Priority values, in ascending priority order. + */ +typedef enum android_LogPriority { + ANDROID_LOG_UNKNOWN = 0, + ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ + ANDROID_LOG_VERBOSE, + ANDROID_LOG_DEBUG, + ANDROID_LOG_INFO, + ANDROID_LOG_WARN, + ANDROID_LOG_ERROR, + ANDROID_LOG_FATAL, + ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ +} android_LogPriority; + +int __android_log_write(int prio, const char *tag, const char *text); + +int __android_log_vprint(int prio, const char *tag, + const char *fmt, va_list ap); + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len); +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len); + +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +#if defined(__GNUC__) + __attribute__ ((format(printf, 3, 4))) +#endif + ; + + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +#if defined(__GNUC__) + __attribute__ ((noreturn)) + __attribute__ ((format(printf, 3, 4))) +#endif + ; + +#ifdef __cplusplus +} +#endif + +#endif /* _LOGD_H */ diff --git a/include/cutils/logger.h b/include/cutils/logger.h new file mode 100644 index 0000000..3a08019 --- /dev/null +++ b/include/cutils/logger.h @@ -0,0 +1,46 @@ +/* utils/logger.h +** +** Copyright 2007, The Android Open Source Project +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#ifndef _UTILS_LOGGER_H +#define _UTILS_LOGGER_H + +#include <stdint.h> + +struct logger_entry { + uint16_t len; /* length of the payload */ + uint16_t __pad; /* no matter what, we get 2 bytes of padding */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + char msg[0]; /* the entry's payload */ +}; + +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" + +#define LOGGER_ENTRY_MAX_LEN (4*1024) +#define LOGGER_ENTRY_MAX_PAYLOAD \ + (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) + +#ifdef HAVE_IOCTL + +#include <sys/ioctl.h> + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ + +#endif // HAVE_IOCTL + +#endif /* _UTILS_LOGGER_H */ diff --git a/include/cutils/logprint.h b/include/cutils/logprint.h new file mode 100644 index 0000000..d6ec480 --- /dev/null +++ b/include/cutils/logprint.h @@ -0,0 +1,156 @@ +/* + * 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 _LOGPRINT_H +#define _LOGPRINT_H + +#include <cutils/log.h> +#include <cutils/logger.h> +#include <cutils/event_tag_map.h> +#include <pthread.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG, +} AndroidLogPrintFormat; + +typedef struct AndroidLogFormat_t AndroidLogFormat; + +typedef struct AndroidLogEntry_t { + time_t tv_sec; + long tv_nsec; + android_LogPriority priority; + pid_t pid; + pthread_t tid; + const char * tag; + size_t messageLen; + const char * message; +} AndroidLogEntry; + +AndroidLogFormat *android_log_format_new(); + +void android_log_format_free(AndroidLogFormat *p_format); + +void android_log_setPrintFormat(AndroidLogFormat *p_format, + AndroidLogPrintFormat format); + +/** + * Returns FORMAT_OFF on invalid string + */ +AndroidLogPrintFormat android_log_formatFromString(const char *s); + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterRule(AndroidLogFormat *p_format, + const char *filterExpression); + + +/** + * filterString: a whitespace-separated set of filter expressions + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterString(AndroidLogFormat *p_format, + const char *filterString); + + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int android_log_shouldPrintLine ( + AndroidLogFormat *p_format, const char *tag, android_LogPriority pri); + + +/** + * Splits a wire-format buffer into an AndroidLogEntry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int android_log_processLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry); + +/** + * Like android_log_processLogBuffer, but for binary logs. + * + * If "map" is non-NULL, it will be used to convert the log tag number + * into a string. + */ +int android_log_processBinaryLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, + int messageBufLen); + + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ + +char *android_log_formatLogLine ( + AndroidLogFormat *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const AndroidLogEntry *p_line, + size_t *p_outLength); + + +/** + * Either print or do not print log line, based on filter + * + * Assumes single threaded execution + * + */ +int android_log_filterAndPrintLogLine( + AndroidLogFormat *p_format, + int fd, + const AndroidLogEntry *entry); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_LOGPRINT_H*/ diff --git a/include/cutils/memory.h b/include/cutils/memory.h new file mode 100644 index 0000000..e725cdd --- /dev/null +++ b/include/cutils/memory.h @@ -0,0 +1,42 @@ +/* + * 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 ANDROID_CUTILS_MEMORY_H +#define ANDROID_CUTILS_MEMORY_H + +#include <stdint.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* size is given in bytes and must be multiple of 2 */ +void android_memset16(uint16_t* dst, uint16_t value, size_t size); + +/* size is given in bytes and must be multiple of 4 */ +void android_memset32(uint32_t* dst, uint32_t value, size_t size); + +#if !HAVE_STRLCPY +/* Declaration of strlcpy() for platforms that don't already have it. */ +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_CUTILS_MEMORY_H diff --git a/include/cutils/misc.h b/include/cutils/misc.h new file mode 100644 index 0000000..2c48dfa --- /dev/null +++ b/include/cutils/misc.h @@ -0,0 +1,48 @@ +/* + * 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 __CUTILS_MISC_H +#define __CUTILS_MISC_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* Load an entire file into a malloc'd chunk of memory + * that is length_of_file + 1 (null terminator). If + * sz is non-zero, return the size of the file via sz. + * Returns 0 on failure. + */ +extern void *load_file(const char *fn, unsigned *sz); + + /* Connects your process to the system debugger daemon + * so that on a crash it may be logged or interactively + * debugged (depending on system settings). + */ +extern void debuggerd_connect(void); + + + /* This is the range of UIDs (and GIDs) that are reserved + * for assigning to applications. + */ +#define FIRST_APPLICATION_UID 10000 +#define LAST_APPLICATION_UID 99999 + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_MISC_H */ diff --git a/include/cutils/mq.h b/include/cutils/mq.h new file mode 100644 index 0000000..b27456d --- /dev/null +++ b/include/cutils/mq.h @@ -0,0 +1,124 @@ +/* + * 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. + */ + +/** + * IPC messaging library. + */ + +#ifndef __MQ_H +#define __MQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** A message. */ +typedef struct MqMessage MqMessage; + +/** A destination to which messages can be sent. */ +typedef struct MqDestination MqDestination; + +/* Array of bytes. */ +typedef struct MqBytes MqBytes; + +/** + * Hears messages. + * + * @param destination to which the message was sent + * @param message the message to hear + */ +typedef void MqMessageListener(MqDestination* destination, MqMessage* message); + +/** + * Hears a destination close. + * + * @param destination that closed + */ +typedef void MqCloseListener(MqDestination* destination); + +/** Message functions. */ + +/** + * Creates a new Message. + * + * @param header as defined by user + * @param body as defined by user + * @param replyTo destination to which replies should be sent, NULL if none + */ +MqMessage* mqCreateMessage(MqBytes header, MqBytes body, + MqDestination* replyTo); + +/** Sends a message to a destination. */ +void mqSendMessage(MqMessage* message, MqDestination* destination); + +/** Destination functions. */ + +/** + * Creates a new destination. Acquires a reference implicitly. + * + * @param messageListener function to call when a message is recieved + * @param closeListener function to call when the destination closes + * @param userData user-specific data to associate with the destination. + * Retrieve using mqGetDestinationUserData(). + */ +MqDestination* mqCreateDestination(MqMessageListener* messageListener, + MqCloseListener* closeListener, void* userData); + +/** + * Gets user data which was associated with the given destination at + * construction time. + * + * It is only valid to call this function in the same process that the + * given destination was created in. + * This function returns a null pointer if you call it on a destination + * created in a remote process. + */ +void* mqGetUserData(MqDestination* destination); + +/** + * Returns 1 if the destination was created in this process, or 0 if + * the destination was created in a different process, in which case you have + * a remote stub. + */ +int mqIsDestinationLocal(MqDestination* destination); + +/** + * Increments the destination's reference count. + */ +void mqKeepDestination(MqDesintation* destination); + +/** + * Decrements the destination's reference count. + */ +void mqFreeDestination(MqDestination* desintation); + +/** Registry API. */ + +/** + * Gets the destination bound to a name. + */ +MqDestination* mqGetDestination(char* name); + +/** + * Binds a destination to a name. + */ +void mqPutDestination(char* name, MqDestination* desintation); + +#ifdef __cplusplus +} +#endif + +#endif /* __MQ_H */ diff --git a/include/cutils/mspace.h b/include/cutils/mspace.h new file mode 100644 index 0000000..33410c1 --- /dev/null +++ b/include/cutils/mspace.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* A wrapper file for dlmalloc.h that defines prototypes for the + * mspace_*() functions, which provide an interface for creating + * multiple heaps. + */ + +#ifndef MSPACE_H_ +#define MSPACE_H_ + +/* It's a pain getting the mallinfo stuff to work + * with Linux, OSX, and klibc, so just turn it off + * for now. + * TODO: make mallinfo work + */ +#define NO_MALLINFO 1 + +/* Allow setting the maximum heap footprint. + */ +#define USE_MAX_ALLOWED_FOOTPRINT 1 + +#define USE_CONTIGUOUS_MSPACES 1 +#if USE_CONTIGUOUS_MSPACES +#define HAVE_MMAP 0 +#define HAVE_MORECORE 1 +#define MORECORE_CONTIGUOUS 0 +#endif + +#define MSPACES 1 +#define ONLY_MSPACES 1 +#include "../../../../bionic/libc/bionic/dlmalloc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + mspace_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. mspace_usable_size can be more useful in + debugging and assertions, for example: + + p = mspace_malloc(msp, n); + assert(mspace_usable_size(msp, p) >= 256); +*/ +size_t mspace_usable_size(mspace, const void*); + +#if USE_CONTIGUOUS_MSPACES +/* + Similar to create_mspace(), but the underlying memory is + guaranteed to be contiguous. No more than max_capacity + bytes is ever allocated to the mspace. + */ +mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity, + int locked); + +/* + Identical to create_contiguous_mspace, but labels the mapping 'mspace/name' + instead of 'mspace' +*/ +mspace create_contiguous_mspace_with_name(size_t starting_capacity, + size_t max_capacity, int locked, const char *name); + +size_t destroy_contiguous_mspace(mspace msp); +#endif + +/* + Call the handler for each block in the specified mspace. + chunkptr and chunklen refer to the heap-level chunk including + the chunk overhead, and userptr and userlen refer to the + user-usable part of the chunk. If the chunk is free, userptr + will be NULL and userlen will be 0. userlen is not guaranteed + to be the same value passed into malloc() for a given chunk; + it is >= the requested size. + */ +void mspace_walk_heap(mspace msp, + void(*handler)(const void *chunkptr, size_t chunklen, + const void *userptr, size_t userlen, void *arg), void *harg); + +/* + mspace_walk_free_pages(handler, harg) + + Calls the provided handler on each free region in the specified + mspace. The memory between start and end are guaranteed not to + contain any important data, so the handler is free to alter the + contents in any way. This can be used to advise the OS that large + free regions may be swapped out. + + The value in harg will be passed to each call of the handler. + */ +void mspace_walk_free_pages(mspace msp, + void(*handler)(void *start, void *end, void *arg), void *harg); + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +#endif /* MSPACE_H_ */ diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h new file mode 100644 index 0000000..2b64893 --- /dev/null +++ b/include/cutils/native_handle.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVE_HANDLE_H_ +#define NATIVE_HANDLE_H_ + +typedef struct +{ + int version; /* sizeof(native_handle) */ + int numFds; /* number of file-descriptors at &data[0] */ + int numInts; /* number of ints at &data[numFds] */ + int data[0]; /* numFds + numInts ints */ +} native_handle; + +#endif /* NATIVE_HANDLE_H_ */ diff --git a/include/cutils/process_name.h b/include/cutils/process_name.h new file mode 100644 index 0000000..1e72e5c --- /dev/null +++ b/include/cutils/process_name.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +/** + * Gives the current process a name. + */ + +#ifndef __PROCESS_NAME_H +#define __PROCESS_NAME_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Sets the current process name. + * + * Warning: This leaks a string every time you call it. Use judiciously! + */ +void set_process_name(const char* process_name); + +/** Gets the current process name. */ +const char* get_process_name(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __PROCESS_NAME_H */ diff --git a/include/cutils/properties.h b/include/cutils/properties.h new file mode 100644 index 0000000..25fd67a --- /dev/null +++ b/include/cutils/properties.h @@ -0,0 +1,70 @@ +/* + * 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 __CUTILS_PROPERTIES_H +#define __CUTILS_PROPERTIES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* System properties are *small* name value pairs managed by the +** property service. If your data doesn't fit in the provided +** space it is not appropriate for a system property. +** +** WARNING: system/bionic/include/sys/system_properties.h also defines +** these, but with different names. (TODO: fix that) +*/ +#define PROPERTY_KEY_MAX 32 +#define PROPERTY_VALUE_MAX 92 + +/* property_get: returns the length of the value which will never be +** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated. +** (the length does not include the terminating zero). +** +** If the property read fails or returns an empty value, the default +** value is used (if nonnull). +*/ +int property_get(const char *key, char *value, const char *default_value); + +/* property_set: returns 0 on success, < 0 on failure +*/ +int property_set(const char *key, const char *value); + +int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie); + + +#ifdef HAVE_SYSTEM_PROPERTY_SERVER +/* + * We have an external property server instead of built-in libc support. + * Used by the simulator. + */ +#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop" + +enum { + kSystemPropertyUnknown = 0, + kSystemPropertyGet, + kSystemPropertySet, + kSystemPropertyList +}; +#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h new file mode 100644 index 0000000..bfac87a --- /dev/null +++ b/include/cutils/record_stream.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * A simple utility for reading fixed records out of a stream fd + */ + +#ifndef _CUTILS_RECORD_STREAM_H +#define _CUTILS_RECORD_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct RecordStream RecordStream; + +extern RecordStream *record_stream_new(int fd, size_t maxRecordLen); +extern void record_stream_free(RecordStream *p_rs); + +extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, + size_t *p_outRecordLen); + +#ifdef __cplusplus +} +#endif + + +#endif /*_CUTILS_RECORD_STREAM_H*/ + diff --git a/include/cutils/selector.h b/include/cutils/selector.h new file mode 100644 index 0000000..dfc2a9d --- /dev/null +++ b/include/cutils/selector.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +/** + * Framework for multiplexing I/O. A selector manages a set of file + * descriptors and calls out to user-provided callback functions to read and + * write data and handle errors. + */ + +#ifndef __SELECTOR_H +#define __SELECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> + +/** + * Manages SelectableFds and invokes their callbacks at appropriate times. + */ +typedef struct Selector Selector; + +/** + * A selectable descriptor. Contains callbacks which the selector can invoke + * before calling select(), when the descriptor is readable or writable, and + * when the descriptor contains out-of-band data. Simply set a callback to + * NULL if you're not interested in that particular event. + * + * A selectable descriptor can indicate that it needs to be removed from the + * selector by setting the 'remove' flag. The selector will remove the + * descriptor at a later time and invoke the onRemove() callback. + * + * SelectableFd fields should only be modified from the selector loop. + */ +typedef struct SelectableFd SelectableFd; +struct SelectableFd { + + /** The file descriptor itself. */ + int fd; + + /** Pointer to user-specific data. Can be NULL. */ + void* data; + + /** + * Set this flag when you no longer wish to be selected. The selector + * will invoke onRemove() when the descriptor is actually removed. + */ + bool remove; + + /** + * Invoked by the selector before calling select. You can set up other + * callbacks from here as necessary. + */ + void (*beforeSelect)(SelectableFd* self); + + /** + * Invoked by the selector when the descriptor has data available. Set to + * NULL to indicate that you're not interested in reading. + */ + void (*onReadable)(SelectableFd* self); + + /** + * Invoked by the selector when the descriptor can accept data. Set to + * NULL to indicate that you're not interested in writing. + */ + void (*onWritable)(SelectableFd* self); + + /** + * Invoked by the selector when out-of-band (OOB) data is available. Set to + * NULL to indicate that you're not interested in OOB data. + */ + void (*onExcept)(SelectableFd* self); + + /** + * Invoked by the selector after the descriptor is removed from the + * selector but before the selector frees the SelectableFd memory. + */ + void (*onRemove)(SelectableFd* self); + + /** + * The selector which selected this fd. Set by the selector itself. + */ + Selector* selector; +}; + +/** + * Creates a new selector. + */ +Selector* selectorCreate(void); + +/** + * Creates a new selectable fd, adds it to the given selector and returns a + * pointer. Outside of 'selector' and 'fd', all fields are set to 0 or NULL + * by default. + * + * The selectable fd should only be modified from the selector loop thread. + */ +SelectableFd* selectorAdd(Selector* selector, int fd); + +/** + * Wakes up the selector even though no I/O events occurred. Use this + * to indicate that you're ready to write to a descriptor. + */ +void selectorWakeUp(Selector* selector); + +/** + * Loops continuously selecting file descriptors and firing events. + * Does not return. + */ +void selectorLoop(Selector* selector); + +#ifdef __cplusplus +} +#endif + +#endif /* __SELECTOR_H */ diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h new file mode 100644 index 0000000..aa8682e --- /dev/null +++ b/include/cutils/sockets.h @@ -0,0 +1,100 @@ +/* + * 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 __CUTILS_SOCKETS_H +#define __CUTILS_SOCKETS_H + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_WINSOCK +#include <winsock2.h> +typedef int socklen_t; +#elif HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_" +#define ANDROID_SOCKET_DIR "/dev/socket" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * android_get_control_socket - simple helper function to get the file + * descriptor of our init-managed Unix domain socket. `name' is the name of the + * socket, as given in init.rc. Returns -1 on error. + * + * This is inline and not in libcutils proper because we want to use this in + * third-party daemons with minimal modification. + */ +static inline int android_get_control_socket(const char *name) +{ + char key[64] = ANDROID_SOCKET_ENV_PREFIX; + const char *val; + int fd; + + /* build our environment variable, counting cycles like a wolf ... */ +#if HAVE_STRLCPY + strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, + name, + sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); +#else /* for the host, which may lack the almightly strncpy ... */ + strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, + name, + sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); + key[sizeof(key)-1] = '\0'; +#endif + + val = getenv(key); + if (!val) + return -1; + + errno = 0; + fd = strtol(val, NULL, 10); + if (errno) + return -1; + + return fd; +} + +/* + * See also android.os.LocalSocketAddress.Namespace + */ +// Linux "abstract" (non-filesystem) namespace +#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0 +// Android "reserved" (/dev/socket) namespace +#define ANDROID_SOCKET_NAMESPACE_RESERVED 1 +// Normal filesystem namespace +#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2 + +extern int socket_loopback_client(int port, int type); +extern int socket_network_client(const char *host, int port, int type); +extern int socket_loopback_server(int port, int type); +extern int socket_local_server(const char *name, int namespaceId, int type); +extern int socket_local_server_bind(int s, const char *name, int namespaceId); +extern int socket_local_client_connect(int fd, + const char *name, int namespaceId, int type); +extern int socket_local_client(const char *name, int namespaceId, int type); +extern int socket_inaddr_any_server(int port, int type); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_SOCKETS_H */ diff --git a/include/cutils/threads.h b/include/cutils/threads.h new file mode 100644 index 0000000..acf8f48 --- /dev/null +++ b/include/cutils/threads.h @@ -0,0 +1,146 @@ +/* + * 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 _LIBS_CUTILS_THREADS_H +#define _LIBS_CUTILS_THREADS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/***********************************************************************/ +/***********************************************************************/ +/***** *****/ +/***** local thread storage *****/ +/***** *****/ +/***********************************************************************/ +/***********************************************************************/ + +#ifdef HAVE_PTHREADS + +#include <pthread.h> + +typedef struct { + pthread_mutex_t lock; + int has_tls; + pthread_key_t tls; + +} thread_store_t; + +#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 } + +#elif defined HAVE_WIN32_THREADS + +#include <windows.h> + +typedef struct { + int lock_init; + int has_tls; + DWORD tls; + CRITICAL_SECTION lock; + +} thread_store_t; + +#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} } + +#else +# error "no thread_store_t implementation for your platform !!" +#endif + +typedef void (*thread_store_destruct_t)(void* value); + +extern void* thread_store_get(thread_store_t* store); + +extern void thread_store_set(thread_store_t* store, + void* value, + thread_store_destruct_t destroy); + +/***********************************************************************/ +/***********************************************************************/ +/***** *****/ +/***** mutexes *****/ +/***** *****/ +/***********************************************************************/ +/***********************************************************************/ + +#ifdef HAVE_PTHREADS + +typedef pthread_mutex_t mutex_t; + +#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +static __inline__ void mutex_lock(mutex_t* lock) +{ + pthread_mutex_lock(lock); +} +static __inline__ void mutex_unlock(mutex_t* lock) +{ + pthread_mutex_unlock(lock); +} +static __inline__ int mutex_init(mutex_t* lock) +{ + return pthread_mutex_init(lock, NULL); +} +static __inline__ void mutex_destroy(mutex_t* lock) +{ + pthread_mutex_destroy(lock); +} +#endif + +#ifdef HAVE_WIN32_THREADS +typedef struct { + int init; + CRITICAL_SECTION lock[1]; +} mutex_t; + +#define MUTEX_INITIALIZER { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} } + +static __inline__ void mutex_lock(mutex_t* lock) +{ + if (!lock->init) { + lock->init = 1; + InitializeCriticalSection( lock->lock ); + lock->init = 2; + } else while (lock->init != 2) + Sleep(10); + + EnterCriticalSection(lock->lock); +} + +static __inline__ void mutex_unlock(mutex_t* lock) +{ + LeaveCriticalSection(lock->lock); +} +static __inline__ int mutex_init(mutex_t* lock) +{ + InitializeCriticalSection(lock->lock); + lock->init = 2; + return 0; +} +static __inline__ void mutex_destroy(mutex_t* lock) +{ + if (lock->init) { + lock->init = 0; + DeleteCriticalSection(lock->lock); + } +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBS_CUTILS_THREADS_H */ diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h new file mode 100644 index 0000000..9b3ece8 --- /dev/null +++ b/include/cutils/tztime.h @@ -0,0 +1,47 @@ +/* + * 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 _CUTILS_TZTIME_H +#define _CUTILS_TZTIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +time_t mktime_tz(struct tm * const tmp, char const * tz); +void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz); + +struct strftime_locale { + const char *mon[12]; /* short names */ + const char *month[12]; /* long names */ + const char *wday[7]; /* short names */ + const char *weekday[7]; /* long names */ + const char *X_fmt; + const char *x_fmt; + const char *c_fmt; + const char *am; + const char *pm; + const char *date_fmt; +}; + +size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_TZTIME_H */ + diff --git a/include/cutils/uio.h b/include/cutils/uio.h new file mode 100644 index 0000000..01a74d2 --- /dev/null +++ b/include/cutils/uio.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +// +// implementation of sys/uio.h for platforms that don't have it (Win32) +// +#ifndef _LIBS_CUTILS_UIO_H +#define _LIBS_CUTILS_UIO_H + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#else + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> + +struct iovec { + const void* iov_base; + size_t iov_len; +}; + +extern int readv( int fd, struct iovec* vecs, int count ); +extern int writev( int fd, const struct iovec* vecs, int count ); + +#ifdef __cplusplus +} +#endif + +#endif /* !HAVE_SYS_UIO_H */ + +#endif /* _LIBS_UTILS_UIO_H */ + diff --git a/include/cutils/zygote.h b/include/cutils/zygote.h new file mode 100644 index 0000000..22721a6 --- /dev/null +++ b/include/cutils/zygote.h @@ -0,0 +1,32 @@ +/* + * 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 __CUTILS_ZYGOTE_H +#define __CUTILS_ZYGOTE_H + +#ifdef __cplusplus +extern "C" { +#endif + +int zygote_run_oneshot(int sendStdio, int argc, const char **argv); +int zygote_run(int argc, const char **argv); +int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int)); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_ZYGOTE_H */ diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h new file mode 100644 index 0000000..7d7d571 --- /dev/null +++ b/include/mincrypt/rsa.h @@ -0,0 +1,56 @@ +/* rsa.h +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _EMBEDDED_RSA_H_ +#define _EMBEDDED_RSA_H_ + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define RSANUMBYTES 256 /* 2048 bit key length */ +#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t)) + +typedef struct RSAPublicKey { + int len; /* Length of n[] in number of uint32_t */ + uint32_t n0inv; /* -1 / n[0] mod 2^32 */ + uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ + uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ +} RSAPublicKey; + +int RSA_verify(const RSAPublicKey *key, + const uint8_t* signature, + const int len, + const uint8_t* sha); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h new file mode 100644 index 0000000..c523460 --- /dev/null +++ b/include/mincrypt/sha.h @@ -0,0 +1,56 @@ +/* sha.h +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _EMBEDDED_SHA_H_ +#define _EMBEDDED_SHA_H_ + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SHA_CTX { + uint64_t count; + uint8_t buf[64]; + uint32_t state[5]; +} SHA_CTX; + +void SHA_init(SHA_CTX *ctx); +void SHA_update(SHA_CTX *ctx, const void* data, int len); +const uint8_t* SHA_final(SHA_CTX *ctx); + +/* Convenience method. Returns digest parameter value. */ +const uint8_t* SHA(const void *data, int len, uint8_t *digest); + +#define SHA_DIGEST_SIZE 20 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/pixelflinger/format.h b/include/pixelflinger/format.h new file mode 100644 index 0000000..6b2050c --- /dev/null +++ b/include/pixelflinger/format.h @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#ifndef ANDROID_PIXELFLINGER_FORMAT_H +#define ANDROID_PIXELFLINGER_FORMAT_H + +#include <stdint.h> +#include <sys/types.h> + +enum GGLPixelFormat { + // these constants need to match those + // in graphics/PixelFormat.java, ui/PixelFormat.h, BlitHardware.h + GGL_PIXEL_FORMAT_UNKNOWN = 0, + GGL_PIXEL_FORMAT_NONE = 0, + + GGL_PIXEL_FORMAT_RGBA_8888 = 1, // 4x8-bit ARGB + GGL_PIXEL_FORMAT_RGBX_8888 = 2, // 3x8-bit RGB stored in 32-bit chunks + GGL_PIXEL_FORMAT_RGB_888 = 3, // 3x8-bit RGB + GGL_PIXEL_FORMAT_RGB_565 = 4, // 16-bit RGB + GGL_PIXEL_FORMAT_BGRA_8888 = 5, // 4x8-bit BGRA + GGL_PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit RGBA + GGL_PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit RGBA + + GGL_PIXEL_FORMAT_A_8 = 8, // 8-bit A + GGL_PIXEL_FORMAT_L_8 = 9, // 8-bit L (R=G=B = L) + GGL_PIXEL_FORMAT_LA_88 = 0xA, // 16-bit LA + GGL_PIXEL_FORMAT_RGB_332 = 0xB, // 8-bit RGB (non paletted) + + // YCbCr formats (SP=semi-planar, P=planar) + GGL_PIXEL_FORMAT_YCbCr_422_SP= 0x10, + GGL_PIXEL_FORMAT_YCbCr_420_SP= 0x11, + GGL_PIXEL_FORMAT_YCbCr_422_P = 0x12, + GGL_PIXEL_FORMAT_YCbCr_420_P = 0x13, + GGL_PIXEL_FORMAT_YCbCr_422_I = 0x14, + GGL_PIXEL_FORMAT_YCbCr_420_I = 0x15, + + // reserved/special formats + GGL_PIXEL_FORMAT_Z_16 = 0x18, + GGL_PIXEL_FORMAT_S_8 = 0x19, + GGL_PIXEL_FORMAT_SZ_24 = 0x1A, + GGL_PIXEL_FORMAT_SZ_8 = 0x1B, +}; + +enum GGLFormatComponents { + GGL_STENCIL_INDEX = 0x1901, + GGL_DEPTH_COMPONENT = 0x1902, + GGL_ALPHA = 0x1906, + GGL_RGB = 0x1907, + GGL_RGBA = 0x1908, + GGL_LUMINANCE = 0x1909, + GGL_LUMINANCE_ALPHA = 0x190A, + GGL_Y_CB_CR_SP = 0x8000, + GGL_Y_CB_CR = GGL_Y_CB_CR_SP, + GGL_Y_CB_CR_P = 0x8001, + GGL_Y_CB_CR_I = 0x8002, +}; + +enum GGLFormatComponentIndex { + GGL_INDEX_ALPHA = 0, + GGL_INDEX_RED = 1, + GGL_INDEX_GREEN = 2, + GGL_INDEX_BLUE = 3, + GGL_INDEX_STENCIL = 0, + GGL_INDEX_DEPTH = 1, + GGL_INDEX_Y = 0, + GGL_INDEX_CB = 1, + GGL_INDEX_CR = 2, +}; + +typedef struct { +#ifdef __cplusplus + enum { + ALPHA = GGL_INDEX_ALPHA, + RED = GGL_INDEX_RED, + GREEN = GGL_INDEX_GREEN, + BLUE = GGL_INDEX_BLUE, + STENCIL = GGL_INDEX_STENCIL, + DEPTH = GGL_INDEX_DEPTH, + LUMA = GGL_INDEX_Y, + CHROMAB = GGL_INDEX_CB, + CHROMAR = GGL_INDEX_CR, + }; + inline uint32_t mask(int i) const { + return ((1<<(c[i].h-c[i].l))-1)<<c[i].l; + } + inline uint32_t bits(int i) const { + return c[i].h - c[i].l; + } +#endif + uint8_t size; // bytes per pixel + uint8_t bitsPerPixel; + union { + struct { + uint8_t ah; // alpha high bit position + 1 + uint8_t al; // alpha low bit position + uint8_t rh; // red high bit position + 1 + uint8_t rl; // red low bit position + uint8_t gh; // green high bit position + 1 + uint8_t gl; // green low bit position + uint8_t bh; // blue high bit position + 1 + uint8_t bl; // blue low bit position + }; + struct { + uint8_t h; + uint8_t l; + } __attribute__((__packed__)) c[4]; + } __attribute__((__packed__)); + uint16_t components; // GGLFormatComponents +} GGLFormat; + + +#ifdef __cplusplus +extern "C" const GGLFormat* gglGetPixelFormatTable(size_t* numEntries = 0); +#else +const GGLFormat* gglGetPixelFormatTable(size_t* numEntries); +#endif + + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_FORMAT_H diff --git a/include/pixelflinger/pixelflinger.h b/include/pixelflinger/pixelflinger.h new file mode 100644 index 0000000..dca0b90 --- /dev/null +++ b/include/pixelflinger/pixelflinger.h @@ -0,0 +1,330 @@ +/* + * 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 ANDROID_PIXELFLINGER_H +#define ANDROID_PIXELFLINGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <pixelflinger/format.h> + +// GGL types + +typedef int8_t GGLbyte; // b +typedef int16_t GGLshort; // s +typedef int32_t GGLint; // i +typedef ssize_t GGLsizei; // i +typedef int32_t GGLfixed; // x +typedef int32_t GGLclampx; // x +typedef float GGLfloat; // f +typedef float GGLclampf; // f +typedef double GGLdouble; // d +typedef double GGLclampd; // d +typedef uint8_t GGLubyte; // ub +typedef uint8_t GGLboolean; // ub +typedef uint16_t GGLushort; // us +typedef uint32_t GGLuint; // ui +typedef unsigned int GGLenum; // ui +typedef unsigned int GGLbitfield; // ui +typedef void GGLvoid; +typedef int32_t GGLfixed32; +typedef int32_t GGLcolor; +typedef int32_t GGLcoord; + +// ---------------------------------------------------------------------------- + +#define GGL_MAX_VIEWPORT_DIMS 4096 +#define GGL_MAX_TEXTURE_SIZE 4096 +#define GGL_MAX_ALIASED_POINT_SIZE 0x7FFFFFF +#define GGL_MAX_SMOOTH_POINT_SIZE 2048 +#define GGL_MAX_SMOOTH_LINE_WIDTH 2048 + +// ---------------------------------------------------------------------------- + +// All these names are compatible with their OpenGL equivalents +// some of them are listed only for completeness +enum GGLNames { + GGL_FALSE = 0, + GGL_TRUE = 1, + + // enable/disable + GGL_SCISSOR_TEST = 0x0C11, + GGL_TEXTURE_2D = 0x0DE1, + GGL_ALPHA_TEST = 0x0BC0, + GGL_BLEND = 0x0BE2, + GGL_COLOR_LOGIC_OP = 0x0BF2, + GGL_DITHER = 0x0BD0, + GGL_STENCIL_TEST = 0x0B90, + GGL_DEPTH_TEST = 0x0B71, + GGL_AA = 0x80000001, + GGL_W_LERP = 0x80000004, + GGL_POINT_SMOOTH_NICE = 0x80000005, + + // buffers, pixel drawing/reading + GGL_COLOR = 0x1800, + + // fog + GGL_FOG = 0x0B60, + + // shade model + GGL_FLAT = 0x1D00, + GGL_SMOOTH = 0x1D01, + + // Texture parameter name + GGL_TEXTURE_MIN_FILTER = 0x2801, + GGL_TEXTURE_MAG_FILTER = 0x2800, + GGL_TEXTURE_WRAP_S = 0x2802, + GGL_TEXTURE_WRAP_T = 0x2803, + GGL_TEXTURE_WRAP_R = 0x2804, + + // Texture Filter + GGL_NEAREST = 0x2600, + GGL_LINEAR = 0x2601, + GGL_NEAREST_MIPMAP_NEAREST = 0x2700, + GGL_LINEAR_MIPMAP_NEAREST = 0x2701, + GGL_NEAREST_MIPMAP_LINEAR = 0x2702, + GGL_LINEAR_MIPMAP_LINEAR = 0x2703, + + // Texture Wrap Mode + GGL_CLAMP = 0x2900, + GGL_REPEAT = 0x2901, + GGL_CLAMP_TO_EDGE = 0x812F, + + // Texture Env Mode + GGL_REPLACE = 0x1E01, + GGL_MODULATE = 0x2100, + GGL_DECAL = 0x2101, + GGL_ADD = 0x0104, + + // Texture Env Parameter + GGL_TEXTURE_ENV_MODE = 0x2200, + GGL_TEXTURE_ENV_COLOR = 0x2201, + + // Texture Env Target + GGL_TEXTURE_ENV = 0x2300, + + // Texture coord generation + GGL_TEXTURE_GEN_MODE = 0x2500, + GGL_S = 0x2000, + GGL_T = 0x2001, + GGL_R = 0x2002, + GGL_Q = 0x2003, + GGL_ONE_TO_ONE = 0x80000002, + GGL_AUTOMATIC = 0x80000003, + + // AlphaFunction + GGL_NEVER = 0x0200, + GGL_LESS = 0x0201, + GGL_EQUAL = 0x0202, + GGL_LEQUAL = 0x0203, + GGL_GREATER = 0x0204, + GGL_NOTEQUAL = 0x0205, + GGL_GEQUAL = 0x0206, + GGL_ALWAYS = 0x0207, + + // LogicOp + GGL_CLEAR = 0x1500, // 0 + GGL_AND = 0x1501, // s & d + GGL_AND_REVERSE = 0x1502, // s & ~d + GGL_COPY = 0x1503, // s + GGL_AND_INVERTED = 0x1504, // ~s & d + GGL_NOOP = 0x1505, // d + GGL_XOR = 0x1506, // s ^ d + GGL_OR = 0x1507, // s | d + GGL_NOR = 0x1508, // ~(s | d) + GGL_EQUIV = 0x1509, // ~(s ^ d) + GGL_INVERT = 0x150A, // ~d + GGL_OR_REVERSE = 0x150B, // s | ~d + GGL_COPY_INVERTED = 0x150C, // ~s + GGL_OR_INVERTED = 0x150D, // ~s | d + GGL_NAND = 0x150E, // ~(s & d) + GGL_SET = 0x150F, // 1 + + // blending equation & function + GGL_ZERO = 0, // SD + GGL_ONE = 1, // SD + GGL_SRC_COLOR = 0x0300, // D + GGL_ONE_MINUS_SRC_COLOR = 0x0301, // D + GGL_SRC_ALPHA = 0x0302, // SD + GGL_ONE_MINUS_SRC_ALPHA = 0x0303, // SD + GGL_DST_ALPHA = 0x0304, // SD + GGL_ONE_MINUS_DST_ALPHA = 0x0305, // SD + GGL_DST_COLOR = 0x0306, // S + GGL_ONE_MINUS_DST_COLOR = 0x0307, // S + GGL_SRC_ALPHA_SATURATE = 0x0308, // S + + // clear bits + GGL_DEPTH_BUFFER_BIT = 0x00000100, + GGL_STENCIL_BUFFER_BIT = 0x00000400, + GGL_COLOR_BUFFER_BIT = 0x00004000, + + // errors + GGL_NO_ERROR = 0, + GGL_INVALID_ENUM = 0x0500, + GGL_INVALID_VALUE = 0x0501, + GGL_INVALID_OPERATION = 0x0502, + GGL_STACK_OVERFLOW = 0x0503, + GGL_STACK_UNDERFLOW = 0x0504, + GGL_OUT_OF_MEMORY = 0x0505 +}; + +// ---------------------------------------------------------------------------- + +typedef struct { + GGLsizei version; // always set to sizeof(GGLSurface) + GGLuint width; // width in pixels + GGLuint height; // height in pixels + GGLint stride; // stride in pixels + GGLubyte* data; // pointer to the bits + GGLubyte format; // pixel format + GGLubyte rfu[3]; // must be zero + // these values are dependent on the used format + union { + GGLint compressedFormat; + GGLint vstride; + }; + void* reserved; +} GGLSurface; + + +typedef struct { + // immediate rendering + void (*pointx)(void *con, const GGLcoord* v, GGLcoord r); + void (*linex)(void *con, + const GGLcoord* v0, const GGLcoord* v1, GGLcoord width); + void (*recti)(void* c, GGLint l, GGLint t, GGLint r, GGLint b); + void (*trianglex)(void* c, + GGLcoord const* v0, GGLcoord const* v1, GGLcoord const* v2); + + // scissor + void (*scissor)(void* c, GGLint x, GGLint y, GGLsizei width, GGLsizei height); + + // Set the textures and color buffers + void (*activeTexture)(void* c, GGLuint tmu); + void (*bindTexture)(void* c, const GGLSurface* surface); + void (*colorBuffer)(void* c, const GGLSurface* surface); + void (*readBuffer)(void* c, const GGLSurface* surface); + void (*depthBuffer)(void* c, const GGLSurface* surface); + void (*bindTextureLod)(void* c, GGLuint tmu, const GGLSurface* surface); + + // enable/disable features + void (*enable)(void* c, GGLenum name); + void (*disable)(void* c, GGLenum name); + void (*enableDisable)(void* c, GGLenum name, GGLboolean en); + + // specify the fragment's color + void (*shadeModel)(void* c, GGLenum mode); + void (*color4xv)(void* c, const GGLclampx* color); + // specify color iterators (16.16) + void (*colorGrad12xv)(void* c, const GGLcolor* grad); + + // specify Z coordinate iterators (0.32) + void (*zGrad3xv)(void* c, const GGLfixed32* grad); + + // specify W coordinate iterators (16.16) + void (*wGrad3xv)(void* c, const GGLfixed* grad); + + // specify fog iterator & color (16.16) + void (*fogGrad3xv)(void* c, const GGLfixed* grad); + void (*fogColor3xv)(void* c, const GGLclampx* color); + + // specify blending parameters + void (*blendFunc)(void* c, GGLenum src, GGLenum dst); + void (*blendFuncSeparate)(void* c, GGLenum src, GGLenum dst, + GGLenum srcAlpha, GGLenum dstAplha); + + // texture environnement (REPLACE / MODULATE / DECAL / BLEND) + void (*texEnvi)(void* c, GGLenum target, + GGLenum pname, + GGLint param); + + void (*texEnvxv)(void* c, GGLenum target, + GGLenum pname, const GGLfixed* params); + + // texture parameters (Wrapping, filter) + void (*texParameteri)(void* c, GGLenum target, + GGLenum pname, + GGLint param); + + // texture iterators (16.16) + void (*texCoord2i)(void* c, GGLint s, GGLint t); + void (*texCoord2x)(void* c, GGLfixed s, GGLfixed t); + + // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale + // This api uses block floating-point for S and T texture coordinates. + // All values are given in 16.16, scaled by 'scale'. In other words, + // set scale to 0, for 16.16 values. + void (*texCoordGradScale8xv)(void* c, GGLint tmu, const int32_t* grad8); + + void (*texGeni)(void* c, GGLenum coord, GGLenum pname, GGLint param); + + // masking + void (*colorMask)(void* c, GGLboolean red, + GGLboolean green, + GGLboolean blue, + GGLboolean alpha); + + void (*depthMask)(void* c, GGLboolean flag); + + void (*stencilMask)(void* c, GGLuint mask); + + // alpha func + void (*alphaFuncx)(void* c, GGLenum func, GGLclampx ref); + + // depth func + void (*depthFunc)(void* c, GGLenum func); + + // logic op + void (*logicOp)(void* c, GGLenum opcode); + + // clear + void (*clear)(void* c, GGLbitfield mask); + void (*clearColorx)(void* c, + GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a); + void (*clearDepthx)(void* c, GGLclampx depth); + void (*clearStencil)(void* c, GGLint s); + + // framebuffer operations + void (*copyPixels)(void* c, GGLint x, GGLint y, + GGLsizei width, GGLsizei height, GGLenum type); + void (*rasterPos2x)(void* c, GGLfixed x, GGLfixed y); + void (*rasterPos2i)(void* c, GGLint x, GGLint y); +} GGLContext; + +// ---------------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" { +#endif + +// construct / destroy the context +ssize_t gglInit(GGLContext** context); +ssize_t gglUninit(GGLContext* context); + +GGLint gglBitBlti( + GGLContext* c, + int tmu, + GGLint crop[4], + GGLint where[4]); + +#ifdef __cplusplus +}; +#endif + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_H diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h new file mode 100644 index 0000000..c855187 --- /dev/null +++ b/include/private/android_filesystem_config.h @@ -0,0 +1,211 @@ +/* + * 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 file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + +#ifndef _ANDROID_FILESYSTEM_CONFIG_H_ +#define _ANDROID_FILESYSTEM_CONFIG_H_ + +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +/* This is the master Users and Groups config for the platform. +** DO NOT EVER RENUMBER. +*/ + +#define AID_ROOT 0 /* traditional unix root user */ + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_RADIO 1001 /* telephony subsystem, RIL */ +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ +#define AID_GRAPHICS 1003 /* graphics devices */ +#define AID_INPUT 1004 /* input devices */ +#define AID_AUDIO 1005 /* audio devices */ +#define AID_CAMERA 1006 /* camera devices */ +#define AID_LOG 1007 /* log devices */ +#define AID_COMPASS 1008 /* compass device */ +#define AID_MOUNT 1009 /* mountd socket */ +#define AID_WIFI 1010 /* wifi subsystem */ +#define AID_ADB 1011 /* android debug bridge (adbd) */ +#define AID_INSTALL 1012 /* group for installing packages */ +#define AID_MEDIA 1013 /* mediaserver process */ +#define AID_DHCP 1014 /* dhcp client */ + +#define AID_SHELL 2000 /* adb and debug shell user */ +#define AID_CACHE 2001 /* cache access */ +#define AID_DIAG 2002 /* access to diagnostic resources */ + +/* The 3000 series are intended for use as supplemental group id's only. + * They indicate special Android capabilities that the kernel is aware of. */ +#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ +#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ +#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ +#define AID_NET_RAW 3004 /* can create raw INET sockets */ + +#define AID_MISC 9998 /* access to misc storage */ +#define AID_NOBODY 9999 + +#define AID_APP 10000 /* first app user */ + +#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) +struct android_id_info { + const char *name; + unsigned aid; +}; + +static struct android_id_info android_ids[] = { + { "root", AID_ROOT, }, + { "system", AID_SYSTEM, }, + { "radio", AID_RADIO, }, + { "bluetooth", AID_BLUETOOTH, }, + { "graphics", AID_GRAPHICS, }, + { "input", AID_INPUT, }, + { "audio", AID_AUDIO, }, + { "camera", AID_CAMERA, }, + { "log", AID_LOG, }, + { "compass", AID_COMPASS, }, + { "mount", AID_MOUNT, }, + { "wifi", AID_WIFI, }, + { "dhcp", AID_DHCP, }, + { "adb", AID_ADB, }, + { "install", AID_INSTALL, }, + { "media", AID_MEDIA, }, + { "shell", AID_SHELL, }, + { "cache", AID_CACHE, }, + { "diag", AID_DIAG, }, + { "net_bt_admin", AID_NET_BT_ADMIN, }, + { "net_bt", AID_NET_BT, }, + { "inet", AID_INET, }, + { "net_raw", AID_NET_RAW, }, + { "misc", AID_MISC, }, + { "nobody", AID_NOBODY, }, +}; + +#define android_id_count \ + (sizeof(android_ids) / sizeof(android_ids[0])) + +struct fs_path_config { + unsigned mode; + unsigned uid; + unsigned gid; + const char *prefix; +}; + +/* Rules for directories. +** These rules are applied based on "first match", so they +** should start with the most specific path and work their +** way up to the root. +*/ + +static struct fs_path_config android_dirs[] = { + { 00770, AID_SYSTEM, AID_CACHE, "cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, "data/app" }, + { 00771, AID_SYSTEM, AID_SYSTEM, "data/app-private" }, + { 00771, AID_SYSTEM, AID_SYSTEM, "data/dalvik-cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, "data/data" }, + { 00771, AID_SHELL, AID_SHELL, "data/local/tmp" }, + { 00771, AID_SHELL, AID_SHELL, "data/local" }, + { 01771, AID_SYSTEM, AID_MISC, "data/misc" }, + { 00770, AID_DHCP, AID_DHCP, "data/misc/dhcp" }, + { 00771, AID_SYSTEM, AID_SYSTEM, "data" }, + { 00750, AID_ROOT, AID_SHELL, "sbin" }, + { 00755, AID_ROOT, AID_SHELL, "system/bin" }, + { 00755, AID_ROOT, AID_SHELL, "system/xbin" }, + { 00777, AID_ROOT, AID_ROOT, "system/etc/ppp" }, /* REMOVE */ + { 00777, AID_ROOT, AID_ROOT, "sdcard" }, + { 00755, AID_ROOT, AID_ROOT, 0 }, +}; + +/* Rules for files. +** These rules are applied based on "first match", so they +** should start with the most specific path and work their +** way up to the root. Prefixes ending in * denotes wildcard +** and will allow partial matches. +*/ +static struct fs_path_config android_files[] = { + { 00555, AID_ROOT, AID_ROOT, "system/etc/ppp/ip-up" }, + { 00555, AID_ROOT, AID_ROOT, "system/etc/ppp/ip-down" }, + { 00440, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.rc" }, + { 00550, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.sh" }, + { 00440, AID_ROOT, AID_SHELL, "system/etc/init.trout.rc" }, + { 00550, AID_ROOT, AID_SHELL, "system/etc/init.ril" }, + { 00550, AID_ROOT, AID_SHELL, "system/etc/init.testmenu" }, + { 00550, AID_ROOT, AID_SHELL, "system/etc/init.gprs-pppd" }, + { 00550, AID_DHCP, AID_SHELL, "system/etc/dhcpcd/dhcpcd-run-hooks" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/hcid.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/input.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/audio.conf" }, + { 00440, AID_RADIO, AID_AUDIO, "/system/etc/AudioPara4.csv" }, + { 00644, AID_SYSTEM, AID_SYSTEM, "data/app/*" }, + { 00644, AID_SYSTEM, AID_SYSTEM, "data/app-private/*" }, + { 00644, AID_APP, AID_APP, "data/data/*" }, + /* the following two files are INTENTIONALLY set-gid and not set-uid. + * Do not change. */ + { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, + { 02755, AID_ROOT, AID_INET, "system/bin/netcfg" }, + /* the following four files are INTENTIONALLY set-uid, but they + * are NOT included on user builds. */ + { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, + { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, + { 06755, AID_ROOT, AID_ROOT, "system/xbin/procrank" }, + { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, + { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, + { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, + { 00755, AID_ROOT, AID_ROOT, "bin/*" }, + { 00750, AID_ROOT, AID_SHELL, "init*" }, + { 00644, AID_ROOT, AID_ROOT, 0 }, +}; + +static inline void fs_config(const char *path, int dir, + unsigned *uid, unsigned *gid, unsigned *mode) +{ + struct fs_path_config *pc; + int plen; + + pc = dir ? android_dirs : android_files; + plen = strlen(path); + for(; pc->prefix; pc++){ + int len = strlen(pc->prefix); + if (dir) { + if(plen < len) continue; + if(!strncmp(pc->prefix, path, len)) break; + continue; + } + /* If name ends in * then allow partial matches. */ + if (pc->prefix[len -1] == '*') { + if(!strncmp(pc->prefix, path, len - 1)) break; + } else if (plen == len){ + if(!strncmp(pc->prefix, path, len)) break; + } + } + *uid = pc->uid; + *gid = pc->gid; + *mode = (*mode & (~07777)) | pc->mode; + +#if 0 + fprintf(stderr,"< '%s' '%s' %d %d %o >\n", + path, pc->prefix ? pc->prefix : "", *uid, *gid, *mode); +#endif +} +#endif +#endif diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h new file mode 100644 index 0000000..3a030c5 --- /dev/null +++ b/include/private/pixelflinger/ggl_context.h @@ -0,0 +1,544 @@ +/* + * 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 ANDROID_GGL_CONTEXT_H +#define ANDROID_GGL_CONTEXT_H + +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include <sys/types.h> + +#include <utils/Endian.h> +#include <pixelflinger/pixelflinger.h> +#include <private/pixelflinger/ggl_fixed.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +#if BYTE_ORDER == LITTLE_ENDIAN + +inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) { + return v; +} +inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) { + return v; +} + +#else + +inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} + +#endif + +// ---------------------------------------------------------------------------- + +const int GGL_DITHER_BITS = 6; // dither weights stored on 6 bits +const int GGL_DITHER_ORDER_SHIFT= 3; +const int GGL_DITHER_ORDER = (1<<GGL_DITHER_ORDER_SHIFT); +const int GGL_DITHER_SIZE = GGL_DITHER_ORDER * GGL_DITHER_ORDER; +const int GGL_DITHER_MASK = GGL_DITHER_ORDER-1; + +// ---------------------------------------------------------------------------- + +const int GGL_SUBPIXEL_BITS = 4; + +// TRI_FRACTION_BITS defines the number of bits we want to use +// for the sub-pixel coordinates during the edge stepping, the +// value shouldn't be more than 7, or bad things are going to +// happen when drawing large triangles (8 doesn't work because +// 32 bit muls will loose the sign bit) + +#define TRI_FRACTION_BITS (GGL_SUBPIXEL_BITS) +#define TRI_ONE (1 << TRI_FRACTION_BITS) +#define TRI_HALF (1 << (TRI_FRACTION_BITS-1)) +#define TRI_FROM_INT(x) ((x) << TRI_FRACTION_BITS) +#define TRI_FRAC(x) ((x) & (TRI_ONE-1)) +#define TRI_FLOOR(x) ((x) & ~(TRI_ONE-1)) +#define TRI_CEIL(x) (((x) + (TRI_ONE-1)) & ~(TRI_ONE-1)) +#define TRI_ROUND(x) (((x) + TRI_HALF ) & ~(TRI_ONE-1)) + +#define TRI_ROUDNING (1 << (16 - TRI_FRACTION_BITS - 1)) +#define TRI_FROM_FIXED(x) (((x)+TRI_ROUDNING) >> (16-TRI_FRACTION_BITS)) + +#define TRI_SNAP_NEXT_HALF(x) (TRI_CEIL((x)+TRI_HALF) - TRI_HALF) +#define TRI_SNAP_PREV_HALF(x) (TRI_CEIL((x)-TRI_HALF) - TRI_HALF) + +// ---------------------------------------------------------------------------- + +const int GGL_COLOR_BITS = 24; + +// To maintain 8-bits color chanels, with a maximum GGLSurface +// size of 4096 and GGL_SUBPIXEL_BITS=4, we need 8 + 12 + 4 = 24 bits +// for encoding the color iterators + +inline GGLcolor gglFixedToIteratedColor(GGLfixed c) { + return (c << 8) - c; +} + +// ---------------------------------------------------------------------------- + +template<bool> struct CTA; +template<> struct CTA<true> { }; + +#define GGL_CONTEXT(con, c) context_t *con = static_cast<context_t *>(c) +#define GGL_OFFSETOF(field) int(&(((context_t*)0)->field)) +#define GGL_INIT_PROC(p, f) p.f = ggl_ ## f; +#define GGL_BETWEEN(x, L, H) (uint32_t((x)-(L)) <= ((H)-(L))) + +#define ggl_likely(x) __builtin_expect(!!(x), 1) +#define ggl_unlikely(x) __builtin_expect(!!(x), 0) + +const int GGL_TEXTURE_UNIT_COUNT = 2; +const int GGL_TMU_STATE = 0x00000001; +const int GGL_CB_STATE = 0x00000002; +const int GGL_PIXEL_PIPELINE_STATE = 0x00000004; + +// ---------------------------------------------------------------------------- + +#define GGL_RESERVE_NEEDS(name, l, s) \ + const uint32_t GGL_NEEDS_##name##_MASK = (((1LU<<(s))-1)<<l); \ + const uint32_t GGL_NEEDS_##name##_SHIFT = (l); + +#define GGL_BUILD_NEEDS(val, name) \ + (((val)<<(GGL_NEEDS_##name##_SHIFT)) & GGL_NEEDS_##name##_MASK) + +#define GGL_READ_NEEDS(name, n) \ + (uint32_t(n & GGL_NEEDS_##name##_MASK) >> GGL_NEEDS_##name##_SHIFT) + +#define GGL_NEED_MASK(name) (uint32_t(GGL_NEEDS_##name##_MASK)) +#define GGL_NEED(name, val) GGL_BUILD_NEEDS(val, name) + +GGL_RESERVE_NEEDS( CB_FORMAT, 0, 6 ) +GGL_RESERVE_NEEDS( SHADE, 6, 1 ) +GGL_RESERVE_NEEDS( W, 7, 1 ) +GGL_RESERVE_NEEDS( BLEND_SRC, 8, 4 ) +GGL_RESERVE_NEEDS( BLEND_DST, 12, 4 ) +GGL_RESERVE_NEEDS( BLEND_SRCA, 16, 4 ) +GGL_RESERVE_NEEDS( BLEND_DSTA, 20, 4 ) +GGL_RESERVE_NEEDS( LOGIC_OP, 24, 4 ) +GGL_RESERVE_NEEDS( MASK_ARGB, 28, 4 ) + +GGL_RESERVE_NEEDS( P_ALPHA_TEST, 0, 3 ) +GGL_RESERVE_NEEDS( P_AA, 3, 1 ) +GGL_RESERVE_NEEDS( P_DEPTH_TEST, 4, 3 ) +GGL_RESERVE_NEEDS( P_MASK_Z, 7, 1 ) +GGL_RESERVE_NEEDS( P_DITHER, 8, 1 ) +GGL_RESERVE_NEEDS( P_FOG, 9, 1 ) +GGL_RESERVE_NEEDS( P_RESERVED1, 10,22 ) + +GGL_RESERVE_NEEDS( T_FORMAT, 0, 6 ) +GGL_RESERVE_NEEDS( T_RESERVED0, 6, 1 ) +GGL_RESERVE_NEEDS( T_POT, 7, 1 ) +GGL_RESERVE_NEEDS( T_S_WRAP, 8, 2 ) +GGL_RESERVE_NEEDS( T_T_WRAP, 10, 2 ) +GGL_RESERVE_NEEDS( T_ENV, 12, 3 ) +GGL_RESERVE_NEEDS( T_LINEAR, 15, 1 ) + +const int GGL_NEEDS_WRAP_CLAMP_TO_EDGE = 0; +const int GGL_NEEDS_WRAP_REPEAT = 1; +const int GGL_NEEDS_WRAP_11 = 2; + +inline uint32_t ggl_wrap_to_needs(uint32_t e) { + switch (e) { + case GGL_CLAMP: return GGL_NEEDS_WRAP_CLAMP_TO_EDGE; + case GGL_REPEAT: return GGL_NEEDS_WRAP_REPEAT; + } + return 0; +} + +inline uint32_t ggl_blendfactor_to_needs(uint32_t b) { + if (b <= 1) return b; + return (b & 0xF)+2; +} + +inline uint32_t ggl_needs_to_blendfactor(uint32_t n) { + if (n <= 1) return n; + return (n - 2) + 0x300; +} + +inline uint32_t ggl_env_to_needs(uint32_t e) { + switch (e) { + case GGL_REPLACE: return 0; + case GGL_MODULATE: return 1; + case GGL_DECAL: return 2; + case GGL_BLEND: return 3; + case GGL_ADD: return 4; + } + return 0; +} + +inline uint32_t ggl_needs_to_env(uint32_t n) { + const uint32_t envs[] = { GGL_REPLACE, GGL_MODULATE, + GGL_DECAL, GGL_BLEND, GGL_ADD }; + return envs[n]; + +} + +// ---------------------------------------------------------------------------- + +enum { + GGL_ENABLE_BLENDING = 0x00000001, + GGL_ENABLE_SMOOTH = 0x00000002, + GGL_ENABLE_AA = 0x00000004, + GGL_ENABLE_LOGIC_OP = 0x00000008, + GGL_ENABLE_ALPHA_TEST = 0x00000010, + GGL_ENABLE_SCISSOR_TEST = 0x00000020, + GGL_ENABLE_TMUS = 0x00000040, + GGL_ENABLE_DEPTH_TEST = 0x00000080, + GGL_ENABLE_STENCIL_TEST = 0x00000100, + GGL_ENABLE_W = 0x00000200, + GGL_ENABLE_DITHER = 0x00000400, + GGL_ENABLE_FOG = 0x00000800, + GGL_ENABLE_POINT_AA_NICE= 0x00001000 +}; + +// ---------------------------------------------------------------------------- + +class needs_filter_t; +struct needs_t { + inline int match(const needs_filter_t& filter); + inline bool operator == (const needs_t& rhs) const { + return (n==rhs.n) && + (p==rhs.p) && + (t[0]==rhs.t[0]) && + (t[1]==rhs.t[1]); + } + inline bool operator != (const needs_t& rhs) const { + return !operator == (rhs); + } + uint32_t n; + uint32_t p; + uint32_t t[GGL_TEXTURE_UNIT_COUNT]; +}; + +inline int compare_type(const needs_t& lhs, const needs_t& rhs) { + return memcmp(&lhs, &rhs, sizeof(needs_t)); +} + +struct needs_filter_t { + needs_t value; + needs_t mask; +}; + +int needs_t::match(const needs_filter_t& filter) { + uint32_t result = + ((filter.value.n ^ n) & filter.mask.n) | + ((filter.value.p ^ p) & filter.mask.p) | + ((filter.value.t[0] ^ t[0]) & filter.mask.t[0]) | + ((filter.value.t[1] ^ t[1]) & filter.mask.t[1]); + return (result == 0); +} + +// ---------------------------------------------------------------------------- + +struct context_t; +class Assembly; + +struct blend_state_t { + uint32_t src; + uint32_t dst; + uint32_t src_alpha; + uint32_t dst_alpha; + uint8_t reserved; + uint8_t alpha_separate; + uint8_t operation; + uint8_t equation; +}; + +struct mask_state_t { + uint8_t color; + uint8_t depth; + uint32_t stencil; +}; + +struct clear_state_t { + GGLclampx r; + GGLclampx g; + GGLclampx b; + GGLclampx a; + GGLclampx depth; + GGLint stencil; + uint32_t colorPacked; + uint32_t depthPacked; + uint32_t stencilPacked; + uint32_t dirty; +}; + +struct fog_state_t { + uint8_t color[3]; + uint8_t reserved; +}; + +struct logic_op_state_t { + uint16_t opcode; +}; + +struct alpha_test_state_t { + uint16_t func; + GGLcolor ref; +}; + +struct depth_test_state_t { + uint16_t func; + GGLclampx clearValue; +}; + +struct scissor_t { + uint32_t user_left; + uint32_t user_right; + uint32_t user_top; + uint32_t user_bottom; + uint32_t left; + uint32_t right; + uint32_t top; + uint32_t bottom; +}; + +struct pixel_t { + uint32_t c[4]; + uint8_t s[4]; +}; + +struct surface_t { + union { + GGLSurface s; + struct { + uint32_t reserved; + uint32_t width; + uint32_t height; + int32_t stride; + uint8_t* data; + uint8_t format; + uint8_t dirty; + uint8_t pad[2]; + }; + }; + void (*read) (const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel); + void (*write)(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, const pixel_t* pixel); +}; + +// ---------------------------------------------------------------------------- + +struct texture_shade_t { + union { + struct { + int32_t is0; + int32_t idsdx; + int32_t idsdy; + int sscale; + int32_t it0; + int32_t idtdx; + int32_t idtdy; + int tscale; + }; + struct { + int32_t v; + int32_t dx; + int32_t dy; + int scale; + } st[2]; + }; +}; + +struct texture_iterators_t { + // these are not encoded in the same way than in the + // texture_shade_t structure + union { + struct { + GGLfixed ydsdy; + GGLfixed dsdx; + GGLfixed dsdy; + int sscale; + GGLfixed ydtdy; + GGLfixed dtdx; + GGLfixed dtdy; + int tscale; + }; + struct { + GGLfixed ydvdy; + GGLfixed dvdx; + GGLfixed dvdy; + int scale; + } st[2]; + }; +}; + +struct texture_t { + surface_t surface; + texture_iterators_t iterators; + texture_shade_t shade; + uint32_t s_coord; + uint32_t t_coord; + uint16_t s_wrap; + uint16_t t_wrap; + uint16_t min_filter; + uint16_t mag_filter; + uint16_t env; + uint8_t env_color[4]; + uint8_t enable; + uint8_t dirty; +}; + +struct raster_t { + GGLfixed x; + GGLfixed y; +}; + +struct framebuffer_t { + surface_t color; + surface_t read; + surface_t depth; + surface_t stencil; + int16_t *coverage; + size_t coverageBufferSize; +}; + +// ---------------------------------------------------------------------------- + +struct iterators_t { + int32_t xl; + int32_t xr; + int32_t y; + GGLcolor ydady; + GGLcolor ydrdy; + GGLcolor ydgdy; + GGLcolor ydbdy; + GGLfixed ydzdy; + GGLfixed ydwdy; + GGLfixed ydfdy; +}; + +struct shade_t { + GGLcolor a0; + GGLcolor dadx; + GGLcolor dady; + GGLcolor r0; + GGLcolor drdx; + GGLcolor drdy; + GGLcolor g0; + GGLcolor dgdx; + GGLcolor dgdy; + GGLcolor b0; + GGLcolor dbdx; + GGLcolor dbdy; + uint32_t z0; + GGLfixed32 dzdx; + GGLfixed32 dzdy; + GGLfixed w0; + GGLfixed dwdx; + GGLfixed dwdy; + uint32_t f0; + GGLfixed dfdx; + GGLfixed dfdy; +}; + +// these are used in the generated code +// we use this mirror structure to improve +// data locality in the pixel pipeline +struct generated_tex_vars_t { + uint32_t width; + uint32_t height; + uint32_t stride; + int32_t data; + int32_t dsdx; + int32_t dtdx; + int32_t spill[2]; +}; + +struct generated_vars_t { + struct { + int32_t c; + int32_t dx; + } argb[4]; + int32_t aref; + int32_t dzdx; + int32_t zbase; + int32_t f; + int32_t dfdx; + int32_t spill[3]; + generated_tex_vars_t texture[GGL_TEXTURE_UNIT_COUNT]; + int32_t rt; + int32_t lb; +}; + +// ---------------------------------------------------------------------------- + +struct state_t { + framebuffer_t buffers; + texture_t texture[GGL_TEXTURE_UNIT_COUNT]; + scissor_t scissor; + raster_t raster; + blend_state_t blend; + alpha_test_state_t alpha_test; + depth_test_state_t depth_test; + mask_state_t mask; + clear_state_t clear; + fog_state_t fog; + logic_op_state_t logic_op; + uint32_t enables; + uint32_t enabled_tmu; + needs_t needs; +}; + +// ---------------------------------------------------------------------------- + +struct context_t { + GGLContext procs; + state_t state; + shade_t shade; + iterators_t iterators; + generated_vars_t generated_vars __attribute__((aligned(32))); + uint8_t ditherMatrix[GGL_DITHER_SIZE] __attribute__((aligned(32))); + uint32_t packed; + uint32_t packed8888; + const GGLFormat* formats; + uint32_t dirty; + texture_t* activeTMU; + uint32_t activeTMUIndex; + + void (*init_y)(context_t* c, int32_t y); + void (*step_y)(context_t* c); + void (*scanline)(context_t* c); + void (*span)(context_t* c); + void (*rect)(context_t* c, size_t yc); + + void* base; + Assembly* scanline_as; + GGLenum error; +}; + +// ---------------------------------------------------------------------------- + +void ggl_init_context(context_t* context); +void ggl_uninit_context(context_t* context); +void ggl_error(context_t* c, GGLenum error); +int64_t ggl_system_time(); + +// ---------------------------------------------------------------------------- + +}; + +#endif // ANDROID_GGL_CONTEXT_H + diff --git a/include/private/pixelflinger/ggl_fixed.h b/include/private/pixelflinger/ggl_fixed.h new file mode 100644 index 0000000..96fdb32 --- /dev/null +++ b/include/private/pixelflinger/ggl_fixed.h @@ -0,0 +1,302 @@ +/* + * 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. + */ + +#ifndef ANDROID_GGL_FIXED_H +#define ANDROID_GGL_FIXED_H + +#include <math.h> +#include <pixelflinger/pixelflinger.h> + +// ---------------------------------------------------------------------------- + +#define CONST __attribute__((const)) +#define ALWAYS_INLINE __attribute__((always_inline)) + +const GGLfixed FIXED_BITS = 16; +const GGLfixed FIXED_EPSILON = 1; +const GGLfixed FIXED_ONE = 1L<<FIXED_BITS; +const GGLfixed FIXED_HALF = 1L<<(FIXED_BITS-1); +const GGLfixed FIXED_MIN = 0x80000000L; +const GGLfixed FIXED_MAX = 0x7FFFFFFFL; + +inline GGLfixed gglIntToFixed(GGLfixed i) ALWAYS_INLINE ; +inline GGLfixed gglFixedToIntRound(GGLfixed f) ALWAYS_INLINE ; +inline GGLfixed gglFixedToIntFloor(GGLfixed f) ALWAYS_INLINE ; +inline GGLfixed gglFixedToIntCeil(GGLfixed f) ALWAYS_INLINE ; +inline GGLfixed gglFracx(GGLfixed v) ALWAYS_INLINE ; +inline GGLfixed gglFloorx(GGLfixed v) ALWAYS_INLINE ; +inline GGLfixed gglCeilx(GGLfixed v) ALWAYS_INLINE ; +inline GGLfixed gglCenterx(GGLfixed v) ALWAYS_INLINE ; +inline GGLfixed gglRoundx(GGLfixed v) ALWAYS_INLINE ; + +GGLfixed gglIntToFixed(GGLfixed i) { + return i<<FIXED_BITS; +} +GGLfixed gglFixedToIntRound(GGLfixed f) { + return (f + FIXED_HALF)>>FIXED_BITS; +} +GGLfixed gglFixedToIntFloor(GGLfixed f) { + return f>>FIXED_BITS; +} +GGLfixed gglFixedToIntCeil(GGLfixed f) { + return (f + ((1<<FIXED_BITS) - 1))>>FIXED_BITS; +} + +GGLfixed gglFracx(GGLfixed v) { + return v & ((1<<FIXED_BITS)-1); +} +GGLfixed gglFloorx(GGLfixed v) { + return gglFixedToIntFloor(v)<<FIXED_BITS; +} +GGLfixed gglCeilx(GGLfixed v) { + return gglFixedToIntCeil(v)<<FIXED_BITS; +} +GGLfixed gglCenterx(GGLfixed v) { + return gglFloorx(v + FIXED_HALF) | FIXED_HALF; +} +GGLfixed gglRoundx(GGLfixed v) { + return gglFixedToIntRound(v)<<FIXED_BITS; +} + +// conversion from (unsigned) int, short, byte to fixed... +#define GGL_B_TO_X(_x) GGLfixed( ((int32_t(_x)+1)>>1)<<10 ) +#define GGL_S_TO_X(_x) GGLfixed( ((int32_t(_x)+1)>>1)<<2 ) +#define GGL_I_TO_X(_x) GGLfixed( ((int32_t(_x)>>1)+1)>>14 ) +#define GGL_UB_TO_X(_x) GGLfixed( uint32_t(_x) + \ + (uint32_t(_x)<<8) + \ + (uint32_t(_x)>>7) ) +#define GGL_US_TO_X(_x) GGLfixed( (_x) + ((_x)>>15) ) +#define GGL_UI_TO_X(_x) GGLfixed( (((_x)>>1)+1)>>15 ) + +// ---------------------------------------------------------------------------- + +GGLfixed gglPowx(GGLfixed x, GGLfixed y) CONST; +GGLfixed gglSqrtx(GGLfixed a) CONST; +GGLfixed gglSqrtRecipx(GGLfixed x) CONST; +GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) CONST; +int32_t gglMulDivi(int32_t a, int32_t b, int32_t c); + +int32_t gglRecipQNormalized(int32_t x, int* exponent); +int32_t gglRecipQ(GGLfixed x, int q) CONST; + +inline GGLfixed gglRecip(GGLfixed x) CONST; +inline GGLfixed gglRecip(GGLfixed x) { + return gglRecipQ(x, 16); +} + +inline GGLfixed gglRecip28(GGLfixed x) CONST; +int32_t gglRecip28(GGLfixed x) { + return gglRecipQ(x, 28); +} + +// ---------------------------------------------------------------------------- + +#if defined(__arm__) && !defined(__thumb__) + +// inline ARM implementations +inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST; +inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) { + GGLfixed result, t; + if (__builtin_constant_p(shift)) { + asm("smull %[lo], %[hi], %[x], %[y] \n" + "movs %[lo], %[lo], lsr %[rshift] \n" + "adc %[lo], %[lo], %[hi], lsl %[lshift] \n" + : [lo]"=r"(result), [hi]"=r"(t), [x]"=r"(x) + : "%[x]"(x), [y]"r"(y), [lshift] "I"(32-shift), [rshift] "I"(shift) + : "cc" + ); + } else { + asm("smull %[lo], %[hi], %[x], %[y] \n" + "movs %[lo], %[lo], lsr %[rshift] \n" + "adc %[lo], %[lo], %[hi], lsl %[lshift] \n" + : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x) + : "%[x]"(x), [y]"r"(y), [lshift] "r"(32-shift), [rshift] "r"(shift) + : "cc" + ); + } + return result; +} + +inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST; +inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) { + GGLfixed result, t; + if (__builtin_constant_p(shift)) { + asm("smull %[lo], %[hi], %[x], %[y] \n" + "add %[lo], %[a], %[lo], lsr %[rshift] \n" + "add %[lo], %[lo], %[hi], lsl %[lshift] \n" + : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x) + : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "I"(32-shift), [rshift] "I"(shift) + ); + } else { + asm("smull %[lo], %[hi], %[x], %[y] \n" + "add %[lo], %[a], %[lo], lsr %[rshift] \n" + "add %[lo], %[lo], %[hi], lsl %[lshift] \n" + : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x) + : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "r"(32-shift), [rshift] "r"(shift) + ); + } + return result; +} + +inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST; +inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) { + GGLfixed result, t; + if (__builtin_constant_p(shift)) { + asm("smull %[lo], %[hi], %[x], %[y] \n" + "rsb %[lo], %[a], %[lo], lsr %[rshift] \n" + "add %[lo], %[lo], %[hi], lsl %[lshift] \n" + : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x) + : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "I"(32-shift), [rshift] "I"(shift) + ); + } else { + asm("smull %[lo], %[hi], %[x], %[y] \n" + "rsb %[lo], %[a], %[lo], lsr %[rshift] \n" + "add %[lo], %[lo], %[hi], lsl %[lshift] \n" + : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x) + : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "r"(32-shift), [rshift] "r"(shift) + ); + } + return result; +} + +inline int64_t gglMulii(int32_t x, int32_t y) CONST; +inline int64_t gglMulii(int32_t x, int32_t y) +{ + // 64-bits result: r0=low, r1=high + union { + struct { + int32_t lo; + int32_t hi; + } s; + int64_t res; + }; + asm("smull %0, %1, %2, %3 \n" + : "=r"(s.lo), "=&r"(s.hi) + : "%r"(x), "r"(y) + : + ); + return res; +} + +#else // ---------------------------------------------------------------------- + +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST; +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) { + return GGLfixed((int64_t(a)*b + (1<<(shift-1)))>>shift); +} +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST; +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) { + return GGLfixed((int64_t(a)*b)>>shift) + c; +} +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST; +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) { + return GGLfixed((int64_t(a)*b)>>shift) - c; +} +inline int64_t gglMulii(int32_t a, int32_t b) CONST; +inline int64_t gglMulii(int32_t a, int32_t b) { + return int64_t(a)*b; +} + +#endif + +// ------------------------------------------------------------------------ + +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b) CONST; +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b) { + return gglMulx(a, b, 16); +} +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c) CONST; +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c) { + return gglMulAddx(a, b, c, 16); +} +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) CONST; +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) { + return gglMulSubx(a, b, c, 16); +} + +// ------------------------------------------------------------------------ + +inline int32_t gglClz(int32_t x) CONST; +inline int32_t gglClz(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + if (!x) return 32; + int32_t exp = 31; + if (x & 0xFFFF0000) { exp -=16; x >>= 16; } + if (x & 0x0000ff00) { exp -= 8; x >>= 8; } + if (x & 0x000000f0) { exp -= 4; x >>= 4; } + if (x & 0x0000000c) { exp -= 2; x >>= 2; } + if (x & 0x00000002) { exp -= 1; } + return exp; +#endif +} + +// ------------------------------------------------------------------------ + +int32_t gglDivQ(GGLfixed n, GGLfixed d, int32_t i) CONST; + +inline int32_t gglDivQ16(GGLfixed n, GGLfixed d) CONST; +inline int32_t gglDivQ16(GGLfixed n, GGLfixed d) { + return gglDivQ(n, d, 16); +} + +inline int32_t gglDivx(GGLfixed n, GGLfixed d) CONST; +inline int32_t gglDivx(GGLfixed n, GGLfixed d) { + return gglDivQ(n, d, 16); +} + +// ------------------------------------------------------------------------ + +inline GGLfixed gglRecipFast(GGLfixed x) CONST; +inline GGLfixed gglRecipFast(GGLfixed x) +{ + // This is a really bad approximation of 1/x, but it's also + // very fast. x must be strictly positive. + // if x between [0.5, 1[ , then 1/x = 3-2*x + // (we use 2.30 fixed-point) + const int32_t lz = gglClz(x); + return (0xC0000000 - (x << (lz - 1))) >> (30-lz); +} + +// ------------------------------------------------------------------------ + +inline GGLfixed gglClampx(GGLfixed c) CONST; +inline GGLfixed gglClampx(GGLfixed c) +{ +#if defined(__thumb__) + // clamp without branches + c &= ~(c>>31); c = FIXED_ONE - c; + c &= ~(c>>31); c = FIXED_ONE - c; +#else +#if defined(__arm__) + // I don't know why gcc thinks its smarter than me! The code below + // clamps to zero in one instruction, but gcc won't generate it and + // replace it by a cmp + movlt (it's quite amazing actually). + asm("bic %0, %1, %1, asr #31\n" : "=r"(c) : "r"(c)); +#else + c &= ~(c>>31); +#endif + if (c>FIXED_ONE) + c = FIXED_ONE; +#endif + return c; +} + +// ------------------------------------------------------------------------ + +#endif // ANDROID_GGL_FIXED_H diff --git a/include/zipfile/zipfile.h b/include/zipfile/zipfile.h new file mode 100644 index 0000000..0ae4ee4 --- /dev/null +++ b/include/zipfile/zipfile.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ZIPFILE_ZIPFILE_H +#define _ZIPFILE_ZIPFILE_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* zipfile_t; +typedef void* zipentry_t; + +// Provide a buffer. Returns NULL on failure. +zipfile_t init_zipfile(const void* data, size_t size); + +// Release the zipfile resources. +void release_zipfile(zipfile_t file); + +// Get a named entry object. Returns NULL if it doesn't exist +// or if we won't be able to decompress it. The zipentry_t is +// freed by release_zipfile() +zipentry_t lookup_zipentry(zipfile_t file, const char* entryName); + +// Return the size of the entry. +size_t get_zipentry_size(zipentry_t entry); + +// return the filename of this entry, you own the memory returned +char* get_zipentry_name(zipentry_t entry); + +// The buffer must be 1.001 times the buffer size returned +// by get_zipentry_size. Returns nonzero on failure. +int decompress_zipentry(zipentry_t entry, void* buf, int bufsize); + +// iterate through the entries in the zip file. pass a pointer to +// a void* initialized to NULL to start. Returns NULL when done +zipentry_t iterate_zipfile(zipfile_t file, void** cookie); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _ZIPFILE_ZIPFILE_H diff --git a/init/Android.mk b/init/Android.mk new file mode 100644 index 0000000..d3766d4 --- /dev/null +++ b/init/Android.mk @@ -0,0 +1,33 @@ +# Copyright 2005 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + builtins.c \ + init.c \ + devices.c \ + property_service.c \ + util.c \ + parser.c \ + logo.c + +ifeq ($(strip $(INIT_BOOTCHART)),true) +LOCAL_SRC_FILES += bootchart.c +LOCAL_CFLAGS += -DBOOTCHART=1 +endif + +LOCAL_MODULE:= init + +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) + +LOCAL_STATIC_LIBRARIES := libcutils libc + +#LOCAL_STATIC_LIBRARIES := libcutils libc libminui libpixelflinger_static +#LOCAL_STATIC_LIBRARIES += libminzip libunz libamend libmtdutils libmincrypt +#LOCAL_STATIC_LIBRARIES += libstdc++_static + +include $(BUILD_EXECUTABLE) + diff --git a/init/MODULE_LICENSE_APACHE2 b/init/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/init/MODULE_LICENSE_APACHE2 diff --git a/init/NOTICE b/init/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/init/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/init/README.BOOTCHART b/init/README.BOOTCHART new file mode 100644 index 0000000..70cf2c3 --- /dev/null +++ b/init/README.BOOTCHART @@ -0,0 +1,52 @@ +This version of init contains code to perform "bootcharting", i.e. generating log +files that can be later processed by the tools provided by www.bootchart.org. + +To activate it, you need to define build 'init' with the INIT_BOOTCHART environment +variable defined to 'true', for example: + + touch system/init/init.c + m INIT_BOOTCHART=true + +On the emulator, use the new -bootchart <timeout> option to boot with bootcharting +activated for <timeout> seconds. + +Otherwise, flash your device, and start it. Then create a file on the /data partition +with a command like the following: + + adb shell 'echo $TIMEOUT > /data/bootchart-start' + +Where the value of $TIMEOUT corresponds to the wanted bootcharted period in seconds; +for example, to bootchart for 2 minutes, do: + + adb shell 'echo 120 > /data/bootchart-start' + +Reboot your device, bootcharting will begin and stop after the period you gave. +You can also stop the bootcharting at any moment by doing the following: + + adb shell 'echo 1 > /data/bootchart-stop' + +Note that /data/bootchart-stop is deleted automatically by init at the end of the +bootcharting. This is not the case of /data/bootchart-start, so don't forget to delete it +when you're done collecting data: + + adb shell rm /data/bootchart-start + +The log files are placed in /data/bootchart/. you must run the script tools/grab-bootchart.sh +which will use ADB to retrieve them and create a bootchart.tgz file that can be used with +the bootchart parser/renderer, or even uploaded directly to the form located at: + + http://www.bootchart.org/download.html + +NOTE: the bootchart.org webform doesn't seem to work at the moment, you can generate an + image on your machine by doing the following: + + 1/ download the sources from www.bootchart.org + 2/ unpack them + 3/ in the source directory, type 'ant' to build the bootchart program + 4/ type 'java -jar bootchart.jar /path/to/bootchart.tgz + +technical note: + +this implementation of bootcharting does use the 'bootchartd' script provided by +www.bootchart.org, but a C re-implementation that is directly compiled into our init +program. diff --git a/init/bootchart.c b/init/bootchart.c new file mode 100644 index 0000000..f72fcaa --- /dev/null +++ b/init/bootchart.c @@ -0,0 +1,378 @@ +/* + * 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. + */ + +/* this code is used to generate a boot sequence profile that can be used + * with the 'bootchart' graphics generation tool. see www.bootchart.org + * note that unlike the original bootchartd, this is not a Bash script but + * some C code that is run right from the init script. + */ + +#include <stdio.h> +#include <time.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> +#include <unistd.h> +#include <fcntl.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/stat.h> +#include "bootchart.h" + +#define VERSION "0.8" +#define SAMPLE_PERIOD 0.2 +#define LOG_ROOT "/data/bootchart" +#define LOG_STAT LOG_ROOT"/proc_stat.log" +#define LOG_PROCS LOG_ROOT"/proc_ps.log" +#define LOG_DISK LOG_ROOT"/proc_diskstats.log" +#define LOG_HEADER LOG_ROOT"/header" +#define LOG_ACCT LOG_ROOT"/kernel_pacct" + +#define LOG_STARTFILE "/data/bootchart-start" +#define LOG_STOPFILE "/data/bootchart-stop" + +static int +unix_read(int fd, void* buff, int len) +{ + int ret; + do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR); + return ret; +} + +static int +unix_write(int fd, const void* buff, int len) +{ + int ret; + do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR); + return ret; +} + +static int +proc_read(const char* filename, char* buff, size_t buffsize) +{ + int len = 0; + int fd = open(filename, O_RDONLY); + if (fd >= 0) { + len = unix_read(fd, buff, buffsize-1); + close(fd); + } + buff[len > 0 ? len : 0] = 0; + return len; +} + +#define FILE_BUFF_SIZE 65536 + +typedef struct { + int count; + int fd; + char data[FILE_BUFF_SIZE]; +} FileBuffRec, *FileBuff; + +static void +file_buff_open( FileBuff buff, const char* path ) +{ + buff->count = 0; + buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755); +} + +static void +file_buff_write( FileBuff buff, const void* src, int len ) +{ + while (len > 0) { + int avail = sizeof(buff->data) - buff->count; + if (avail > len) + avail = len; + + memcpy( buff->data + buff->count, src, avail ); + len -= avail; + src = (char*)src + avail; + + buff->count += avail; + if (buff->count == FILE_BUFF_SIZE) { + unix_write( buff->fd, buff->data, buff->count ); + buff->count = 0; + } + } +} + +static void +file_buff_done( FileBuff buff ) +{ + if (buff->count > 0) { + unix_write( buff->fd, buff->data, buff->count ); + buff->count = 0; + } +} + +static void +log_header(void) +{ + FILE* out; + char cmdline[1024]; + char uname[128]; + char cpuinfo[128]; + char* cpu; + char date[32]; + time_t now_t = time(NULL); + struct tm now = *localtime(&now_t); + strftime(date, sizeof(date), "%x %X", &now); + + out = fopen( LOG_HEADER, "w" ); + if (out == NULL) + return; + + proc_read("/proc/cmdline", cmdline, sizeof(cmdline)); + proc_read("/proc/version", uname, sizeof(uname)); + proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo)); + + cpu = strchr( cpuinfo, ':' ); + if (cpu) { + char* p = strchr(cpu, '\n'); + cpu += 2; + if (p) + *p = 0; + } + + fprintf(out, "version = %s\n", VERSION); + fprintf(out, "title = Boot chart for Android ( %s )\n", date); + fprintf(out, "system.uname = %s\n", uname); + fprintf(out, "system.release = 0.0\n"); + fprintf(out, "system.cpu = %s\n", cpu); + fprintf(out, "system.kernel.options = %s\n", cmdline); + fclose(out); +} + +static void +close_on_exec(int fd) +{ + fcntl(fd, F_SETFD, FD_CLOEXEC); +} + +static void +open_log_file(int* plogfd, const char* logfile) +{ + int logfd = *plogfd; + + /* create log file if needed */ + if (logfd < 0) + { + logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755); + if (logfd < 0) { + *plogfd = -2; + return; + } + close_on_exec(logfd); + *plogfd = logfd; + } +} + +static void +do_log_uptime(FileBuff log) +{ + char buff[65]; + int fd, ret, len; + + fd = open("/proc/uptime",O_RDONLY); + if (fd >= 0) { + int ret; + ret = unix_read(fd, buff, 64); + close(fd); + buff[64] = 0; + if (ret >= 0) { + long long jiffies = 100LL*strtod(buff,NULL); + int len; + snprintf(buff,sizeof(buff),"%lld\n",jiffies); + len = strlen(buff); + file_buff_write(log, buff, len); + } + } +} + +static void +do_log_ln(FileBuff log) +{ + file_buff_write(log, "\n", 1); +} + + +static void +do_log_file(FileBuff log, const char* procfile) +{ + char buff[1024]; + int fd; + + do_log_uptime(log); + + /* append file content */ + fd = open(procfile,O_RDONLY); + if (fd >= 0) { + close_on_exec(fd); + for (;;) { + int ret; + ret = unix_read(fd, buff, sizeof(buff)); + if (ret <= 0) + break; + + file_buff_write(log, buff, ret); + if (ret < (int)sizeof(buff)) + break; + } + close(fd); + } + + do_log_ln(log); +} + +static void +do_log_procs(FileBuff log) +{ + DIR* dir = opendir("/proc"); + struct dirent* entry; + + do_log_uptime(log); + + while ((entry = readdir(dir)) != NULL) { + /* only match numeric values */ + char* end; + int pid = strtol( entry->d_name, &end, 10); + if (end != NULL && end > entry->d_name && *end == 0) { + char filename[32]; + char buff[1024]; + char cmdline[1024]; + int len; + int fd; + + /* read command line and extract program name */ + snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid); + proc_read(filename, cmdline, sizeof(cmdline)); + + /* read process stat line */ + snprintf(filename,sizeof(filename),"/proc/%d/stat",pid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + len = unix_read(fd, buff, sizeof(buff)-1); + close(fd); + if (len > 0) { + int len2 = strlen(cmdline); + if (len2 > 0) { + /* we want to substitute the process name with its real name */ + const char* p1; + const char* p2; + buff[len] = 0; + p1 = strchr(buff, '('); + p2 = strchr(p1, ')'); + file_buff_write(log, buff, p1+1-buff); + file_buff_write(log, cmdline, strlen(cmdline)); + file_buff_write(log, p2, strlen(p2)); + } else { + /* no substitution */ + file_buff_write(log,buff,len); + } + } + } + } + } + closedir(dir); + do_log_ln(log); +} + +static FileBuffRec log_stat[1]; +static FileBuffRec log_procs[1]; +static FileBuffRec log_disks[1]; + +/* called to setup bootcharting */ +int bootchart_init( void ) +{ + int ret; + char buff[4]; + int timeout = 0, count = 0; + + buff[0] = 0; + proc_read( LOG_STARTFILE, buff, sizeof(buff) ); + if (buff[0] != 0) { + timeout = atoi(buff); + } + else { + /* when running with emulator, androidboot.bootchart=<timeout> + * might be passed by as kernel parameters to specify the bootchart + * timeout. this is useful when using -wipe-data since the /data + * partition is fresh + */ + char cmdline[1024]; + char* s; +#define KERNEL_OPTION "androidboot.bootchart=" + proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) ); + s = strstr(cmdline, KERNEL_OPTION); + if (s) { + s += sizeof(KERNEL_OPTION)-1; + timeout = atoi(s); + } + } + if (timeout == 0) + return 0; + + if (timeout > BOOTCHART_MAX_TIME_SEC) + timeout = BOOTCHART_MAX_TIME_SEC; + + count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS; + + do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR); + + file_buff_open(log_stat, LOG_STAT); + file_buff_open(log_procs, LOG_PROCS); + file_buff_open(log_disks, LOG_DISK); + + /* create kernel process accounting file */ + { + int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644); + if (fd >= 0) { + close(fd); + acct( LOG_ACCT ); + } + } + + log_header(); + return count; +} + +/* called each time you want to perform a bootchart sampling op */ +int bootchart_step( void ) +{ + do_log_file(log_stat, "/proc/stat"); + do_log_file(log_disks, "/proc/diskstats"); + do_log_procs(log_procs); + + /* we stop when /data/bootchart-stop contains 1 */ + { + char buff[2]; + if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') { + return -1; + } + } + + return 0; +} + +void bootchart_finish( void ) +{ + unlink( LOG_STOPFILE ); + file_buff_done(log_stat); + file_buff_done(log_disks); + file_buff_done(log_procs); + acct(NULL); +} diff --git a/init/bootchart.h b/init/bootchart.h new file mode 100644 index 0000000..39d2d4f --- /dev/null +++ b/init/bootchart.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BOOTCHART_H +#define _BOOTCHART_H + +#ifndef BOOTCHART +# define BOOTCHART 0 +#endif + +#if BOOTCHART + +extern int bootchart_init(void); +extern int bootchart_step(void); +extern void bootchart_finish(void); + +# define BOOTCHART_POLLING_MS 200 /* polling period in ms */ +# define BOOTCHART_DEFAULT_TIME_SEC (2*60) /* default polling time in seconds */ +# define BOOTCHART_MAX_TIME_SEC (10*60) /* max polling time in seconds */ + +#endif /* BOOTCHART */ + +#endif /* _BOOTCHART_H */ diff --git a/init/builtins.c b/init/builtins.c new file mode 100644 index 0000000..95fb223 --- /dev/null +++ b/init/builtins.c @@ -0,0 +1,442 @@ +/* + * 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 <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <linux/kd.h> +#include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <sys/mount.h> +#include <sys/resource.h> + +#include "init.h" +#include "keywords.h" +#include "property_service.h" +#include "devices.h" + +#include <private/android_filesystem_config.h> + +void add_environment(const char *name, const char *value); + +extern int init_module(void *, unsigned long, const char *); + +static int write_file(const char *path, const char *value) +{ + int fd, ret, len; + + fd = open(path, O_WRONLY|O_CREAT, 0622); + + if (fd < 0) + return -1; + + len = strlen(value); + + do { + ret = write(fd, value, len); + } while (ret < 0 && errno == EINTR); + + close(fd); + if (ret < 0) { + return -1; + } else { + return 0; + } +} + +static int insmod(const char *filename, char *options) +{ + void *module; + unsigned size; + int ret; + + module = read_file(filename, &size); + if (!module) + return -1; + + ret = init_module(module, size, options); + + free(module); + + return ret; +} + +static int setkey(struct kbentry *kbe) +{ + int fd, ret; + + fd = open("/dev/tty0", O_RDWR | O_SYNC); + if (fd < 0) + return -1; + + ret = ioctl(fd, KDSKBENT, kbe); + + close(fd); + return ret; +} + +static int __ifupdown(const char *interface, int up) +{ + struct ifreq ifr; + int s, ret; + + strlcpy(ifr.ifr_name, interface, IFNAMSIZ); + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return -1; + + ret = ioctl(s, SIOCGIFFLAGS, &ifr); + if (ret < 0) { + goto done; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + ret = ioctl(s, SIOCSIFFLAGS, &ifr); + +done: + close(s); + return ret; +} + +static void service_start_if_not_disabled(struct service *svc) +{ + if (!(svc->flags & SVC_DISABLED)) { + service_start(svc); + } +} + +int do_class_start(int nargs, char **args) +{ + /* Starting a class does not start services + * which are explicitly disabled. They must + * be started individually. + */ + service_for_each_class(args[1], service_start_if_not_disabled); + return 0; +} + +int do_class_stop(int nargs, char **args) +{ + service_for_each_class(args[1], service_stop); + return 0; +} + +int do_domainname(int nargs, char **args) +{ + return write_file("/proc/sys/kernel/domainname", args[1]); +} + +int do_exec(int nargs, char **args) +{ + return -1; +} + +int do_export(int nargs, char **args) +{ + add_environment(args[1], args[2]); + return 0; +} + +int do_hostname(int nargs, char **args) +{ + return write_file("/proc/sys/kernel/hostname", args[1]); +} + +int do_ifup(int nargs, char **args) +{ + return __ifupdown(args[1], 1); +} + + +static int do_insmod_inner(int nargs, char **args, int opt_len) +{ + char options[opt_len + 1]; + int i; + + options[0] = '\0'; + if (nargs > 2) { + strcpy(options, args[2]); + for (i = 3; i < nargs; ++i) { + strcat(options, " "); + strcat(options, args[i]); + } + } + + return insmod(args[1], options); +} + +int do_insmod(int nargs, char **args) +{ + int i; + int size = 0; + + if (nargs > 2) { + for (i = 2; i < nargs; ++i) + size += strlen(args[i]) + 1; + } + + return do_insmod_inner(nargs, args, size); +} + +int do_import(int nargs, char **args) +{ + return -1; +} + +int do_mkdir(int nargs, char **args) +{ + mode_t mode = 0755; + + /* mkdir <path> [mode] [owner] [group] */ + + if (nargs >= 3) { + mode = strtoul(args[2], 0, 8); + } + + if (mkdir(args[1], mode)) { + return -errno; + } + + if (nargs >= 4) { + uid_t uid = decode_uid(args[3]); + gid_t gid = -1; + + if (nargs == 5) { + gid = decode_uid(args[4]); + } + + if (chown(args[1], uid, gid)) { + return -errno; + } + } + + return 0; +} + +static struct { + const char *name; + unsigned flag; +} mount_flags[] = { + { "noatime", MS_NOATIME }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "defaults", 0 }, + { 0, 0 }, +}; + +/* mount <type> <device> <path> <flags ...> <options> */ +int do_mount(int nargs, char **args) +{ + char tmp[64]; + char *source; + char *options = NULL; + unsigned flags = 0; + int n, i; + + for (n = 4; n < nargs; n++) { + for (i = 0; mount_flags[i].name; i++) { + if (!strcmp(args[n], mount_flags[i].name)) { + flags |= mount_flags[i].flag; + break; + } + } + + /* if our last argument isn't a flag, wolf it up as an option string */ + if (n + 1 == nargs && !mount_flags[i].name) + options = args[n]; + } + + source = args[2]; + if (!strncmp(source, "mtd@", 4)) { + n = mtd_name_to_number(source + 4); + if (n >= 0) { + sprintf(tmp, "/dev/block/mtdblock%d", n); + source = tmp; + } + } + return mount(source, args[3], args[1], flags, options); +} + +int do_setkey(int nargs, char **args) +{ + struct kbentry kbe; + kbe.kb_table = strtoul(args[1], 0, 0); + kbe.kb_index = strtoul(args[2], 0, 0); + kbe.kb_value = strtoul(args[3], 0, 0); + return setkey(&kbe); +} + +int do_setprop(int nargs, char **args) +{ + property_set(args[1], args[2]); + return 0; +} + +int do_setrlimit(int nargs, char **args) +{ + struct rlimit limit; + int resource; + resource = atoi(args[1]); + limit.rlim_cur = atoi(args[2]); + limit.rlim_max = atoi(args[3]); + return setrlimit(resource, &limit); +} + +int do_start(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + service_start(svc); + } + return 0; +} + +int do_stop(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + service_stop(svc); + } + return 0; +} + +int do_restart(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + service_stop(svc); + service_start(svc); + } + return 0; +} + +int do_trigger(int nargs, char **args) +{ + return 0; +} + +int do_symlink(int nargs, char **args) +{ + return symlink(args[1], args[2]); +} + +int do_sysclktz(int nargs, char **args) +{ + struct timezone tz; + + if (nargs != 2) + return -1; + + memset(&tz, 0, sizeof(tz)); + tz.tz_minuteswest = atoi(args[1]); + if (settimeofday(NULL, &tz)) + return -1; + return 0; +} + +int do_write(int nargs, char **args) +{ + return write_file(args[1], args[2]); +} + +int do_chown(int nargs, char **args) { + /* GID is optional. */ + if (nargs == 3) { + if (chown(args[2], decode_uid(args[1]), -1) < 0) + return -errno; + } else if (nargs == 4) { + if (chown(args[3], decode_uid(args[1]), decode_uid(args[2]))) + return -errno; + } else { + return -1; + } + return 0; +} + +static mode_t get_mode(const char *s) { + mode_t mode = 0; + while (*s) { + if (*s >= '0' && *s <= '7') { + mode = (mode<<3) | (*s-'0'); + } else { + return -1; + } + s++; + } + return mode; +} + +int do_chmod(int nargs, char **args) { + mode_t mode = get_mode(args[1]); + if (chmod(args[2], mode) < 0) { + return -errno; + } + return 0; +} + +int do_loglevel(int nargs, char **args) { + if (nargs == 2) { + log_set_level(atoi(args[1])); + return 0; + } + return -1; +} + +int do_device(int nargs, char **args) { + int len; + char tmp[64]; + char *source = args[1]; + int prefix = 0; + + if (nargs != 5) + return -1; + /* Check for wildcard '*' at the end which indicates a prefix. */ + len = strlen(args[1]) - 1; + if (args[1][len] == '*') { + args[1][len] = '\0'; + prefix = 1; + } + /* If path starts with mtd@ lookup the mount number. */ + if (!strncmp(source, "mtd@", 4)) { + int n = mtd_name_to_number(source + 4); + if (n >= 0) { + snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n); + source = tmp; + } + } + add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]), + decode_uid(args[4]), prefix); + return 0; +} diff --git a/init/devices.c b/init/devices.c new file mode 100644 index 0000000..b1ef6ab --- /dev/null +++ b/init/devices.c @@ -0,0 +1,626 @@ +/* + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <linux/netlink.h> +#include <private/android_filesystem_config.h> +#include <sys/time.h> +#include <asm/page.h> + +#include "init.h" +#include "devices.h" + +#define CMDLINE_PREFIX "/dev" +#define SYSFS_PREFIX "/sys" +#define FIRMWARE_DIR "/etc/firmware" +#define MAX_QEMU_PERM 6 + +struct uevent { + const char *action; + const char *path; + const char *subsystem; + const char *firmware; + int major; + int minor; +}; + +static int open_uevent_socket(void) +{ + struct sockaddr_nl addr; + int sz = 64*1024; // XXX larger? udev uses 16MB! + int s; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0xffffffff; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(s < 0) + return -1; + + setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(s); + return -1; + } + + return s; +} + +struct perms_ { + char *name; + mode_t perm; + unsigned int uid; + unsigned int gid; + unsigned short prefix; +}; +static struct perms_ devperms[] = { + { "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 }, + { "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 }, + + /* logger should be world writable (for logging) but not readable */ + { "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 }, + + /* these should not be world writable */ + { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 }, + { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 }, + { "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, + { "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, + { "/dev/uinput", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, + { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 }, + { "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 }, + { "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 }, + { "/dev/hw3d", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, + { "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 }, + { "/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 }, + { "/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 }, + { "/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, + { "/dev/pmem_gpu", 0660, AID_SYSTEM, AID_GRAPHICS, 1 }, + { "/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 }, + { "/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 }, + { "/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 }, + { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, + { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, + { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 }, + { "/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/msm_snd", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 }, + { "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 }, + { "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 }, + { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 }, + { "/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 }, + { "/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 }, + { "/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 }, + { "/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 }, + { NULL, 0, 0, 0, 0 }, +}; + +/* devperms_partners list and perm_node are for hardware specific /dev entries */ +struct perm_node { + struct perms_ dp; + struct listnode plist; +}; +list_declare(devperms_partners); + +/* + * Permission override when in emulator mode, must be parsed before + * system properties is initalized. + */ +static int qemu_perm_count; +static struct perms_ qemu_perms[MAX_QEMU_PERM + 1]; + +int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, + unsigned int gid, unsigned short prefix) { + int size; + struct perm_node *node = malloc(sizeof (struct perm_node)); + if (!node) + return -ENOMEM; + + size = strlen(name) + 1; + if ((node->dp.name = malloc(size)) == NULL) + return -ENOMEM; + + memcpy(node->dp.name, name, size); + node->dp.perm = perm; + node->dp.uid = uid; + node->dp.gid = gid; + node->dp.prefix = prefix; + + list_add_tail(&devperms_partners, &node->plist); + return 0; +} + +void qemu_init(void) { + qemu_perm_count = 0; + memset(&qemu_perms, 0, sizeof(qemu_perms)); +} + +static int qemu_perm(const char* name, mode_t perm, unsigned int uid, + unsigned int gid, unsigned short prefix) +{ + char *buf; + if (qemu_perm_count == MAX_QEMU_PERM) + return -ENOSPC; + + buf = malloc(strlen(name) + 1); + if (!buf) + return -errno; + + strlcpy(buf, name, strlen(name) + 1); + qemu_perms[qemu_perm_count].name = buf; + qemu_perms[qemu_perm_count].perm = perm; + qemu_perms[qemu_perm_count].uid = uid; + qemu_perms[qemu_perm_count].gid = gid; + qemu_perms[qemu_perm_count].prefix = prefix; + + qemu_perm_count++; + return 0; +} + +/* Permission overrides for emulator that are parsed from /proc/cmdline. */ +void qemu_cmdline(const char* name, const char *value) +{ + char *buf; + if (!strcmp(name, "android.ril")) { + /* cmd line params currently assume /dev/ prefix */ + if (asprintf(&buf, CMDLINE_PREFIX"/%s", value) == -1) { + return; + } + INFO("nani- buf:: %s\n", buf); + qemu_perm(buf, 0660, AID_RADIO, AID_ROOT, 0); + } +} + +static int get_device_perm_inner(struct perms_ *perms, const char *path, + unsigned *uid, unsigned *gid, mode_t *perm) +{ + int i; + for(i = 0; perms[i].name; i++) { + + if(perms[i].prefix) { + if(strncmp(path, perms[i].name, strlen(perms[i].name))) + continue; + } else { + if(strcmp(path, perms[i].name)) + continue; + } + *uid = perms[i].uid; + *gid = perms[i].gid; + *perm = perms[i].perm; + return 0; + } + return -1; +} + +/* First checks for emulator specific permissions specified in /proc/cmdline. */ +static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) +{ + mode_t perm; + + if (get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) { + return perm; + } else if (get_device_perm_inner(devperms, path, uid, gid, &perm) == 0) { + return perm; + } else { + struct listnode *node; + struct perm_node *perm_node; + struct perms_ *dp; + + /* Check partners list. */ + list_for_each(node, &devperms_partners) { + perm_node = node_to_item(node, struct perm_node, plist); + dp = &perm_node->dp; + + if (dp->prefix) { + if (strncmp(path, dp->name, strlen(dp->name))) + continue; + } else { + if (strcmp(path, dp->name)) + continue; + } + /* Found perm in partner list. */ + *uid = dp->uid; + *gid = dp->gid; + return dp->perm; + } + /* Default if nothing found. */ + *uid = 0; + *gid = 0; + return 0600; + } +} + +static void make_device(const char *path, int block, int major, int minor) +{ + unsigned uid; + unsigned gid; + mode_t mode; + dev_t dev; + + if(major > 255 || minor > 255) + return; + + mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); + dev = (major << 8) | minor; + mknod(path, mode, dev); + chown(path, uid, gid); +} + +#ifdef LOG_UEVENTS + +static inline suseconds_t get_usecs(void) +{ + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec; +} + +#define log_event_print(x...) INFO(x) + +#else + +#define log_event_print(fmt, args...) do { } while (0) +#define get_usecs() 0 + +#endif + +static void parse_event(const char *msg, struct uevent *uevent) +{ + uevent->action = ""; + uevent->path = ""; + uevent->subsystem = ""; + uevent->firmware = ""; + uevent->major = -1; + uevent->minor = -1; + + /* currently ignoring SEQNUM */ + while(*msg) { + if(!strncmp(msg, "ACTION=", 7)) { + msg += 7; + uevent->action = msg; + } else if(!strncmp(msg, "DEVPATH=", 8)) { + msg += 8; + uevent->path = msg; + } else if(!strncmp(msg, "SUBSYSTEM=", 10)) { + msg += 10; + uevent->subsystem = msg; + } else if(!strncmp(msg, "FIRMWARE=", 9)) { + msg += 9; + uevent->firmware = msg; + } else if(!strncmp(msg, "MAJOR=", 6)) { + msg += 6; + uevent->major = atoi(msg); + } else if(!strncmp(msg, "MINOR=", 6)) { + msg += 6; + uevent->minor = atoi(msg); + } + + /* advance to after the next \0 */ + while(*msg++) + ; + } + + log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n", + uevent->action, uevent->path, uevent->subsystem, + uevent->firmware, uevent->major, uevent->minor); +} + +static void handle_device_event(struct uevent *uevent) +{ + char devpath[96]; + char *base, *name; + int block; + + /* if it's not a /dev device, nothing to do */ + if((uevent->major < 0) || (uevent->minor < 0)) + return; + + /* do we have a name? */ + name = strrchr(uevent->path, '/'); + if(!name) + return; + name++; + + /* too-long names would overrun our buffer */ + if(strlen(name) > 64) + return; + + /* are we block or char? where should we live? */ + if(!strncmp(uevent->subsystem, "block", 5)) { + block = 1; + base = "/dev/block/"; + mkdir(base, 0755); + } else { + block = 0; + /* this should probably be configurable somehow */ + if(!strncmp(uevent->subsystem, "graphics", 8)) { + base = "/dev/graphics/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { + base = "/dev/oncrpc/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "adsp", 4)) { + base = "/dev/adsp/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "input", 5)) { + base = "/dev/input/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "mtd", 3)) { + base = "/dev/mtd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "misc", 4) && + !strncmp(name, "log_", 4)) { + base = "/dev/log/"; + mkdir(base, 0755); + name += 4; + } else + base = "/dev/"; + } + + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + + if(!strcmp(uevent->action, "add")) { + make_device(devpath, block, uevent->major, uevent->minor); + return; + } + + if(!strcmp(uevent->action, "remove")) { + unlink(devpath); + return; + } +} + +static int load_firmware(int fw_fd, int loading_fd, int data_fd) +{ + struct stat st; + long len_to_copy; + int ret = 0; + + if(fstat(fw_fd, &st) < 0) + return -1; + len_to_copy = st.st_size; + + write(loading_fd, "1", 1); /* start transfer */ + + while (len_to_copy > 0) { + char buf[PAGE_SIZE]; + ssize_t nr; + + nr = read(fw_fd, buf, sizeof(buf)); + if(!nr) + break; + if(nr < 0) { + ret = -1; + break; + } + + len_to_copy -= nr; + while (nr > 0) { + ssize_t nw = 0; + + nw = write(data_fd, buf + nw, nr); + if(nw <= 0) { + ret = -1; + goto out; + } + nr -= nw; + } + } + +out: + if(!ret) + write(loading_fd, "0", 1); /* successful end of transfer */ + else + write(loading_fd, "-1", 2); /* abort transfer */ + + return ret; +} + +static void process_firmware_event(struct uevent *uevent) +{ + char *root, *loading, *data, *file; + int l, loading_fd, data_fd, fw_fd; + + log_event_print("firmware event { '%s', '%s' }\n", + uevent->path, uevent->firmware); + + l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); + if (l == -1) + return; + + l = asprintf(&loading, "%sloading", root); + if (l == -1) + goto root_free_out; + + l = asprintf(&data, "%sdata", root); + if (l == -1) + goto loading_free_out; + + l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware); + if (l == -1) + goto data_free_out; + + loading_fd = open(loading, O_WRONLY); + if(loading_fd < 0) + goto file_free_out; + + data_fd = open(data, O_WRONLY); + if(data_fd < 0) + goto loading_close_out; + + fw_fd = open(file, O_RDONLY); + if(fw_fd < 0) + goto data_close_out; + + if(!load_firmware(fw_fd, loading_fd, data_fd)) + log_event_print("firmware copy success { '%s', '%s' }\n", root, file); + else + log_event_print("firmware copy failure { '%s', '%s' }\n", root, file); + + close(fw_fd); +data_close_out: + close(data_fd); +loading_close_out: + close(loading_fd); +file_free_out: + free(file); +data_free_out: + free(data); +loading_free_out: + free(loading); +root_free_out: + free(root); +} + +static void handle_firmware_event(struct uevent *uevent) +{ + pid_t pid; + + if(strcmp(uevent->subsystem, "firmware")) + return; + + if(strcmp(uevent->action, "add")) + return; + + /* we fork, to avoid making large memory allocations in init proper */ + pid = fork(); + if (!pid) { + process_firmware_event(uevent); + exit(EXIT_SUCCESS); + } +} + +#define UEVENT_MSG_LEN 1024 +void handle_device_fd(int fd) +{ + char msg[UEVENT_MSG_LEN+2]; + int n; + + while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { + struct uevent uevent; + + if(n == UEVENT_MSG_LEN) /* overflow -- discard */ + continue; + + msg[n] = '\0'; + msg[n+1] = '\0'; + + parse_event(msg, &uevent); + + handle_device_event(&uevent); + handle_firmware_event(&uevent); + } +} + +/* Coldboot walks parts of the /sys tree and pokes the uevent files +** to cause the kernel to regenerate device add events that happened +** before init's device manager was started +** +** We drain any pending events from the netlink socket every time +** we poke another uevent file to make sure we don't overrun the +** socket's buffer. +*/ + +static void do_coldboot(int event_fd, DIR *d) +{ + struct dirent *de; + int dfd, fd; + + dfd = dirfd(d); + + fd = openat(dfd, "uevent", O_WRONLY); + if(fd >= 0) { + write(fd, "add\n", 4); + close(fd); + handle_device_fd(event_fd); + } + + while((de = readdir(d))) { + DIR *d2; + + if(de->d_type != DT_DIR || de->d_name[0] == '.') + continue; + + fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); + if(fd < 0) + continue; + + d2 = fdopendir(fd); + if(d2 == 0) + close(fd); + else { + do_coldboot(event_fd, d2); + closedir(d2); + } + } +} + +static void coldboot(int event_fd, const char *path) +{ + DIR *d = opendir(path); + if(d) { + do_coldboot(event_fd, d); + closedir(d); + } +} + +int device_init(void) +{ + suseconds_t t0, t1; + int fd; + + fd = open_uevent_socket(); + if(fd < 0) + return -1; + + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + + t0 = get_usecs(); + coldboot(fd, "/sys/class"); + coldboot(fd, "/sys/block"); + coldboot(fd, "/sys/devices"); + t1 = get_usecs(); + + log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); + + return fd; +} diff --git a/init/devices.h b/init/devices.h new file mode 100644 index 0000000..b484da4 --- /dev/null +++ b/init/devices.h @@ -0,0 +1,27 @@ +/* + * 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 _INIT_DEVICES_H +#define _INIT_DEVICES_H + +extern void handle_device_fd(int fd); +extern int device_init(void); +extern void qemu_init(void); +extern void qemu_cmdline(const char* name, const char *value); +extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, + unsigned int gid, unsigned short prefix); + +#endif /* _INIT_DEVICES_H */ diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh new file mode 100755 index 0000000..7fe8904 --- /dev/null +++ b/init/grab-bootchart.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# this script is used to retrieve the bootchart log generated +# by init when compiled with INIT_BOOTCHART=true. +# +# for all details, see //device/system/init/README.BOOTCHART +# +TMPDIR=/tmp/android-bootchart +rm -rf $TMPDIR +mkdir -p $TMPDIR + +LOGROOT=/data/bootchart +TARBALL=bootchart.tgz + +FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct" + +for f in $FILES; do + adb pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null +done +(cd $TMPDIR && tar -czf $TARBALL $FILES) +cp -f $TMPDIR/$TARBALL ./$TARBALL +echo "look at $TARBALL" diff --git a/init/init.c b/init/init.c new file mode 100644 index 0000000..b8b4f40 --- /dev/null +++ b/init/init.c @@ -0,0 +1,989 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <time.h> +#include <errno.h> +#include <stdarg.h> +#include <mtd/mtd-user.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/reboot.h> + +#include <cutils/sockets.h> +#include <termios.h> +#include <linux/kd.h> +#include <linux/keychord.h> + +#include <sys/system_properties.h> + +#include "devices.h" +#include "init.h" +#include "property_service.h" +#include "bootchart.h" + +static int property_triggers_enabled = 0; + +#if BOOTCHART +static int bootchart_count; +#endif + +static char console[32]; +static char serialno[32]; +static char bootmode[32]; +static char baseband[32]; +static char carrier[32]; +static char bootloader[32]; +static char hardware[32]; +static unsigned revision = 0; +static char qemu[32]; +static struct input_keychord *keychords = 0; +static int keychords_count = 0; +static int keychords_length = 0; + +static void drain_action_queue(void); + +static void notify_service_state(const char *name, const char *state) +{ + char pname[PROP_NAME_MAX]; + int len = strlen(name); + if ((len + 10) > PROP_NAME_MAX) + return; + snprintf(pname, sizeof(pname), "init.svc.%s", name); + property_set(pname, state); +} + +static int have_console; +static char *console_name = "/dev/console"; +static time_t process_needs_restart; + +static const char *ENV[32]; + +/* add_environment - add "key=value" to the current environment */ +int add_environment(const char *key, const char *val) +{ + int n; + + for (n = 0; n < 31; n++) { + if (!ENV[n]) { + size_t len = strlen(key) + strlen(val) + 2; + char *entry = malloc(len); + snprintf(entry, len, "%s=%s", key, val); + ENV[n] = entry; + return 0; + } + } + + return 1; +} + +static void zap_stdio(void) +{ + int fd; + fd = open("/dev/null", O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); +} + +static void open_console() +{ + int fd; + if ((fd = open(console_name, O_RDWR)) < 0) { + fd = open("/dev/null", O_RDWR); + } + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); +} + +/* + * gettime() - returns the time in seconds of the system's monotonic clock or + * zero on error. + */ +static time_t gettime(void) +{ + struct timespec ts; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret < 0) { + ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); + return 0; + } + + return ts.tv_sec; +} + +static void publish_socket(const char *name, int fd) +{ + char key[64] = ANDROID_SOCKET_ENV_PREFIX; + char val[64]; + + strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, + name, + sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); + snprintf(val, sizeof(val), "%d", fd); + add_environment(key, val); + + /* make sure we don't close-on-exec */ + fcntl(fd, F_SETFD, 0); +} + +void service_start(struct service *svc) +{ + struct stat s; + pid_t pid; + int needs_console; + int n; + + /* starting a service removes it from the disabled + * state and immediately takes it out of the restarting + * state if it was in there + */ + svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING)); + svc->time_started = 0; + + /* running processes require no additional work -- if + * they're in the process of exiting, we've ensured + * that they will immediately restart on exit, unless + * they are ONESHOT + */ + if (svc->flags & SVC_RUNNING) { + return; + } + + needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; + if (needs_console && (!have_console)) { + ERROR("service '%s' requires console\n", svc->name); + svc->flags |= SVC_DISABLED; + return; + } + + if (stat(svc->args[0], &s) != 0) { + ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); + svc->flags |= SVC_DISABLED; + return; + } + + NOTICE("starting '%s'\n", svc->name); + + pid = fork(); + + if (pid == 0) { + struct socketinfo *si; + struct svcenvinfo *ei; + char tmp[32]; + int fd, sz; + + get_property_workspace(&fd, &sz); + sprintf(tmp, "%d,%d", dup(fd), sz); + add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); + + for (ei = svc->envvars; ei; ei = ei->next) + add_environment(ei->name, ei->value); + + for (si = svc->sockets; si; si = si->next) { + int s = create_socket(si->name, + !strcmp(si->type, "dgram") ? + SOCK_DGRAM : SOCK_STREAM, + si->perm, si->uid, si->gid); + if (s >= 0) { + publish_socket(si->name, s); + } + } + + if (needs_console) { + setsid(); + open_console(); + } else { + zap_stdio(); + } + +#if 0 + for (n = 0; svc->args[n]; n++) { + INFO("args[%d] = '%s'\n", n, svc->args[n]); + } + for (n = 0; ENV[n]; n++) { + INFO("env[%d] = '%s'\n", n, ENV[n]); + } +#endif + + setpgid(0, getpid()); + + /* as requested, set our gid, supplemental gids, and uid */ + if (svc->gid) { + setgid(svc->gid); + } + if (svc->nr_supp_gids) { + setgroups(svc->nr_supp_gids, svc->supp_gids); + } + if (svc->uid) { + setuid(svc->uid); + } + + execve(svc->args[0], (char**) svc->args, (char**) ENV); + _exit(127); + } + + if (pid < 0) { + ERROR("failed to start '%s'\n", svc->name); + svc->pid = 0; + return; + } + + svc->time_started = gettime(); + svc->pid = pid; + svc->flags |= SVC_RUNNING; + + notify_service_state(svc->name, "running"); +} + +void service_stop(struct service *svc) +{ + /* we are no longer running, nor should we + * attempt to restart + */ + svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING)); + + /* if the service has not yet started, prevent + * it from auto-starting with its class + */ + svc->flags |= SVC_DISABLED; + + if (svc->pid) { + NOTICE("service '%s' is being killed\n", svc->name); + kill(-svc->pid, SIGTERM); + notify_service_state(svc->name, "stopping"); + } else { + notify_service_state(svc->name, "stopped"); + } +} + +void property_changed(const char *name, const char *value) +{ + if (property_triggers_enabled) { + queue_property_triggers(name, value); + drain_action_queue(); + } +} + +#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */ +#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/ + +static int wait_for_one_process(int block) +{ + pid_t pid; + int status; + struct service *svc; + struct socketinfo *si; + time_t now; + struct listnode *node; + struct command *cmd; + + while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); + if (pid <= 0) return -1; + INFO("waitpid returned pid %d, status = %08x\n", pid, status); + + svc = service_find_by_pid(pid); + if (!svc) { + ERROR("untracked pid %d exited\n", pid); + return 0; + } + + NOTICE("process '%s', pid %d exited\n", svc->name, pid); + + if (!(svc->flags & SVC_ONESHOT)) { + kill(-pid, SIGKILL); + NOTICE("process '%s' killing any children in process group\n", svc->name); + } + + /* remove any sockets we may have created */ + for (si = svc->sockets; si; si = si->next) { + char tmp[128]; + snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); + unlink(tmp); + } + + svc->pid = 0; + svc->flags &= (~SVC_RUNNING); + + /* oneshot processes go into the disabled state on exit */ + if (svc->flags & SVC_ONESHOT) { + svc->flags |= SVC_DISABLED; + } + + /* disabled processes do not get restarted automatically */ + if (svc->flags & SVC_DISABLED) { + notify_service_state(svc->name, "stopped"); + return 0; + } + + now = gettime(); + if (svc->flags & SVC_CRITICAL) { + if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { + if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { + ERROR("critical process '%s' exited %d times in %d minutes; " + "rebooting into recovery mode\n", svc->name, + CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); + sync(); + __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, "recovery"); + return 0; + } + } else { + svc->time_crashed = now; + svc->nr_crashed = 1; + } + } + + /* Execute all onrestart commands for this service. */ + list_for_each(node, &svc->onrestart.commands) { + cmd = node_to_item(node, struct command, clist); + cmd->func(cmd->nargs, cmd->args); + } + svc->flags |= SVC_RESTARTING; + notify_service_state(svc->name, "restarting"); + return 0; +} + +static void restart_service_if_needed(struct service *svc) +{ + time_t next_start_time = svc->time_started + 5; + + if (next_start_time <= gettime()) { + svc->flags &= (~SVC_RESTARTING); + service_start(svc); + return; + } + + if ((next_start_time < process_needs_restart) || + (process_needs_restart == 0)) { + process_needs_restart = next_start_time; + } +} + +static void restart_processes() +{ + process_needs_restart = 0; + service_for_each_flags(SVC_RESTARTING, + restart_service_if_needed); +} + +static int signal_fd = -1; + +static void sigchld_handler(int s) +{ + write(signal_fd, &s, 1); +} + +static void msg_start(const char *name) +{ + struct service *svc = service_find_by_name(name); + + if (svc) { + service_start(svc); + } else { + ERROR("no such service '%s'\n", name); + } +} + +static void msg_stop(const char *name) +{ + struct service *svc = service_find_by_name(name); + + if (svc) { + service_stop(svc); + } else { + ERROR("no such service '%s'\n"); + } +} + +void handle_control_message(const char *msg, const char *arg) +{ + if (!strcmp(msg,"start")) { + msg_start(arg); + } else if (!strcmp(msg,"stop")) { + msg_stop(arg); + } else { + ERROR("unknown control msg '%s'\n", msg); + } +} + +#define MAX_MTD_PARTITIONS 16 + +static struct { + char name[16]; + int number; +} mtd_part_map[MAX_MTD_PARTITIONS]; + +static int mtd_part_count = -1; + +static void find_mtd_partitions(void) +{ + int fd; + char buf[1024]; + char *pmtdbufp; + ssize_t pmtdsize; + int r; + + fd = open("/proc/mtd", O_RDONLY); + if (fd < 0) + return; + + buf[sizeof(buf) - 1] = '\0'; + pmtdsize = read(fd, buf, sizeof(buf) - 1); + pmtdbufp = buf; + while (pmtdsize > 0) { + int mtdnum, mtdsize, mtderasesize; + char mtdname[16]; + mtdname[0] = '\0'; + mtdnum = -1; + r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", + &mtdnum, &mtdsize, &mtderasesize, mtdname); + if ((r == 4) && (mtdname[0] == '"')) { + char *x = strchr(mtdname + 1, '"'); + if (x) { + *x = 0; + } + INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); + if (mtd_part_count < MAX_MTD_PARTITIONS) { + strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); + mtd_part_map[mtd_part_count].number = mtdnum; + mtd_part_count++; + } else { + ERROR("too many mtd partitions\n"); + } + } + while (pmtdsize > 0 && *pmtdbufp != '\n') { + pmtdbufp++; + pmtdsize--; + } + if (pmtdsize > 0) { + pmtdbufp++; + pmtdsize--; + } + } + close(fd); +} + +int mtd_name_to_number(const char *name) +{ + int n; + if (mtd_part_count < 0) { + mtd_part_count = 0; + find_mtd_partitions(); + } + for (n = 0; n < mtd_part_count; n++) { + if (!strcmp(name, mtd_part_map[n].name)) { + return mtd_part_map[n].number; + } + } + return -1; +} + +static void import_kernel_nv(char *name, int in_qemu) +{ + char *value = strchr(name, '='); + + if (value == 0) return; + *value++ = 0; + if (*name == 0) return; + + if (!in_qemu) + { + /* on a real device, white-list the kernel options */ + if (!strcmp(name,"qemu")) { + strlcpy(qemu, value, sizeof(qemu)); + } else if (!strcmp(name,"androidboot.console")) { + strlcpy(console, value, sizeof(console)); + } else if (!strcmp(name,"androidboot.mode")) { + strlcpy(bootmode, value, sizeof(bootmode)); + } else if (!strcmp(name,"androidboot.serialno")) { + strlcpy(serialno, value, sizeof(serialno)); + } else if (!strcmp(name,"androidboot.baseband")) { + strlcpy(baseband, value, sizeof(baseband)); + } else if (!strcmp(name,"androidboot.carrier")) { + strlcpy(carrier, value, sizeof(carrier)); + } else if (!strcmp(name,"androidboot.bootloader")) { + strlcpy(bootloader, value, sizeof(bootloader)); + } else if (!strcmp(name,"androidboot.hardware")) { + strlcpy(hardware, value, sizeof(hardware)); + } else { + qemu_cmdline(name, value); + } + } else { + /* in the emulator, export any kernel option with the + * ro.kernel. prefix */ + char buff[32]; + int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name ); + if (len < (int)sizeof(buff)) { + property_set( buff, value ); + } + } +} + +static void import_kernel_cmdline(int in_qemu) +{ + char cmdline[1024]; + char *ptr; + int fd; + + fd = open("/proc/cmdline", O_RDONLY); + if (fd >= 0) { + int n = read(fd, cmdline, 1023); + if (n < 0) n = 0; + + /* get rid of trailing newline, it happens */ + if (n > 0 && cmdline[n-1] == '\n') n--; + + cmdline[n] = 0; + close(fd); + } else { + cmdline[0] = 0; + } + + ptr = cmdline; + while (ptr && *ptr) { + char *x = strchr(ptr, ' '); + if (x != 0) *x++ = 0; + import_kernel_nv(ptr, in_qemu); + ptr = x; + } + + /* don't expose the raw commandline to nonpriv processes */ + chmod("/proc/cmdline", 0440); +} + +static void get_hardware_name(void) +{ + char data[1024]; + int fd, n; + char *x, *hw, *rev; + + /* Hardware string was provided on kernel command line */ + if (hardware[0]) + return; + + fd = open("/proc/cpuinfo", O_RDONLY); + if (fd < 0) return; + + n = read(fd, data, 1023); + close(fd); + if (n < 0) return; + + data[n] = 0; + hw = strstr(data, "\nHardware"); + rev = strstr(data, "\nRevision"); + + if (hw) { + x = strstr(hw, ": "); + if (x) { + x += 2; + n = 0; + while (*x && !isspace(*x)) { + hardware[n++] = tolower(*x); + x++; + if (n == 31) break; + } + hardware[n] = 0; + } + } + + if (rev) { + x = strstr(rev, ": "); + if (x) { + revision = strtoul(x + 2, 0, 16); + } + } +} + +static void drain_action_queue(void) +{ + struct listnode *node; + struct command *cmd; + struct action *act; + int ret; + + while ((act = action_remove_queue_head())) { + INFO("processing action %p (%s)\n", act, act->name); + list_for_each(node, &act->commands) { + cmd = node_to_item(node, struct command, clist); + ret = cmd->func(cmd->nargs, cmd->args); + INFO("command '%s' r=%d\n", cmd->args[0], ret); + } + } +} + +void open_devnull_stdio(void) +{ + int fd; + static const char *name = "/dev/__null__"; + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { + fd = open(name, O_RDWR); + unlink(name); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) { + close(fd); + } + return; + } + } + + exit(1); +} + +void add_service_keycodes(struct service *svc) +{ + struct input_keychord *keychord; + int i, size; + + if (svc->keycodes) { + /* add a new keychord to the list */ + size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); + keychords = realloc(keychords, keychords_length + size); + if (!keychords) { + ERROR("could not allocate keychords\n"); + keychords_length = 0; + keychords_count = 0; + return; + } + + keychord = (struct input_keychord *)((char *)keychords + keychords_length); + keychord->version = KEYCHORD_VERSION; + keychord->id = keychords_count + 1; + keychord->count = svc->nkeycodes; + svc->keychord_id = keychord->id; + + for (i = 0; i < svc->nkeycodes; i++) { + keychord->keycodes[i] = svc->keycodes[i]; + } + keychords_count++; + keychords_length += size; + } +} + +int open_keychord() +{ + int fd, ret; + + service_for_each(add_service_keycodes); + + /* nothing to do if no services require keychords */ + if (!keychords) + return -1; + + fd = open("/dev/keychord", O_RDWR); + if (fd < 0) { + ERROR("could not open /dev/keychord\n"); + return fd; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + + ret = write(fd, keychords, keychords_length); + if (ret != keychords_length) { + ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); + close(fd); + fd = -1; + } + + free(keychords); + keychords = 0; + + return fd; +} + +void handle_keychord(int fd) +{ + struct service *svc; + int ret; + __u16 id; + + ret = read(fd, &id, sizeof(id)); + if (ret != sizeof(id)) { + ERROR("could not read keychord id\n"); + return; + } + + svc = service_find_by_keychord(id); + if (svc) { + INFO("starting service %s from keychord\n", svc->name); + service_start(svc); + } else { + ERROR("service for keychord %d not found\n", id); + } +} + +int main(int argc, char **argv) +{ + int device_fd = -1; + int property_set_fd = -1; + int signal_recv_fd = -1; + int keychord_fd = -1; + int fd_count; + int s[2]; + int fd; + struct sigaction act; + char tmp[PROP_VALUE_MAX]; + struct pollfd ufds[4]; + char *tmpdev; + char* debuggable; + + act.sa_handler = sigchld_handler; + act.sa_flags = SA_NOCLDSTOP; + act.sa_mask = 0; + act.sa_restorer = NULL; + sigaction(SIGCHLD, &act, 0); + + /* clear the umask */ + umask(0); + + /* Get the basic filesystem setup we need put + * together in the initramdisk on / and then we'll + * let the rc file figure out the rest. + */ + mkdir("/dev", 0755); + mkdir("/proc", 0755); + mkdir("/sys", 0755); + + mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755"); + mkdir("/dev/pts", 0755); + mkdir("/dev/socket", 0755); + mount("devpts", "/dev/pts", "devpts", 0, NULL); + mount("proc", "/proc", "proc", 0, NULL); + mount("sysfs", "/sys", "sysfs", 0, NULL); + + /* We must have some place other than / to create the + * device nodes for kmsg and null, otherwise we won't + * be able to remount / read-only later on. + * Now that tmpfs is mounted on /dev, we can actually + * talk to the outside world. + */ + open_devnull_stdio(); + log_init(); + + INFO("reading config file\n"); + parse_config_file("/init.rc"); + + /* pull the kernel commandline and ramdisk properties file in */ + qemu_init(); + import_kernel_cmdline(0); + + get_hardware_name(); + snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); + parse_config_file(tmp); + + action_for_each_trigger("early-init", action_add_queue_tail); + drain_action_queue(); + + INFO("device init\n"); + device_fd = device_init(); + + property_init(); + + // only listen for keychords if ro.debuggable is true + debuggable = property_get("ro.debuggable"); + if (debuggable && !strcmp(debuggable, "1")) { + keychord_fd = open_keychord(); + } + + if (console[0]) { + snprintf(tmp, sizeof(tmp), "/dev/%s", console); + console_name = strdup(tmp); + } + + fd = open(console_name, O_RDWR); + if (fd >= 0) + have_console = 1; + close(fd); + + if( load_565rle_image(INIT_IMAGE_FILE) ) { + fd = open("/dev/tty0", O_WRONLY); + if (fd >= 0) { + const char *msg; + msg = "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" // console is 40 cols x 30 lines + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " A N D R O I D "; + write(fd, msg, strlen(msg)); + close(fd); + } + } + + if (qemu[0]) + import_kernel_cmdline(1); + + if (!strcmp(bootmode,"factory")) + property_set("ro.factorytest", "1"); + else if (!strcmp(bootmode,"factory2")) + property_set("ro.factorytest", "2"); + else + property_set("ro.factorytest", "0"); + + property_set("ro.serialno", serialno[0] ? serialno : ""); + property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); + property_set("ro.baseband", baseband[0] ? baseband : "unknown"); + property_set("ro.carrier", carrier[0] ? carrier : "unknown"); + property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); + + property_set("ro.hardware", hardware); + snprintf(tmp, PROP_VALUE_MAX, "%d", revision); + property_set("ro.revision", tmp); + + /* execute all the boot actions to get us started */ + action_for_each_trigger("init", action_add_queue_tail); + drain_action_queue(); + + /* read any property files on system or data and + * fire up the property service. This must happen + * after the ro.foo properties are set above so + * that /data/local.prop cannot interfere with them. + */ + property_set_fd = start_property_service(); + + /* create a signalling mechanism for the sigchld handler */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { + signal_fd = s[0]; + signal_recv_fd = s[1]; + fcntl(s[0], F_SETFD, FD_CLOEXEC); + fcntl(s[0], F_SETFL, O_NONBLOCK); + fcntl(s[1], F_SETFD, FD_CLOEXEC); + fcntl(s[1], F_SETFL, O_NONBLOCK); + } + + /* make sure we actually have all the pieces we need */ + if ((device_fd < 0) || + (property_set_fd < 0) || + (signal_recv_fd < 0)) { + ERROR("init startup failure\n"); + return 1; + } + + /* execute all the boot actions to get us started */ + action_for_each_trigger("early-boot", action_add_queue_tail); + action_for_each_trigger("boot", action_add_queue_tail); + drain_action_queue(); + + /* run all property triggers based on current state of the properties */ + queue_all_property_triggers(); + drain_action_queue(); + + /* enable property triggers */ + property_triggers_enabled = 1; + + ufds[0].fd = device_fd; + ufds[0].events = POLLIN; + ufds[1].fd = property_set_fd; + ufds[1].events = POLLIN; + ufds[2].fd = signal_recv_fd; + ufds[2].events = POLLIN; + fd_count = 3; + + if (keychord_fd > 0) { + ufds[3].fd = keychord_fd; + ufds[3].events = POLLIN; + fd_count++; + } else { + ufds[3].events = 0; + ufds[3].revents = 0; + } + +#if BOOTCHART + bootchart_count = bootchart_init(); + if (bootchart_count < 0) { + ERROR("bootcharting init failure\n"); + } else if (bootchart_count > 0) { + NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); + } else { + NOTICE("bootcharting ignored\n"); + } +#endif + + for(;;) { + int nr, i, timeout = -1; + + for (i = 0; i < fd_count; i++) + ufds[i].revents = 0; + + drain_action_queue(); + restart_processes(); + + if (process_needs_restart) { + timeout = (process_needs_restart - gettime()) * 1000; + if (timeout < 0) + timeout = 0; + } + +#if BOOTCHART + if (bootchart_count > 0) { + if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) + timeout = BOOTCHART_POLLING_MS; + if (bootchart_step() < 0 || --bootchart_count == 0) { + bootchart_finish(); + bootchart_count = 0; + } + } +#endif + nr = poll(ufds, fd_count, timeout); + if (nr <= 0) + continue; + + if (ufds[2].revents == POLLIN) { + /* we got a SIGCHLD - reap and restart as needed */ + read(signal_recv_fd, tmp, sizeof(tmp)); + while (!wait_for_one_process(0)) + ; + continue; + } + + if (ufds[0].revents == POLLIN) + handle_device_fd(device_fd); + + if (ufds[1].revents == POLLIN) + handle_property_set_fd(property_set_fd); + if (ufds[3].revents == POLLIN) + handle_keychord(keychord_fd); + } + + return 0; +} diff --git a/init/init.h b/init/init.h new file mode 100644 index 0000000..b686869 --- /dev/null +++ b/init/init.h @@ -0,0 +1,174 @@ +/* + * 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 _INIT_INIT_H +#define _INIT_INIT_H + +int mtd_name_to_number(const char *name); + +void handle_control_message(const char *msg, const char *arg); + +int create_socket(const char *name, int type, mode_t perm, + uid_t uid, gid_t gid); + +void *read_file(const char *fn, unsigned *_sz); + +void log_init(void); +void log_set_level(int level); +void log_close(void); +void log_write(int level, const char *fmt, ...); + +#define ERROR(x...) log_write(3, "<3>init: " x) +#define NOTICE(x...) log_write(5, "<5>init: " x) +#define INFO(x...) log_write(6, "<6>init: " x) + +#define LOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ +#define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ + +unsigned int decode_uid(const char *s); + +struct listnode +{ + struct listnode *next; + struct listnode *prev; +}; + +#define node_to_item(node, container, member) \ + (container *) (((char*) (node)) - offsetof(container, member)) + +#define list_declare(name) \ + struct listnode name = { \ + .next = &name, \ + .prev = &name, \ + } + +#define list_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +void list_init(struct listnode *list); +void list_add_tail(struct listnode *list, struct listnode *item); +void list_remove(struct listnode *item); + +#define list_empty(list) ((list) == (list)->next) +#define list_head(list) ((list)->next) +#define list_tail(list) ((list)->prev) + +struct command +{ + /* list of commands in an action */ + struct listnode clist; + + int (*func)(int nargs, char **args); + int nargs; + char *args[1]; +}; + +struct action { + /* node in list of all actions */ + struct listnode alist; + /* node in the queue of pending actions */ + struct listnode qlist; + /* node in list of actions for a trigger */ + struct listnode tlist; + + unsigned hash; + const char *name; + + struct listnode commands; + struct command *current; +}; + +struct socketinfo { + struct socketinfo *next; + const char *name; + const char *type; + uid_t uid; + gid_t gid; + int perm; +}; + +struct svcenvinfo { + struct svcenvinfo *next; + const char *name; + const char *value; +}; + +#define SVC_DISABLED 0x01 /* do not autostart with class */ +#define SVC_ONESHOT 0x02 /* do not restart on exit */ +#define SVC_RUNNING 0x04 /* currently active */ +#define SVC_RESTARTING 0x08 /* waiting to restart */ +#define SVC_CONSOLE 0x10 /* requires console */ +#define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */ + +#define NR_SVC_SUPP_GIDS 6 /* six supplementary groups */ + +struct service { + /* list of all services */ + struct listnode slist; + + const char *name; + const char *classname; + + unsigned flags; + pid_t pid; + time_t time_started; /* time of last start */ + time_t time_crashed; /* first crash within inspection window */ + int nr_crashed; /* number of times crashed within window */ + + uid_t uid; + gid_t gid; + gid_t supp_gids[NR_SVC_SUPP_GIDS]; + size_t nr_supp_gids; + + struct socketinfo *sockets; + struct svcenvinfo *envvars; + + int nargs; + char *args[1]; + struct action onrestart; /* Actions to execute on restart. */ + + /* keycodes for triggering this service via /dev/keychord */ + int *keycodes; + int nkeycodes; + int keychord_id; +}; + +int parse_config_file(const char *fn); + +struct service *service_find_by_name(const char *name); +struct service *service_find_by_pid(pid_t pid); +struct service *service_find_by_keychord(int keychord_id); +void service_for_each(void (*func)(struct service *svc)); +void service_for_each_class(const char *classname, + void (*func)(struct service *svc)); +void service_for_each_flags(unsigned matchflags, + void (*func)(struct service *svc)); +void service_stop(struct service *svc); +void service_start(struct service *svc); +void property_changed(const char *name, const char *value); + +struct action *action_remove_queue_head(void); +void action_add_queue_tail(struct action *act); +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)); +void queue_property_triggers(const char *name, const char *value); +void queue_all_property_triggers(); + +#define INIT_IMAGE_FILE "/initlogo.rle" + +int load_565rle_image( char *file_name ); + +#endif /* _INIT_INIT_H */ diff --git a/init/keywords.h b/init/keywords.h new file mode 100644 index 0000000..6f47379 --- /dev/null +++ b/init/keywords.h @@ -0,0 +1,78 @@ + +#ifndef KEYWORD +int do_class_start(int nargs, char **args); +int do_class_stop(int nargs, char **args); +int do_domainname(int nargs, char **args); +int do_exec(int nargs, char **args); +int do_export(int nargs, char **args); +int do_hostname(int nargs, char **args); +int do_ifup(int nargs, char **args); +int do_insmod(int nargs, char **args); +int do_import(int nargs, char **args); +int do_mkdir(int nargs, char **args); +int do_mount(int nargs, char **args); +int do_restart(int nargs, char **args); +int do_setkey(int nargs, char **args); +int do_setprop(int nargs, char **args); +int do_setrlimit(int nargs, char **args); +int do_start(int nargs, char **args); +int do_stop(int nargs, char **args); +int do_trigger(int nargs, char **args); +int do_symlink(int nargs, char **args); +int do_sysclktz(int nargs, char **args); +int do_write(int nargs, char **args); +int do_chown(int nargs, char **args); +int do_chmod(int nargs, char **args); +int do_loglevel(int nargs, char **args); +int do_device(int nargs, char **args); +#define __MAKE_KEYWORD_ENUM__ +#define KEYWORD(symbol, flags, nargs, func) K_##symbol, +enum { + K_UNKNOWN, +#endif + KEYWORD(capability, OPTION, 0, 0) + KEYWORD(class, OPTION, 0, 0) + KEYWORD(class_start, COMMAND, 1, do_class_start) + KEYWORD(class_stop, COMMAND, 1, do_class_stop) + KEYWORD(console, OPTION, 0, 0) + KEYWORD(critical, OPTION, 0, 0) + KEYWORD(disabled, OPTION, 0, 0) + KEYWORD(domainname, COMMAND, 1, do_domainname) + KEYWORD(exec, COMMAND, 1, do_exec) + KEYWORD(export, COMMAND, 2, do_export) + KEYWORD(group, OPTION, 0, 0) + KEYWORD(hostname, COMMAND, 1, do_hostname) + KEYWORD(ifup, COMMAND, 1, do_ifup) + KEYWORD(insmod, COMMAND, 1, do_insmod) + KEYWORD(import, COMMAND, 1, do_import) + KEYWORD(keycodes, OPTION, 0, 0) + KEYWORD(mkdir, COMMAND, 1, do_mkdir) + KEYWORD(mount, COMMAND, 3, do_mount) + KEYWORD(on, SECTION, 0, 0) + KEYWORD(oneshot, OPTION, 0, 0) + KEYWORD(onrestart, OPTION, 0, 0) + KEYWORD(restart, COMMAND, 1, do_restart) + KEYWORD(service, SECTION, 0, 0) + KEYWORD(setenv, OPTION, 2, 0) + KEYWORD(setkey, COMMAND, 0, do_setkey) + KEYWORD(setprop, COMMAND, 2, do_setprop) + KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) + KEYWORD(socket, OPTION, 0, 0) + KEYWORD(start, COMMAND, 1, do_start) + KEYWORD(stop, COMMAND, 1, do_stop) + KEYWORD(trigger, COMMAND, 1, do_trigger) + KEYWORD(symlink, COMMAND, 1, do_symlink) + KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) + KEYWORD(user, OPTION, 0, 0) + KEYWORD(write, COMMAND, 2, do_write) + KEYWORD(chown, COMMAND, 2, do_chown) + KEYWORD(chmod, COMMAND, 2, do_chmod) + KEYWORD(loglevel, COMMAND, 1, do_loglevel) + KEYWORD(device, COMMAND, 4, do_device) +#ifdef __MAKE_KEYWORD_ENUM__ + KEYWORD_COUNT, +}; +#undef __MAKE_KEYWORD_ENUM__ +#undef KEYWORD +#endif + diff --git a/init/logo.c b/init/logo.c new file mode 100644 index 0000000..6a740bf --- /dev/null +++ b/init/logo.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <linux/fb.h> +#include <linux/kd.h> + +#include "init.h" + +#ifdef ANDROID +#include <cutils/memory.h> +#else +void android_memset16(void *_ptr, unsigned short val, unsigned count) +{ + unsigned short *ptr = _ptr; + count >>= 1; + while(count--) + *ptr++ = val; +} +#endif + +struct FB { + unsigned short *bits; + unsigned size; + int fd; + struct fb_fix_screeninfo fi; + struct fb_var_screeninfo vi; +}; + +#define fb_width(fb) ((fb)->vi.xres) +#define fb_height(fb) ((fb)->vi.yres) +#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) + +static int fb_open(struct FB *fb) +{ + fb->fd = open("/dev/graphics/fb0", O_RDWR); + if (fb->fd < 0) + return -1; + + if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) + goto fail; + if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) + goto fail; + + fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, + MAP_SHARED, fb->fd, 0); + if (fb->bits == MAP_FAILED) + goto fail; + + return 0; + +fail: + close(fb->fd); + return -1; +} + +static void fb_close(struct FB *fb) +{ + munmap(fb->bits, fb_size(fb)); + close(fb->fd); +} + +/* there's got to be a more portable way to do this ... */ +static void fb_update(struct FB *fb) +{ + fb->vi.yoffset = 1; + ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); + fb->vi.yoffset = 0; + ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); +} + +static int vt_set_mode(int graphics) +{ + int fd, r; + fd = open("/dev/tty0", O_RDWR | O_SYNC); + if (fd < 0) + return -1; + r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); + close(fd); + return r; +} + +/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ + +int load_565rle_image(char *fn) +{ + struct FB fb; + struct stat s; + unsigned short *data, *bits, *ptr; + unsigned count, max; + int fd; + + if (vt_set_mode(1)) + return -1; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + ERROR("cannot open '%s'\n", fn); + goto fail_restore_text; + } + + if (fstat(fd, &s) < 0) { + goto fail_close_file; + } + + data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + goto fail_close_file; + + if (fb_open(&fb)) + goto fail_unmap_data; + + max = fb_width(&fb) * fb_height(&fb); + ptr = data; + count = s.st_size; + bits = fb.bits; + while (count > 3) { + unsigned n = ptr[0]; + if (n > max) + break; + android_memset16(bits, ptr[1], n << 1); + bits += n; + max -= n; + ptr += 2; + count -= 4; + } + + munmap(data, s.st_size); + fb_update(&fb); + fb_close(&fb); + close(fd); + unlink(fn); + return 0; + +fail_unmap_data: + munmap(data, s.st_size); +fail_close_file: + close(fd); +fail_restore_text: + vt_set_mode(0); + return -1; +} + diff --git a/init/parser.c b/init/parser.c new file mode 100644 index 0000000..6a22d24 --- /dev/null +++ b/init/parser.c @@ -0,0 +1,797 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <stddef.h> +#include <ctype.h> + +#include "init.h" +#include "property_service.h" + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + +static list_declare(service_list); +static list_declare(action_list); +static list_declare(action_queue); + +#define RAW(x...) log_write(6, x) + +void DUMP(void) +{ +#if 0 + struct service *svc; + struct action *act; + struct command *cmd; + struct listnode *node; + struct listnode *node2; + struct socketinfo *si; + int n; + + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + RAW("service %s\n", svc->name); + RAW(" class '%s'\n", svc->classname); + RAW(" exec"); + for (n = 0; n < svc->nargs; n++) { + RAW(" '%s'", svc->args[n]); + } + RAW("\n"); + for (si = svc->sockets; si; si = si->next) { + RAW(" socket %s %s 0%o\n", si->name, si->type, si->perm); + } + } + + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + RAW("on %s\n", act->name); + list_for_each(node2, &act->commands) { + cmd = node_to_item(node2, struct command, clist); + RAW(" %p", cmd->func); + for (n = 0; n < cmd->nargs; n++) { + RAW(" %s", cmd->args[n]); + } + RAW("\n"); + } + RAW("\n"); + } +#endif +} + +#define MAXARGS 64 + +#define T_EOF 0 +#define T_TEXT 1 +#define T_NEWLINE 2 + +struct parse_state +{ + char *ptr; + char *text; + int line; + int nexttoken; + void *context; + void (*parse_line)(struct parse_state *state, int nargs, char **args); + const char *filename; +}; + +static void *parse_service(struct parse_state *state, int nargs, char **args); +static void parse_line_service(struct parse_state *state, int nargs, char **args); + +static void *parse_action(struct parse_state *state, int nargs, char **args); +static void parse_line_action(struct parse_state *state, int nargs, char **args); + +void parse_error(struct parse_state *state, const char *fmt, ...) +{ + va_list ap; + char buf[128]; + int off; + + snprintf(buf, 128, "%s: %d: ", state->filename, state->line); + buf[127] = 0; + off = strlen(buf); + + va_start(ap, fmt); + vsnprintf(buf + off, 128 - off, fmt, ap); + va_end(ap); + buf[127] = 0; + ERROR("%s", buf); +} + +#define SECTION 0x01 +#define COMMAND 0x02 +#define OPTION 0x04 + +#include "keywords.h" + +#define KEYWORD(symbol, flags, nargs, func) \ + [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, + +struct { + const char *name; + int (*func)(int nargs, char **args); + unsigned char nargs; + unsigned char flags; +} keyword_info[KEYWORD_COUNT] = { + [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, +#include "keywords.h" +}; +#undef KEYWORD + +#define kw_is(kw, type) (keyword_info[kw].flags & (type)) +#define kw_name(kw) (keyword_info[kw].name) +#define kw_func(kw) (keyword_info[kw].func) +#define kw_nargs(kw) (keyword_info[kw].nargs) + +int lookup_keyword(const char *s) +{ + switch (*s++) { + case 'c': + if (!strcmp(s, "apability")) return K_capability; + if (!strcmp(s, "lass")) return K_class; + if (!strcmp(s, "lass_start")) return K_class_start; + if (!strcmp(s, "lass_stop")) return K_class_stop; + if (!strcmp(s, "onsole")) return K_console; + if (!strcmp(s, "hown")) return K_chown; + if (!strcmp(s, "hmod")) return K_chmod; + if (!strcmp(s, "ritical")) return K_critical; + break; + case 'd': + if (!strcmp(s, "isabled")) return K_disabled; + if (!strcmp(s, "omainname")) return K_domainname; + if (!strcmp(s, "evice")) return K_device; + break; + case 'e': + if (!strcmp(s, "xec")) return K_exec; + if (!strcmp(s, "xport")) return K_export; + break; + case 'g': + if (!strcmp(s, "roup")) return K_group; + break; + case 'h': + if (!strcmp(s, "ostname")) return K_hostname; + break; + case 'i': + if (!strcmp(s, "fup")) return K_ifup; + if (!strcmp(s, "nsmod")) return K_insmod; + if (!strcmp(s, "mport")) return K_import; + break; + case 'k': + if (!strcmp(s, "eycodes")) return K_keycodes; + break; + case 'l': + if (!strcmp(s, "oglevel")) return K_loglevel; + break; + case 'm': + if (!strcmp(s, "kdir")) return K_mkdir; + if (!strcmp(s, "ount")) return K_mount; + break; + case 'o': + if (!strcmp(s, "n")) return K_on; + if (!strcmp(s, "neshot")) return K_oneshot; + if (!strcmp(s, "nrestart")) return K_onrestart; + break; + case 'r': + if (!strcmp(s, "estart")) return K_restart; + break; + case 's': + if (!strcmp(s, "ervice")) return K_service; + if (!strcmp(s, "etenv")) return K_setenv; + if (!strcmp(s, "etkey")) return K_setkey; + if (!strcmp(s, "etprop")) return K_setprop; + if (!strcmp(s, "etrlimit")) return K_setrlimit; + if (!strcmp(s, "ocket")) return K_socket; + if (!strcmp(s, "tart")) return K_start; + if (!strcmp(s, "top")) return K_stop; + if (!strcmp(s, "ymlink")) return K_symlink; + if (!strcmp(s, "ysclktz")) return K_sysclktz; + break; + case 't': + if (!strcmp(s, "rigger")) return K_trigger; + break; + case 'u': + if (!strcmp(s, "ser")) return K_user; + break; + case 'w': + if (!strcmp(s, "rite")) return K_write; + break; + } + return K_UNKNOWN; +} + +void parse_line_no_op(struct parse_state *state, int nargs, char **args) +{ +} + +int next_token(struct parse_state *state) +{ + char *x = state->ptr; + char *s; + + if (state->nexttoken) { + int t = state->nexttoken; + state->nexttoken = 0; + return t; + } + + for (;;) { + switch (*x) { + case 0: + state->ptr = x; + return T_EOF; + case '\n': + state->line++; + x++; + state->ptr = x; + return T_NEWLINE; + case ' ': + case '\t': + case '\r': + x++; + continue; + case '#': + while (*x && (*x != '\n')) x++; + state->line++; + state->ptr = x; + return T_NEWLINE; + default: + goto text; + } + } + +textdone: + state->ptr = x; + *s = 0; + return T_TEXT; +text: + state->text = s = x; +textresume: + for (;;) { + switch (*x) { + case 0: + goto textdone; + case ' ': + case '\t': + case '\r': + x++; + goto textdone; + case '\n': + state->nexttoken = T_NEWLINE; + x++; + goto textdone; + case '"': + x++; + for (;;) { + switch (*x) { + case 0: + /* unterminated quoted thing */ + state->ptr = x; + return T_EOF; + case '"': + x++; + goto textresume; + default: + *s++ = *x++; + } + } + break; + case '\\': + x++; + switch (*x) { + case 0: + goto textdone; + case 'n': + *s++ = '\n'; + break; + case 'r': + *s++ = '\r'; + break; + case 't': + *s++ = '\t'; + break; + case '\\': + *s++ = '\\'; + break; + case '\r': + /* \ <cr> <lf> -> line continuation */ + if (x[1] != '\n') { + x++; + continue; + } + case '\n': + /* \ <lf> -> line continuation */ + state->line++; + x++; + /* eat any extra whitespace */ + while((*x == ' ') || (*x == '\t')) x++; + continue; + default: + /* unknown escape -- just copy */ + *s++ = *x++; + } + continue; + default: + *s++ = *x++; + } + } + return T_EOF; +} + +void parse_line(int nargs, char **args) +{ + int n; + int id = lookup_keyword(args[0]); + printf("%s(%d)", args[0], id); + for (n = 1; n < nargs; n++) { + printf(" '%s'", args[n]); + } + printf("\n"); +} + +void parse_new_section(struct parse_state *state, int kw, + int nargs, char **args) +{ + printf("[ %s %s ]\n", args[0], + nargs > 1 ? args[1] : ""); + switch(kw) { + case K_service: + state->context = parse_service(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_service; + return; + } + break; + case K_on: + state->context = parse_action(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_action; + return; + } + break; + } + state->parse_line = parse_line_no_op; +} + +static void parse_config(const char *fn, char *s) +{ + struct parse_state state; + char *args[MAXARGS]; + int nargs; + + nargs = 0; + state.filename = fn; + state.line = 1; + state.ptr = s; + state.nexttoken = 0; + state.parse_line = parse_line_no_op; + for (;;) { + switch (next_token(&state)) { + case T_EOF: + state.parse_line(&state, 0, 0); + return; + case T_NEWLINE: + if (nargs) { + int kw = lookup_keyword(args[0]); + if (kw_is(kw, SECTION)) { + state.parse_line(&state, 0, 0); + parse_new_section(&state, kw, nargs, args); + } else { + state.parse_line(&state, nargs, args); + } + nargs = 0; + } + break; + case T_TEXT: + if (nargs < MAXARGS) { + args[nargs++] = state.text; + } + break; + } + } +} + +int parse_config_file(const char *fn) +{ + char *data; + data = read_file(fn, 0); + if (!data) return -1; + + parse_config(fn, data); + DUMP(); + return 0; +} + +static int valid_name(const char *name) +{ + if (strlen(name) > 16) { + return 0; + } + while (*name) { + if (!isalnum(*name) && (*name != '_') && (*name != '-')) { + return 0; + } + name++; + } + return 1; +} + +struct service *service_find_by_name(const char *name) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (!strcmp(svc->name, name)) { + return svc; + } + } + return 0; +} + +struct service *service_find_by_pid(pid_t pid) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->pid == pid) { + return svc; + } + } + return 0; +} + +struct service *service_find_by_keychord(int keychord_id) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->keychord_id == keychord_id) { + return svc; + } + } + return 0; +} + +void service_for_each(void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + func(svc); + } +} + +void service_for_each_class(const char *classname, + void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (!strcmp(svc->classname, classname)) { + func(svc); + } + } +} + +void service_for_each_flags(unsigned matchflags, + void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->flags & matchflags) { + func(svc); + } + } +} + +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)) +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strcmp(act->name, trigger)) { + func(act); + } + } +} + +void queue_property_triggers(const char *name, const char *value) +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strncmp(act->name, "property:", strlen("property:"))) { + const char *test = act->name + strlen("property:"); + int name_length = strlen(name); + + if (!strncmp(name, test, name_length) && + test[name_length] == '=' && + !strcmp(test + name_length + 1, value)) { + action_add_queue_tail(act); + } + } + } +} + +void queue_all_property_triggers() +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strncmp(act->name, "property:", strlen("property:"))) { + /* parse property name and value + syntax is property:<name>=<value> */ + const char* name = act->name + strlen("property:"); + const char* equals = strchr(name, '='); + if (equals) { + char* prop_name[PROP_NAME_MAX + 1]; + const char* value; + int length = equals - name; + if (length > PROP_NAME_MAX) { + ERROR("property name too long in trigger %s", act->name); + } else { + memcpy(prop_name, name, length); + prop_name[length] = 0; + + /* does the property exist, and match the trigger value? */ + value = property_get((const char *)&prop_name[0]); + if (value && !strcmp(equals + 1, value)) { + action_add_queue_tail(act); + } + } + } + } + } +} + +void action_add_queue_tail(struct action *act) +{ + list_add_tail(&action_queue, &act->qlist); +} + +struct action *action_remove_queue_head(void) +{ + if (list_empty(&action_queue)) { + return 0; + } else { + struct listnode *node = list_head(&action_queue); + struct action *act = node_to_item(node, struct action, qlist); + list_remove(node); + return act; + } +} + +static void *parse_service(struct parse_state *state, int nargs, char **args) +{ + struct service *svc; + if (nargs < 3) { + parse_error(state, "services must have a name and a program\n"); + return 0; + } + if (!valid_name(args[1])) { + parse_error(state, "invalid service name '%s'\n", args[1]); + return 0; + } + + svc = service_find_by_name(args[1]); + if (svc) { + parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); + return 0; + } + + nargs -= 2; + svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); + if (!svc) { + parse_error(state, "out of memory\n"); + return 0; + } + svc->name = args[1]; + svc->classname = "default"; + memcpy(svc->args, args + 2, sizeof(char*) * nargs); + svc->args[nargs] = 0; + svc->nargs = nargs; + svc->onrestart.name = "onrestart"; + list_init(&svc->onrestart.commands); + list_add_tail(&service_list, &svc->slist); + return svc; +} + +static void parse_line_service(struct parse_state *state, int nargs, char **args) +{ + struct service *svc = state->context; + struct command *cmd; + int i, kw, kw_nargs; + + if (nargs == 0) { + return; + } + + kw = lookup_keyword(args[0]); + switch (kw) { + case K_capability: + break; + case K_class: + if (nargs != 2) { + parse_error(state, "class option requires a classname\n"); + } else { + svc->classname = args[1]; + } + break; + case K_console: + svc->flags |= SVC_CONSOLE; + break; + case K_disabled: + svc->flags |= SVC_DISABLED; + break; + case K_group: + if (nargs < 2) { + parse_error(state, "group option requires a group id\n"); + } else if (nargs > NR_SVC_SUPP_GIDS + 2) { + parse_error(state, "group option accepts at most %d supp. groups\n", + NR_SVC_SUPP_GIDS); + } else { + int n; + svc->gid = decode_uid(args[1]); + for (n = 2; n < nargs; n++) { + svc->supp_gids[n-2] = decode_uid(args[n]); + } + svc->nr_supp_gids = n - 2; + } + break; + case K_keycodes: + if (nargs < 2) { + parse_error(state, "keycodes option requires atleast one keycode\n"); + } else { + svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0])); + if (!svc->keycodes) { + parse_error(state, "could not allocate keycodes\n"); + } else { + svc->nkeycodes = nargs - 1; + for (i = 1; i < nargs; i++) { + svc->keycodes[i - 1] = atoi(args[i]); + } + } + } + break; + case K_oneshot: + svc->flags |= SVC_ONESHOT; + break; + case K_onrestart: + nargs--; + args++; + kw = lookup_keyword(args[0]); + if (!kw_is(kw, COMMAND)) { + parse_error(state, "invalid command '%s'\n", args[0]); + break; + } + kw_nargs = kw_nargs(kw); + if (nargs < kw_nargs) { + parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, + kw_nargs > 2 ? "arguments" : "argument"); + break; + } + + cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); + cmd->func = kw_func(kw); + cmd->nargs = nargs; + memcpy(cmd->args, args, sizeof(char*) * nargs); + list_add_tail(&svc->onrestart.commands, &cmd->clist); + break; + case K_critical: + svc->flags |= SVC_CRITICAL; + break; + case K_setenv: { /* name value */ + struct svcenvinfo *ei; + if (nargs < 2) { + parse_error(state, "setenv option requires name and value arguments\n"); + break; + } + ei = calloc(1, sizeof(*ei)); + if (!ei) { + parse_error(state, "out of memory\n"); + break; + } + ei->name = args[1]; + ei->value = args[2]; + ei->next = svc->envvars; + svc->envvars = ei; + break; + } + case K_socket: {/* name type perm [ uid gid ] */ + struct socketinfo *si; + if (nargs < 4) { + parse_error(state, "socket option requires name, type, perm arguments\n"); + break; + } + if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) { + parse_error(state, "socket type must be 'dgram' or 'stream'\n"); + break; + } + si = calloc(1, sizeof(*si)); + if (!si) { + parse_error(state, "out of memory\n"); + break; + } + si->name = args[1]; + si->type = args[2]; + si->perm = strtoul(args[3], 0, 8); + if (nargs > 4) + si->uid = decode_uid(args[4]); + if (nargs > 5) + si->gid = decode_uid(args[5]); + si->next = svc->sockets; + svc->sockets = si; + break; + } + case K_user: + if (nargs != 2) { + parse_error(state, "user option requires a user id\n"); + } else { + svc->uid = decode_uid(args[1]); + } + break; + default: + parse_error(state, "invalid option '%s'\n", args[0]); + } +} + +static void *parse_action(struct parse_state *state, int nargs, char **args) +{ + struct action *act; + if (nargs < 2) { + parse_error(state, "actions must have a trigger\n"); + return 0; + } + if (nargs > 2) { + parse_error(state, "actions may not have extra parameters\n"); + return 0; + } + act = calloc(1, sizeof(*act)); + act->name = args[1]; + list_init(&act->commands); + list_add_tail(&action_list, &act->alist); + /* XXX add to hash */ + return act; +} + +static void parse_line_action(struct parse_state* state, int nargs, char **args) +{ + struct command *cmd; + struct action *act = state->context; + int (*func)(int nargs, char **args); + int kw, n; + + if (nargs == 0) { + return; + } + + kw = lookup_keyword(args[0]); + if (!kw_is(kw, COMMAND)) { + parse_error(state, "invalid command '%s'\n", args[0]); + return; + } + + n = kw_nargs(kw); + if (nargs < n) { + parse_error(state, "%s requires %d %s\n", args[0], n - 1, + n > 2 ? "arguments" : "argument"); + return; + } + cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); + cmd->func = kw_func(kw); + cmd->nargs = nargs; + memcpy(cmd->args, args, sizeof(char*) * nargs); + list_add_tail(&act->commands, &cmd->clist); +} diff --git a/init/property_service.c b/init/property_service.c new file mode 100644 index 0000000..7a6416b --- /dev/null +++ b/init/property_service.c @@ -0,0 +1,502 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdarg.h> +#include <dirent.h> +#include <limits.h> +#include <errno.h> + +#include <cutils/misc.h> +#include <cutils/sockets.h> +#include <cutils/ashmem.h> + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/select.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/mman.h> +#include <sys/atomics.h> +#include <private/android_filesystem_config.h> + +#include "property_service.h" +#include "init.h" + +#define PERSISTENT_PROPERTY_DIR "/data/property" + +static int persistent_properties_loaded = 0; + +/* White list of permissions for setting property services. */ +struct { + const char *prefix; + unsigned int uid; +} property_perms[] = { + { "net.rmnet0.", AID_RADIO }, + { "net.gprs.", AID_RADIO }, + { "ril.", AID_RADIO }, + { "gsm.", AID_RADIO }, + { "net.dns", AID_RADIO }, + { "net.", AID_SYSTEM }, + { "dev.", AID_SYSTEM }, + { "runtime.", AID_SYSTEM }, + { "hw.", AID_SYSTEM }, + { "sys.", AID_SYSTEM }, + { "service.", AID_SYSTEM }, + { "wlan.", AID_SYSTEM }, + { "dhcp.", AID_SYSTEM }, + { "dhcp.", AID_DHCP }, + { "debug.", AID_SHELL }, + { "log.", AID_SHELL }, + { "persist.sys.", AID_SYSTEM }, + { "persist.service.", AID_SYSTEM }, + { NULL, 0 } +}; + +/* + * White list of UID that are allowed to start/stop services. + * Currently there are no user apps that require. + */ +struct { + const char *service; + unsigned int uid; +} control_perms[] = { + {NULL, 0 } +}; + +typedef struct { + void *data; + size_t size; + int fd; +} workspace; + +static int init_workspace(workspace *w, size_t size) +{ + void *data; + int fd; + + fd = ashmem_create_region("system_properties", size); + if(fd < 0) + return -1; + + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if(data == MAP_FAILED) + goto out; + + /* allow the wolves we share with to do nothing but read */ + ashmem_set_prot_region(fd, PROT_READ); + + w->data = data; + w->size = size; + w->fd = fd; + + return 0; + +out: + close(fd); + return -1; +} + +/* (8 header words + 247 toc words) = 1020 bytes */ +/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */ + +#define PA_COUNT_MAX 247 +#define PA_INFO_START 1024 +#define PA_SIZE 32768 + +static workspace pa_workspace; +static prop_info *pa_info_array; + +extern prop_area *__system_property_area__; + +static int init_property_area(void) +{ + prop_area *pa; + + if(pa_info_array) + return -1; + + if(init_workspace(&pa_workspace, PA_SIZE)) + return -1; + + fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); + + pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START); + + pa = pa_workspace.data; + memset(pa, 0, PA_SIZE); + pa->magic = PROP_AREA_MAGIC; + pa->version = PROP_AREA_VERSION; + + /* plug into the lib property services */ + __system_property_area__ = pa; + + return 0; +} + +static void update_prop_info(prop_info *pi, const char *value, unsigned len) +{ + pi->serial = pi->serial | 1; + memcpy(pi->value, value, len + 1); + pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); + __futex_wake(&pi->serial, INT32_MAX); +} + +static int property_write(prop_info *pi, const char *value) +{ + int valuelen = strlen(value); + if(valuelen >= PROP_VALUE_MAX) return -1; + update_prop_info(pi, value, valuelen); + return 0; +} + + +/* + * Checks permissions for starting/stoping system services. + * AID_SYSTEM and AID_ROOT are always allowed. + * + * Returns 1 if uid allowed, 0 otherwise. + */ +static int check_control_perms(const char *name, int uid) { + int i; + if (uid == AID_SYSTEM || uid == AID_ROOT) + return 1; + + /* Search the ACL */ + for (i = 0; control_perms[i].service; i++) { + if (strcmp(control_perms[i].service, name) == 0) { + if (control_perms[i].uid == uid) + return 1; + } + } + return 0; +} + +/* + * Checks permissions for setting system properties. + * Returns 1 if uid allowed, 0 otherwise. + */ +static int check_perms(const char *name, unsigned int uid) +{ + int i; + if (uid == 0) + return 1; + + if(!strncmp(name, "ro.", 3)) + name +=3; + + for (i = 0; property_perms[i].prefix; i++) { + int tmp; + if (strncmp(property_perms[i].prefix, name, + strlen(property_perms[i].prefix)) == 0) { + if (property_perms[i].uid == uid) { + return 1; + } + } + } + + return 0; +} + +const char* property_get(const char *name) +{ + prop_info *pi; + + if(strlen(name) >= PROP_NAME_MAX) return 0; + + pi = (prop_info*) __system_property_find(name); + + if(pi != 0) { + return pi->value; + } else { + return 0; + } +} + +static void write_peristent_property(const char *name, const char *value) +{ + const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp"; + char path[PATH_MAX]; + int fd, length; + + snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); + + fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (fd < 0) { + ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno); + return; + } + write(fd, value, strlen(value)); + close(fd); + + if (rename(tempPath, path)) { + unlink(tempPath); + ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); + } +} + +int property_set(const char *name, const char *value) +{ + prop_area *pa; + prop_info *pi; + + int namelen = strlen(name); + int valuelen = strlen(value); + + if(namelen >= PROP_NAME_MAX) return -1; + if(valuelen >= PROP_VALUE_MAX) return -1; + if(namelen < 1) return -1; + + pi = (prop_info*) __system_property_find(name); + + if(pi != 0) { + /* ro.* properties may NEVER be modified once set */ + if(!strncmp(name, "ro.", 3)) return -1; + + pa = __system_property_area__; + update_prop_info(pi, value, valuelen); + pa->serial++; + __futex_wake(&pa->serial, INT32_MAX); + } else { + pa = __system_property_area__; + if(pa->count == PA_COUNT_MAX) return -1; + + pi = pa_info_array + pa->count; + pi->serial = (valuelen << 24); + memcpy(pi->name, name, namelen + 1); + memcpy(pi->value, value, valuelen + 1); + + pa->toc[pa->count] = + (namelen << 24) | (((unsigned) pi) - ((unsigned) pa)); + + pa->count++; + pa->serial++; + __futex_wake(&pa->serial, INT32_MAX); + } + /* If name starts with "net." treat as a DNS property. */ + if (strncmp("net.", name, sizeof("net.") - 1) == 0) { + if (strcmp("net.change", name) == 0) { + return 0; + } + /* + * The 'net.change' property is a special property used track when any + * 'net.*' property name is updated. It is _ONLY_ updated here. Its value + * contains the last updated 'net.*' property. + */ + property_set("net.change", name); + } else if (persistent_properties_loaded && + strncmp("persist.", name, sizeof("persist.") - 1) == 0) { + /* + * Don't write properties to disk until after we have read all default properties + * to prevent them from being overwritten by default values. + */ + write_peristent_property(name, value); + } + property_changed(name, value); + return 0; +} + +static int property_list(void (*propfn)(const char *key, const char *value, void *cookie), + void *cookie) +{ + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; + const prop_info *pi; + unsigned n; + + for(n = 0; (pi = __system_property_find_nth(n)); n++) { + __system_property_read(pi, name, value); + propfn(name, value, cookie); + } + return 0; +} + +void handle_property_set_fd(int fd) +{ + prop_msg msg; + int s; + int r; + int res; + struct ucred cr; + struct sockaddr_un addr; + socklen_t addr_size = sizeof(addr); + socklen_t cr_size = sizeof(cr); + + if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) { + return; + } + + /* Check socket options here */ + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { + close(s); + ERROR("Unable to recieve socket options\n"); + return; + } + + r = recv(s, &msg, sizeof(msg), 0); + close(s); + if(r != sizeof(prop_msg)) { + ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n", + r, sizeof(prop_msg)); + return; + } + + switch(msg.cmd) { + case PROP_MSG_SETPROP: + msg.name[PROP_NAME_MAX-1] = 0; + msg.value[PROP_VALUE_MAX-1] = 0; + + if(memcmp(msg.name,"ctl.",4) == 0) { + if (check_control_perms(msg.value, cr.uid)) { + handle_control_message((char*) msg.name + 4, (char*) msg.value); + } else { + ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n", + msg.name + 4, msg.value, cr.uid, cr.pid); + } + } else { + if (check_perms(msg.name, cr.uid)) { + property_set((char*) msg.name, (char*) msg.value); + } else { + ERROR("sys_prop: permission denied uid:%d name:%s\n", + cr.uid, msg.name); + } + } + break; + + default: + break; + } +} + +void get_property_workspace(int *fd, int *sz) +{ + *fd = pa_workspace.fd; + *sz = pa_workspace.size; +} + +static void load_properties(char *data) +{ + char *key, *value, *eol, *sol, *tmp; + + sol = data; + while((eol = strchr(sol, '\n'))) { + key = sol; + *eol++ = 0; + sol = eol; + + value = strchr(key, '='); + if(value == 0) continue; + *value++ = 0; + + while(isspace(*key)) key++; + if(*key == '#') continue; + tmp = value - 2; + while((tmp > key) && isspace(*tmp)) *tmp-- = 0; + + while(isspace(*value)) value++; + tmp = eol - 2; + while((tmp > value) && isspace(*tmp)) *tmp-- = 0; + + property_set(key, value); + } +} + +static void load_properties_from_file(const char *fn) +{ + char *data; + unsigned sz; + + data = read_file(fn, &sz); + + if(data != 0) { + load_properties(data); + free(data); + } +} + +static void load_persistent_properties() +{ + DIR* dir = opendir(PERSISTENT_PROPERTY_DIR); + struct dirent* entry; + char path[PATH_MAX]; + char value[PROP_VALUE_MAX]; + int fd, length; + + if (dir) { + while ((entry = readdir(dir)) != NULL) { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") || + strncmp("persist.", entry->d_name, sizeof("persist.") - 1)) + continue; +#if HAVE_DIRENT_D_TYPE + if (entry->d_type != DT_REG) + continue; +#endif + /* open the file and read the property value */ + snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name); + fd = open(path, O_RDONLY); + if (fd >= 0) { + length = read(fd, value, sizeof(value) - 1); + if (length >= 0) { + value[length] = 0; + property_set(entry->d_name, value); + } else { + ERROR("Unable to read persistent property file %s errno: %d\n", path, errno); + } + close(fd); + } else { + ERROR("Unable to open persistent property file %s errno: %d\n", path, errno); + } + } + closedir(dir); + } else { + ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno); + } + + persistent_properties_loaded = 1; +} + +void property_init(void) +{ + init_property_area(); + load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); +} + +int start_property_service(void) +{ + int fd; + + load_properties_from_file(PROP_PATH_SYSTEM_BUILD); + load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + /* Read persistent properties after all default values have been loaded. */ + load_persistent_properties(); + + fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); + if(fd < 0) return -1; + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + + listen(fd, 8); + return fd; +} diff --git a/init/property_service.h b/init/property_service.h new file mode 100644 index 0000000..d12f1f3 --- /dev/null +++ b/init/property_service.h @@ -0,0 +1,28 @@ +/* + * 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 _INIT_PROPERTY_H +#define _INIT_PROPERTY_H + +extern void handle_property_fd(int fd); +extern void handle_property_set_fd(int fd); +extern void property_init(void); +extern int start_property_service(void); +void get_property_workspace(int *fd, int *sz); +extern const char* property_get(const char *name); +extern int property_set(const char *name, const char *value); + +#endif /* _INIT_PROPERTY_H */ diff --git a/init/readme.txt b/init/readme.txt new file mode 100644 index 0000000..665090b --- /dev/null +++ b/init/readme.txt @@ -0,0 +1,293 @@ + +Android Init Language +--------------------- + +The Android Init Language consists of four broad classes of statements, +which are Actions, Commands, Services, and Options. + +All of these are line-oriented, consisting of tokens separated by +whitespace. The c-style backslash escapes may be used to insert +whitespace into a token. Double quotes may also be used to prevent +whitespace from breaking text into multiple tokens. The backslash, +when it is the last character on a line, may be used for line-folding. + +Lines which start with a # (leading whitespace allowed) are comments. + +Actions and Services implicitly declare a new section. All commands +or options belong to the section most recently declared. Commands +or options before the first section are ignored. + +Actions and Services have unique names. If a second Action or Service +is declared with the same name as an existing one, it is ignored as +an error. (??? should we override instead) + + +Actions +------- +Actions are named sequences of commands. Actions have a trigger which +is used to determine when the action should occur. When an event +occurs which matches an action's trigger, that action is added to +the tail of a to-be-executed queue (unless it is already on the +queue). + +Each action in the queue is dequeued in sequence and each command in +that action is executed in sequence. Init handles other activities +(device creation/destruction, property setting, process restarting) +"between" the execution of the commands in activities. + +Actions take the form of: + +on <trigger> + <command> + <command> + <command> + + +Services +-------- +Services are programs which init launches and (optionally) restarts +when they exit. Services take the form of: + +service <name> <pathname> [ <argument> ]* + <option> + <option> + ... + + +Options +------- +Options are modifiers to services. They affect how and when init +runs the service. + +critical + This is a device-critical service. If it exits more than four times in + four minutes, the device will reboot into recovery mode. + +disabled + This service will not automatically start with its class. + It must be explicitly started by name. + +setenv <name> <value> + Set the environment variable <name> to <value> in the launched process. + +socket <name> <type> <perm> [ <user> [ <group> ] ] + Create a unix domain socket named /dev/socket/<name> and pass + its fd to the launched process. <type> must be "dgram" or "stream". + User and group default to 0. + +user <username> + Change to username before exec'ing this service. + Currently defaults to root. (??? probably should default to nobody) + Currently, if your process requires linux capabilities then you cannot use + this command. You must instead request the capabilities in-process while + still root, and then drop to your desired uid. + +group <groupname> [ <groupname> ]* + Change to groupname before exec'ing this service. Additional + groupnames beyond the (required) first one are used to set the + supplemental groups of the process (via setgroups()). + Currently defaults to root. (??? probably should default to nobody) + +oneshot + Do not restart the service when it exits. + +class <name> + Specify a class name for the service. All services in a + named class may be started or stopped together. A service + is in the class "default" if one is not specified via the + class option. + +onrestart + Execute a Command (see below) when service restarts. + +Triggers +-------- + Triggers are strings which can be used to match certain kinds + of events and used to cause an action to occur. + +boot + This is the first trigger that will occur when init starts + (after /init.conf is loaded) + +<name>=<value> + Triggers of this form occur when the property <name> is set + to the specific value <value>. + +device-added-<path> +device-removed-<path> + Triggers of these forms occur when a device node is added + or removed. + +service-exited-<name> + Triggers of this form occur when the specified service exits. + + +Commands +-------- + +exec <path> [ <argument> ]* + Fork and execute a program (<path>). This will block until + the program completes execution. It is best to avoid exec + as unlike the builtin commands, it runs the risk of getting + init "stuck". (??? maybe there should be a timeout?) + +export <name> <value> + Set the environment variable <name> equal to <value> in the + global environment (which will be inherited by all processes + started after this command is executed) + +ifup <interface> + Bring the network interface <interface> online. + +import <filename> + Parse an init config file, extending the current configuration. + +hostname <name> + Set the host name. + +chmod <octal-mode> <path> + Change file access permissions. + +chown <owner> <group> <path> + Change file owner and group. + +class_start <serviceclass> + Start all services of the specified class if they are + not already running. + +class_stop <serviceclass> + Stop all services of the specified class if they are + currently running. + +domainname <name> + Set the domain name. + +insmod <path> + Install the module at <path> + +mkdir <path> [mode] [owner] [group] + Create a directory at <path>, optionally with the given mode, owner, and + group. If not provided, the directory is created with permissions 755 and + owned by the root user and root group. + +mount <type> <device> <dir> [ <mountoption> ]* + Attempt to mount the named device at the directory <dir> + <device> may be of the form mtd@name to specify a mtd block + device by name. + <mountoption>s include "ro", "rw", "remount", "noatime", ... + +setkey + TBD + +setprop <name> <value> + Set system property <name> to <value>. + +setrlimit <resource> <cur> <max> + Set the rlimit for a resource. + +start <service> + Start a service running if it is not already running. + +stop <service> + Stop a service from running if it is currently running. + +symlink <target> <path> + Create a symbolic link at <path> with the value <target> + +sysclktz <mins_west_of_gmt> + Set the system clock base (0 if system clock ticks in GMT) + +trigger <event> + Trigger an event. Used to queue an action from another + action. + +write <path> <string> [ <string> ]* + Open the file at <path> and write one or more strings + to it with write(2) + + +Properties +---------- +Init updates some system properties to provide some insight into +what it's doing: + +init.action + Equal to the name of the action currently being executed or "" if none + +init.command + Equal to the command being executed or "" if none. + +init.svc.<name> + State of a named service ("stopped", "running", "restarting") + + +Example init.conf +----------------- + +# not complete -- just providing some examples of usage +# +on boot + export PATH /sbin:/system/sbin:/system/bin + export LD_LIBRARY_PATH /system/lib + + mkdir /dev + mkdir /proc + mkdir /sys + + mount tmpfs tmpfs /dev + mkdir /dev/pts + mkdir /dev/socket + mount devpts devpts /dev/pts + mount proc proc /proc + mount sysfs sysfs /sys + + write /proc/cpu/alignment 4 + + ifup lo + + hostname localhost + domainname localhost + + mount yaffs2 mtd@system /system + mount yaffs2 mtd@userdata /data + + import /system/etc/init.conf + + class_start default + +service adbd /sbin/adbd + user adb + group adb + +service usbd /system/bin/usbd -r + user usbd + group usbd + socket usbd 666 + +service zygote /system/bin/app_process -Xzygote /system/bin --zygote + socket zygote 666 + +service runtime /system/bin/runtime + user system + group system + +on device-added-/dev/compass + start akmd + +on device-removed-/dev/compass + stop akmd + +service akmd /sbin/akmd + disabled + user akmd + group akmd + +Debugging notes +--------------- +By default, programs executed by init will drop stdout and stderr into +/dev/null. To help with debugging, you can execute your program via the +Andoird program logwrapper. This will redirect stdout/stderr into the +Android logging system (accessed via logcat). + +For example +service akmd /system/bin/logwrapper /sbin/akmd diff --git a/init/util.c b/init/util.c new file mode 100644 index 0000000..0b7667d --- /dev/null +++ b/init/util.c @@ -0,0 +1,211 @@ +/* + * 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 <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +/* for ANDROID_SOCKET_* */ +#include <cutils/sockets.h> + +#include <private/android_filesystem_config.h> + +#include "init.h" + +static int log_fd = -1; +/* Inital log level before init.rc is parsed and this this is reset. */ +static int log_level = LOG_DEFAULT_LEVEL; + + +void log_set_level(int level) { + log_level = level; +} + +void log_init(void) +{ + static const char *name = "/dev/__kmsg__"; + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { + log_fd = open(name, O_WRONLY); + fcntl(log_fd, F_SETFD, FD_CLOEXEC); + unlink(name); + } +} + +#define LOG_BUF_MAX 512 + +void log_write(int level, const char *fmt, ...) +{ + char buf[LOG_BUF_MAX]; + va_list ap; + + if (level > log_level) return; + if (log_fd < 0) return; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_MAX, fmt, ap); + buf[LOG_BUF_MAX - 1] = 0; + va_end(ap); + write(log_fd, buf, strlen(buf)); +} + +/* + * android_name_to_id - returns the integer uid/gid associated with the given + * name, or -1U on error. + */ +static unsigned int android_name_to_id(const char *name) +{ + struct android_id_info *info = android_ids; + unsigned int n; + + for (n = 0; n < android_id_count; n++) { + if (!strcmp(info[n].name, name)) + return info[n].aid; + } + + return -1U; +} + +/* + * decode_uid - decodes and returns the given string, which can be either the + * numeric or name representation, into the integer uid or gid. Returns -1U on + * error. + */ +unsigned int decode_uid(const char *s) +{ + unsigned int v; + + if (!s || *s == '\0') + return -1U; + if (isalpha(s[0])) + return android_name_to_id(s); + + errno = 0; + v = (unsigned int) strtoul(s, 0, 0); + if (errno) + return -1U; + return v; +} + +/* + * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR + * ("/dev/socket") as dictated in init.rc. This socket is inherited by the + * daemon. We communicate the file descriptor's value via the environment + * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). + */ +int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) +{ + struct sockaddr_un addr; + int fd, ret; + + fd = socket(PF_UNIX, type, 0); + if (fd < 0) { + ERROR("Failed to open socket '%s': %s\n", name, strerror(errno)); + return -1; + } + + memset(&addr, 0 , sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", + name); + + ret = unlink(addr.sun_path); + if (ret != 0 && errno != ENOENT) { + ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno)); + goto out_close; + } + + ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); + if (ret) { + ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno)); + goto out_unlink; + } + + chown(addr.sun_path, uid, gid); + chmod(addr.sun_path, perm); + + INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n", + addr.sun_path, perm, uid, gid); + + return fd; + +out_unlink: + unlink(addr.sun_path); +out_close: + close(fd); + return -1; +} + +/* reads a file, making sure it is terminated with \n \0 */ +void *read_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 + 2); + if(data == 0) goto oops; + + if(read(fd, data, sz) != sz) goto oops; + close(fd); + data[sz] = '\n'; + data[sz+1] = 0; + if(_sz) *_sz = sz; + return data; + +oops: + close(fd); + if(data != 0) free(data); + return 0; +} + +void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} + diff --git a/libctest/Android.mk b/libctest/Android.mk new file mode 100644 index 0000000..815fabb --- /dev/null +++ b/libctest/Android.mk @@ -0,0 +1,7 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE:= libctest +LOCAL_SRC_FILES := ctest.c + +include $(BUILD_SHARED_LIBRARY) diff --git a/libctest/ctest.c b/libctest/ctest.c new file mode 100644 index 0000000..ee6331f --- /dev/null +++ b/libctest/ctest.c @@ -0,0 +1,161 @@ +/* + * 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 <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <ctest/ctest.h> + +#define MAX_TESTS 255 + +/** Semi-random number used to identify assertion errors. */ +#define ASSERTION_ERROR 42 + +typedef void TestCase(); + +/** A suite of tests. */ +typedef struct { + int size; + const char* testNames[MAX_TESTS]; + TestCase* tests[MAX_TESTS]; + int currentTest; + FILE* out; +} TestSuite; + +/** Gets the test suite. Creates it if necessary. */ +static TestSuite* getTestSuite() { + static TestSuite* suite = NULL; + + if (suite != NULL) { + return suite; + } + + suite = calloc(1, sizeof(TestSuite)); + assert(suite != NULL); + + suite->out = tmpfile(); + assert(suite->out != NULL); + + return suite; +} + +void addNamedTest(const char* name, TestCase* test) { + TestSuite* testSuite = getTestSuite(); + assert(testSuite->size <= MAX_TESTS); + + int index = testSuite->size; + testSuite->testNames[index] = name; + testSuite->tests[index] = test; + + testSuite->size++; +} + +/** Prints failures to stderr. */ +static void printFailures(int failures) { + TestSuite* suite = getTestSuite(); + + fprintf(stderr, "FAILURE! %d of %d tests failed. Failures:\n", + failures, suite->size); + + // Copy test output to stdout. + rewind(suite->out); + char buffer[512]; + size_t read; + while ((read = fread(buffer, sizeof(char), 512, suite->out)) > 0) { + // TODO: Make sure we actually wrote 'read' bytes. + fwrite(buffer, sizeof(char), read, stderr); + } +} + +/** Runs a single test case. */ +static int runCurrentTest() { + TestSuite* suite = getTestSuite(); + + pid_t pid = fork(); + if (pid == 0) { + // Child process. Runs test case. + suite->tests[suite->currentTest](); + + // Exit successfully. + exit(0); + } else if (pid < 0) { + fprintf(stderr, "Fork failed."); + exit(1); + } else { + // Parent process. Wait for child. + int status; + waitpid(pid, &status, 0); + + if (!WIFEXITED(status)) { + return -1; + } + + return WEXITSTATUS(status); + } +} + +void runTests() { + TestSuite* suite = getTestSuite(); + + int failures = 0; + for (suite->currentTest = 0; suite->currentTest < suite->size; + suite->currentTest++) { + // Flush stdout before forking. + fflush(stdout); + + int result = runCurrentTest(); + + if (result != 0) { + printf("X"); + + failures++; + + // Handle errors other than assertions. + if (result != ASSERTION_ERROR) { + // TODO: Report file name. + fprintf(suite->out, "Process failed: [%s] status: %d\n", + suite->testNames[suite->currentTest], result); + fflush(suite->out); + } + } else { + printf("."); + } + } + + printf("\n"); + + if (failures > 0) { + printFailures(failures); + } else { + printf("SUCCESS! %d tests ran successfully.\n", suite->size); + } +} + +void assertTrueWithSource(int value, const char* file, int line, char* message) { + if (!value) { + TestSuite* suite = getTestSuite(); + + fprintf(suite->out, "Assertion failed: [%s:%d] %s: %s\n", file, line, + suite->testNames[suite->currentTest], message); + fflush(suite->out); + + // Exit the process for this test case. + exit(ASSERTION_ERROR); + } +} diff --git a/libcutils/Android.mk b/libcutils/Android.mk new file mode 100644 index 0000000..a43f7e3 --- /dev/null +++ b/libcutils/Android.mk @@ -0,0 +1,113 @@ +# +# 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 \ + tzstrftime.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) ashmem-dev.c mq.c + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += memset32.S atomic-android-arm.S +else # !arm +LOCAL_SRC_FILES += memory.c +endif # !arm + +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..c56ec5d --- /dev/null +++ b/libcutils/atomic-android-arm.S @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <machine/cpu-features.h> + +/* + * NOTE: these atomic operations are SMP safe on all architectures, + * 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + add r1, r0, #1 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + add r1, r0, #1 + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + sub r0, r1, #1 + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + sub r1, r0, #1 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + sub r1, r0, #1 + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + add r0, r1, #1 + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + add r1, r0, r4 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + add r1, r0, r4 + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + sub r0, r1, r4 + ldmia sp!, {r4, lr} + bx lr + + +/* + * ---------------------------------------------------------------------------- + * 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #8 + mov r5, r0 /* r5 = save address[0] */ + and r1, r0, r4 /* r1 = new value */ + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ +#else + mov r5, r0 /* r5 = save address[0] */ + and r1, r0, r4 /* r1 = new value */ + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ + mov lr, pc + bx r3 +#endif + bcc 1b + mov r0, r5 + ldmia sp!, {r4, r5, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #8 + mov r5, r0 /* r5 = save address[0] */ + orr r1, r0, r4 /* r1 = new value */ + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ +#else + mov r5, r0 /* r5 = save address[0] */ + orr r1, r0, r4 /* r1 = new value */ + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ + mov lr, pc + bx r3 +#endif + bcc 1b + mov r0, r5 + ldmia sp!, {r4, r5, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * 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 +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + mov r0, r4 /* r0 = oldvalue */ + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + mov r0, r4 /* r0 = oldvalue */ + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcs 2f /* swap was made. we're good, return. */ + ldr r3, [r2] /* swap not made, see if it's because *ptr!=oldvalue */ + cmp r3, r4 + beq 1b +2: @ android_atomic_cmpxchg + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * 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/tzstrftime.c b/libcutils/tzstrftime.c new file mode 100644 index 0000000..29c5015 --- /dev/null +++ b/libcutils/tzstrftime.c @@ -0,0 +1,834 @@ +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)strftime.c 8.1"; +/* +** Based on the UCB version with the ID appearing below. +** This is ANSIish only when "multibyte character == plain character". +*/ +#endif /* !defined NOID */ +#endif /* !defined lint */ + +#include <time.h> +#include <tzfile.h> +#include <limits.h> +#include <cutils/tztime.h> + +/* +** Copyright (c) 1989 The Regents of the University of California. +** All rights reserved. +** +** Redistribution and use in source and binary forms are permitted +** provided that the above copyright notice and this paragraph are +** duplicated in all such forms and that any documentation, +** advertising materials, and other materials related to such +** distribution and use acknowledge that the software was developed +** by the University of California, Berkeley. The name of the +** University may not be used to endorse or promote products derived +** from this software without specific prior written permission. +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifndef LIBC_SCCS +#ifndef lint +static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; +#endif /* !defined lint */ +#endif /* !defined LIBC_SCCS */ + +#include <ctype.h> + +#define P(x) x + +static char * _add P((const char *, char *, const char *, int)); +static char * _conv P((int, const char *, char *, const char *)); +static char * _fmt P((const char *, const struct tm *, char *, const char *, + int *, const struct strftime_locale *Locale)); +static char * _yconv P((int, int, int, int, char *, const char *, int)); +static char * getformat P((int, char *, char *, char *, char *)); + +extern char * tzname[]; + + + + + +/* from private.h */ + +#ifndef TYPE_BIT +#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) +#endif /* !defined TYPE_BIT */ + +#ifndef TYPE_SIGNED +#define TYPE_SIGNED(type) (((type) -1) < 0) +#endif /* !defined TYPE_SIGNED */ + +#ifndef INT_STRLEN_MAXIMUM +/* + * ** 302 / 1000 is log10(2.0) rounded up. + * ** Subtract one for the sign bit if the type is signed; + * ** add one for integer division truncation; + * ** add one more for a minus sign if the type is signed. + * */ +#define INT_STRLEN_MAXIMUM(type) \ + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) +#endif /* !defined INT_STRLEN_MAXIMUM */ + +/* end of part from private.h */ + + + + +#ifndef YEAR_2000_NAME +#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" +#endif /* !defined YEAR_2000_NAME */ + +#define IN_NONE 0 +#define IN_SOME 1 +#define IN_THIS 2 +#define IN_ALL 3 + +#define FORCE_LOWER_CASE 0x100 + +size_t +strftime_tz(s, maxsize, format, t, Locale) +char * const s; +const size_t maxsize; +const char * const format; +const struct tm * const t; +const struct strftime_locale *Locale; +{ + char * p; + int warn; + + warn = IN_NONE; + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, Locale); +#if 0 + if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { + (void) fprintf(stderr, "\n"); + if (format == NULL) + (void) fprintf(stderr, "NULL strftime format "); + else (void) fprintf(stderr, "strftime format \"%s\" ", + format); + (void) fprintf(stderr, "yields only two digits of years in "); + if (warn == IN_SOME) + (void) fprintf(stderr, "some locales"); + else if (warn == IN_THIS) + (void) fprintf(stderr, "the current locale"); + else (void) fprintf(stderr, "all locales"); + (void) fprintf(stderr, "\n"); + } +#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ + if (p == s + maxsize) + return 0; + *p = '\0'; + return p - s; +} + +static char *getformat(int modifier, char *normal, char *underscore, + char *dash, char *zero) { + switch (modifier) { + case '_': + return underscore; + + case '-': + return dash; + + case '0': + return zero; + } + + return normal; +} + +static char * +_fmt(format, t, pt, ptlim, warnp, Locale) +const char * format; +const struct tm * const t; +char * pt; +const char * const ptlim; +int * warnp; +const struct strftime_locale *Locale; +{ + for ( ; *format; ++format) { + if (*format == '%') { + int modifier = 0; +label: + switch (*++format) { + case '\0': + --format; + break; + case 'A': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->weekday[t->tm_wday], + pt, ptlim, modifier); + continue; + case 'a': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->wday[t->tm_wday], + pt, ptlim, modifier); + continue; + case 'B': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->month[t->tm_mon], + pt, ptlim, modifier); + continue; + case 'b': + case 'h': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->mon[t->tm_mon], + pt, ptlim, modifier); + continue; + case 'C': + /* + ** %C used to do a... + ** _fmt("%a %b %e %X %Y", t); + ** ...whereas now POSIX 1003.2 calls for + ** something completely different. + ** (ado, 1993-05-24) + */ + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, + pt, ptlim, modifier); + continue; + case 'c': + { + int warn2 = IN_SOME; + + pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, Locale); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'D': + pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, Locale); + continue; + case 'd': + pt = _conv(t->tm_mday, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'E': + case 'O': + /* + ** C99 locale modifiers. + ** The sequences + ** %Ec %EC %Ex %EX %Ey %EY + ** %Od %oe %OH %OI %Om %OM + ** %OS %Ou %OU %OV %Ow %OW %Oy + ** are supposed to provide alternate + ** representations. + */ + goto label; + case '_': + case '-': + case '0': + case '^': + case '#': + modifier = *format; + goto label; + case 'e': + pt = _conv(t->tm_mday, + getformat(modifier, "%2d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'F': + pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, Locale); + continue; + case 'H': + pt = _conv(t->tm_hour, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'I': + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'j': + pt = _conv(t->tm_yday + 1, + getformat(modifier, "%03d", "%3d", "%d", "%03d"), + pt, ptlim); + continue; + case 'k': + /* + ** This used to be... + ** _conv(t->tm_hour % 12 ? + ** t->tm_hour % 12 : 12, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbins' + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = _conv(t->tm_hour, + getformat(modifier, "%2d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; +#ifdef KITCHEN_SINK + case 'K': + /* + ** After all this time, still unclaimed! + */ + pt = _add("kitchen sink", pt, ptlim, modifier); + continue; +#endif /* defined KITCHEN_SINK */ + case 'l': + /* + ** This used to be... + ** _conv(t->tm_hour, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbin's + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + getformat(modifier, "%2d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'M': + pt = _conv(t->tm_min, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'm': + pt = _conv(t->tm_mon + 1, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'n': + pt = _add("\n", pt, ptlim, modifier); + continue; + case 'p': + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + Locale->pm : + Locale->am, + pt, ptlim, modifier); + continue; + case 'P': + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + Locale->pm : + Locale->am, + pt, ptlim, FORCE_LOWER_CASE); + continue; + case 'R': + pt = _fmt("%H:%M", t, pt, ptlim, warnp, Locale); + continue; + case 'r': + pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, Locale); + continue; + case 'S': + pt = _conv(t->tm_sec, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 's': + { + struct tm tm; + char buf[INT_STRLEN_MAXIMUM( + time_t) + 1]; + time_t mkt; + + tm = *t; + mkt = mktime(&tm); + if (TYPE_SIGNED(time_t)) + (void) sprintf(buf, "%ld", + (long) mkt); + else (void) sprintf(buf, "%lu", + (unsigned long) mkt); + pt = _add(buf, pt, ptlim, modifier); + } + continue; + case 'T': + pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, Locale); + continue; + case 't': + pt = _add("\t", pt, ptlim, modifier); + continue; + case 'U': + pt = _conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'u': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "ISO 8601: Weekday as a decimal number + ** [1 (Monday) - 7]" + ** (ado, 1993-05-24) + */ + pt = _conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); + continue; + case 'V': /* ISO 8601 week number */ + case 'G': /* ISO 8601 year (four digits) */ + case 'g': /* ISO 8601 year (two digits) */ +/* +** From Arnold Robbins' strftime version 3.0: "the week number of the +** year (the first Monday as the first day of week 1) as a decimal number +** (01-53)." +** (ado, 1993-05-24) +** +** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: +** "Week 01 of a year is per definition the first week which has the +** Thursday in this year, which is equivalent to the week which contains +** the fourth day of January. In other words, the first week of a new year +** is the week which has the majority of its days in the new year. Week 01 +** might also contain days from the previous year and the week before week +** 01 of a year is the last week (52 or 53) of the previous year even if +** it contains days from the new year. A week starts with Monday (day 1) +** and ends with Sunday (day 7). For example, the first week of the year +** 1997 lasts from 1996-12-30 to 1997-01-05..." +** (ado, 1996-01-02) +*/ + { + int year; + int base; + int yday; + int wday; + int w; + + year = t->tm_year; + base = TM_YEAR_BASE; + yday = t->tm_yday; + wday = t->tm_wday; + for ( ; ; ) { + int len; + int bot; + int top; + + len = isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + /* + ** What yday (-3 ... 3) does + ** the ISO year begin on? + */ + bot = ((yday + 11 - wday) % + DAYSPERWEEK) - 3; + /* + ** What yday does the NEXT + ** ISO year begin on? + */ + top = bot - + (len % DAYSPERWEEK); + if (top < -3) + top += DAYSPERWEEK; + top += len; + if (yday >= top) { + ++base; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / + DAYSPERWEEK); + break; + } + --base; + yday += isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + } +#ifdef XPG4_1994_04_09 + if ((w == 52 && + t->tm_mon == TM_JANUARY) || + (w == 1 && + t->tm_mon == TM_DECEMBER)) + w = 53; +#endif /* defined XPG4_1994_04_09 */ + if (*format == 'V') + pt = _conv(w, + getformat(modifier, + "%02d", + "%2d", + "%d", + "%02d"), + pt, ptlim); + else if (*format == 'g') { + *warnp = IN_ALL; + pt = _yconv(year, base, 0, 1, + pt, ptlim, modifier); + } else pt = _yconv(year, base, 1, 1, + pt, ptlim, modifier); + } + continue; + case 'v': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "date as dd-bbb-YYYY" + ** (ado, 1993-05-24) + */ + pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, Locale); + continue; + case 'W': + pt = _conv((t->tm_yday + DAYSPERWEEK - + (t->tm_wday ? + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'w': + pt = _conv(t->tm_wday, "%d", pt, ptlim); + continue; + case 'X': + pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, Locale); + continue; + case 'x': + { + int warn2 = IN_SOME; + + pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, Locale); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'y': + *warnp = IN_ALL; + pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, + pt, ptlim, modifier); + continue; + case 'Y': + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, + pt, ptlim, modifier); + continue; + case 'Z': +#ifdef TM_ZONE + if (t->TM_ZONE != NULL) + pt = _add(t->TM_ZONE, pt, ptlim, + modifier); + else +#endif /* defined TM_ZONE */ + if (t->tm_isdst >= 0) + pt = _add(tzname[t->tm_isdst != 0], + pt, ptlim, modifier); + /* + ** C99 says that %Z must be replaced by the + ** empty string if the time zone is not + ** determinable. + */ + continue; + case 'z': + { + int diff; + char const * sign; + + if (t->tm_isdst < 0) + continue; +#ifdef TM_GMTOFF + diff = t->TM_GMTOFF; +#else /* !defined TM_GMTOFF */ + /* + ** C99 says that the UTC offset must + ** be computed by looking only at + ** tm_isdst. This requirement is + ** incorrect, since it means the code + ** must rely on magic (in this case + ** altzone and timezone), and the + ** magic might not have the correct + ** offset. Doing things correctly is + ** tricky and requires disobeying C99; + ** see GNU C strftime for details. + ** For now, punt and conform to the + ** standard, even though it's incorrect. + ** + ** C99 says that %z must be replaced by the + ** empty string if the time zone is not + ** determinable, so output nothing if the + ** appropriate variables are not available. + */ + if (t->tm_isdst == 0) +#ifdef USG_COMPAT + diff = -timezone; +#else /* !defined USG_COMPAT */ + continue; +#endif /* !defined USG_COMPAT */ + else +#ifdef ALTZONE + diff = -altzone; +#else /* !defined ALTZONE */ + continue; +#endif /* !defined ALTZONE */ +#endif /* !defined TM_GMTOFF */ + if (diff < 0) { + sign = "-"; + diff = -diff; + } else sign = "+"; + pt = _add(sign, pt, ptlim, modifier); + diff /= SECSPERMIN; + diff = (diff / MINSPERHOUR) * 100 + + (diff % MINSPERHOUR); + pt = _conv(diff, + getformat(modifier, "%04d", + "%4d", "%d", "%04d"), + pt, ptlim); + } + continue; + case '+': + pt = _fmt(Locale->date_fmt, t, pt, ptlim, + warnp, Locale); + continue; + case '%': + /* + ** X311J/88-090 (4.12.3.5): if conversion char is + ** undefined, behavior is undefined. Print out the + ** character itself as printf(3) also does. + */ + default: + break; + } + } + if (pt == ptlim) + break; + *pt++ = *format; + } + return pt; +} + +static char * +_conv(n, format, pt, ptlim) +const int n; +const char * const format; +char * const pt; +const char * const ptlim; +{ + char buf[INT_STRLEN_MAXIMUM(int) + 1]; + + (void) sprintf(buf, format, n); + return _add(buf, pt, ptlim, 0); +} + +static char * +_add(str, pt, ptlim, modifier) +const char * str; +char * pt; +const char * const ptlim; +int modifier; +{ + int c; + + switch (modifier) { + case FORCE_LOWER_CASE: + while (pt < ptlim && (*pt = tolower(*str++)) != '\0') { + ++pt; + } + break; + + case '^': + while (pt < ptlim && (*pt = toupper(*str++)) != '\0') { + ++pt; + } + break; + + case '#': + while (pt < ptlim && (c = *str++) != '\0') { + if (isupper(c)) { + c = tolower(c); + } else if (islower(c)) { + c = toupper(c); + } + *pt = c; + ++pt; + } + + break; + + default: + while (pt < ptlim && (*pt = *str++) != '\0') { + ++pt; + } + } + + return pt; +} + +/* +** POSIX and the C Standard are unclear or inconsistent about +** what %C and %y do if the year is negative or exceeds 9999. +** Use the convention that %C concatenated with %y yields the +** same output as %Y, and that %Y contains at least 4 bytes, +** with more only if necessary. +*/ + +static char * +_yconv(a, b, convert_top, convert_yy, pt, ptlim, modifier) +const int a; +const int b; +const int convert_top; +const int convert_yy; +char * pt; +const char * const ptlim; +int modifier; +{ + register int lead; + register int trail; + +#define DIVISOR 100 + trail = a % DIVISOR + b % DIVISOR; + lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) { + if (lead == 0 && trail < 0) + pt = _add("-0", pt, ptlim, modifier); + else pt = _conv(lead, getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + } + if (convert_yy) + pt = _conv(((trail < 0) ? -trail : trail), + getformat(modifier, "%02d", "%2d", "%d", "%02d"), + pt, ptlim); + return pt; +} + +#ifdef LOCALE_HOME +static struct lc_time_T * +_loc P((void)) +{ + static const char locale_home[] = LOCALE_HOME; + static const char lc_time[] = "LC_TIME"; + static char * locale_buf; + + int fd; + int oldsun; /* "...ain't got nothin' to do..." */ + char * lbuf; + char * name; + char * p; + const char ** ap; + const char * plim; + char filename[FILENAME_MAX]; + struct stat st; + size_t namesize; + size_t bufsize; + + /* + ** Use localebuf.mon[0] to signal whether locale is already set up. + */ + if (localebuf.mon[0]) + return &localebuf; + name = setlocale(LC_TIME, (char *) NULL); + if (name == NULL || *name == '\0') + goto no_locale; + /* + ** If the locale name is the same as our cache, use the cache. + */ + lbuf = locale_buf; + if (lbuf != NULL && strcmp(name, lbuf) == 0) { + p = lbuf; + for (ap = (const char **) &localebuf; + ap < (const char **) (&localebuf + 1); + ++ap) + *ap = p += strlen(p) + 1; + return &localebuf; + } + /* + ** Slurp the locale file into the cache. + */ + namesize = strlen(name) + 1; + if (sizeof filename < + ((sizeof locale_home) + namesize + (sizeof lc_time))) + goto no_locale; + oldsun = 0; + (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time); + fd = open(filename, O_RDONLY); + if (fd < 0) { + /* + ** Old Sun systems have a different naming and data convention. + */ + oldsun = 1; + (void) sprintf(filename, "%s/%s/%s", locale_home, + lc_time, name); + fd = open(filename, O_RDONLY); + if (fd < 0) + goto no_locale; + } + if (fstat(fd, &st) != 0) + goto bad_locale; + if (st.st_size <= 0) + goto bad_locale; + bufsize = namesize + st.st_size; + locale_buf = NULL; + lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize); + if (lbuf == NULL) + goto bad_locale; + (void) strcpy(lbuf, name); + p = lbuf + namesize; + plim = p + st.st_size; + if (read(fd, p, (size_t) st.st_size) != st.st_size) + goto bad_lbuf; + if (close(fd) != 0) + goto bad_lbuf; + /* + ** Parse the locale file into localebuf. + */ + if (plim[-1] != '\n') + goto bad_lbuf; + for (ap = (const char **) &localebuf; + ap < (const char **) (&localebuf + 1); + ++ap) { + if (p == plim) + goto bad_lbuf; + *ap = p; + while (*p != '\n') + ++p; + *p++ = '\0'; + } + if (oldsun) { + /* + ** SunOS 4 used an obsolescent format; see localdtconv(3). + ** c_fmt had the ``short format for dates and times together'' + ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale); + ** date_fmt had the ``long format for dates'' + ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale). + ** Discard the latter in favor of the former. + */ + localebuf.date_fmt = localebuf.c_fmt; + } + /* + ** Record the successful parse in the cache. + */ + locale_buf = lbuf; + + return &localebuf; + +bad_lbuf: + free(lbuf); +bad_locale: + (void) close(fd); +no_locale: + localebuf = C_time_locale; + locale_buf = NULL; + return &localebuf; +} +#endif /* defined LOCALE_HOME */ diff --git a/libcutils/tztime.c b/libcutils/tztime.c 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 = ' '; + } + } +} + + + diff --git a/liblog/Android.mk b/liblog/Android.mk new file mode 100644 index 0000000..0eec87f --- /dev/null +++ b/liblog/Android.mk @@ -0,0 +1,72 @@ +# +# 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) + +liblog_sources := logd_write.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 := true + endif +endif +# USE_MINGW is defined when we build against Mingw on Linux +ifneq ($(strip $(USE_MINGW)),) + WITH_MINGW := true +endif + +ifndef WITH_MINGW + liblog_sources += \ + logprint.c \ + event_tag_map.c +endif + +liblog_host_sources := $(liblog_sources) fake_log_device.c + +# Static library for host +# ======================================================== +LOCAL_MODULE := liblog +LOCAL_SRC_FILES := $(liblog_host_sources) +LOCAL_LDLIBS := -lpthread +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 +include $(BUILD_HOST_STATIC_LIBRARY) + +ifeq ($(TARGET_SIMULATOR),true) + # Shared library for simulator + # ======================================================== + include $(CLEAR_VARS) + LOCAL_MODULE := liblog + LOCAL_SRC_FILES := $(liblog_host_sources) + LOCAL_LDLIBS := -lpthread + LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 + include $(BUILD_SHARED_LIBRARY) +else # !sim + # Shared and static library for target + # ======================================================== + include $(CLEAR_VARS) + LOCAL_MODULE := liblog + LOCAL_SRC_FILES := $(liblog_sources) + include $(BUILD_STATIC_LIBRARY) + + include $(CLEAR_VARS) + LOCAL_MODULE := liblog + LOCAL_WHOLE_STATIC_LIBRARIES := liblog + include $(BUILD_SHARED_LIBRARY) +endif # !sim diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c new file mode 100644 index 0000000..e70754e --- /dev/null +++ b/liblog/event_tag_map.c @@ -0,0 +1,438 @@ +/* + * 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/event_tag_map.h" +#include "cutils/log.h" + +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <errno.h> +#include <assert.h> + +#define OUT_TAG "EventTagMap" + +/* + * Single entry. + */ +typedef struct EventTag { + unsigned int tagIndex; + const char* tagStr; +} EventTag; + +/* + * Map. + */ +struct EventTagMap { + /* memory-mapped source file; we get strings from here */ + void* mapAddr; + size_t mapLen; + + /* array of event tags, sorted numerically by tag index */ + EventTag* tagArray; + int numTags; +}; + +/* fwd */ +static int processFile(EventTagMap* map); +static int countMapLines(const EventTagMap* map); +static int parseMapLines(EventTagMap* map); +static int scanTagLine(char** pData, EventTag* tag, int lineNum); +static int sortTags(EventTagMap* map); +static void dumpTags(const EventTagMap* map); + + +/* + * Open the map file and allocate a structure to manage it. + * + * We create a private mapping because we want to terminate the log tag + * strings with '\0'. + */ +EventTagMap* android_openEventTagMap(const char* fileName) +{ + EventTagMap* newTagMap; + off_t end; + int fd = -1; + + newTagMap = calloc(1, sizeof(EventTagMap)); + if (newTagMap == NULL) + return NULL; + + fd = open(fileName, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s: unable to open map '%s': %s\n", + OUT_TAG, fileName, strerror(errno)); + goto fail; + } + + end = lseek(fd, 0L, SEEK_END); + (void) lseek(fd, 0L, SEEK_SET); + if (end < 0) { + fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName); + goto fail; + } + + newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + if (newTagMap->mapAddr == MAP_FAILED) { + fprintf(stderr, "%s: mmap(%s) failed: %s\n", + OUT_TAG, fileName, strerror(errno)); + goto fail; + } + newTagMap->mapLen = end; + + if (processFile(newTagMap) != 0) + goto fail; + + return newTagMap; + +fail: + android_closeEventTagMap(newTagMap); + if (fd >= 0) + close(fd); + return NULL; +} + +/* + * Close the map. + */ +void android_closeEventTagMap(EventTagMap* map) +{ + if (map == NULL) + return; + + munmap(map->mapAddr, map->mapLen); + free(map); +} + +/* + * Look up an entry in the map. + * + * The entries are sorted by tag number, so we can do a binary search. + */ +const char* android_lookupEventTag(const EventTagMap* map, int tag) +{ + int hi, lo, mid; + + lo = 0; + hi = map->numTags-1; + + while (lo <= hi) { + int cmp; + + mid = (lo+hi)/2; + cmp = map->tagArray[mid].tagIndex - tag; + if (cmp < 0) { + /* tag is bigger */ + lo = mid + 1; + } else if (cmp > 0) { + /* tag is smaller */ + hi = mid - 1; + } else { + /* found */ + return map->tagArray[mid].tagStr; + } + } + + return NULL; +} + + + +/* + * Determine whether "c" is a whitespace char. + */ +static inline int isCharWhitespace(char c) +{ + return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); +} + +/* + * Determine whether "c" is a valid tag char. + */ +static inline int isCharValidTag(char c) +{ + return ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '_')); +} + +/* + * Determine whether "c" is a valid decimal digit. + */ +static inline int isCharDigit(char c) +{ + return (c >= '0' && c <= '9'); +} + + +/* + * Crunch through the file, parsing the contents and creating a tag index. + */ +static int processFile(EventTagMap* map) +{ + EventTag* tagArray = NULL; + + /* get a tag count */ + map->numTags = countMapLines(map); + if (map->numTags < 0) + return -1; + + //printf("+++ found %d tags\n", map->numTags); + + /* allocate storage for the tag index array */ + map->tagArray = calloc(1, sizeof(EventTag) * map->numTags); + if (map->tagArray == NULL) + return -1; + + /* parse the file, null-terminating tag strings */ + if (parseMapLines(map) != 0) { + fprintf(stderr, "%s: file parse failed\n", OUT_TAG); + return -1; + } + + /* sort the tags and check for duplicates */ + if (sortTags(map) != 0) + return -1; + + return 0; +} + +/* + * Run through all lines in the file, determining whether they're blank, + * comments, or possibly have a tag entry. + * + * This is a very "loose" scan. We don't try to detect syntax errors here. + * The later pass is more careful, but the number of tags found there must + * match the number of tags found here. + * + * Returns the number of potential tag entries found. + */ +static int countMapLines(const EventTagMap* map) +{ + int numTags, unknown; + const char* cp; + const char* endp; + + cp = (const char*) map->mapAddr; + endp = cp + map->mapLen; + + numTags = 0; + unknown = 1; + while (cp < endp) { + if (*cp == '\n') { + unknown = 1; + } else if (unknown) { + if (isCharDigit(*cp)) { + /* looks like a tag to me */ + numTags++; + unknown = 0; + } else if (isCharWhitespace(*cp)) { + /* might be leading whitespace before tag num, keep going */ + } else { + /* assume comment; second pass can complain in detail */ + unknown = 0; + } + } else { + /* we've made up our mind; just scan to end of line */ + } + cp++; + } + + return numTags; +} + +/* + * Parse the tags out of the file. + */ +static int parseMapLines(EventTagMap* map) +{ + int tagNum, lineStart, lineNum; + char* cp; + char* endp; + + cp = (char*) map->mapAddr; + endp = cp + map->mapLen; + + /* insist on EOL at EOF; simplifies parsing and null-termination */ + if (*(endp-1) != '\n') { + fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG); + return -1; + } + + tagNum = 0; + lineStart = 1; + lineNum = 1; + while (cp < endp) { + //printf("{%02x}", *cp); fflush(stdout); + if (*cp == '\n') { + lineStart = 1; + lineNum++; + } else if (lineStart) { + if (*cp == '#') { + /* comment; just scan to end */ + lineStart = 0; + } else if (isCharDigit(*cp)) { + /* looks like a tag; scan it out */ + if (tagNum >= map->numTags) { + fprintf(stderr, + "%s: more tags than expected (%d)\n", OUT_TAG, tagNum); + return -1; + } + if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) + return -1; + tagNum++; + lineNum++; // we eat the '\n' + /* leave lineStart==1 */ + } else if (isCharWhitespace(*cp)) { + /* looks like leading whitespace; keep scanning */ + } else { + fprintf(stderr, + "%s: unexpected chars (0x%02x) in tag number on line %d\n", + OUT_TAG, *cp, lineNum); + return -1; + } + } else { + /* this is a blank or comment line */ + } + cp++; + } + + if (tagNum != map->numTags) { + fprintf(stderr, "%s: parsed %d tags, expected %d\n", + OUT_TAG, tagNum, map->numTags); + return -1; + } + + return 0; +} + +/* + * Scan one tag line. + * + * "*pData" should be pointing to the first digit in the tag number. On + * successful return, it will be pointing to the last character in the + * tag line (i.e. the character before the start of the next line). + * + * Returns 0 on success, nonzero on failure. + */ +static int scanTagLine(char** pData, EventTag* tag, int lineNum) +{ + char* cp = *pData; + char* startp; + char* endp; + unsigned long val; + + startp = cp; + while (isCharDigit(*++cp)) + ; + *cp = '\0'; + + val = strtoul(startp, &endp, 10); + assert(endp == cp); + if (endp != cp) + fprintf(stderr, "ARRRRGH\n"); + + tag->tagIndex = val; + + while (*++cp != '\n' && isCharWhitespace(*cp)) + ; + + if (*cp == '\n') { + fprintf(stderr, + "%s: missing tag string on line %d\n", OUT_TAG, lineNum); + return -1; + } + + tag->tagStr = cp; + + while (isCharValidTag(*++cp)) + ; + + if (*cp == '\n') { + /* null terminate and return */ + *cp = '\0'; + } else if (isCharWhitespace(*cp)) { + /* CRLF or trailin spaces; zap this char, then scan for the '\n' */ + *cp = '\0'; + + /* just ignore the rest of the line till \n + TODO: read the tag description that follows the tag name + */ + while (*++cp != '\n') { + } + } else { + fprintf(stderr, + "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum); + return -1; + } + + *pData = cp; + + //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr); + return 0; +} + +/* + * Compare two EventTags. + */ +static int compareEventTags(const void* v1, const void* v2) +{ + const EventTag* tag1 = (const EventTag*) v1; + const EventTag* tag2 = (const EventTag*) v2; + + return tag1->tagIndex - tag2->tagIndex; +} + +/* + * Sort the EventTag array so we can do fast lookups by tag index. After + * the sort we do a quick check for duplicate tag indices. + * + * Returns 0 on success. + */ +static int sortTags(EventTagMap* map) +{ + int i; + + qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags); + + for (i = 1; i < map->numTags; i++) { + if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) { + fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n", + OUT_TAG, + map->tagArray[i].tagIndex, map->tagArray[i].tagStr, + map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr); + return -1; + } + } + + return 0; +} + +/* + * Dump the tag array for debugging. + */ +static void dumpTags(const EventTagMap* map) +{ + int i; + + for (i = 0; i < map->numTags; i++) { + const EventTag* tag = &map->tagArray[i]; + printf(" %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr); + } +} + diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c new file mode 100644 index 0000000..ed9d699 --- /dev/null +++ b/liblog/fake_log_device.c @@ -0,0 +1,685 @@ +/* + * 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. + */ +/* + * Intercepts log messages intended for the Android log device. + * When running in the context of the simulator, the messages are + * passed on to the underlying (fake) log device. When not in the + * simulator, messages are printed to stderr. + */ +#include "cutils/logd.h" + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> + +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif + +#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ + +#define kTagSetSize 16 /* arbitrary */ + +#if 0 +#define TRACE(...) printf("fake_log_device: " __VA_ARGS__) +#else +#define TRACE(...) ((void)0) +#endif + +/* from the long-dead utils/Log.cpp */ +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG +} LogFormat; + + +/* + * Log driver state. + */ +typedef struct LogState { + /* the fake fd that's seen by the user */ + int fakeFd; + + /* a printable name for this fake device */ + char *debugName; + + /* nonzero if this is a binary log */ + int isBinary; + + /* global minimum priority */ + int globalMinPriority; + + /* output format */ + LogFormat outputFormat; + + /* tags and priorities */ + struct { + char tag[kMaxTagLen]; + int minPriority; + } tagSet[kTagSetSize]; +} LogState; + + +#ifdef HAVE_PTHREADS +/* + * Locking. Since we're emulating a device, we need to be prepared + * to have multiple callers at the same time. This lock is used + * to both protect the fd list and to prevent LogStates from being + * freed out from under a user. + */ +static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER; + +static void lock() +{ + pthread_mutex_lock(&fakeLogDeviceLock); +} + +static void unlock() +{ + pthread_mutex_unlock(&fakeLogDeviceLock); +} +#else // !HAVE_PTHREADS +#define lock() ((void)0) +#define unlock() ((void)0) +#endif // !HAVE_PTHREADS + + +/* + * File descriptor management. + */ +#define FAKE_FD_BASE 10000 +#define MAX_OPEN_LOGS 16 +static LogState *openLogTable[MAX_OPEN_LOGS]; + +/* + * Allocate an fd and associate a new LogState with it. + * The fd is available via the fakeFd field of the return value. + */ +static LogState *createLogState() +{ + size_t i; + + for (i = 0; i < sizeof(openLogTable); i++) { + if (openLogTable[i] == NULL) { + openLogTable[i] = calloc(1, sizeof(LogState)); + openLogTable[i]->fakeFd = FAKE_FD_BASE + i; + return openLogTable[i]; + } + } + return NULL; +} + +/* + * Translate an fd to a LogState. + */ +static LogState *fdToLogState(int fd) +{ + if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) { + return openLogTable[fd - FAKE_FD_BASE]; + } + return NULL; +} + +/* + * Unregister the fake fd and free the memory it pointed to. + */ +static void deleteFakeFd(int fd) +{ + LogState *ls; + + lock(); + + ls = fdToLogState(fd); + if (ls != NULL) { + openLogTable[fd - FAKE_FD_BASE] = NULL; + free(ls->debugName); + free(ls); + } + + unlock(); +} + +/* + * Configure logging based on ANDROID_LOG_TAGS environment variable. We + * need to parse a string that looks like + * + * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i + * + * The tag (or '*' for the global level) comes first, followed by a colon + * and a letter indicating the minimum priority level we're expected to log. + * This can be used to reveal or conceal logs with specific tags. + * + * We also want to check ANDROID_PRINTF_LOG to determine how the output + * will look. + */ +static void configureInitialState(const char* pathName, LogState* logState) +{ + static const int kDevLogLen = sizeof("/dev/log/") - 1; + + logState->debugName = strdup(pathName); + + /* identify binary logs */ + if (strcmp(pathName + kDevLogLen, "events") == 0) { + logState->isBinary = 1; + } + + /* global min priority defaults to "info" level */ + logState->globalMinPriority = ANDROID_LOG_INFO; + + /* + * This is based on the the long-dead utils/Log.cpp code. + */ + const char* tags = getenv("ANDROID_LOG_TAGS"); + TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags); + if (tags != NULL) { + int entry = 0; + + while (*tags != '\0') { + char tagName[kMaxTagLen]; + int i, minPrio; + + while (isspace(*tags)) + tags++; + + i = 0; + while (*tags != '\0' && !isspace(*tags) && *tags != ':' && + i < kMaxTagLen) + { + tagName[i++] = *tags++; + } + if (i == kMaxTagLen) { + TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1); + return; + } + tagName[i] = '\0'; + + /* default priority, if there's no ":" part; also zero out '*' */ + minPrio = ANDROID_LOG_VERBOSE; + if (tagName[0] == '*' && tagName[1] == '\0') { + minPrio = ANDROID_LOG_DEBUG; + tagName[0] = '\0'; + } + + if (*tags == ':') { + tags++; + if (*tags >= '0' && *tags <= '9') { + if (*tags >= ('0' + ANDROID_LOG_SILENT)) + minPrio = ANDROID_LOG_VERBOSE; + else + minPrio = *tags - '\0'; + } else { + switch (*tags) { + case 'v': minPrio = ANDROID_LOG_VERBOSE; break; + case 'd': minPrio = ANDROID_LOG_DEBUG; break; + case 'i': minPrio = ANDROID_LOG_INFO; break; + case 'w': minPrio = ANDROID_LOG_WARN; break; + case 'e': minPrio = ANDROID_LOG_ERROR; break; + case 'f': minPrio = ANDROID_LOG_FATAL; break; + case 's': minPrio = ANDROID_LOG_SILENT; break; + default: minPrio = ANDROID_LOG_DEFAULT; break; + } + } + + tags++; + if (*tags != '\0' && !isspace(*tags)) { + TRACE("ERROR: garbage in tag env; expected whitespace\n"); + TRACE(" env='%s'\n", tags); + return; + } + } + + if (tagName[0] == 0) { + logState->globalMinPriority = minPrio; + TRACE("+++ global min prio %d\n", logState->globalMinPriority); + } else { + logState->tagSet[entry].minPriority = minPrio; + strcpy(logState->tagSet[entry].tag, tagName); + TRACE("+++ entry %d: %s:%d\n", + entry, + logState->tagSet[entry].tag, + logState->tagSet[entry].minPriority); + entry++; + } + } + } + + + /* + * Taken from the long-dead utils/Log.cpp + */ + const char* fstr = getenv("ANDROID_PRINTF_LOG"); + LogFormat format; + if (fstr == NULL) { + format = FORMAT_BRIEF; + } else { + if (strcmp(fstr, "brief") == 0) + format = FORMAT_BRIEF; + else if (strcmp(fstr, "process") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "tag") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "thread") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "raw") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "time") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "long") == 0) + format = FORMAT_PROCESS; + else + format = (LogFormat) atoi(fstr); // really?! + } + + logState->outputFormat = format; +} + +/* + * Return a human-readable string for the priority level. Always returns + * a valid string. + */ +static const char* getPriorityString(int priority) +{ + /* the first character of each string should be unique */ + static const char* priorityStrings[] = { + "Verbose", "Debug", "Info", "Warn", "Error", "Assert" + }; + int idx; + + idx = (int) priority - (int) ANDROID_LOG_VERBOSE; + if (idx < 0 || + idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0]))) + return "?unknown?"; + return priorityStrings[idx]; +} + +#ifndef HAVE_WRITEV +/* + * Some platforms like WIN32 do not have writev(). + * Make up something to replace it. + */ +static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { + int result = 0; + struct iovec* end = iov + iovcnt; + for (; iov < end; iov++) { + int w = write(fd, iov->iov_base, iov->iov_len); + if (w != iov->iov_len) { + if (w < 0) + return w; + return result + w; + } + result += w; + } + return result; +} + +#define writev fake_writev +#endif + + +/* + * Write a filtered log message to stderr. + * + * Log format parsing taken from the long-dead utils/Log.cpp. + */ +static void showLog(LogState *state, + int logPrio, const char* tag, const char* msg) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + time_t when; + pid_t pid, tid; + + TRACE("LOG %d: %s %s", logPrio, tag, msg); + + priChar = getPriorityString(logPrio)[0]; + when = time(NULL); + pid = tid = getpid(); // find gettid()? + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&when, &tmBuf); +#else + ptm = localtime(&when); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (state->outputFormat) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%p) ", priChar, pid, (void*)tid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; prefixLen = 0; + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %-8s\n\t", timeBuf, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s %5d:%p %c/%-8s ]\n", + timeBuf, pid, (void*)tid, priChar, tag); + strcpy(suffixBuf, "\n\n"); suffixLen = 2; + break; + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, tag, pid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + } + + /* + * Figure out how many lines there will be. + */ + const char* end = msg + strlen(msg); + size_t numLines = 0; + const char* p = msg; + while (p < end) { + if (*p++ == '\n') numLines++; + } + if (p > msg && *(p-1) != '\n') numLines++; + + /* + * Create an array of iovecs large enough to write all of + * the lines with a prefix and a suffix. + */ + const size_t INLINE_VECS = 6; + const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*)); + struct iovec stackVec[INLINE_VECS]; + struct iovec* vec = stackVec; + size_t numVecs; + + if (numLines > MAX_LINES) + numLines = MAX_LINES; + + numVecs = numLines*3; // 3 iovecs per line. + if (numVecs > INLINE_VECS) { + vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines); + if (vec == NULL) { + msg = "LOG: write failed, no memory"; + numVecs = 3; + numLines = 1; + vec = stackVec; + } + } + + /* + * Fill in the iovec pointers. + */ + p = msg; + struct iovec* v = vec; + int totalLen = 0; + while (numLines > 0 && p < end) { + if (prefixLen > 0) { + v->iov_base = prefixBuf; + v->iov_len = prefixLen; + totalLen += prefixLen; + v++; + } + const char* start = p; + while (p < end && *p != '\n') p++; + if ((p-start) > 0) { + v->iov_base = (void*)start; + v->iov_len = p-start; + totalLen += p-start; + v++; + } + if (*p == '\n') p++; + if (suffixLen > 0) { + v->iov_base = suffixBuf; + v->iov_len = suffixLen; + totalLen += suffixLen; + v++; + } + numLines -= 1; + } + + /* + * Write the entire message to the log file with a single writev() call. + * We need to use this rather than a collection of printf()s on a FILE* + * because of multi-threading and multi-process issues. + * + * If the file was not opened with O_APPEND, this will produce interleaved + * output when called on the same file from multiple processes. + * + * If the file descriptor is actually a network socket, the writev() + * call may return with a partial write. Putting the writev() call in + * a loop can result in interleaved data. This can be alleviated + * somewhat by wrapping the writev call in the Mutex. + */ + + for(;;) { + int cc = writev(fileno(stderr), vec, v-vec); + + if (cc == totalLen) break; + + if (cc < 0) { + if(errno == EINTR) continue; + + /* can't really log the failure; for now, throw out a stderr */ + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + break; + } else { + /* shouldn't happen when writing to file or tty */ + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen); + break; + } + } + + /* if we allocated storage for the iovecs, free it */ + if (vec != stackVec) + free(vec); +} + + +/* + * Receive a log message. We happen to know that "vector" has three parts: + * + * priority (1 byte) + * tag (N bytes -- null-terminated ASCII string) + * message (N bytes -- null-terminated ASCII string) + */ +static ssize_t logWritev(int fd, const struct iovec* vector, int count) +{ + LogState* state; + + /* Make sure that no-one frees the LogState while we're using it. + * Also guarantees that only one thread is in showLog() at a given + * time (if it matters). + */ + lock(); + + state = fdToLogState(fd); + if (state == NULL) { + errno = EBADF; + goto error; + } + + if (state->isBinary) { + TRACE("%s: ignoring binary log\n", state->debugName); + goto bail; + } + + if (count != 3) { + TRACE("%s: writevLog with count=%d not expected\n", + state->debugName, count); + goto error; + } + + /* pull out the three fields */ + int logPrio = *(const char*)vector[0].iov_base; + const char* tag = (const char*) vector[1].iov_base; + const char* msg = (const char*) vector[2].iov_base; + + /* see if this log tag is configured */ + int i; + int minPrio = state->globalMinPriority; + for (i = 0; i < kTagSetSize; i++) { + if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN) + break; /* reached end of configured values */ + + if (strcmp(state->tagSet[i].tag, tag) == 0) { + //TRACE("MATCH tag '%s'\n", tag); + minPrio = state->tagSet[i].minPriority; + break; + } + } + + if (logPrio >= minPrio) { + showLog(state, logPrio, tag, msg); + } else { + //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg); + } + +bail: + unlock(); + return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len; +error: + unlock(); + return -1; +} + +/* + * Free up our state and close the fake descriptor. + */ +static int logClose(int fd) +{ + deleteFakeFd(fd); + return 0; +} + +/* + * Open a log output device and return a fake fd. + */ +static int logOpen(const char* pathName, int flags) +{ + LogState *logState; + int fd = -1; + + lock(); + + logState = createLogState(); + if (logState != NULL) { + configureInitialState(pathName, logState); + fd = logState->fakeFd; + } else { + errno = ENFILE; + } + + unlock(); + + return fd; +} + + +/* + * Runtime redirection. If this binary is running in the simulator, + * just pass log messages to the emulated device. If it's running + * outside of the simulator, write the log messages to stderr. + */ + +static int (*redirectOpen)(const char *pathName, int flags) = NULL; +static int (*redirectClose)(int fd) = NULL; +static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count) + = NULL; + +static void setRedirects() +{ + const char *ws; + + /* Wrapsim sets this environment variable on children that it's + * created using its LD_PRELOAD wrapper. + */ + ws = getenv("ANDROID_WRAPSIM"); + if (ws != NULL && strcmp(ws, "1") == 0) { + /* We're running inside wrapsim, so we can just write to the device. */ + redirectOpen = (int (*)(const char *pathName, int flags))open; + redirectClose = close; + redirectWritev = writev; + } else { + /* There's no device to delegate to; handle the logging ourselves. */ + redirectOpen = logOpen; + redirectClose = logClose; + redirectWritev = logWritev; + } +} + +int fakeLogOpen(const char *pathName, int flags) +{ + if (redirectOpen == NULL) { + setRedirects(); + } + return redirectOpen(pathName, flags); +} + +int fakeLogClose(int fd) +{ + /* Assume that open() was called first. */ + return redirectClose(fd); +} + +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) +{ + /* Assume that open() was called first. */ + return redirectWritev(fd, vector, count); +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c new file mode 100644 index 0000000..80867d1 --- /dev/null +++ b/liblog/logd_write.c @@ -0,0 +1,230 @@ +/* + * 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 <time.h> +#include <stdio.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + +#include <cutils/logger.h> +#include <cutils/logd.h> + +#define LOG_BUF_SIZE 1024 + +#if FAKE_LOG_DEVICE +// This will be defined when building for the host. +#define log_open(pathname, flags) fakeLogOpen(pathname, flags) +#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) +#define log_close(filedes) fakeLogClose(filedes) +#else +#define log_open(pathname, flags) open(pathname, flags) +#define log_writev(filedes, vector, count) writev(filedes, vector, count) +#define log_close(filedes) close(filedes) +#endif + +typedef enum { + LOG_ID_MAIN = 0, + LOG_ID_RADIO, + LOG_ID_EVENTS, + LOG_ID_MAX +} log_id_t; + +static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); +static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = + __write_to_log_init; +#ifdef HAVE_PTHREADS +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1 }; + +/* + * This is used by the C++ code to decide if it should write logs through + * the C code. Basically, if /dev/log/... is available, we're running in + * the simulator rather than a desktop tool and want to use the device. + */ +static enum { + kLogUninitialized, kLogNotAvailable, kLogAvailable +} g_log_status = kLogUninitialized; +int __android_log_dev_available(void) +{ + if (g_log_status == kLogUninitialized) { + if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + g_log_status = kLogAvailable; + else + g_log_status = kLogNotAvailable; + } + + return (g_log_status == kLogAvailable); +} + +static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +{ + ssize_t ret; + int log_fd; + + if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { + log_fd = log_fds[(int)log_id]; + } else { + return EBADF; + } + + do { + ret = log_writev(log_fd, vec, nr); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) +{ +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&log_init_lock); +#endif + + if (write_to_log == __write_to_log_init) { + log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + + write_to_log = __write_to_log_kernel; + + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || + log_fds[LOG_ID_EVENTS] < 0) { + log_close(log_fds[LOG_ID_MAIN]); + log_close(log_fds[LOG_ID_RADIO]); + log_close(log_fds[LOG_ID_EVENTS]); + log_fds[LOG_ID_MAIN] = -1; + log_fds[LOG_ID_RADIO] = -1; + log_fds[LOG_ID_EVENTS] = -1; + write_to_log = __write_to_log_null; + } + } + +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&log_init_lock); +#endif + + return write_to_log(log_id, vec, nr); +} + +int __android_log_write(int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + log_id_t log_id = LOG_ID_MAIN; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if (!strcmp(tag, "HTC_RIL") || + !strcmp(tag, "RILJ") || + !strcmp(tag, "RILC") || + !strcmp(tag, "RILD") || + !strcmp(tag, "RIL") || + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK")) + log_id = LOG_ID_RADIO; + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(log_id, vec, 3); +} + +int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) +{ + char buf[LOG_BUF_SIZE]; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_write(prio, tag, buf); +} + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + __android_log_write(ANDROID_LOG_FATAL, tag, buf); + + __builtin_trap(); /* trap so we have a chance to debug the situation */ +} + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len) +{ + struct iovec vec[2]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = (void*)payload; + vec[1].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 2); +} + +/* + * Like __android_log_bwrite, but takes the type as well. Doesn't work + * for the general case where we're generating lists of stuff, but very + * handy if we just want to dump an integer into the log. + */ +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len) +{ + struct iovec vec[3]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = (void*)payload; + vec[2].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 3); +} diff --git a/liblog/logprint.c b/liblog/logprint.c new file mode 100644 index 0000000..2cf1254 --- /dev/null +++ b/liblog/logprint.c @@ -0,0 +1,972 @@ +/* //device/libs/cutils/logprint.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 _GNU_SOURCE /* for asprintf */ + +#include <ctype.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <alloca.h> +#include <assert.h> +#include <arpa/inet.h> + +#include <cutils/logd.h> +#include <cutils/logprint.h> + +typedef struct FilterInfo_t { + char *mTag; + android_LogPriority mPri; + struct FilterInfo_t *p_next; +} FilterInfo; + +struct AndroidLogFormat_t { + android_LogPriority global_pri; + FilterInfo *filters; + AndroidLogPrintFormat format; +}; + +static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) +{ + FilterInfo *p_ret; + + p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo)); + p_ret->mTag = strdup(tag); + p_ret->mPri = pri; + + return p_ret; +} + +static void filterinfo_free(FilterInfo *p_info) +{ + if (p_info == NULL) { + return; + } + + free(p_info->mTag); + p_info->mTag = NULL; +} + +/* + * Note: also accepts 0-9 priorities + * returns ANDROID_LOG_UNKNOWN if the character is unrecognized + */ +static android_LogPriority filterCharToPri (char c) +{ + android_LogPriority pri; + + c = tolower(c); + + if (c >= '0' && c <= '9') { + if (c >= ('0'+ANDROID_LOG_SILENT)) { + pri = ANDROID_LOG_VERBOSE; + } else { + pri = (android_LogPriority)(c - '0'); + } + } else if (c == 'v') { + pri = ANDROID_LOG_VERBOSE; + } else if (c == 'd') { + pri = ANDROID_LOG_DEBUG; + } else if (c == 'i') { + pri = ANDROID_LOG_INFO; + } else if (c == 'w') { + pri = ANDROID_LOG_WARN; + } else if (c == 'e') { + pri = ANDROID_LOG_ERROR; + } else if (c == 'f') { + pri = ANDROID_LOG_FATAL; + } else if (c == 's') { + pri = ANDROID_LOG_SILENT; + } else if (c == '*') { + pri = ANDROID_LOG_DEFAULT; + } else { + pri = ANDROID_LOG_UNKNOWN; + } + + return pri; +} + +static char filterPriToChar (android_LogPriority pri) +{ + switch (pri) { + case ANDROID_LOG_VERBOSE: return 'V'; + case ANDROID_LOG_DEBUG: return 'D'; + case ANDROID_LOG_INFO: return 'I'; + case ANDROID_LOG_WARN: return 'W'; + case ANDROID_LOG_ERROR: return 'E'; + case ANDROID_LOG_FATAL: return 'F'; + case ANDROID_LOG_SILENT: return 'S'; + + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: return '?'; + } +} + +static android_LogPriority filterPriForTag( + AndroidLogFormat *p_format, const char *tag) +{ + FilterInfo *p_curFilter; + + for (p_curFilter = p_format->filters + ; p_curFilter != NULL + ; p_curFilter = p_curFilter->p_next + ) { + if (0 == strcmp(tag, p_curFilter->mTag)) { + if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) { + return p_format->global_pri; + } else { + return p_curFilter->mPri; + } + } + } + + return p_format->global_pri; +} + +/** for debugging */ +static void dumpFilters(AndroidLogFormat *p_format) +{ + FilterInfo *p_fi; + + for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { + char cPri = filterPriToChar(p_fi->mPri); + if (p_fi->mPri == ANDROID_LOG_DEFAULT) { + cPri = filterPriToChar(p_format->global_pri); + } + fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri); + } + + fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri)); + +} + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int android_log_shouldPrintLine ( + AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) +{ + return pri >= filterPriForTag(p_format, tag); +} + +AndroidLogFormat *android_log_format_new() +{ + AndroidLogFormat *p_ret; + + p_ret = calloc(1, sizeof(AndroidLogFormat)); + + p_ret->global_pri = ANDROID_LOG_VERBOSE; + p_ret->format = FORMAT_BRIEF; + + return p_ret; +} + +void android_log_format_free(AndroidLogFormat *p_format) +{ + FilterInfo *p_info, *p_info_old; + + p_info = p_format->filters; + + while (p_info != NULL) { + p_info_old = p_info; + p_info = p_info->p_next; + + free(p_info_old); + } + + free(p_format); +} + + + +void android_log_setPrintFormat(AndroidLogFormat *p_format, + AndroidLogPrintFormat format) +{ + p_format->format=format; +} + +/** + * Returns FORMAT_OFF on invalid string + */ +AndroidLogPrintFormat android_log_formatFromString(const char * formatString) +{ + static AndroidLogPrintFormat format; + + if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF; + else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS; + else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG; + else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD; + else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW; + else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; + else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; + else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; + else format = FORMAT_OFF; + + return format; +} + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + */ + +int android_log_addFilterRule(AndroidLogFormat *p_format, + const char *filterExpression) +{ + size_t i=0; + size_t tagNameLength; + android_LogPriority pri = ANDROID_LOG_DEFAULT; + + tagNameLength = strcspn(filterExpression, ":"); + + if (tagNameLength == 0) { + goto error; + } + + if(filterExpression[tagNameLength] == ':') { + pri = filterCharToPri(filterExpression[tagNameLength+1]); + + if (pri == ANDROID_LOG_UNKNOWN) { + goto error; + } + } + + if(0 == strncmp("*", filterExpression, tagNameLength)) { + // This filter expression refers to the global filter + // The default level for this is DEBUG if the priority + // is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_DEBUG; + } + + p_format->global_pri = pri; + } else { + // for filter expressions that don't refer to the global + // filter, the default is verbose if the priority is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_VERBOSE; + } + + char *tagName; + +// Presently HAVE_STRNDUP is never defined, so the second case is always taken +// Darwin doesn't have strnup, everything else does +#ifdef HAVE_STRNDUP + tagName = strndup(filterExpression, tagNameLength); +#else + //a few extra bytes copied... + tagName = strdup(filterExpression); + tagName[tagNameLength] = '\0'; +#endif /*HAVE_STRNDUP*/ + + FilterInfo *p_fi = filterinfo_new(tagName, pri); + free(tagName); + + p_fi->p_next = p_format->filters; + p_format->filters = p_fi; + } + + return 0; +error: + return -1; +} + + +/** + * filterString: a comma/whitespace-separated set of filter expressions + * + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterString(AndroidLogFormat *p_format, + const char *filterString) +{ + char *filterStringCopy = strdup (filterString); + char *p_cur = filterStringCopy; + char *p_ret; + int err; + + // Yes, I'm using strsep + while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { + // ignore whitespace-only entries + if(p_ret[0] != '\0') { + err = android_log_addFilterRule(p_format, p_ret); + + if (err < 0) { + goto error; + } + } + } + + free (filterStringCopy); + return 0; +error: + free (filterStringCopy); + return -1; +} + +static inline char * strip_end(char *str) +{ + char *end = str + strlen(str) - 1; + + while (end >= str && isspace(*end)) + *end-- = '\0'; + return str; +} + +/** + * Splits a wire-format buffer into an AndroidLogEntry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int android_log_processLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry) +{ + size_t tag_len; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->priority = buf->msg[0]; + entry->pid = buf->pid; + entry->tid = buf->tid; + entry->tag = buf->msg + 1; + tag_len = strlen(entry->tag); + entry->messageLen = buf->len - tag_len - 3; + entry->message = entry->tag + tag_len + 1; + + return 0; +} + +/* + * Extract a 4-byte value from a byte stream. + */ +static inline uint32_t get4LE(const uint8_t* src) +{ + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +/* + * Extract an 8-byte value from a byte stream. + */ +static inline uint64_t get8LE(const uint8_t* src) +{ + uint32_t low, high; + + low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); + return ((long long) high << 32) | (long long) low; +} + + +/* + * Recursively convert binary log data to printable form. + * + * This needs to be recursive because you can have lists of lists. + * + * If we run out of room, we stop processing immediately. It's important + * for us to check for space on every output element to avoid producing + * garbled output. + * + * Returns 0 on success, 1 on buffer full, -1 on failure. + */ +static int android_log_printBinaryEvent(const unsigned char** pEventData, + size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) +{ + const unsigned char* eventData = *pEventData; + size_t eventDataLen = *pEventDataLen; + char* outBuf = *pOutBuf; + size_t outBufLen = *pOutBufLen; + unsigned char type; + size_t outCount; + int result = 0; + + if (eventDataLen < 1) + return -1; + type = *eventData++; + eventDataLen--; + + //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); + + switch (type) { + case EVENT_TYPE_INT: + /* 32-bit signed int */ + { + int ival; + + if (eventDataLen < 4) + return -1; + ival = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + outCount = snprintf(outBuf, outBufLen, "%d", ival); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_LONG: + /* 64-bit signed long */ + { + long long lval; + + if (eventDataLen < 8) + return -1; + lval = get8LE(eventData); + eventData += 8; + eventDataLen -= 8; + + outCount = snprintf(outBuf, outBufLen, "%lld", lval); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_STRING: + /* UTF-8 chars, not NULL-terminated */ + { + unsigned int strLen; + + if (eventDataLen < 4) + return -1; + strLen = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + if (eventDataLen < strLen) + return -1; + + if (strLen < outBufLen) { + memcpy(outBuf, eventData, strLen); + outBuf += strLen; + outBufLen -= strLen; + } else if (outBufLen > 0) { + /* copy what we can */ + memcpy(outBuf, eventData, outBufLen); + outBuf += outBufLen; + outBufLen -= outBufLen; + goto no_room; + } + eventData += strLen; + eventDataLen -= strLen; + break; + } + case EVENT_TYPE_LIST: + /* N items, all different types */ + { + unsigned char count; + int i; + + if (eventDataLen < 1) + return -1; + + count = *eventData++; + eventDataLen--; + + if (outBufLen > 0) { + *outBuf++ = '['; + outBufLen--; + } else { + goto no_room; + } + + for (i = 0; i < count; i++) { + result = android_log_printBinaryEvent(&eventData, &eventDataLen, + &outBuf, &outBufLen); + if (result != 0) + goto bail; + + if (i < count-1) { + if (outBufLen > 0) { + *outBuf++ = ','; + outBufLen--; + } else { + goto no_room; + } + } + } + + if (outBufLen > 0) { + *outBuf++ = ']'; + outBufLen--; + } else { + goto no_room; + } + } + break; + default: + fprintf(stderr, "Unknown binary event type %d\n", type); + return -1; + } + +bail: + *pEventData = eventData; + *pEventDataLen = eventDataLen; + *pOutBuf = outBuf; + *pOutBufLen = outBufLen; + return result; + +no_room: + result = 1; + goto bail; +} + +/** + * Convert a binary log entry to ASCII form. + * + * For convenience we mimic the processLogBuffer API. There is no + * pre-defined output length for the binary data, since we're free to format + * it however we choose, which means we can't really use a fixed-size buffer + * here. + */ +int android_log_processBinaryLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, + int messageBufLen) +{ + size_t inCount; + unsigned int tagIndex; + const unsigned char* eventData; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->priority = ANDROID_LOG_INFO; + entry->pid = buf->pid; + entry->tid = buf->tid; + + /* + * Pull the tag out. + */ + eventData = (const unsigned char*) buf->msg; + inCount = buf->len; + if (inCount < 4) + return -1; + tagIndex = get4LE(eventData); + eventData += 4; + inCount -= 4; + + if (map != NULL) { + entry->tag = android_lookupEventTag(map, tagIndex); + } else { + entry->tag = NULL; + } + + /* + * If we don't have a map, or didn't find the tag number in the map, + * stuff a generated tag value into the start of the output buffer and + * shift the buffer pointers down. + */ + if (entry->tag == NULL) { + int tagLen; + + tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); + entry->tag = messageBuf; + messageBuf += tagLen+1; + messageBufLen -= tagLen+1; + } + + /* + * Format the event log data into the buffer. + */ + char* outBuf = messageBuf; + size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ + int result; + result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, + &outRemaining); + if (result < 0) { + fprintf(stderr, "Binary log entry conversion failed\n"); + return -1; + } else if (result == 1) { + if (outBuf > messageBuf) { + /* leave an indicator */ + *(outBuf-1) = '!'; + } else { + /* no room to output anything at all */ + *outBuf++ = '!'; + outRemaining--; + } + /* pretend we ate all the data */ + inCount = 0; + } + + /* eat the silly terminating '\n' */ + if (inCount == 1 && *eventData == '\n') { + eventData++; + inCount--; + } + + if (inCount != 0) { + fprintf(stderr, + "Warning: leftover binary log data (%d bytes)\n", inCount); + } + + /* + * Terminate the buffer. The NUL byte does not count as part of + * entry->messageLen. + */ + *outBuf = '\0'; + entry->messageLen = outBuf - messageBuf; + assert(entry->messageLen == (messageBufLen-1) - outRemaining); + + entry->message = messageBuf; + + return 0; +} + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ + +char *android_log_formatLogLine ( + AndroidLogFormat *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const AndroidLogEntry *entry, + size_t *p_outLength) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char headerBuf[128]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + int prefixSuffixIsHeaderFooter = 0; + char * ret = NULL; + + priChar = filterPriToChar(entry->priority); + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&(entry->tv_sec), &tmBuf); +#else + ptm = localtime(&(entry->tv_sec)); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (p_format->format) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, entry->tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, entry->pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", entry->tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%p) ", priChar, entry->pid, (void*)entry->tid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; + prefixLen = 0; + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, + priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + (int)entry->pid, (int)entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s.%03ld %5d:%p %c/%-8s ]\n", + timeBuf, entry->tv_nsec / 1000000, entry->pid, + (void*)entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n\n"); + suffixLen = 2; + prefixSuffixIsHeaderFooter = 1; + break; + case FORMAT_BRIEF: + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + } + + /* the following code is tragically unreadable */ + + size_t numLines; + size_t i; + char *p; + size_t bufferSize; + const char *pm; + + if (prefixSuffixIsHeaderFooter) { + // we're just wrapping message with a header/footer + numLines = 1; + } else { + pm = entry->message; + numLines = 0; + + // The line-end finding here must match the line-end finding + // in for ( ... numLines...) loop below + while (pm < (entry->message + entry->messageLen)) { + if (*pm++ == '\n') numLines++; + } + // plus one line for anything not newline-terminated at the end + if (pm > entry->message && *(pm-1) != '\n') numLines++; + } + + // this is an upper bound--newlines in message may be counted + // extraneously + bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + + if (defaultBufferSize >= bufferSize) { + ret = defaultBuffer; + } else { + ret = (char *)malloc(bufferSize); + + if (ret == NULL) { + return ret; + } + } + + ret[0] = '\0'; /* to start strcat off */ + + p = ret; + pm = entry->message; + + if (prefixSuffixIsHeaderFooter) { + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, entry->message, entry->messageLen); + p += entry->messageLen; + strcat(p, suffixBuf); + p += suffixLen; + } else { + while(pm < (entry->message + entry->messageLen)) { + const char *lineStart; + size_t lineLen; + + lineStart = pm; + + // Find the next end-of-line in message + while (pm < (entry->message + entry->messageLen) + && *pm != '\n') pm++; + lineLen = pm - lineStart; + + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, lineStart, lineLen); + p += lineLen; + strcat(p, suffixBuf); + p += suffixLen; + + if (*pm == '\n') pm++; + } + } + + if (p_outLength != NULL) { + *p_outLength = p - ret; + } + + return ret; +} + +/** + * Either print or do not print log line, based on filter + * + * Returns count bytes written + */ + +int android_log_filterAndPrintLogLine( + AndroidLogFormat *p_format, + int fd, + const AndroidLogEntry *entry) +{ + int ret; + char defaultBuffer[512]; + char *outBuffer = NULL; + size_t totalLen; + + if (0 == android_log_shouldPrintLine(p_format, entry->tag, + entry->priority)) { + return 0; + } + + outBuffer = android_log_formatLogLine(p_format, defaultBuffer, + sizeof(defaultBuffer), entry, &totalLen); + + if (!outBuffer) + return -1; + + do { + ret = write(fd, outBuffer, totalLen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + ret = 0; + goto done; + } + + if (((size_t)ret) < totalLen) { + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, + (int)totalLen); + goto done; + } + +done: + if (outBuffer != defaultBuffer) { + free(outBuffer); + } + + return ret; +} + + + +void logprint_run_tests() +{ +#if 0 + + fprintf(stderr, "tests disabled\n"); + +#else + + int err; + const char *tag; + AndroidLogFormat *p_format; + + p_format = android_log_format_new(); + + fprintf(stderr, "running tests\n"); + + tag = "random"; + + android_log_addFilterRule(p_format,"*:i"); + + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + android_log_addFilterRule(p_format, "*"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:i"); + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "random"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:d"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:w"); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "crap:*"); + assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); + assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); + + // invalid expression + err = android_log_addFilterRule(p_format, "random:z"); + assert (err < 0); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + // Issue #550946 + err = android_log_addFilterString(p_format, " "); + assert(err == 0); + assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + + // note trailing space + err = android_log_addFilterString(p_format, "*:s random:d "); + assert(err == 0); + assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + + err = android_log_addFilterString(p_format, "*:s random:z"); + assert(err < 0); + + +#if 0 + char *ret; + char defaultBuffer[512]; + + ret = android_log_formatLogLine(p_format, + defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, + 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); +#endif + + + fprintf(stderr, "tests complete\n"); +#endif +} diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk new file mode 100644 index 0000000..b09a941 --- /dev/null +++ b/libmincrypt/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2008 The Android Open Source Project +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libmincrypt +LOCAL_SRC_FILES := rsa.c sha.c +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libmincrypt +LOCAL_SRC_FILES := rsa.c sha.c +include $(BUILD_HOST_STATIC_LIBRARY) + + +# TODO: drop the hyphen once these are checked in +include $(LOCAL_PATH)/tools/Android.mk diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c new file mode 100644 index 0000000..d7124fb --- /dev/null +++ b/libmincrypt/rsa.c @@ -0,0 +1,198 @@ +/* rsa.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, const uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. +** Input and output big-endian byte array in inout. +*/ +static void modpow3(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t *aaa = aR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +** other flavor which omits the optional parameter entirely). This code does not +** accept signatures without the optional parameter. +*/ +static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, + 0x04,0x14 +}; + +/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. +** Returns 0 on failure, 1 on success. +*/ +int RSA_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; /* Wrong key passed in. */ + } + + if (len != sizeof(buf)) { + return 0; /* Wrong input length. */ + } + + for (i = 0; i < len; ++i) { + buf[i] = signature[i]; + } + + modpow3(key, buf); + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < (int) sizeof(padding); ++i) { + if (buf[i] != padding[i]) { + return 0; + } + } + + /* Check sha digest matches. */ + for (; i < len; ++i) { + if (buf[i] != *sha++) { + return 0; + } + } + + return 1; +} diff --git a/libmincrypt/sha.c b/libmincrypt/sha.c new file mode 100644 index 0000000..d6120da --- /dev/null +++ b/libmincrypt/sha.c @@ -0,0 +1,142 @@ +/* sha.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/sha.h" + +#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static void SHA1_transform(SHA_CTX *ctx) { + uint32_t W[80]; + uint32_t A, B, C, D, E; + uint8_t *p = ctx->buf; + int t; + + for(t = 0; t < 16; ++t) { + uint32_t tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + + for(; t < 80; t++) { + W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + + for(t = 0; t < 80; t++) { + uint32_t tmp = rol(5,A) + E + W[t]; + + if (t < 20) + tmp += (D^(B&(C^D))) + 0x5A827999; + else if ( t < 40) + tmp += (B^C^D) + 0x6ED9EBA1; + else if ( t < 60) + tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; + else + tmp += (B^C^D) + 0xCA62C1D6; + + E = D; + D = C; + C = rol(30,B); + B = A; + A = tmp; + } + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA_init(SHA_CTX *ctx) { + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} + +void SHA_update(SHA_CTX *ctx, const void *data, int len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len--) { + ctx->buf[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_transform(ctx); + i = 0; + } + } +} +const uint8_t *SHA_final(SHA_CTX *ctx) { + uint8_t *p = ctx->buf; + uint64_t cnt = ctx->count * 8; + int i; + + SHA_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + uint32_t tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + + return ctx->buf; +} + +/* Convenience function */ +const uint8_t* SHA(const void *data, int len, uint8_t *digest) { + const uint8_t *p; + int i; + SHA_CTX ctx; + SHA_init(&ctx); + SHA_update(&ctx, data, len); + p = SHA_final(&ctx); + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + digest[i] = *p++; + } + return digest; +} diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk new file mode 100644 index 0000000..b61234a --- /dev/null +++ b/libmincrypt/tools/Android.mk @@ -0,0 +1,21 @@ +# 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 := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := dumpkey +LOCAL_SRC_FILES := DumpPublicKey.java +LOCAL_JAR_MANIFEST := DumpPublicKey.mf +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java new file mode 100644 index 0000000..c9e7e4d --- /dev/null +++ b/libmincrypt/tools/DumpPublicKey.java @@ -0,0 +1,130 @@ +/* + * 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. + */ + +package com.android.dumpkey; + +import java.io.FileInputStream; +import java.math.BigInteger; +import java.security.cert.CertificateFactory; +import java.security.cert.Certificate; +import java.security.KeyStore; +import java.security.Key; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import sun.misc.BASE64Encoder; + +/** + * Command line tool to extract RSA public keys from X.509 certificates + * and output source code with data initializers for the keys. + * @hide + */ +class DumpPublicKey { + /** + * @param key to perform sanity checks on + * @throws Exception if the key has the wrong size or public exponent + */ + static void check(RSAPublicKey key) throws Exception { + BigInteger pubexp = key.getPublicExponent(); + BigInteger modulus = key.getModulus(); + + if (!pubexp.equals(BigInteger.valueOf(3))) + throw new Exception("Public exponent should be 3 but is " + + pubexp.toString(10) + "."); + + if (modulus.bitLength() != 2048) + throw new Exception("Modulus should be 2048 bits long but is " + + modulus.bitLength() + " bits."); + } + + /** + * @param key to output + * @return a C initializer representing this public key. + */ + static String print(RSAPublicKey key) throws Exception { + check(key); + + BigInteger N = key.getModulus(); + + StringBuilder result = new StringBuilder(); + + int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus + + result.append("{"); + result.append(nwords); + + BigInteger B = BigInteger.valueOf(0x100000000L); // 2^32 + BigInteger N0inv = B.subtract(N.modInverse(B)); // -1 / N[0] mod 2^32 + + result.append(",0x"); + result.append(N0inv.toString(16)); + + BigInteger R = BigInteger.valueOf(2).pow(N.bitLength()); + BigInteger RR = R.multiply(R).mod(N); // 2^4096 mod N + + // Write out modulus as little endian array of integers. + result.append(",{"); + for (int i = 0; i < nwords; ++i) { + int n = N.mod(B).intValue(); + result.append(n); + + if (i != nwords - 1) { + result.append(","); + } + + N = N.divide(B); + } + result.append("}"); + + // Write R^2 as little endian array of integers. + result.append(",{"); + for (int i = 0; i < nwords; ++i) { + int rr = RR.mod(B).intValue(); + result.append(rr); + + if (i != nwords - 1) { + result.append(","); + } + + RR = RR.divide(B); + } + result.append("}"); + + result.append("}"); + return result.toString(); + } + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("Usage: DumpPublicKey certfile ... > source.c"); + System.exit(1); + } + try { + for (int i = 0; i < args.length; i++) { + FileInputStream input = new FileInputStream(args[i]); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Certificate cert = cf.generateCertificate(input); + RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey()); + check(key); + System.out.print(print(key)); + System.out.println(i < args.length - 1 ? "," : ""); + } + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + System.exit(0); + } +} diff --git a/libmincrypt/tools/DumpPublicKey.mf b/libmincrypt/tools/DumpPublicKey.mf new file mode 100644 index 0000000..7bb3bc8 --- /dev/null +++ b/libmincrypt/tools/DumpPublicKey.mf @@ -0,0 +1 @@ +Main-Class: com.android.dumpkey.DumpPublicKey diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk new file mode 100644 index 0000000..46102d5 --- /dev/null +++ b/libnetutils/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + dhcpclient.c \ + dhcpmsg.c \ + dhcp_utils.c \ + ifc_utils.c \ + packet.c + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt -lpthread + endif +endif + +LOCAL_MODULE:= libnetutils + +include $(BUILD_SHARED_LIBRARY) diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c new file mode 100644 index 0000000..bad2e2f --- /dev/null +++ b/libnetutils/dhcp_utils.c @@ -0,0 +1,207 @@ +/* + * Copyright 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. + */ + +/* Utilities for managing the dhcpcd DHCP client daemon */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <cutils/properties.h> + +static const char DAEMON_NAME[] = "dhcpcd"; +static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; +static const char DHCP_PROP_NAME_PREFIX[] = "dhcp"; +static const int NAP_TIME = 1; /* wait for 1 second at a time */ + /* when polling for property values */ +static char errmsg[100]; + +/* + * Wait for a system property to be assigned a specified value. + * If desired_value is NULL, then just wait for the property to + * be created with any value. maxwait is the maximum amount of + * time in seconds to wait before giving up. + */ +static int wait_for_property(const char *name, const char *desired_value, int maxwait) +{ + char value[PROPERTY_VALUE_MAX] = {'\0'}; + int maxnaps = maxwait / NAP_TIME; + + if (maxnaps < 1) { + maxnaps = 1; + } + + while (maxnaps-- > 0) { + usleep(1000000); + if (property_get(name, value, NULL)) { + if (desired_value == NULL || + strcmp(value, desired_value) == 0) { + return 0; + } + } + } + return -1; /* failure */ +} + +static void fill_ip_info(const char *interface, + in_addr_t *ipaddr, + in_addr_t *gateway, + in_addr_t *mask, + in_addr_t *dns1, + in_addr_t *dns2, + in_addr_t *server, + uint32_t *lease) +{ + char prop_name[PROPERTY_KEY_MAX]; + char prop_value[PROPERTY_VALUE_MAX]; + struct in_addr addr; + in_addr_t iaddr; + + snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { + *ipaddr = addr.s_addr; + } else { + *ipaddr = 0; + } + snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { + *gateway = addr.s_addr; + } else { + *gateway = 0; + } + snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { + *mask = addr.s_addr; + } else { + *mask = 0; + } + snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { + *dns1 = addr.s_addr; + } else { + *dns1 = 0; + } + snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { + *dns2 = addr.s_addr; + } else { + *dns2 = 0; + } + snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { + *server = addr.s_addr; + } else { + *server = 0; + } + snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, interface); + if (property_get(prop_name, prop_value, NULL)) { + *lease = atol(prop_value); + } +} + +/* + * Start the dhcp client daemon, and wait for it to finish + * configuring the interface. + */ +int dhcp_do_request(const char *interface, + in_addr_t *ipaddr, + in_addr_t *gateway, + in_addr_t *mask, + in_addr_t *dns1, + in_addr_t *dns2, + in_addr_t *server, + uint32_t *lease) +{ + char result_prop_name[PROPERTY_KEY_MAX]; + char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; + const char *ctrl_prop = "ctl.start"; + const char *desired_status = "running"; + + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", + DHCP_PROP_NAME_PREFIX, + interface); + /* Erase any previous setting of the dhcp result property */ + property_set(result_prop_name, ""); + + /* Start the daemon and wait until it's ready */ + property_set(ctrl_prop, DAEMON_NAME); + if (wait_for_property(DAEMON_PROP_NAME, desired_status, 10) < 0) { + snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start"); + return -1; + } + + /* Wait for the daemon to return a result */ + if (wait_for_property(result_prop_name, NULL, 30) < 0) { + snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish"); + return -1; + } + + if (!property_get(result_prop_name, prop_value, NULL)) { + /* shouldn't ever happen, given the success of wait_for_property() */ + snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set"); + return -1; + } + if (strcmp(prop_value, "ok") == 0) { + fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease); + return 0; + } else { + snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value); + return -1; + } +} + +/** + * Stop the DHCP client daemon. + */ +int dhcp_stop(const char *interface) +{ + char result_prop_name[PROPERTY_KEY_MAX]; + const char *ctrl_prop = "ctl.stop"; + const char *desired_status = "stopped"; + + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", + DHCP_PROP_NAME_PREFIX, + interface); + /* Stop the daemon and wait until it's reported to be stopped */ + property_set(ctrl_prop, DAEMON_NAME); + if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) { + return -1; + } + property_set(result_prop_name, "failed"); + return 0; +} + +/** + * Release the current DHCP client lease. + */ +int dhcp_release_lease(const char *interface) +{ + const char *ctrl_prop = "ctl.stop"; + const char *desired_status = "stopped"; + + /* Stop the daemon and wait until it's reported to be stopped */ + property_set(ctrl_prop, DAEMON_NAME); + if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) { + return -1; + } + return 0; +} + +char *dhcp_get_errmsg() { + return errmsg; +} diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c new file mode 100644 index 0000000..45e392a --- /dev/null +++ b/libnetutils/dhcpclient.c @@ -0,0 +1,562 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include <time.h> +#include <sys/time.h> +#include <poll.h> + +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include <cutils/properties.h> +#define LOG_TAG "DHCP" +#include <cutils/log.h> + +#include <dirent.h> + +#include "dhcpmsg.h" +#include "ifc_utils.h" +#include "packet.h" + +#define VERBOSE 2 + +static int verbose = 1; +static char errmsg[2048]; + +typedef unsigned long long msecs_t; +#if VERBOSE +void dump_dhcp_msg(); +#endif + +msecs_t get_msecs(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { + return 0; + } else { + return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) + + (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000)); + } +} + +void printerr(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(errmsg, sizeof(errmsg), fmt, ap); + va_end(ap); + + LOGD(errmsg); +} + +const char *dhcp_lasterror() +{ + return errmsg; +} + +int fatal(const char *reason) +{ + printerr("%s: %s\n", reason, strerror(errno)); + return -1; +// exit(1); +} + +const char *ipaddr(uint32_t addr) +{ + static char buf[32]; + + sprintf(buf,"%d.%d.%d.%d", + addr & 255, + ((addr >> 8) & 255), + ((addr >> 16) & 255), + (addr >> 24)); + return buf; +} + +typedef struct dhcp_info dhcp_info; + +struct dhcp_info { + uint32_t type; + + uint32_t ipaddr; + uint32_t gateway; + uint32_t netmask; + + uint32_t dns1; + uint32_t dns2; + + uint32_t serveraddr; + uint32_t lease; +}; + +dhcp_info last_good_info; + +void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *mask, + uint32_t *dns1, uint32_t *dns2, uint32_t *server, + uint32_t *lease) +{ + *ipaddr = last_good_info.ipaddr; + *gateway = last_good_info.gateway; + *mask = last_good_info.netmask; + *dns1 = last_good_info.dns1; + *dns2 = last_good_info.dns2; + *server = last_good_info.serveraddr; + *lease = last_good_info.lease; +} + +static int ifc_configure(const char *ifname, dhcp_info *info) +{ + char dns_prop_name[PROPERTY_KEY_MAX]; + + if (ifc_set_addr(ifname, info->ipaddr)) { + printerr("failed to set ipaddr %s: %s\n", ipaddr(info->ipaddr), strerror(errno)); + return -1; + } + if (ifc_set_mask(ifname, info->netmask)) { + printerr("failed to set netmask %s: %s\n", ipaddr(info->netmask), strerror(errno)); + return -1; + } + if (ifc_create_default_route(ifname, info->gateway)) { + printerr("failed to set default route %s: %s\n", ipaddr(info->gateway), strerror(errno)); + return -1; + } + + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname); + property_set(dns_prop_name, info->dns1 ? ipaddr(info->dns1) : ""); + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname); + property_set(dns_prop_name, info->dns2 ? ipaddr(info->dns2) : ""); + + last_good_info = *info; + + return 0; +} + +static const char *dhcp_type_to_name(uint32_t type) +{ + switch(type) { + case DHCPDISCOVER: return "discover"; + case DHCPOFFER: return "offer"; + case DHCPREQUEST: return "request"; + case DHCPDECLINE: return "decline"; + case DHCPACK: return "ack"; + case DHCPNAK: return "nak"; + case DHCPRELEASE: return "release"; + case DHCPINFORM: return "inform"; + default: return "???"; + } +} + +void dump_dhcp_info(dhcp_info *info) +{ + char addr[20], gway[20], mask[20]; + LOGD("--- dhcp %s (%d) ---", + dhcp_type_to_name(info->type), info->type); + strcpy(addr, ipaddr(info->ipaddr)); + strcpy(gway, ipaddr(info->gateway)); + strcpy(mask, ipaddr(info->netmask)); + LOGD("ip %s gw %s mask %s", addr, gway, mask); + if (info->dns1) LOGD("dns1: %s", ipaddr(info->dns1)); + if (info->dns2) LOGD("dns2: %s", ipaddr(info->dns2)); + LOGD("server %s, lease %d seconds", + ipaddr(info->serveraddr), info->lease); +} + + +int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) +{ + uint8_t *x; + unsigned int opt; + int optlen; + + memset(info, 0, sizeof(dhcp_info)); + if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1; + + len -= (DHCP_MSG_FIXED_SIZE + 4); + + if (msg->options[0] != OPT_COOKIE1) return -1; + if (msg->options[1] != OPT_COOKIE2) return -1; + if (msg->options[2] != OPT_COOKIE3) return -1; + if (msg->options[3] != OPT_COOKIE4) return -1; + + x = msg->options + 4; + + while (len > 2) { + opt = *x++; + if (opt == OPT_PAD) { + len--; + continue; + } + if (opt == OPT_END) { + break; + } + optlen = *x++; + len -= 2; + if (optlen > len) { + break; + } + switch(opt) { + case OPT_SUBNET_MASK: + if (optlen >= 4) memcpy(&info->netmask, x, 4); + break; + case OPT_GATEWAY: + if (optlen >= 4) memcpy(&info->gateway, x, 4); + break; + case OPT_DNS: + if (optlen >= 4) memcpy(&info->dns1, x + 0, 4); + if (optlen >= 8) memcpy(&info->dns2, x + 4, 4); + break; + case OPT_LEASE_TIME: + if (optlen >= 4) { + memcpy(&info->lease, x, 4); + info->lease = ntohl(info->lease); + } + break; + case OPT_SERVER_ID: + if (optlen >= 4) memcpy(&info->serveraddr, x, 4); + break; + case OPT_MESSAGE_TYPE: + info->type = *x; + break; + default: + break; + } + x += optlen; + len -= optlen; + } + + info->ipaddr = msg->yiaddr; + + return 0; +} + +#if VERBOSE + +static void hex2str(char *buf, const unsigned char *array, int len) +{ + int i; + char *cp = buf; + + for (i = 0; i < len; i++) { + cp += sprintf(cp, " %02x ", array[i]); + } +} + +void dump_dhcp_msg(dhcp_msg *msg, int len) +{ + unsigned char *x; + unsigned int n,c; + int optsz; + const char *name; + char buf[2048]; + + LOGD("===== DHCP message:"); + if (len < DHCP_MSG_FIXED_SIZE) { + LOGD("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE); + return; + } + + len -= DHCP_MSG_FIXED_SIZE; + + if (msg->op == OP_BOOTREQUEST) + name = "BOOTREQUEST"; + else if (msg->op == OP_BOOTREPLY) + name = "BOOTREPLY"; + else + name = "????"; + LOGD("op = %s (%d), htype = %d, hlen = %d, hops = %d", + name, msg->op, msg->htype, msg->hlen, msg->hops); + LOGD("xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d", + ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len); + LOGD("ciaddr = %s", ipaddr(msg->ciaddr)); + LOGD("yiaddr = %s", ipaddr(msg->yiaddr)); + LOGD("siaddr = %s", ipaddr(msg->siaddr)); + LOGD("giaddr = %s", ipaddr(msg->giaddr)); + + c = msg->hlen > 16 ? 16 : msg->hlen; + hex2str(buf, msg->chaddr, c); + LOGD("chaddr = {%s}", buf); + + for (n = 0; n < 64; n++) { + if ((msg->sname[n] < ' ') || (msg->sname[n] > 127)) { + if (msg->sname[n] == 0) break; + msg->sname[n] = '.'; + } + } + msg->sname[63] = 0; + + for (n = 0; n < 128; n++) { + if ((msg->file[n] < ' ') || (msg->file[n] > 127)) { + if (msg->file[n] == 0) break; + msg->file[n] = '.'; + } + } + msg->file[127] = 0; + + LOGD("sname = '%s'", msg->sname); + LOGD("file = '%s'", msg->file); + + if (len < 4) return; + len -= 4; + x = msg->options + 4; + + while (len > 2) { + if (*x == 0) { + x++; + len--; + continue; + } + if (*x == OPT_END) { + break; + } + len -= 2; + optsz = x[1]; + if (optsz > len) break; + if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) { + if ((unsigned int)optsz < sizeof(buf) - 1) { + n = optsz; + } else { + n = sizeof(buf) - 1; + } + memcpy(buf, &x[2], n); + buf[n] = '\0'; + } else { + hex2str(buf, &x[2], optsz); + } + if (x[0] == OPT_MESSAGE_TYPE) + name = dhcp_type_to_name(x[2]); + else + name = NULL; + LOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name); + len -= optsz; + x = x + optsz + 2; + } +} + +#endif + +static int send_message(int sock, int if_index, dhcp_msg *msg, int size) +{ +#if VERBOSE > 1 + dump_dhcp_msg(msg, size); +#endif + return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST, + PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER); +} + +static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz) +{ + if (sz < DHCP_MSG_FIXED_SIZE) { + if (verbose) LOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE); + return 0; + } + if (reply->op != OP_BOOTREPLY) { + if (verbose) LOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY); + return 0; + } + if (reply->xid != msg->xid) { + if (verbose) LOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid), + ntohl(msg->xid)); + return 0; + } + if (reply->htype != msg->htype) { + if (verbose) LOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype); + return 0; + } + if (reply->hlen != msg->hlen) { + if (verbose) LOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen); + return 0; + } + if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) { + if (verbose) LOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr)); + return 0; + } + return 1; +} + +#define STATE_SELECTING 1 +#define STATE_REQUESTING 2 + +#define TIMEOUT_INITIAL 4000 +#define TIMEOUT_MAX 32000 + +int dhcp_init_ifc(const char *ifname) +{ + dhcp_msg discover_msg; + dhcp_msg request_msg; + dhcp_msg reply; + dhcp_msg *msg; + dhcp_info info; + int s, r, size; + int valid_reply; + uint32_t xid; + unsigned char hwaddr[6]; + struct pollfd pfd; + unsigned int state; + unsigned int timeout; + int if_index; + + xid = (uint32_t) get_msecs(); + + if (ifc_get_hwaddr(ifname, hwaddr)) { + return fatal("cannot obtain interface address"); + } + if (ifc_get_ifindex(ifname, &if_index)) { + return fatal("cannot obtain interface index"); + } + + s = open_raw_socket(ifname, hwaddr, if_index); + + timeout = TIMEOUT_INITIAL; + state = STATE_SELECTING; + info.type = 0; + goto transmit; + + for (;;) { + pfd.fd = s; + pfd.events = POLLIN; + pfd.revents = 0; + r = poll(&pfd, 1, timeout); + + if (r == 0) { +#if VERBOSE + printerr("TIMEOUT\n"); +#endif + if (timeout >= TIMEOUT_MAX) { + printerr("timed out\n"); + if ( info.type == DHCPOFFER ) { + printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname); + return ifc_configure(ifname, &info); + } + errno = ETIME; + close(s); + return -1; + } + timeout = timeout * 2; + + transmit: + size = 0; + msg = NULL; + switch(state) { + case STATE_SELECTING: + msg = &discover_msg; + size = init_dhcp_discover_msg(msg, hwaddr, xid); + break; + case STATE_REQUESTING: + msg = &request_msg; + size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr); + break; + default: + r = 0; + } + if (size != 0) { + r = send_message(s, if_index, msg, size); + if (r < 0) { + printerr("error sending dhcp msg: %s\n", strerror(errno)); + } + } + continue; + } + + if (r < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) { + continue; + } + return fatal("poll failed"); + } + + errno = 0; + r = receive_packet(s, &reply); + if (r < 0) { + if (errno != 0) { + LOGD("receive_packet failed (%d): %s", r, strerror(errno)); + if (errno == ENETDOWN || errno == ENXIO) { + return -1; + } + } + continue; + } + +#if VERBOSE > 1 + dump_dhcp_msg(&reply, r); +#endif + decode_dhcp_msg(&reply, r, &info); + + if (state == STATE_SELECTING) { + valid_reply = is_valid_reply(&discover_msg, &reply, r); + } else { + valid_reply = is_valid_reply(&request_msg, &reply, r); + } + if (!valid_reply) { + printerr("invalid reply\n"); + continue; + } + + if (verbose) dump_dhcp_info(&info); + + switch(state) { + case STATE_SELECTING: + if (info.type == DHCPOFFER) { + state = STATE_REQUESTING; + timeout = TIMEOUT_INITIAL; + xid++; + goto transmit; + } + break; + case STATE_REQUESTING: + if (info.type == DHCPACK) { + printerr("configuring %s\n", ifname); + close(s); + return ifc_configure(ifname, &info); + } else if (info.type == DHCPNAK) { + printerr("configuration request denied\n"); + close(s); + return -1; + } else { + printerr("ignoring %s message in state %d\n", + dhcp_type_to_name(info.type), state); + } + break; + } + } + close(s); + return 0; +} + +int do_dhcp(char *iname) +{ + if (ifc_set_addr(iname, 0)) { + printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno)); + return -1; + } + + if (ifc_up(iname)) { + printerr("failed to bring up interface %s: %s\n", iname, strerror(errno)); + return -1; + } + + return dhcp_init_ifc(iname); +} diff --git a/libnetutils/dhcpmsg.c b/libnetutils/dhcpmsg.c new file mode 100644 index 0000000..1e0a233 --- /dev/null +++ b/libnetutils/dhcpmsg.c @@ -0,0 +1,100 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <netinet/in.h> + +#include "dhcpmsg.h" + +static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid) +{ + uint8_t *x; + + memset(msg, 0, sizeof(dhcp_msg)); + + msg->op = OP_BOOTREQUEST; + msg->htype = HTYPE_ETHER; + msg->hlen = 6; + msg->hops = 0; + + msg->flags = htons(FLAGS_BROADCAST); + + msg->xid = xid; + + memcpy(msg->chaddr, hwaddr, 6); + + x = msg->options; + + *x++ = OPT_COOKIE1; + *x++ = OPT_COOKIE2; + *x++ = OPT_COOKIE3; + *x++ = OPT_COOKIE4; + + *x++ = OPT_MESSAGE_TYPE; + *x++ = 1; + *x++ = type; + + return x; +} + +int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid) +{ + uint8_t *x; + + x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid); + + *x++ = OPT_PARAMETER_LIST; + *x++ = 4; + *x++ = OPT_SUBNET_MASK; + *x++ = OPT_GATEWAY; + *x++ = OPT_DNS; + *x++ = OPT_BROADCAST_ADDR; + + *x++ = OPT_END; + + return DHCP_MSG_FIXED_SIZE + (x - msg->options); +} + +int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid, + uint32_t ipaddr, uint32_t serveraddr) +{ + uint8_t *x; + + x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid); + + *x++ = OPT_PARAMETER_LIST; + *x++ = 4; + *x++ = OPT_SUBNET_MASK; + *x++ = OPT_GATEWAY; + *x++ = OPT_DNS; + *x++ = OPT_BROADCAST_ADDR; + + *x++ = OPT_REQUESTED_IP; + *x++ = 4; + memcpy(x, &ipaddr, 4); + x += 4; + + *x++ = OPT_SERVER_ID; + *x++ = 4; + memcpy(x, &serveraddr, 4); + x += 4; + + *x++ = OPT_END; + + return DHCP_MSG_FIXED_SIZE + (x - msg->options); +} diff --git a/libnetutils/dhcpmsg.h b/libnetutils/dhcpmsg.h new file mode 100644 index 0000000..c86e400 --- /dev/null +++ b/libnetutils/dhcpmsg.h @@ -0,0 +1,106 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIFI_DHCP_H_ +#define _WIFI_DHCP_H_ + +#include <sys/types.h> + +#define PORT_BOOTP_SERVER 67 +#define PORT_BOOTP_CLIENT 68 + +/* RFC 2131 p 9 */ +typedef struct dhcp_msg dhcp_msg; + +#define OP_BOOTREQUEST 1 +#define OP_BOOTREPLY 2 + +#define FLAGS_BROADCAST 0x8000 + +#define HTYPE_ETHER 1 + +struct dhcp_msg +{ + uint8_t op; /* BOOTREQUEST / BOOTREPLY */ + uint8_t htype; /* hw addr type */ + uint8_t hlen; /* hw addr len */ + uint8_t hops; /* client set to 0 */ + + uint32_t xid; /* transaction id */ + + uint16_t secs; /* seconds since start of acq */ + uint16_t flags; + + uint32_t ciaddr; /* client IP addr */ + uint32_t yiaddr; /* your (client) IP addr */ + uint32_t siaddr; /* ip addr of next server */ + /* (DHCPOFFER and DHCPACK) */ + uint32_t giaddr; /* relay agent IP addr */ + + uint8_t chaddr[16]; /* client hw addr */ + char sname[64]; /* asciiz server hostname */ + char file[128]; /* asciiz boot file name */ + + uint8_t options[1024]; /* optional parameters */ +}; + +#define DHCP_MSG_FIXED_SIZE 236 + +/* first four bytes of options are a cookie to indicate that +** the payload are DHCP options as opposed to some other BOOTP +** extension. +*/ +#define OPT_COOKIE1 0x63 +#define OPT_COOKIE2 0x82 +#define OPT_COOKIE3 0x53 +#define OPT_COOKIE4 0x63 + +/* BOOTP/DHCP options - see RFC 2132 */ +#define OPT_PAD 0 + +#define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */ +#define OPT_TIME_OFFSET 2 /* 4 <seconds> */ +#define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */ +#define OPT_DNS 6 /* 4*n <ipaddr> * n */ +#define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */ +#define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */ + +#define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */ +#define OPT_LEASE_TIME 51 /* 4 <seconds> */ +#define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */ +#define OPT_SERVER_ID 54 /* 4 <ipaddr> */ +#define OPT_PARAMETER_LIST 55 /* n <optcode> * n */ +#define OPT_MESSAGE 56 /* n <errorstring> */ +#define OPT_CLASS_ID 60 /* n <opaque> */ +#define OPT_CLIENT_ID 61 /* n <opaque> */ +#define OPT_END 255 + +/* DHCP message types */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid); + +int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid, + uint32_t ipaddr, uint32_t serveraddr); + +#endif diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c new file mode 100644 index 0000000..88635d9 --- /dev/null +++ b/libnetutils/ifc_utils.c @@ -0,0 +1,428 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <linux/if.h> +#include <linux/sockios.h> +#include <linux/route.h> +#include <linux/wireless.h> + +#ifdef ANDROID +#define LOG_TAG "NetUtils" +#include <cutils/log.h> +#include <cutils/properties.h> +#else +#include <stdio.h> +#include <string.h> +#define LOGD printf +#define LOGW printf +#endif + +static int ifc_ctl_sock = -1; +void printerr(char *fmt, ...); + +static const char *ipaddr_to_string(uint32_t addr) +{ + struct in_addr in_addr; + + in_addr.s_addr = addr; + return inet_ntoa(in_addr); +} + +int ifc_init(void) +{ + if (ifc_ctl_sock == -1) { + ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (ifc_ctl_sock < 0) { + printerr("socket() failed: %s\n", strerror(errno)); + } + } + return ifc_ctl_sock < 0 ? -1 : 0; +} + +void ifc_close(void) +{ + if (ifc_ctl_sock != -1) { + (void)close(ifc_ctl_sock); + ifc_ctl_sock = -1; + } +} + +static void ifc_init_ifr(const char *name, struct ifreq *ifr) +{ + memset(ifr, 0, sizeof(struct ifreq)); + strncpy(ifr->ifr_name, name, IFNAMSIZ); + ifr->ifr_name[IFNAMSIZ - 1] = 0; +} + +int ifc_get_hwaddr(const char *name, void *ptr) +{ + int r; + struct ifreq ifr; + ifc_init_ifr(name, &ifr); + + r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr); + if(r < 0) return -1; + + memcpy(ptr, &ifr.ifr_hwaddr.sa_data, 6); + return 0; +} + +int ifc_get_ifindex(const char *name, int *if_indexp) +{ + int r; + struct ifreq ifr; + ifc_init_ifr(name, &ifr); + + r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr); + if(r < 0) return -1; + + *if_indexp = ifr.ifr_ifindex; + return 0; +} + +static int ifc_set_flags(const char *name, unsigned set, unsigned clr) +{ + struct ifreq ifr; + ifc_init_ifr(name, &ifr); + + if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1; + ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set; + return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr); +} + +int ifc_up(const char *name) +{ + return ifc_set_flags(name, IFF_UP, 0); +} + +int ifc_down(const char *name) +{ + return ifc_set_flags(name, 0, IFF_UP); +} + +static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr) +{ + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = addr; +} + +int ifc_set_addr(const char *name, in_addr_t addr) +{ + struct ifreq ifr; + + ifc_init_ifr(name, &ifr); + init_sockaddr_in(&ifr.ifr_addr, addr); + + return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); +} + +int ifc_set_mask(const char *name, in_addr_t mask) +{ + struct ifreq ifr; + + ifc_init_ifr(name, &ifr); + init_sockaddr_in(&ifr.ifr_addr, mask); + + return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr); +} + +int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags) +{ + struct ifreq ifr; + ifc_init_ifr(name, &ifr); + + if (addr != NULL) { + if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) { + *addr = 0; + } else { + *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + } + } + + if (mask != NULL) { + if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) { + *mask = 0; + } else { + *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + } + } + + if (flags != NULL) { + if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) { + *flags = 0; + } else { + *flags = ifr.ifr_flags; + } + } + + return 0; +} + + +int ifc_create_default_route(const char *name, in_addr_t addr) +{ + struct rtentry rt; + + memset(&rt, 0, sizeof(rt)); + + rt.rt_dst.sa_family = AF_INET; + rt.rt_flags = RTF_UP | RTF_GATEWAY; + rt.rt_dev = (void*) name; + init_sockaddr_in(&rt.rt_genmask, 0); + init_sockaddr_in(&rt.rt_gateway, addr); + + return ioctl(ifc_ctl_sock, SIOCADDRT, &rt); +} + +int ifc_add_host_route(const char *name, in_addr_t addr) +{ + struct rtentry rt; + int result; + + memset(&rt, 0, sizeof(rt)); + + rt.rt_dst.sa_family = AF_INET; + rt.rt_flags = RTF_UP | RTF_HOST; + rt.rt_dev = (void*) name; + init_sockaddr_in(&rt.rt_dst, addr); + init_sockaddr_in(&rt.rt_genmask, 0); + init_sockaddr_in(&rt.rt_gateway, 0); + + ifc_init(); + result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt); + if (result < 0 && errno == EEXIST) { + result = 0; + } + ifc_close(); + return result; +} + +int ifc_disable(const char *ifname) +{ + int result; + + ifc_init(); + result = ifc_down(ifname); + ifc_set_addr(ifname, 0); + ifc_close(); + return result; +} + +int ifc_reset_connections(const char *ifname) +{ +#ifdef HAVE_ANDROID_OS + int result; + in_addr_t myaddr; + struct ifreq ifr; + + ifc_init(); + ifc_get_info(ifname, &myaddr, NULL, NULL); + ifc_init_ifr(ifname, &ifr); + init_sockaddr_in(&ifr.ifr_addr, myaddr); + result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr); + ifc_close(); + + return result; +#else + return 0; +#endif +} + +/* + * Remove the routes associated with the named interface. + */ +int ifc_remove_host_routes(const char *name) +{ + char ifname[64]; + in_addr_t dest, gway, mask; + int flags, refcnt, use, metric, mtu, win, irtt; + struct rtentry rt; + FILE *fp; + struct in_addr addr; + + fp = fopen("/proc/net/route", "r"); + if (fp == NULL) + return -1; + /* Skip the header line */ + if (fscanf(fp, "%*[^\n]\n") < 0) { + fclose(fp); + return -1; + } + ifc_init(); + for (;;) { + int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n", + ifname, &dest, &gway, &flags, &refcnt, &use, &metric, &mask, + &mtu, &win, &irtt); + if (nread != 11) { + break; + } + if ((flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST) + || strcmp(ifname, name) != 0) { + continue; + } + memset(&rt, 0, sizeof(rt)); + rt.rt_dev = (void *)name; + init_sockaddr_in(&rt.rt_dst, dest); + init_sockaddr_in(&rt.rt_gateway, gway); + init_sockaddr_in(&rt.rt_genmask, mask); + addr.s_addr = dest; + if (ioctl(ifc_ctl_sock, SIOCDELRT, &rt) < 0) { + LOGD("failed to remove route for %s to %s: %s", + ifname, inet_ntoa(addr), strerror(errno)); + } + } + fclose(fp); + ifc_close(); + return 0; +} + +/* + * Return the address of the default gateway + * + * TODO: factor out common code from this and remove_host_routes() + * so that we only scan /proc/net/route in one place. + */ +int ifc_get_default_route(const char *ifname) +{ + char name[64]; + in_addr_t dest, gway, mask; + int flags, refcnt, use, metric, mtu, win, irtt; + int result; + FILE *fp; + + fp = fopen("/proc/net/route", "r"); + if (fp == NULL) + return 0; + /* Skip the header line */ + if (fscanf(fp, "%*[^\n]\n") < 0) { + fclose(fp); + return 0; + } + ifc_init(); + result = 0; + for (;;) { + int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n", + name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask, + &mtu, &win, &irtt); + if (nread != 11) { + break; + } + if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY) + && dest == 0 + && strcmp(ifname, name) == 0) { + result = gway; + break; + } + } + fclose(fp); + ifc_close(); + return result; +} + +/* + * Sets the specified gateway as the default route for the named interface. + */ +int ifc_set_default_route(const char *ifname, in_addr_t gateway) +{ + struct in_addr addr; + int result; + + ifc_init(); + addr.s_addr = gateway; + if ((result = ifc_create_default_route(ifname, gateway)) < 0) { + LOGD("failed to add %s as default route for %s: %s", + inet_ntoa(addr), ifname, strerror(errno)); + } + ifc_close(); + return result; +} + +/* + * Removes the default route for the named interface. + */ +int ifc_remove_default_route(const char *ifname) +{ + struct rtentry rt; + int result; + + ifc_init(); + memset(&rt, 0, sizeof(rt)); + rt.rt_dev = (void *)ifname; + rt.rt_flags = RTF_UP|RTF_GATEWAY; + init_sockaddr_in(&rt.rt_dst, 0); + if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) { + LOGD("failed to remove default route for %s: %s", ifname, strerror(errno)); + } + ifc_close(); + return result; +} + +int +ifc_configure(const char *ifname, + in_addr_t address, + in_addr_t netmask, + in_addr_t gateway, + in_addr_t dns1, + in_addr_t dns2) { + + char dns_prop_name[PROPERTY_KEY_MAX]; + + ifc_init(); + + if (ifc_up(ifname)) { + printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno)); + ifc_close(); + return -1; + } + if (ifc_set_addr(ifname, address)) { + printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno)); + ifc_close(); + return -1; + } + if (ifc_set_mask(ifname, netmask)) { + printerr("failed to set netmask %s: %s\n", ipaddr_to_string(netmask), strerror(errno)); + ifc_close(); + return -1; + } + if (ifc_create_default_route(ifname, gateway)) { + printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno)); + ifc_close(); + return -1; + } + + ifc_close(); + + snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns1", ifname); + property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : ""); + snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns2", ifname); + property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : ""); + + return 0; +} diff --git a/libnetutils/ifc_utils.h b/libnetutils/ifc_utils.h new file mode 100644 index 0000000..49b8747 --- /dev/null +++ b/libnetutils/ifc_utils.h @@ -0,0 +1,35 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _IFC_UTILS_H_ +#define _IFC_UTILS_H_ + +int ifc_init(void); + +int ifc_get_ifindex(const char *name, int *if_indexp); +int ifc_get_hwaddr(const char *name, void *ptr); + +int ifc_up(const char *name); +int ifc_down(const char *name); + +int ifc_set_addr(const char *name, unsigned addr); +int ifc_set_mask(const char *name, unsigned mask); + +int ifc_create_default_route(const char *name, unsigned addr); + +int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags); + +#endif diff --git a/libnetutils/packet.c b/libnetutils/packet.c new file mode 100644 index 0000000..9388345 --- /dev/null +++ b/libnetutils/packet.c @@ -0,0 +1,239 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#include <errno.h> + +#ifdef ANDROID +#define LOG_TAG "DHCP" +#include <cutils/log.h> +#else +#include <stdio.h> +#include <string.h> +#define LOGD printf +#define LOGW printf +#endif + +#include "dhcpmsg.h" + +int fatal(); + +int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index) +{ + int s, flag; + struct sockaddr_ll bindaddr; + + if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + return fatal("socket(PF_PACKET)"); + } + + memset(&bindaddr, 0, sizeof(bindaddr)); + bindaddr.sll_family = AF_PACKET; + bindaddr.sll_protocol = htons(ETH_P_IP); + bindaddr.sll_halen = ETH_ALEN; + memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN); + bindaddr.sll_ifindex = if_index; + + if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { + return fatal("Cannot bind raw socket to interface"); + } + + return s; +} + +static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum) +{ + uint16_t *up = (uint16_t *)buffer; + uint32_t sum = startsum; + uint32_t upper16; + + while (count > 1) { + sum += *up++; + count -= 2; + } + if (count > 0) { + sum += (uint16_t) *(uint8_t *)up; + } + while ((upper16 = (sum >> 16)) != 0) { + sum = (sum & 0xffff) + upper16; + } + return sum; +} + +static uint32_t finish_sum(uint32_t sum) +{ + return ~sum & 0xffff; +} + +int send_packet(int s, int if_index, struct dhcp_msg *msg, int size, + uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport) +{ + struct iphdr ip; + struct udphdr udp; + struct iovec iov[3]; + uint32_t udpsum; + uint16_t temp; + struct msghdr msghdr; + struct sockaddr_ll destaddr; + + ip.version = IPVERSION; + ip.ihl = sizeof(ip) >> 2; + ip.tos = 0; + ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size); + ip.id = 0; + ip.frag_off = 0; + ip.ttl = IPDEFTTL; + ip.protocol = IPPROTO_UDP; + ip.check = 0; + ip.saddr = saddr; + ip.daddr = daddr; + ip.check = finish_sum(checksum(&ip, sizeof(ip), 0)); + + udp.source = htons(sport); + udp.dest = htons(dport); + udp.len = htons(sizeof(udp) + size); + udp.check = 0; + + /* Calculate checksum for pseudo header */ + udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0); + udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum); + temp = htons(IPPROTO_UDP); + udpsum = checksum(&temp, sizeof(temp), udpsum); + temp = udp.len; + udpsum = checksum(&temp, sizeof(temp), udpsum); + + /* Add in the checksum for the udp header */ + udpsum = checksum(&udp, sizeof(udp), udpsum); + + /* Add in the checksum for the data */ + udpsum = checksum(msg, size, udpsum); + udp.check = finish_sum(udpsum); + + iov[0].iov_base = (char *)&ip; + iov[0].iov_len = sizeof(ip); + iov[1].iov_base = (char *)&udp; + iov[1].iov_len = sizeof(udp); + iov[2].iov_base = (char *)msg; + iov[2].iov_len = size; + memset(&destaddr, 0, sizeof(destaddr)); + destaddr.sll_family = AF_PACKET; + destaddr.sll_protocol = htons(ETH_P_IP); + destaddr.sll_ifindex = if_index; + destaddr.sll_halen = ETH_ALEN; + memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN); + + msghdr.msg_name = &destaddr; + msghdr.msg_namelen = sizeof(destaddr); + msghdr.msg_iov = iov; + msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec); + msghdr.msg_flags = 0; + msghdr.msg_control = 0; + msghdr.msg_controllen = 0; + return sendmsg(s, &msghdr, 0); +} + +int receive_packet(int s, struct dhcp_msg *msg) +{ + int nread; + int is_valid; + struct dhcp_packet { + struct iphdr ip; + struct udphdr udp; + struct dhcp_msg dhcp; + } packet; + int dhcp_size; + uint32_t sum; + uint16_t temp; + uint32_t saddr, daddr; + + nread = read(s, &packet, sizeof(packet)); + if (nread < 0) { + return -1; + } + /* + * The raw packet interface gives us all packets received by the + * network interface. We need to filter out all packets that are + * not meant for us. + */ + is_valid = 0; + if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) { +#if VERBOSE + LOGD("Packet is too small (%d) to be a UDP datagram", nread); +#endif + } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) { +#if VERBOSE + LOGD("Not a valid IP packet"); +#endif + } else if (nread < ntohs(packet.ip.tot_len)) { +#if VERBOSE + LOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len)); +#endif + } else if (packet.ip.protocol != IPPROTO_UDP) { +#if VERBOSE + LOGD("IP protocol (%d) is not UDP", packet.ip.protocol); +#endif + } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) { +#if VERBOSE + LOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest)); +#endif + } else { + is_valid = 1; + } + + if (!is_valid) { + return -1; + } + + /* Seems like it's probably a valid DHCP packet */ + /* validate IP header checksum */ + sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0)); + if (sum != 0) { + LOGW("IP header checksum failure (0x%x)", packet.ip.check); + return -1; + } + /* + * Validate the UDP checksum. + * Since we don't need the IP header anymore, we "borrow" it + * to construct the pseudo header used in the checksum calculation. + */ + dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp); + saddr = packet.ip.saddr; + daddr = packet.ip.daddr; + nread = ntohs(packet.ip.tot_len); + memset(&packet.ip, 0, sizeof(packet.ip)); + packet.ip.saddr = saddr; + packet.ip.daddr = daddr; + packet.ip.protocol = IPPROTO_UDP; + packet.ip.tot_len = packet.udp.len; + temp = packet.udp.check; + packet.udp.check = 0; + sum = finish_sum(checksum(&packet, nread, 0)); + packet.udp.check = temp; + if (temp != sum) { + LOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp); + return -1; + } + memcpy(msg, &packet.dhcp, dhcp_size); + return dhcp_size; +} diff --git a/libnetutils/packet.h b/libnetutils/packet.h new file mode 100644 index 0000000..aade392 --- /dev/null +++ b/libnetutils/packet.h @@ -0,0 +1,25 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIFI_PACKET_H_ +#define _WIFI_PACKET_H_ + +int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index); +int send_packet(int s, int if_index, struct dhcp_msg *msg, int size, + uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport); +int receive_packet(int s, struct dhcp_msg *msg); + +#endif diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk new file mode 100644 index 0000000..50eb5f5 --- /dev/null +++ b/libpixelflinger/Android.mk @@ -0,0 +1,92 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# +# ARMv6 specific objects +# + +ifeq ($(TARGET_ARCH),arm) +LOCAL_ASFLAGS := -march=armv6 +LOCAL_SRC_FILES := rotate90CW_4x4_16v6.S +LOCAL_MODULE := libpixelflinger_armv6 +include $(BUILD_STATIC_LIBRARY) +endif + +# +# C/C++ and ARMv5 objects +# + +include $(CLEAR_VARS) +PIXELFLINGER_SRC_FILES:= \ + codeflinger/ARMAssemblerInterface.cpp \ + codeflinger/ARMAssemblerProxy.cpp \ + codeflinger/ARMAssembler.cpp \ + codeflinger/CodeCache.cpp \ + codeflinger/GGLAssembler.cpp \ + codeflinger/load_store.cpp \ + codeflinger/blending.cpp \ + codeflinger/texturing.cpp \ + codeflinger/disassem.c \ + tinyutils/SharedBuffer.cpp \ + tinyutils/VectorImpl.cpp \ + fixed.cpp.arm \ + picker.cpp.arm \ + pixelflinger.cpp.arm \ + trap.cpp.arm \ + scanline.cpp.arm \ + format.cpp \ + clear.cpp \ + raster.cpp \ + buffer.cpp + +ifeq ($(TARGET_ARCH),arm) +PIXELFLINGER_SRC_FILES += t32cb16blend.S +endif + +ifeq ($(TARGET_ARCH),arm) +# special optimization flags for pixelflinger +PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer +endif + +LOCAL_SHARED_LIBRARIES := libcutils + +ifneq ($(TARGET_ARCH),arm) +# Required to define logging functions on the simulator. +# TODO: move the simulator logging functions into libcutils with +# the rest of the basic log stuff. +LOCAL_SHARED_LIBRARIES += libutils +endif + +# +# Shared library +# + +LOCAL_MODULE:= libpixelflinger +LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) +LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) +ifneq ($(BUILD_TINY_ANDROID),true) +# Really this should go away entirely or at least not depend on +# libhardware, but this at least gets us built. +LOCAL_SHARED_LIBRARIES += libhardware_legacy +LOCAL_CFLAGS += -DWITH_LIB_HARDWARE +endif +ifeq ($(TARGET_ARCH),arm) +LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6 +endif +include $(BUILD_SHARED_LIBRARY) + +# +# Static library version +# + +include $(CLEAR_VARS) +LOCAL_MODULE:= libpixelflinger_static +LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) +LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) +ifeq ($(TARGET_ARCH),arm) +LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6 +endif +include $(BUILD_STATIC_LIBRARY) + + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libpixelflinger/MODULE_LICENSE_APACHE2 b/libpixelflinger/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libpixelflinger/MODULE_LICENSE_APACHE2 diff --git a/libpixelflinger/NOTICE b/libpixelflinger/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/libpixelflinger/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/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp new file mode 100644 index 0000000..af7356b --- /dev/null +++ b/libpixelflinger/buffer.cpp @@ -0,0 +1,384 @@ +/* libs/pixelflinger/buffer.cpp +** +** 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 <assert.h> + +#include "buffer.h" + +namespace android { +// ---------------------------------------------------------------------------- + +static void read_pixel(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel); +static void write_pixel(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, const pixel_t* pixel); +static void readRGB565(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel); +static void readABGR8888(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel); + +static uint32_t logic_op(int op, uint32_t s, uint32_t d); +static uint32_t extract(uint32_t v, int h, int l, int bits); +static uint32_t expand(uint32_t v, int sbits, int dbits); +static uint32_t downshift_component(uint32_t in, uint32_t v, + int sh, int sl, int dh, int dl, int ch, int cl, int dither); + +// ---------------------------------------------------------------------------- + +void ggl_init_texture(context_t* c) +{ + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + texture_t& t = c->state.texture[i]; + t.s_coord = GGL_ONE_TO_ONE; + t.t_coord = GGL_ONE_TO_ONE; + t.s_wrap = GGL_REPEAT; + t.t_wrap = GGL_REPEAT; + t.min_filter = GGL_NEAREST; + t.mag_filter = GGL_NEAREST; + t.env = GGL_MODULATE; + } + c->activeTMU = &(c->state.texture[0]); +} + +void ggl_set_surface(context_t* c, surface_t* dst, const GGLSurface* src) +{ + dst->width = src->width; + dst->height = src->height; + dst->stride = src->stride; + dst->data = src->data; + dst->format = src->format; + dst->dirty = 1; + if (__builtin_expect(dst->stride < 0, false)) { + const GGLFormat& pixelFormat(c->formats[dst->format]); + const int32_t bpr = -dst->stride * pixelFormat.size; + dst->data += bpr * (dst->height-1); + } +} + +static void pick_read_write(surface_t* s) +{ + // Choose best reader/writers. + switch (s->format) { + case GGL_PIXEL_FORMAT_RGBA_8888: s->read = readABGR8888; break; + case GGL_PIXEL_FORMAT_RGB_565: s->read = readRGB565; break; + default: s->read = read_pixel; break; + } + s->write = write_pixel; +} + +void ggl_pick_texture(context_t* c) +{ + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + surface_t& s = c->state.texture[i].surface; + if ((!c->state.texture[i].enable) || (!s.dirty)) + continue; + s.dirty = 0; + pick_read_write(&s); + generated_tex_vars_t& gen = c->generated_vars.texture[i]; + gen.width = s.width; + gen.height = s.height; + gen.stride = s.stride; + gen.data = int32_t(s.data); + } +} + +void ggl_pick_cb(context_t* c) +{ + surface_t& s = c->state.buffers.color; + if (s.dirty) { + s.dirty = 0; + pick_read_write(&s); + } +} + +// ---------------------------------------------------------------------------- + +void read_pixel(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel) +{ + assert((x < s->width) && (y < s->height)); + + const GGLFormat* f = &(c->formats[s->format]); + int32_t index = x + (s->stride * y); + uint8_t* const data = s->data + index * f->size; + uint32_t v = 0; + switch (f->size) { + case 1: v = *data; break; + case 2: v = *(uint16_t*)data; break; + case 3: v = (data[2]<<16)|(data[1]<<8)|data[0]; break; + case 4: v = GGL_RGBA_TO_HOST(*(uint32_t*)data); break; + } + for (int i=0 ; i<4 ; i++) { + pixel->s[i] = f->c[i].h - f->c[i].l; + if (pixel->s[i]) + pixel->c[i] = extract(v, f->c[i].h, f->c[i].l, f->size*8); + } +} + +void readRGB565(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel) +{ + uint16_t v = *(reinterpret_cast<uint16_t*>(s->data) + (x + (s->stride * y))); + pixel->c[0] = 0; + pixel->c[1] = v>>11; + pixel->c[2] = (v>>5)&0x3F; + pixel->c[3] = v&0x1F; + pixel->s[0] = 0; + pixel->s[1] = 5; + pixel->s[2] = 6; + pixel->s[3] = 5; +} + +void readABGR8888(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, pixel_t* pixel) +{ + uint32_t v = *(reinterpret_cast<uint32_t*>(s->data) + (x + (s->stride * y))); + v = GGL_RGBA_TO_HOST(v); + pixel->c[0] = v>>24; // A + pixel->c[1] = v&0xFF; // R + pixel->c[2] = (v>>8)&0xFF; // G + pixel->c[3] = (v>>16)&0xFF; // B + pixel->s[0] = + pixel->s[1] = + pixel->s[2] = + pixel->s[3] = 8; +} + +void write_pixel(const surface_t* s, context_t* c, + uint32_t x, uint32_t y, const pixel_t* pixel) +{ + assert((x < s->width) && (y < s->height)); + + int dither = -1; + if (c->state.enables & GGL_ENABLE_DITHER) { + dither = c->ditherMatrix[ (x & GGL_DITHER_MASK) + + ((y & GGL_DITHER_MASK)<<GGL_DITHER_ORDER_SHIFT) ]; + } + + const GGLFormat* f = &(c->formats[s->format]); + int32_t index = x + (s->stride * y); + uint8_t* const data = s->data + index * f->size; + + uint32_t mask = 0; + uint32_t v = 0; + for (int i=0 ; i<4 ; i++) { + const int component_mask = 1 << i; + if (f->components>=GGL_LUMINANCE && + (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) { + // destinations L formats don't have G or B + continue; + } + const int l = f->c[i].l; + const int h = f->c[i].h; + if (h && (c->state.mask.color & component_mask)) { + mask |= (((1<<(h-l))-1)<<l); + uint32_t u = pixel->c[i]; + int32_t pixelSize = pixel->s[i]; + if (pixelSize < (h-l)) { + u = expand(u, pixelSize, h-l); + pixelSize = h-l; + } + v = downshift_component(v, u, pixelSize, 0, h, l, 0, 0, dither); + } + } + + if ((c->state.mask.color != 0xF) || + (c->state.enables & GGL_ENABLE_LOGIC_OP)) { + uint32_t d = 0; + switch (f->size) { + case 1: d = *data; break; + case 2: d = *(uint16_t*)data; break; + case 3: d = (data[2]<<16)|(data[1]<<8)|data[0]; break; + case 4: d = GGL_RGBA_TO_HOST(*(uint32_t*)data); break; + } + if (c->state.enables & GGL_ENABLE_LOGIC_OP) { + v = logic_op(c->state.logic_op.opcode, v, d); + v &= mask; + } + v |= (d & ~mask); + } + + switch (f->size) { + case 1: *data = v; break; + case 2: *(uint16_t*)data = v; break; + case 3: + data[0] = v; + data[1] = v>>8; + data[2] = v>>16; + break; + case 4: *(uint32_t*)data = GGL_HOST_TO_RGBA(v); break; + } +} + +static uint32_t logic_op(int op, uint32_t s, uint32_t d) +{ + switch(op) { + case GGL_CLEAR: return 0; + case GGL_AND: return s & d; + case GGL_AND_REVERSE: return s & ~d; + case GGL_COPY: return s; + case GGL_AND_INVERTED: return ~s & d; + case GGL_NOOP: return d; + case GGL_XOR: return s ^ d; + case GGL_OR: return s | d; + case GGL_NOR: return ~(s | d); + case GGL_EQUIV: return ~(s ^ d); + case GGL_INVERT: return ~d; + case GGL_OR_REVERSE: return s | ~d; + case GGL_COPY_INVERTED: return ~s; + case GGL_OR_INVERTED: return ~s | d; + case GGL_NAND: return ~(s & d); + case GGL_SET: return ~0; + }; + return s; +} + + +uint32_t ggl_expand(uint32_t v, int sbits, int dbits) +{ + return expand(v, sbits, dbits); +} + +uint32_t ggl_pack_color(context_t* c, int32_t format, + GGLcolor r, GGLcolor g, GGLcolor b, GGLcolor a) +{ + const GGLFormat* f = &(c->formats[format]); + uint32_t p = 0; + const int32_t hbits = GGL_COLOR_BITS; + const int32_t lbits = GGL_COLOR_BITS - 8; + p = downshift_component(p, r, hbits, lbits, f->rh, f->rl, 0, 1, -1); + p = downshift_component(p, g, hbits, lbits, f->gh, f->gl, 0, 1, -1); + p = downshift_component(p, b, hbits, lbits, f->bh, f->bl, 0, 1, -1); + p = downshift_component(p, a, hbits, lbits, f->ah, f->al, 0, 1, -1); + switch (f->size) { + case 1: p |= p << 8; // fallthrough + case 2: p |= p << 16; + } + return p; +} + +// ---------------------------------------------------------------------------- + +// extract a component from a word +uint32_t extract(uint32_t v, int h, int l, int bits) +{ + assert(h); + if (l) { + v >>= l; + } + if (h != bits) { + v &= (1<<(h-l))-1; + } + return v; +} + +// expand a component from sbits to dbits +uint32_t expand(uint32_t v, int sbits, int dbits) +{ + if (dbits > sbits) { + assert(sbits); + if (sbits==1) { + v = (v<<dbits) - v; + } else { + if (dbits % sbits) { + v <<= (dbits-sbits); + dbits -= sbits; + do { + v |= v>>sbits; + dbits -= sbits; + sbits *= 2; + } while (dbits>0); + } else { + dbits -= sbits; + do { + v |= v<<sbits; + dbits -= sbits; + if (sbits*2 < dbits) { + sbits *= 2; + } + } while (dbits > 0); + } + } + } + return v; +} + +// downsample a component from sbits to dbits +// and shift / construct the pixel +uint32_t downshift_component( uint32_t in, uint32_t v, + int sh, int sl, // src + int dh, int dl, // dst + int ch, int cl, // clear + int dither) +{ + const int sbits = sh-sl; + const int dbits = dh-dl; + + assert(sbits>=dbits); + + + if (sbits>dbits) { + if (dither>=0) { + v -= (v>>dbits); // fix up + const int shift = (GGL_DITHER_BITS - (sbits-dbits)); + if (shift >= 0) v += (dither >> shift) << sl; + else v += (dither << (-shift)) << sl; + } else { + // don't do that right now, so we can reproduce the same + // artifacts we get on ARM (Where we don't do this) + // -> this is not really needed if we don't dither + //if (dBits > 1) { // result already OK if dBits==1 + // v -= (v>>dbits); // fix up + // v += 1 << ((sbits-dbits)-1); // rounding + //} + } + } + + + // we need to clear the high bits of the source + if (ch) { + v <<= 32-sh; + sl += 32-sh; + sh = 32; + } + + if (dl) { + if (cl || (sbits>dbits)) { + v >>= sh-dbits; + sl = 0; + sh = dbits; + in |= v<<dl; + } else { + // sbits==dbits and we don't need to clean the lower bits + // so we just have to shift the component to the right location + int shift = dh-sh; + in |= v<<shift; + } + } else { + // destination starts at bit 0 + // ie: sh-dh == sh-dbits + int shift = sh-dh; + if (shift > 0) in |= v>>shift; + else if (shift < 0) in |= v<<shift; + else in |= v; + } + return in; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libpixelflinger/buffer.h b/libpixelflinger/buffer.h new file mode 100644 index 0000000..9c9e4bc --- /dev/null +++ b/libpixelflinger/buffer.h @@ -0,0 +1,39 @@ +/* libs/pixelflinger/buffer.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_GGL_TEXTURE_H +#define ANDROID_GGL_TEXTURE_H + +#include <private/pixelflinger/ggl_context.h> + +namespace android { + +void ggl_init_texture(context_t* c); + +void ggl_set_surface(context_t* c, surface_t* dst, const GGLSurface* src); + +void ggl_pick_texture(context_t* c); +void ggl_pick_cb(context_t* c); + +uint32_t ggl_expand(uint32_t v, int sbits, int dbits); +uint32_t ggl_pack_color(context_t* c, int32_t format, + GGLcolor r, GGLcolor g, GGLcolor b, GGLcolor a); + +}; // namespace android + +#endif // ANDROID_GGL_TEXTURE_H diff --git a/libpixelflinger/clear.cpp b/libpixelflinger/clear.cpp new file mode 100644 index 0000000..b962456 --- /dev/null +++ b/libpixelflinger/clear.cpp @@ -0,0 +1,171 @@ +/* libs/pixelflinger/clear.cpp +** +** 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/memory.h> + +#include "clear.h" +#include "buffer.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static void ggl_clear(void* c, GGLbitfield mask); +static void ggl_clearColorx(void* c, + GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a); +static void ggl_clearDepthx(void* c, GGLclampx depth); +static void ggl_clearStencil(void* c, GGLint s); + +// ---------------------------------------------------------------------------- + +void ggl_init_clear(context_t* c) +{ + GGLContext& procs = *(GGLContext*)c; + GGL_INIT_PROC(procs, clear); + GGL_INIT_PROC(procs, clearColorx); + GGL_INIT_PROC(procs, clearDepthx); + GGL_INIT_PROC(procs, clearStencil); + c->state.clear.dirty = GGL_STENCIL_BUFFER_BIT | + GGL_COLOR_BUFFER_BIT | + GGL_DEPTH_BUFFER_BIT; + c->state.clear.depth = FIXED_ONE; +} + +// ---------------------------------------------------------------------------- + +static void memset2d(context_t* c, const surface_t& s, uint32_t packed, + uint32_t l, uint32_t t, uint32_t w, uint32_t h) +{ + const uint32_t size = c->formats[s.format].size; + const int32_t stride = s.stride * size; + uint8_t* dst = (uint8_t*)s.data + (l + t*s.stride)*size; + w *= size; + + if (ggl_likely(int32_t(w) == stride)) { + // clear the whole thing in one call + w *= h; + h = 1; + } + + switch (size) { + case 1: + do { + memset(dst, packed, w); + dst += stride; + } while(--h); + break; + case 2: + do { + android_memset16((uint16_t*)dst, packed, w); + dst += stride; + } while(--h); + break; + case 3: // XXX: 24-bit clear. + break; + case 4: + do { + android_memset32((uint32_t*)dst, packed, w); + dst += stride; + } while(--h); + break; + } +} + +static inline GGLfixed fixedToZ(GGLfixed z) { + return GGLfixed(((int64_t(z) << 16) - z) >> 16); +} + +static void ggl_clear(void* con, GGLbitfield mask) +{ + GGL_CONTEXT(c, con); + + // XXX: rgba-dithering, rgba-masking + // XXX: handle all formats of Z and S + + const uint32_t l = c->state.scissor.left; + const uint32_t t = c->state.scissor.top; + uint32_t w = c->state.scissor.right - l; + uint32_t h = c->state.scissor.bottom - t; + + if (!w || !h) + return; + + // unexsiting buffers have no effect... + if (c->state.buffers.color.format == 0) + mask &= ~GGL_COLOR_BUFFER_BIT; + + if (c->state.buffers.depth.format == 0) + mask &= ~GGL_DEPTH_BUFFER_BIT; + + if (c->state.buffers.stencil.format == 0) + mask &= ~GGL_STENCIL_BUFFER_BIT; + + if (mask & GGL_COLOR_BUFFER_BIT) { + if (c->state.clear.dirty & GGL_COLOR_BUFFER_BIT) { + c->state.clear.dirty &= ~GGL_COLOR_BUFFER_BIT; + + uint32_t colorPacked = ggl_pack_color(c, + c->state.buffers.color.format, + gglFixedToIteratedColor(c->state.clear.r), + gglFixedToIteratedColor(c->state.clear.g), + gglFixedToIteratedColor(c->state.clear.b), + gglFixedToIteratedColor(c->state.clear.a)); + + c->state.clear.colorPacked = GGL_HOST_TO_RGBA(colorPacked); + } + const uint32_t packed = c->state.clear.colorPacked; + memset2d(c, c->state.buffers.color, packed, l, t, w, h); + } + if (mask & GGL_DEPTH_BUFFER_BIT) { + if (c->state.clear.dirty & GGL_DEPTH_BUFFER_BIT) { + c->state.clear.dirty &= ~GGL_DEPTH_BUFFER_BIT; + uint32_t depth = fixedToZ(c->state.clear.depth); + c->state.clear.depthPacked = (depth<<16)|depth; + } + const uint32_t packed = c->state.clear.depthPacked; + memset2d(c, c->state.buffers.depth, packed, l, t, w, h); + } + + // XXX: do stencil buffer +} + +static void ggl_clearColorx(void* con, + GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a) +{ + GGL_CONTEXT(c, con); + c->state.clear.r = gglClampx(r); + c->state.clear.g = gglClampx(g); + c->state.clear.b = gglClampx(b); + c->state.clear.a = gglClampx(a); + c->state.clear.dirty |= GGL_COLOR_BUFFER_BIT; +} + +static void ggl_clearDepthx(void* con, GGLclampx depth) +{ + GGL_CONTEXT(c, con); + c->state.clear.depth = gglClampx(depth); + c->state.clear.dirty |= GGL_DEPTH_BUFFER_BIT; +} + +static void ggl_clearStencil(void* con, GGLint s) +{ + GGL_CONTEXT(c, con); + c->state.clear.stencil = s; + c->state.clear.dirty |= GGL_STENCIL_BUFFER_BIT; +} + +}; // namespace android diff --git a/libpixelflinger/clear.h b/libpixelflinger/clear.h new file mode 100644 index 0000000..b071df0 --- /dev/null +++ b/libpixelflinger/clear.h @@ -0,0 +1,30 @@ +/* libs/pixelflinger/clear.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_GGL_CLEAR_H +#define ANDROID_GGL_CLEAR_H + +#include <pixelflinger/pixelflinger.h> +#include <private/pixelflinger/ggl_context.h> + +namespace android { + +void ggl_init_clear(context_t* c); + +}; // namespace android + +#endif // ANDROID_GGL_CLEAR_H diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp new file mode 100644 index 0000000..ff7b0b3 --- /dev/null +++ b/libpixelflinger/codeflinger/ARMAssembler.cpp @@ -0,0 +1,428 @@ +/* libs/pixelflinger/codeflinger/ARMAssembler.cpp +** +** 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 LOG_TAG "ARMAssembler" + +#include <stdio.h> +#include <stdlib.h> +#include <cutils/log.h> +#include <cutils/properties.h> + +#if defined(WITH_LIB_HARDWARE) +#include <hardware_legacy/qemu_tracing.h> +#endif + +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/ARMAssembler.h" +#include "codeflinger/CodeCache.h" +#include "codeflinger/disassem.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark ARMAssembler... +#endif + +ARMAssembler::ARMAssembler(const sp<Assembly>& assembly) + : ARMAssemblerInterface(), + mAssembly(assembly) +{ + mBase = mPC = (uint32_t *)assembly->base(); + mDuration = ggl_system_time(); +#if defined(WITH_LIB_HARDWARE) + mQemuTracing = true; +#endif +} + +ARMAssembler::~ARMAssembler() +{ +} + +uint32_t* ARMAssembler::pc() const +{ + return mPC; +} + +uint32_t* ARMAssembler::base() const +{ + return mBase; +} + +void ARMAssembler::reset() +{ + mBase = mPC = (uint32_t *)mAssembly->base(); + mBranchTargets.clear(); + mLabels.clear(); + mLabelsInverseMapping.clear(); + mComments.clear(); +} + +// ---------------------------------------------------------------------------- + +void ARMAssembler::disassemble(const char* name) +{ + if (name) { + printf("%s:\n", name); + } + size_t count = pc()-base(); + uint32_t* i = base(); + while (count--) { + ssize_t label = mLabelsInverseMapping.indexOfKey(i); + if (label >= 0) { + printf("%s:\n", mLabelsInverseMapping.valueAt(label)); + } + ssize_t comment = mComments.indexOfKey(i); + if (comment >= 0) { + printf("; %s\n", mComments.valueAt(comment)); + } + printf("%08x: %08x ", int(i), int(i[0])); + ::disassemble((u_int)i); + i++; + } +} + +void ARMAssembler::comment(const char* string) +{ + mComments.add(mPC, string); +} + +void ARMAssembler::label(const char* theLabel) +{ + mLabels.add(theLabel, mPC); + mLabelsInverseMapping.add(mPC, theLabel); +} + +void ARMAssembler::B(int cc, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (cc<<28) | (0xA<<24) | 0; +} + +void ARMAssembler::BL(int cc, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (cc<<28) | (0xB<<24) | 0; +} + +#if 0 +#pragma mark - +#pragma mark Prolog/Epilog & Generate... +#endif + + +void ARMAssembler::prolog() +{ + // write dummy prolog code + mPrologPC = mPC; + STM(AL, FD, SP, 1, LSAVED); +} + +void ARMAssembler::epilog(uint32_t touched) +{ + touched &= LSAVED; + if (touched) { + // write prolog code + uint32_t* pc = mPC; + mPC = mPrologPC; + STM(AL, FD, SP, 1, touched | LLR); + mPC = pc; + // write epilog code + LDM(AL, FD, SP, 1, touched | LLR); + BX(AL, LR); + } else { // heh, no registers to save! + // write prolog code + uint32_t* pc = mPC; + mPC = mPrologPC; + MOV(AL, 0, R0, R0); // NOP + mPC = pc; + // write epilog code + BX(AL, LR); + } +} + +int ARMAssembler::generate(const char* name) +{ + // fixup all the branches + size_t count = mBranchTargets.size(); + while (count--) { + const branch_target_t& bt = mBranchTargets[count]; + uint32_t* target_pc = mLabels.valueFor(bt.label); + LOG_ALWAYS_FATAL_IF(!target_pc, + "error resolving branch targets, target_pc is null"); + int32_t offset = int32_t(target_pc - (bt.pc+2)); + *bt.pc |= offset & 0xFFFFFF; + } + + mAssembly->resize( int(pc()-base())*4 ); + + // the instruction cache is flushed by CodeCache + const int64_t duration = ggl_system_time() - mDuration; + const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n"; + LOGI(format, name, int(pc()-base()), base(), pc(), duration); + +#if defined(WITH_LIB_HARDWARE) + if (__builtin_expect(mQemuTracing, 0)) { + int err = qemu_add_mapping(int(base()), name); + mQemuTracing = (err >= 0); + } +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.pf.disasm", value, "0"); + if (atoi(value) != 0) { + printf(format, name, int(pc()-base()), base(), pc(), duration); + disassemble(name); + } + + return NO_ERROR; +} + +uint32_t* ARMAssembler::pcForLabel(const char* label) +{ + return mLabels.valueFor(label); +} + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Data Processing... +#endif + +void ARMAssembler::dataProcessing(int opcode, int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + *mPC++ = (cc<<28) | (opcode<<21) | (s<<20) | (Rn<<16) | (Rd<<12) | Op2; +} + +#if 0 +#pragma mark - +#pragma mark Multiply... +#endif + +// multiply... +void ARMAssembler::MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn) { + if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } + LOG_FATAL_IF(Rd==Rm, "MLA(r%u,r%u,r%u,r%u)", Rd,Rm,Rs,Rn); + *mPC++ = (cc<<28) | (1<<21) | (s<<20) | + (Rd<<16) | (Rn<<12) | (Rs<<8) | 0x90 | Rm; +} +void ARMAssembler::MUL(int cc, int s, + int Rd, int Rm, int Rs) { + if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } + LOG_FATAL_IF(Rd==Rm, "MUL(r%u,r%u,r%u)", Rd,Rm,Rs); + *mPC++ = (cc<<28) | (s<<20) | (Rd<<16) | (Rs<<8) | 0x90 | Rm; +} +void ARMAssembler::UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "UMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + *mPC++ = (cc<<28) | (1<<23) | (s<<20) | + (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; +} +void ARMAssembler::UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) | + (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; +} +void ARMAssembler::SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) | + (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; +} +void ARMAssembler::SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) | + (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; +} + +#if 0 +#pragma mark - +#pragma mark Branches... +#endif + +// branches... +void ARMAssembler::B(int cc, uint32_t* pc) +{ + int32_t offset = int32_t(pc - (mPC+2)); + *mPC++ = (cc<<28) | (0xA<<24) | (offset & 0xFFFFFF); +} + +void ARMAssembler::BL(int cc, uint32_t* pc) +{ + int32_t offset = int32_t(pc - (mPC+2)); + *mPC++ = (cc<<28) | (0xB<<24) | (offset & 0xFFFFFF); +} + +void ARMAssembler::BX(int cc, int Rn) +{ + *mPC++ = (cc<<28) | 0x12FFF10 | Rn; +} + +#if 0 +#pragma mark - +#pragma mark Data Transfer... +#endif + +// data transfert... +void ARMAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<26) | (1<<20) | (Rn<<16) | (Rd<<12) | offset; +} +void ARMAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<26) | (1<<22) | (1<<20) | (Rn<<16) | (Rd<<12) | offset; +} +void ARMAssembler::STR(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<26) | (Rn<<16) | (Rd<<12) | offset; +} +void ARMAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<26) | (1<<22) | (Rn<<16) | (Rd<<12) | offset; +} + +void ARMAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xB0 | offset; +} +void ARMAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xD0 | offset; +} +void ARMAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xF0 | offset; +} +void ARMAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset) { + *mPC++ = (cc<<28) | (Rn<<16) | (Rd<<12) | 0xB0 | offset; +} + +#if 0 +#pragma mark - +#pragma mark Block Data Transfer... +#endif + +// block data transfer... +void ARMAssembler::LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // ED FD EA FA IB IA DB DA + const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 }; + *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list; +} + +void ARMAssembler::STM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // FA EA FD ED IB IA DB DA + const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 }; + const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 }; + *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list; +} + +#if 0 +#pragma mark - +#pragma mark Special... +#endif + +// special... +void ARMAssembler::SWP(int cc, int Rn, int Rd, int Rm) { + *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; +} +void ARMAssembler::SWPB(int cc, int Rn, int Rd, int Rm) { + *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; +} +void ARMAssembler::SWI(int cc, uint32_t comment) { + *mPC++ = (cc<<28) | (0xF<<24) | comment; +} + +#if 0 +#pragma mark - +#pragma mark DSP instructions... +#endif + +// DSP instructions... +void ARMAssembler::PLD(int Rn, uint32_t offset) { + LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))), + "PLD only P=1, W=0"); + *mPC++ = 0xF550F000 | (Rn<<16) | offset; +} + +void ARMAssembler::CLZ(int cc, int Rd, int Rm) +{ + *mPC++ = (cc<<28) | 0x16F0F10| (Rd<<12) | Rm; +} + +void ARMAssembler::QADD(int cc, int Rd, int Rm, int Rn) +{ + *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm; +} + +void ARMAssembler::QDADD(int cc, int Rd, int Rm, int Rn) +{ + *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm; +} + +void ARMAssembler::QSUB(int cc, int Rd, int Rm, int Rn) +{ + *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm; +} + +void ARMAssembler::QDSUB(int cc, int Rd, int Rm, int Rn) +{ + *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm; +} + +void ARMAssembler::SMUL(int cc, int xy, + int Rd, int Rm, int Rs) +{ + *mPC++ = (cc<<28) | 0x1600080 | (Rd<<16) | (Rs<<8) | (xy<<4) | Rm; +} + +void ARMAssembler::SMULW(int cc, int y, + int Rd, int Rm, int Rs) +{ + *mPC++ = (cc<<28) | 0x12000A0 | (Rd<<16) | (Rs<<8) | (y<<4) | Rm; +} + +void ARMAssembler::SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn) +{ + *mPC++ = (cc<<28) | 0x1000080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (xy<<4) | Rm; +} + +void ARMAssembler::SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) +{ + *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm; +} + +void ARMAssembler::SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn) +{ + *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm; +} + +}; // namespace android + diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h new file mode 100644 index 0000000..8837e07 --- /dev/null +++ b/libpixelflinger/codeflinger/ARMAssembler.h @@ -0,0 +1,155 @@ +/* libs/pixelflinger/codeflinger/ARMAssembler.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_ARMASSEMBLER_H +#define ANDROID_ARMASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +#include "tinyutils/smartpointer.h" +#include "codeflinger/ARMAssemblerInterface.h" +#include "codeflinger/CodeCache.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class ARMAssembler : public ARMAssemblerInterface +{ +public: + ARMAssembler(const sp<Assembly>& assembly); + virtual ~ARMAssembler(); + + uint32_t* base() const; + uint32_t* pc() const; + + + void disassemble(const char* name); + + // ------------------------------------------------------------------------ + // ARMAssemblerInterface... + // ------------------------------------------------------------------------ + + virtual void reset(); + + virtual int generate(const char* name); + + virtual void prolog(); + virtual void epilog(uint32_t touched); + virtual void comment(const char* string); + + virtual void dataProcessing(int opcode, int cc, int s, + int Rd, int Rn, + uint32_t Op2); + virtual void MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn); + virtual void MUL(int cc, int s, + int Rd, int Rm, int Rs); + virtual void UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + + virtual void B(int cc, uint32_t* pc); + virtual void BL(int cc, uint32_t* pc); + virtual void BX(int cc, int Rn); + virtual void label(const char* theLabel); + virtual void B(int cc, const char* label); + virtual void BL(int cc, const char* label); + + virtual uint32_t* pcForLabel(const char* label); + + virtual void LDR (int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void LDRB(int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void STR (int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void STRB(int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void LDRH (int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void LDRSB(int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void LDRSH(int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void STRH (int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + virtual void STM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + + virtual void SWP(int cc, int Rn, int Rd, int Rm); + virtual void SWPB(int cc, int Rn, int Rd, int Rm); + virtual void SWI(int cc, uint32_t comment); + + virtual void PLD(int Rn, uint32_t offset); + virtual void CLZ(int cc, int Rd, int Rm); + virtual void QADD(int cc, int Rd, int Rm, int Rn); + virtual void QDADD(int cc, int Rd, int Rm, int Rn); + virtual void QSUB(int cc, int Rd, int Rm, int Rn); + virtual void QDSUB(int cc, int Rd, int Rm, int Rn); + virtual void SMUL(int cc, int xy, + int Rd, int Rm, int Rs); + virtual void SMULW(int cc, int y, + int Rd, int Rm, int Rs); + virtual void SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn); + virtual void SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm); + virtual void SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn); + +private: + ARMAssembler(const ARMAssembler& rhs); + ARMAssembler& operator = (const ARMAssembler& rhs); + + sp<Assembly> mAssembly; + uint32_t* mBase; + uint32_t* mPC; + uint32_t* mPrologPC; + int64_t mDuration; +#if defined(WITH_LIB_HARDWARE) + bool mQemuTracing; +#endif + + struct branch_target_t { + inline branch_target_t() : label(0), pc(0) { } + inline branch_target_t(const char* l, uint32_t* p) + : label(l), pc(p) { } + const char* label; + uint32_t* pc; + }; + + Vector<branch_target_t> mBranchTargets; + KeyedVector< const char*, uint32_t* > mLabels; + KeyedVector< uint32_t*, const char* > mLabelsInverseMapping; + KeyedVector< uint32_t*, const char* > mComments; +}; + +}; // namespace android + +#endif //ANDROID_ARMASSEMBLER_H diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp new file mode 100644 index 0000000..7fa0de0 --- /dev/null +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp @@ -0,0 +1,173 @@ +/* libs/pixelflinger/codeflinger/ARMAssemblerInterface.cpp +** +** 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 <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/log.h> +#include "codeflinger/ARMAssemblerInterface.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +ARMAssemblerInterface::~ARMAssemblerInterface() +{ +} + +int ARMAssemblerInterface::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + rot = 0; + imm = immediate; + if (imm > 0x7F) { // skip the easy cases + while (!(imm&3) || (imm&0xFC000000)) { + uint32_t newval; + newval = imm >> 2; + newval |= (imm&3) << 30; + imm = newval; + rot += 2; + if (rot == 32) { + rot = 0; + break; + } + } + } + rot = (16 - (rot>>1)) & 0xF; + + if (imm>=0x100) + return -EINVAL; + + if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate) + return -1; + + return 0; +} + +// shifters... + +bool ARMAssemblerInterface::isValidImmediate(uint32_t immediate) +{ + uint32_t rot, imm; + return buildImmediate(immediate, rot, imm) == 0; +} + +uint32_t ARMAssemblerInterface::imm(uint32_t immediate) +{ + uint32_t rot, imm; + int err = buildImmediate(immediate, rot, imm); + + LOG_ALWAYS_FATAL_IF(err==-EINVAL, + "immediate %08x cannot be encoded", + immediate); + + LOG_ALWAYS_FATAL_IF(err, + "immediate (%08x) encoding bogus!", + immediate); + + return (1<<25) | (rot<<8) | imm; +} + +uint32_t ARMAssemblerInterface::reg_imm(int Rm, int type, uint32_t shift) +{ + return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF); +} + +uint32_t ARMAssemblerInterface::reg_rrx(int Rm) +{ + return (ROR<<5) | (Rm&0xF); +} + +uint32_t ARMAssemblerInterface::reg_reg(int Rm, int type, int Rs) +{ + return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF); +} + +// addressing modes... +// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) +uint32_t ARMAssemblerInterface::immed12_pre(int32_t immed12, int W) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + return (1<<24) | (((uint32_t(immed12)>>31)^1)<<23) | + ((W&1)<<21) | (abs(immed12)&0x7FF); +} + +uint32_t ARMAssemblerInterface::immed12_post(int32_t immed12) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + + return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF); +} + +uint32_t ARMAssemblerInterface::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + return (1<<25) | (1<<24) | + (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | + reg_imm(abs(Rm), type, shift); +} + +uint32_t ARMAssemblerInterface::reg_scale_post(int Rm, int type, uint32_t shift) +{ + return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift); +} + +// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) +uint32_t ARMAssemblerInterface::immed8_pre(int32_t immed8, int W) +{ + uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + + return (1<<24) | (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | + ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF)); +} + +uint32_t ARMAssemblerInterface::immed8_post(int32_t immed8) +{ + uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + + return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | + (((offset&0xF0)<<4) | (offset&0xF)); +} + +uint32_t ARMAssemblerInterface::reg_pre(int Rm, int W) +{ + return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF); +} + +uint32_t ARMAssemblerInterface::reg_post(int Rm) +{ + return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF); +} + + +}; // namespace android + diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h new file mode 100644 index 0000000..465b3bd --- /dev/null +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h @@ -0,0 +1,324 @@ +/* libs/pixelflinger/codeflinger/ARMAssemblerInterface.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_ARMASSEMBLER_INTERFACE_H +#define ANDROID_ARMASSEMBLER_INTERFACE_H + +#include <stdint.h> +#include <sys/types.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +class ARMAssemblerInterface +{ +public: + virtual ~ARMAssemblerInterface(); + + enum { + EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV, + HS = CS, + LO = CC + }; + enum { + S = 1 + }; + enum { + LSL, LSR, ASR, ROR + }; + enum { + ED, FD, EA, FA, + IB, IA, DB, DA + }; + enum { + R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, + SP = R13, + LR = R14, + PC = R15 + }; + enum { + #define LIST(rr) L##rr=1<<rr + LIST(R0), LIST(R1), LIST(R2), LIST(R3), LIST(R4), LIST(R5), LIST(R6), + LIST(R7), LIST(R8), LIST(R9), LIST(R10), LIST(R11), LIST(R12), + LIST(R13), LIST(R14), LIST(R15), + LIST(SP), LIST(LR), LIST(PC), + #undef LIST + LSAVED = LR4|LR5|LR6|LR7|LR8|LR9|LR10|LR11 | LLR + }; + + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + // shifters... + static bool isValidImmediate(uint32_t immed); + static int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + static uint32_t imm(uint32_t immediate); + static uint32_t reg_imm(int Rm, int type, uint32_t shift); + static uint32_t reg_rrx(int Rm); + static uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + static uint32_t immed12_pre(int32_t immed12, int W=0); + static uint32_t immed12_post(int32_t immed12); + static uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + static uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + static uint32_t immed8_pre(int32_t immed8, int W=0); + static uint32_t immed8_post(int32_t immed8); + static uint32_t reg_pre(int Rm, int W=0); + static uint32_t reg_post(int Rm); + + // ----------------------------------------------------------------------- + // basic instructions & code generation + // ----------------------------------------------------------------------- + + // generate the code + virtual void reset() = 0; + virtual int generate(const char* name) = 0; + virtual void disassemble(const char* name) = 0; + + // construct prolog and epilog + virtual void prolog() = 0; + virtual void epilog(uint32_t touched) = 0; + virtual void comment(const char* string) = 0; + + // data processing... + enum { + opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC, + opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN + }; + + virtual void + dataProcessing( int opcode, int cc, int s, + int Rd, int Rn, + uint32_t Op2) = 0; + + // multiply... + virtual void MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn) = 0; + virtual void MUL(int cc, int s, + int Rd, int Rm, int Rs) = 0; + virtual void UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) = 0; + virtual void UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) = 0; + virtual void SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) = 0; + virtual void SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) = 0; + + // branches... + virtual void B(int cc, uint32_t* pc) = 0; + virtual void BL(int cc, uint32_t* pc) = 0; + virtual void BX(int cc, int Rn) = 0; + + virtual void label(const char* theLabel) = 0; + virtual void B(int cc, const char* label) = 0; + virtual void BL(int cc, const char* label) = 0; + + // valid only after generate() has been called + virtual uint32_t* pcForLabel(const char* label) = 0; + + // data transfer... + virtual void LDR (int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)) = 0; + virtual void LDRB(int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)) = 0; + virtual void STR (int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)) = 0; + virtual void STRB(int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)) = 0; + + virtual void LDRH (int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)) = 0; + virtual void LDRSB(int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)) = 0; + virtual void LDRSH(int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)) = 0; + virtual void STRH (int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)) = 0; + + // block data transfer... + virtual void LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list) = 0; + virtual void STM(int cc, int dir, + int Rn, int W, uint32_t reg_list) = 0; + + // special... + virtual void SWP(int cc, int Rn, int Rd, int Rm) = 0; + virtual void SWPB(int cc, int Rn, int Rd, int Rm) = 0; + virtual void SWI(int cc, uint32_t comment) = 0; + + // DSP instructions... + enum { + // B=0, T=1 + // yx + xyBB = 0, // 0000, + xyTB = 2, // 0010, + xyBT = 4, // 0100, + xyTT = 6, // 0110, + yB = 0, // 0000, + yT = 4, // 0100 + }; + + virtual void PLD(int Rn, uint32_t offset) = 0; + + virtual void CLZ(int cc, int Rd, int Rm) = 0; + + virtual void QADD(int cc, int Rd, int Rm, int Rn) = 0; + virtual void QDADD(int cc, int Rd, int Rm, int Rn) = 0; + virtual void QSUB(int cc, int Rd, int Rm, int Rn) = 0; + virtual void QDSUB(int cc, int Rd, int Rm, int Rn) = 0; + + virtual void SMUL(int cc, int xy, + int Rd, int Rm, int Rs) = 0; + virtual void SMULW(int cc, int y, + int Rd, int Rm, int Rs) = 0; + virtual void SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn) = 0; + virtual void SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) = 0; + virtual void SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn) = 0; + + // ----------------------------------------------------------------------- + // convenience... + // ----------------------------------------------------------------------- + inline void + ADC(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opADC, cc, s, Rd, Rn, Op2); + } + inline void + ADD(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opADD, cc, s, Rd, Rn, Op2); + } + inline void + AND(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opAND, cc, s, Rd, Rn, Op2); + } + inline void + BIC(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opBIC, cc, s, Rd, Rn, Op2); + } + inline void + EOR(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opEOR, cc, s, Rd, Rn, Op2); + } + inline void + MOV(int cc, int s, int Rd, uint32_t Op2) { + dataProcessing(opMOV, cc, s, Rd, 0, Op2); + } + inline void + MVN(int cc, int s, int Rd, uint32_t Op2) { + dataProcessing(opMVN, cc, s, Rd, 0, Op2); + } + inline void + ORR(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opORR, cc, s, Rd, Rn, Op2); + } + inline void + RSB(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opRSB, cc, s, Rd, Rn, Op2); + } + inline void + RSC(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opRSC, cc, s, Rd, Rn, Op2); + } + inline void + SBC(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opSBC, cc, s, Rd, Rn, Op2); + } + inline void + SUB(int cc, int s, int Rd, int Rn, uint32_t Op2) { + dataProcessing(opSUB, cc, s, Rd, Rn, Op2); + } + inline void + TEQ(int cc, int Rn, uint32_t Op2) { + dataProcessing(opTEQ, cc, 1, 0, Rn, Op2); + } + inline void + TST(int cc, int Rn, uint32_t Op2) { + dataProcessing(opTST, cc, 1, 0, Rn, Op2); + } + inline void + CMP(int cc, int Rn, uint32_t Op2) { + dataProcessing(opCMP, cc, 1, 0, Rn, Op2); + } + inline void + CMN(int cc, int Rn, uint32_t Op2) { + dataProcessing(opCMN, cc, 1, 0, Rn, Op2); + } + + inline void SMULBB(int cc, int Rd, int Rm, int Rs) { + SMUL(cc, xyBB, Rd, Rm, Rs); } + inline void SMULTB(int cc, int Rd, int Rm, int Rs) { + SMUL(cc, xyTB, Rd, Rm, Rs); } + inline void SMULBT(int cc, int Rd, int Rm, int Rs) { + SMUL(cc, xyBT, Rd, Rm, Rs); } + inline void SMULTT(int cc, int Rd, int Rm, int Rs) { + SMUL(cc, xyTT, Rd, Rm, Rs); } + + inline void SMULWB(int cc, int Rd, int Rm, int Rs) { + SMULW(cc, yB, Rd, Rm, Rs); } + inline void SMULWT(int cc, int Rd, int Rm, int Rs) { + SMULW(cc, yT, Rd, Rm, Rs); } + + inline void + SMLABB(int cc, int Rd, int Rm, int Rs, int Rn) { + SMLA(cc, xyBB, Rd, Rm, Rs, Rn); } + inline void + SMLATB(int cc, int Rd, int Rm, int Rs, int Rn) { + SMLA(cc, xyTB, Rd, Rm, Rs, Rn); } + inline void + SMLABT(int cc, int Rd, int Rm, int Rs, int Rn) { + SMLA(cc, xyBT, Rd, Rm, Rs, Rn); } + inline void + SMLATT(int cc, int Rd, int Rm, int Rs, int Rn) { + SMLA(cc, xyTT, Rd, Rm, Rs, Rn); } + + inline void + SMLALBB(int cc, int RdHi, int RdLo, int Rs, int Rm) { + SMLAL(cc, xyBB, RdHi, RdLo, Rs, Rm); } + inline void + SMLALTB(int cc, int RdHi, int RdLo, int Rs, int Rm) { + SMLAL(cc, xyTB, RdHi, RdLo, Rs, Rm); } + inline void + SMLALBT(int cc, int RdHi, int RdLo, int Rs, int Rm) { + SMLAL(cc, xyBT, RdHi, RdLo, Rs, Rm); } + inline void + SMLALTT(int cc, int RdHi, int RdLo, int Rs, int Rm) { + SMLAL(cc, xyTT, RdHi, RdLo, Rs, Rm); } + + inline void + SMLAWB(int cc, int Rd, int Rm, int Rs, int Rn) { + SMLAW(cc, yB, Rd, Rm, Rs, Rn); } + inline void + SMLAWT(int cc, int Rd, int Rm, int Rs, int Rn) { + SMLAW(cc, yT, Rd, Rm, Rs, Rn); } +}; + +}; // namespace android + +#endif //ANDROID_ARMASSEMBLER_INTERFACE_H diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp new file mode 100644 index 0000000..18c4618 --- /dev/null +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp @@ -0,0 +1,200 @@ +/* libs/pixelflinger/codeflinger/ARMAssemblerProxy.cpp +** +** 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 <stdint.h> +#include <sys/types.h> + +#include "codeflinger/ARMAssemblerProxy.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +ARMAssemblerProxy::ARMAssemblerProxy() + : mTarget(0) +{ +} + +ARMAssemblerProxy::ARMAssemblerProxy(ARMAssemblerInterface* target) + : mTarget(target) +{ +} + +ARMAssemblerProxy::~ARMAssemblerProxy() +{ + delete mTarget; +} + +void ARMAssemblerProxy::setTarget(ARMAssemblerInterface* target) +{ + delete mTarget; + mTarget = target; +} + +void ARMAssemblerProxy::reset() { + mTarget->reset(); +} +int ARMAssemblerProxy::generate(const char* name) { + return mTarget->generate(name); +} +void ARMAssemblerProxy::disassemble(const char* name) { + return mTarget->disassemble(name); +} +void ARMAssemblerProxy::prolog() { + mTarget->prolog(); +} +void ARMAssemblerProxy::epilog(uint32_t touched) { + mTarget->epilog(touched); +} +void ARMAssemblerProxy::comment(const char* string) { + mTarget->comment(string); +} + + +void ARMAssemblerProxy::dataProcessing( int opcode, int cc, int s, + int Rd, int Rn, uint32_t Op2) +{ + mTarget->dataProcessing(opcode, cc, s, Rd, Rn, Op2); +} + +void ARMAssemblerProxy::MLA(int cc, int s, int Rd, int Rm, int Rs, int Rn) { + mTarget->MLA(cc, s, Rd, Rm, Rs, Rn); +} +void ARMAssemblerProxy::MUL(int cc, int s, int Rd, int Rm, int Rs) { + mTarget->MUL(cc, s, Rd, Rm, Rs); +} +void ARMAssemblerProxy::UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mTarget->UMULL(cc, s, RdLo, RdHi, Rm, Rs); +} +void ARMAssemblerProxy::UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mTarget->UMUAL(cc, s, RdLo, RdHi, Rm, Rs); +} +void ARMAssemblerProxy::SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mTarget->SMULL(cc, s, RdLo, RdHi, Rm, Rs); +} +void ARMAssemblerProxy::SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mTarget->SMUAL(cc, s, RdLo, RdHi, Rm, Rs); +} + +void ARMAssemblerProxy::B(int cc, uint32_t* pc) { + mTarget->B(cc, pc); +} +void ARMAssemblerProxy::BL(int cc, uint32_t* pc) { + mTarget->BL(cc, pc); +} +void ARMAssemblerProxy::BX(int cc, int Rn) { + mTarget->BX(cc, Rn); +} +void ARMAssemblerProxy::label(const char* theLabel) { + mTarget->label(theLabel); +} +void ARMAssemblerProxy::B(int cc, const char* label) { + mTarget->B(cc, label); +} +void ARMAssemblerProxy::BL(int cc, const char* label) { + mTarget->BL(cc, label); +} + +uint32_t* ARMAssemblerProxy::pcForLabel(const char* label) { + return mTarget->pcForLabel(label); +} + +void ARMAssemblerProxy::LDR(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->LDR(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::LDRB(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->LDRB(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::STR(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->STR(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::STRB(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->STRB(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::LDRH(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->LDRH(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::LDRSB(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->LDRSB(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::LDRSH(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->LDRSH(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::STRH(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->STRH(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::LDM(int cc, int dir, int Rn, int W, uint32_t reg_list) { + mTarget->LDM(cc, dir, Rn, W, reg_list); +} +void ARMAssemblerProxy::STM(int cc, int dir, int Rn, int W, uint32_t reg_list) { + mTarget->STM(cc, dir, Rn, W, reg_list); +} + +void ARMAssemblerProxy::SWP(int cc, int Rn, int Rd, int Rm) { + mTarget->SWP(cc, Rn, Rd, Rm); +} +void ARMAssemblerProxy::SWPB(int cc, int Rn, int Rd, int Rm) { + mTarget->SWPB(cc, Rn, Rd, Rm); +} +void ARMAssemblerProxy::SWI(int cc, uint32_t comment) { + mTarget->SWI(cc, comment); +} + + +void ARMAssemblerProxy::PLD(int Rn, uint32_t offset) { + mTarget->PLD(Rn, offset); +} +void ARMAssemblerProxy::CLZ(int cc, int Rd, int Rm) { + mTarget->CLZ(cc, Rd, Rm); +} +void ARMAssemblerProxy::QADD(int cc, int Rd, int Rm, int Rn) { + mTarget->QADD(cc, Rd, Rm, Rn); +} +void ARMAssemblerProxy::QDADD(int cc, int Rd, int Rm, int Rn) { + mTarget->QDADD(cc, Rd, Rm, Rn); +} +void ARMAssemblerProxy::QSUB(int cc, int Rd, int Rm, int Rn) { + mTarget->QSUB(cc, Rd, Rm, Rn); +} +void ARMAssemblerProxy::QDSUB(int cc, int Rd, int Rm, int Rn) { + mTarget->QDSUB(cc, Rd, Rm, Rn); +} +void ARMAssemblerProxy::SMUL(int cc, int xy, int Rd, int Rm, int Rs) { + mTarget->SMUL(cc, xy, Rd, Rm, Rs); +} +void ARMAssemblerProxy::SMULW(int cc, int y, int Rd, int Rm, int Rs) { + mTarget->SMULW(cc, y, Rd, Rm, Rs); +} +void ARMAssemblerProxy::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn) { + mTarget->SMLA(cc, xy, Rd, Rm, Rs, Rn); +} +void ARMAssemblerProxy::SMLAL( int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) { + mTarget->SMLAL(cc, xy, RdHi, RdLo, Rs, Rm); +} +void ARMAssemblerProxy::SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn) { + mTarget->SMLAW(cc, y, Rd, Rm, Rs, Rn); +} + + +}; // namespace android + diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h new file mode 100644 index 0000000..4bdca9c --- /dev/null +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h @@ -0,0 +1,123 @@ +/* libs/pixelflinger/codeflinger/ARMAssemblerProxy.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_ARMASSEMBLER_PROXY_H +#define ANDROID_ARMASSEMBLER_PROXY_H + +#include <stdint.h> +#include <sys/types.h> + +#include "codeflinger/ARMAssemblerInterface.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class ARMAssemblerProxy : public ARMAssemblerInterface +{ +public: + // ARMAssemblerProxy take ownership of the target + + ARMAssemblerProxy(); + ARMAssemblerProxy(ARMAssemblerInterface* target); + virtual ~ARMAssemblerProxy(); + + void setTarget(ARMAssemblerInterface* target); + + virtual void reset(); + virtual int generate(const char* name); + virtual void disassemble(const char* name); + + virtual void prolog(); + virtual void epilog(uint32_t touched); + virtual void comment(const char* string); + + virtual void dataProcessing(int opcode, int cc, int s, + int Rd, int Rn, + uint32_t Op2); + virtual void MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn); + virtual void MUL(int cc, int s, + int Rd, int Rm, int Rs); + virtual void UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + + virtual void B(int cc, uint32_t* pc); + virtual void BL(int cc, uint32_t* pc); + virtual void BX(int cc, int Rn); + virtual void label(const char* theLabel); + virtual void B(int cc, const char* label); + virtual void BL(int cc, const char* label); + + uint32_t* pcForLabel(const char* label); + + virtual void LDR (int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void LDRB(int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void STR (int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void STRB(int cc, int Rd, + int Rn, uint32_t offset = immed12_pre(0)); + virtual void LDRH (int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void LDRSB(int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void LDRSH(int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void STRH (int cc, int Rd, + int Rn, uint32_t offset = immed8_pre(0)); + virtual void LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + virtual void STM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + + virtual void SWP(int cc, int Rn, int Rd, int Rm); + virtual void SWPB(int cc, int Rn, int Rd, int Rm); + virtual void SWI(int cc, uint32_t comment); + + virtual void PLD(int Rn, uint32_t offset); + virtual void CLZ(int cc, int Rd, int Rm); + virtual void QADD(int cc, int Rd, int Rm, int Rn); + virtual void QDADD(int cc, int Rd, int Rm, int Rn); + virtual void QSUB(int cc, int Rd, int Rm, int Rn); + virtual void QDSUB(int cc, int Rd, int Rm, int Rn); + virtual void SMUL(int cc, int xy, + int Rd, int Rm, int Rs); + virtual void SMULW(int cc, int y, + int Rd, int Rm, int Rs); + virtual void SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn); + virtual void SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm); + virtual void SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn); + +private: + ARMAssemblerInterface* mTarget; +}; + +}; // namespace android + +#endif //ANDROID_ARMASSEMBLER_PROXY_H diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp new file mode 100644 index 0000000..29410c8 --- /dev/null +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -0,0 +1,151 @@ +/* libs/pixelflinger/codeflinger/CodeCache.cpp +** +** 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> + +#include "codeflinger/CodeCache.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +#if defined(__arm__) +#include <unistd.h> +#include <errno.h> +#endif + +// ---------------------------------------------------------------------------- + +Assembly::Assembly(size_t size) + : mCount(1), mSize(0) +{ + mBase = (uint32_t*)malloc(size); + if (mBase) { + mSize = size; + } +} + +Assembly::~Assembly() +{ + free(mBase); +} + +void Assembly::incStrong(const void*) const +{ + android_atomic_inc(&mCount); +} + +void Assembly::decStrong(const void*) const +{ + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} + +ssize_t Assembly::size() const +{ + if (!mBase) return NO_MEMORY; + return mSize; +} + +uint32_t* Assembly::base() const +{ + return mBase; +} + +ssize_t Assembly::resize(size_t newSize) +{ + mBase = (uint32_t*)realloc(mBase, newSize); + mSize = newSize; + return size(); +} + +// ---------------------------------------------------------------------------- + +CodeCache::CodeCache(size_t size) + : mCacheSize(size), mCacheInUse(0) +{ + pthread_mutex_init(&mLock, 0); +} + +CodeCache::~CodeCache() +{ + pthread_mutex_destroy(&mLock); +} + +sp<Assembly> CodeCache::lookup(const AssemblyKeyBase& keyBase) const +{ + pthread_mutex_lock(&mLock); + sp<Assembly> r; + ssize_t index = mCacheData.indexOfKey(key_t(keyBase)); + if (index >= 0) { + const cache_entry_t& e = mCacheData.valueAt(index); + e.when = mWhen++; + r = e.entry; + } + pthread_mutex_unlock(&mLock); + return r; +} + +int CodeCache::cache( const AssemblyKeyBase& keyBase, + const sp<Assembly>& assembly) +{ + pthread_mutex_lock(&mLock); + + const ssize_t assemblySize = assembly->size(); + while (mCacheInUse + assemblySize > mCacheSize) { + // evict the LRU + size_t lru = 0; + size_t count = mCacheData.size(); + for (size_t i=0 ; i<count ; i++) { + const cache_entry_t& e = mCacheData.valueAt(i); + if (e.when < mCacheData.valueAt(lru).when) { + lru = i; + } + } + const cache_entry_t& e = mCacheData.valueAt(lru); + mCacheInUse -= e.entry->size(); + mCacheData.removeItemsAt(lru); + } + + ssize_t err = mCacheData.add(key_t(keyBase), cache_entry_t(assembly, mWhen)); + if (err >= 0) { + mCacheInUse += assemblySize; + mWhen++; + // synchronize caches... +#if defined(__arm__) + const long base = long(assembly->base()); + const long curr = base + long(assembly->size()); + err = cacheflush(base, curr, 0); + LOGE_IF(err, "__ARM_NR_cacheflush error %s\n", + strerror(errno)); +#endif + } + + pthread_mutex_unlock(&mLock); + return err; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h new file mode 100644 index 0000000..370ce17 --- /dev/null +++ b/libpixelflinger/codeflinger/CodeCache.h @@ -0,0 +1,134 @@ +/* libs/pixelflinger/codeflinger/CodeCache.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_CODECACHE_H +#define ANDROID_CODECACHE_H + +#include <stdint.h> +#include <pthread.h> +#include <sys/types.h> + +#include <utils/KeyedVector.h> + +#include "tinyutils/smartpointer.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class AssemblyKeyBase { +public: + virtual ~AssemblyKeyBase() { } + virtual int compare_type(const AssemblyKeyBase& key) const = 0; +}; + +template <typename T> +class AssemblyKey : public AssemblyKeyBase +{ +public: + AssemblyKey(const T& rhs) : mKey(rhs) { } + virtual int compare_type(const AssemblyKeyBase& key) const { + const T& rhs = static_cast<const AssemblyKey&>(key).mKey; + return android::compare_type(mKey, rhs); + } +private: + T mKey; +}; + +// ---------------------------------------------------------------------------- + +class Assembly +{ +public: + Assembly(size_t size); + virtual ~Assembly(); + + ssize_t size() const; + uint32_t* base() const; + ssize_t resize(size_t size); + + // protocol for sp<> + void incStrong(const void* id) const; + void decStrong(const void* id) const; + typedef void weakref_type; + +private: + mutable int32_t mCount; + uint32_t* mBase; + ssize_t mSize; +}; + +// ---------------------------------------------------------------------------- + +class CodeCache +{ +public: +// pretty simple cache API... + CodeCache(size_t size); + ~CodeCache(); + + sp<Assembly> lookup(const AssemblyKeyBase& key) const; + + int cache( const AssemblyKeyBase& key, + const sp<Assembly>& assembly); + +private: + // nothing to see here... + struct cache_entry_t { + inline cache_entry_t() { } + inline cache_entry_t(const sp<Assembly>& a, int64_t w) + : entry(a), when(w) { } + sp<Assembly> entry; + mutable int64_t when; + }; + + class key_t { + friend int compare_type( + const key_value_pair_t<key_t, cache_entry_t>&, + const key_value_pair_t<key_t, cache_entry_t>&); + const AssemblyKeyBase* mKey; + public: + key_t() { }; + key_t(const AssemblyKeyBase& k) : mKey(&k) { } + }; + + mutable pthread_mutex_t mLock; + mutable int64_t mWhen; + size_t mCacheSize; + size_t mCacheInUse; + KeyedVector<key_t, cache_entry_t> mCacheData; + + friend int compare_type( + const key_value_pair_t<key_t, cache_entry_t>&, + const key_value_pair_t<key_t, cache_entry_t>&); +}; + +// KeyedVector uses compare_type(), which is more efficient, than +// just using operator < () +inline int compare_type( + const key_value_pair_t<CodeCache::key_t, CodeCache::cache_entry_t>& lhs, + const key_value_pair_t<CodeCache::key_t, CodeCache::cache_entry_t>& rhs) +{ + return lhs.key.mKey->compare_type(*(rhs.key.mKey)); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif //ANDROID_CODECACHE_H diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp new file mode 100644 index 0000000..1cd189c --- /dev/null +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -0,0 +1,1150 @@ +/* libs/pixelflinger/codeflinger/GGLAssembler.cpp +** +** 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 LOG_TAG "GGLAssembler" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "codeflinger/GGLAssembler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +GGLAssembler::GGLAssembler(ARMAssemblerInterface* target) + : ARMAssemblerProxy(target), RegisterAllocator(), mOptLevel(7) +{ +} + +GGLAssembler::~GGLAssembler() +{ +} + +void GGLAssembler::prolog() +{ + ARMAssemblerProxy::prolog(); +} + +void GGLAssembler::epilog(uint32_t touched) +{ + ARMAssemblerProxy::epilog(touched); +} + +void GGLAssembler::reset(int opt_level) +{ + ARMAssemblerProxy::reset(); + RegisterAllocator::reset(); + mOptLevel = opt_level; +} + +// --------------------------------------------------------------------------- + +int GGLAssembler::scanline(const needs_t& needs, context_t const* c) +{ + int err = 0; + int opt_level = mOptLevel; + while (opt_level >= 0) { + reset(opt_level); + err = scanline_core(needs, c); + if (err == 0) + break; + opt_level--; + } + + // XXX: in theory, pcForLabel is not valid before generate() + uint32_t* fragment_start_pc = pcForLabel("fragment_loop"); + uint32_t* fragment_end_pc = pcForLabel("epilog"); + const int per_fragment_ops = int(fragment_end_pc - fragment_start_pc); + + // build a name for our pipeline + char name[64]; + sprintf(name, + "scanline__%08X:%08X_%08X_%08X [%3d ipp]", + needs.p, needs.n, needs.t[0], needs.t[1], per_fragment_ops); + + if (err) { + LOGE("Error while generating ""%s""\n", name); + disassemble(name); + return -1; + } + + return generate(name); +} + +int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c) +{ + int64_t duration = ggl_system_time(); + + mBlendFactorCached = 0; + mBlending = 0; + mMasking = 0; + mAA = GGL_READ_NEEDS(P_AA, needs.p); + mDithering = GGL_READ_NEEDS(P_DITHER, needs.p); + mAlphaTest = GGL_READ_NEEDS(P_ALPHA_TEST, needs.p) + GGL_NEVER; + mDepthTest = GGL_READ_NEEDS(P_DEPTH_TEST, needs.p) + GGL_NEVER; + mFog = GGL_READ_NEEDS(P_FOG, needs.p) != 0; + mSmooth = GGL_READ_NEEDS(SHADE, needs.n) != 0; + mBuilderContext.needs = needs; + mBuilderContext.c = c; + mBuilderContext.Rctx = reserveReg(R0); // context always in R0 + mCbFormat = c->formats[ GGL_READ_NEEDS(CB_FORMAT, needs.n) ]; + + // ------------------------------------------------------------------------ + + decodeLogicOpNeeds(needs); + + decodeTMUNeeds(needs, c); + + mBlendSrc = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRC, needs.n)); + mBlendDst = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DST, needs.n)); + mBlendSrcA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRCA, needs.n)); + mBlendDstA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DSTA, needs.n)); + + if (!mCbFormat.c[GGLFormat::ALPHA].h) { + if ((mBlendSrc == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendSrc == GGL_DST_ALPHA)) { + mBlendSrc = GGL_ONE; + } + if ((mBlendSrcA == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendSrcA == GGL_DST_ALPHA)) { + mBlendSrcA = GGL_ONE; + } + if ((mBlendDst == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendDst == GGL_DST_ALPHA)) { + mBlendDst = GGL_ONE; + } + if ((mBlendDstA == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendDstA == GGL_DST_ALPHA)) { + mBlendDstA = GGL_ONE; + } + } + + // if we need the framebuffer, read it now + const int blending = blending_codes(mBlendSrc, mBlendDst) | + blending_codes(mBlendSrcA, mBlendDstA); + + // XXX: handle special cases, destination not modified... + if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) && + (mBlendDst==GGL_ONE) && (mBlendDstA==GGL_ONE)) { + // Destination unmodified (beware of logic ops) + } else if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) && + (mBlendDst==GGL_ZERO) && (mBlendDstA==GGL_ZERO)) { + // Destination is zero (beware of logic ops) + } + + int fbComponents = 0; + const int masking = GGL_READ_NEEDS(MASK_ARGB, needs.n); + for (int i=0 ; i<4 ; i++) { + const int mask = 1<<i; + component_info_t& info = mInfo[i]; + int fs = i==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc; + int fd = i==GGLFormat::ALPHA ? mBlendDstA : mBlendDst; + if (fs==GGL_SRC_ALPHA_SATURATE && i==GGLFormat::ALPHA) + fs = GGL_ONE; + info.masked = !!(masking & mask); + info.inDest = !info.masked && mCbFormat.c[i].h && + ((mLogicOp & LOGIC_OP_SRC) || (!mLogicOp)); + if (mCbFormat.components >= GGL_LUMINANCE && + (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) { + info.inDest = false; + } + info.needed = (i==GGLFormat::ALPHA) && + (isAlphaSourceNeeded() || mAlphaTest != GGL_ALWAYS); + info.replaced = !!(mTextureMachine.replaced & mask); + info.iterated = (!info.replaced && (info.inDest || info.needed)); + info.smooth = mSmooth && info.iterated; + info.fog = mFog && info.inDest && (i != GGLFormat::ALPHA); + info.blend = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO)); + + mBlending |= (info.blend ? mask : 0); + mMasking |= (mCbFormat.c[i].h && info.masked) ? mask : 0; + fbComponents |= mCbFormat.c[i].h ? mask : 0; + } + + mAllMasked = (mMasking == fbComponents); + if (mAllMasked) { + mDithering = 0; + } + + fragment_parts_t parts; + + // ------------------------------------------------------------------------ + prolog(); + // ------------------------------------------------------------------------ + + build_scanline_prolog(parts, needs); + + if (registerFile().status()) + return registerFile().status(); + + // ------------------------------------------------------------------------ + label("fragment_loop"); + // ------------------------------------------------------------------------ + { + Scratch regs(registerFile()); + + if (mDithering) { + // update the dither index. + MOV(AL, 0, parts.count.reg, + reg_imm(parts.count.reg, ROR, GGL_DITHER_ORDER_SHIFT)); + ADD(AL, 0, parts.count.reg, parts.count.reg, + imm( 1 << (32 - GGL_DITHER_ORDER_SHIFT))); + MOV(AL, 0, parts.count.reg, + reg_imm(parts.count.reg, ROR, 32 - GGL_DITHER_ORDER_SHIFT)); + } + + // XXX: could we do an early alpha-test here in some cases? + // It would probaly be used only with smooth-alpha and no texture + // (or no alpha component in the texture). + + // Early z-test + if (mAlphaTest==GGL_ALWAYS) { + build_depth_test(parts, Z_TEST|Z_WRITE); + } else { + // we cannot do the z-write here, because + // it might be killed by the alpha-test later + build_depth_test(parts, Z_TEST); + } + + { // texture coordinates + Scratch scratches(registerFile()); + + // texel generation + build_textures(parts, regs); + } + + if ((blending & (FACTOR_DST|BLEND_DST)) || + (mMasking && !mAllMasked) || + (mLogicOp & LOGIC_OP_DST)) + { + // blending / logic_op / masking need the framebuffer + mDstPixel.setTo(regs.obtain(), &mCbFormat); + + // load the framebuffer pixel + comment("fetch color-buffer"); + load(parts.cbPtr, mDstPixel); + } + + if (registerFile().status()) + return registerFile().status(); + + pixel_t pixel; + int directTex = mTextureMachine.directTexture; + if (directTex | parts.packed) { + // note: we can't have both here + // iterated color or direct texture + pixel = directTex ? parts.texel[directTex-1] : parts.iterated; + pixel.flags &= ~CORRUPTIBLE; + } else { + if (mDithering) { + const int ctxtReg = mBuilderContext.Rctx; + const int mask = GGL_DITHER_SIZE-1; + parts.dither = reg_t(regs.obtain()); + AND(AL, 0, parts.dither.reg, parts.count.reg, imm(mask)); + ADD(AL, 0, parts.dither.reg, parts.dither.reg, ctxtReg); + LDRB(AL, parts.dither.reg, parts.dither.reg, + immed12_pre(GGL_OFFSETOF(ditherMatrix))); + } + + // allocate a register for the resulting pixel + pixel.setTo(regs.obtain(), &mCbFormat, FIRST); + + build_component(pixel, parts, GGLFormat::ALPHA, regs); + + if (mAlphaTest!=GGL_ALWAYS) { + // only handle the z-write part here. We know z-test + // was successful, as well as alpha-test. + build_depth_test(parts, Z_WRITE); + } + + build_component(pixel, parts, GGLFormat::RED, regs); + build_component(pixel, parts, GGLFormat::GREEN, regs); + build_component(pixel, parts, GGLFormat::BLUE, regs); + + pixel.flags |= CORRUPTIBLE; + } + + if (registerFile().status()) + return registerFile().status(); + + if (pixel.reg == -1) { + // be defensive here. if we're here it's probably + // that this whole fragment is a no-op. + pixel = mDstPixel; + } + + if (!mAllMasked) { + // logic operation + build_logic_op(pixel, regs); + + // masking + build_masking(pixel, regs); + + comment("store"); + store(parts.cbPtr, pixel, WRITE_BACK); + } + } + + if (registerFile().status()) + return registerFile().status(); + + // update the iterated color... + if (parts.reload != 3) { + build_smooth_shade(parts); + } + + // update iterated z + build_iterate_z(parts); + + // update iterated fog + build_iterate_f(parts); + + SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16)); + B(PL, "fragment_loop"); + label("epilog"); + epilog(registerFile().touched()); + + if ((mAlphaTest!=GGL_ALWAYS) || (mDepthTest!=GGL_ALWAYS)) { + if (mDepthTest!=GGL_ALWAYS) { + label("discard_before_textures"); + build_iterate_texture_coordinates(parts); + } + label("discard_after_textures"); + build_smooth_shade(parts); + build_iterate_z(parts); + build_iterate_f(parts); + if (!mAllMasked) { + ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3)); + } + SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16)); + B(PL, "fragment_loop"); + epilog(registerFile().touched()); + } + + return registerFile().status(); +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_scanline_prolog( + fragment_parts_t& parts, const needs_t& needs) +{ + Scratch scratches(registerFile()); + int Rctx = mBuilderContext.Rctx; + + // compute count + comment("compute ct (# of pixels to process)"); + parts.count.setTo(obtainReg()); + int Rx = scratches.obtain(); + int Ry = scratches.obtain(); + CONTEXT_LOAD(Rx, iterators.xl); + CONTEXT_LOAD(parts.count.reg, iterators.xr); + CONTEXT_LOAD(Ry, iterators.y); + + // parts.count = iterators.xr - Rx + SUB(AL, 0, parts.count.reg, parts.count.reg, Rx); + SUB(AL, 0, parts.count.reg, parts.count.reg, imm(1)); + + if (mDithering) { + // parts.count.reg = 0xNNNNXXDD + // NNNN = count-1 + // DD = dither offset + // XX = 0xxxxxxx (x = garbage) + Scratch scratches(registerFile()); + int tx = scratches.obtain(); + int ty = scratches.obtain(); + AND(AL, 0, tx, Rx, imm(GGL_DITHER_MASK)); + AND(AL, 0, ty, Ry, imm(GGL_DITHER_MASK)); + ADD(AL, 0, tx, tx, reg_imm(ty, LSL, GGL_DITHER_ORDER_SHIFT)); + ORR(AL, 0, parts.count.reg, tx, reg_imm(parts.count.reg, LSL, 16)); + } else { + // parts.count.reg = 0xNNNN0000 + // NNNN = count-1 + MOV(AL, 0, parts.count.reg, reg_imm(parts.count.reg, LSL, 16)); + } + + if (!mAllMasked) { + // compute dst ptr + comment("compute color-buffer pointer"); + const int cb_bits = mCbFormat.size*8; + int Rs = scratches.obtain(); + parts.cbPtr.setTo(obtainReg(), cb_bits); + CONTEXT_LOAD(Rs, state.buffers.color.stride); + CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data); + SMLABB(AL, Rs, Ry, Rs, Rx); // Rs = Rx + Ry*Rs + base_offset(parts.cbPtr, parts.cbPtr, Rs); + scratches.recycle(Rs); + } + + // init fog + const int need_fog = GGL_READ_NEEDS(P_FOG, needs.p); + if (need_fog) { + comment("compute initial fog coordinate"); + Scratch scratches(registerFile()); + int dfdx = scratches.obtain(); + int ydfdy = scratches.obtain(); + int f = ydfdy; + CONTEXT_LOAD(dfdx, generated_vars.dfdx); + CONTEXT_LOAD(ydfdy, iterators.ydfdy); + MLA(AL, 0, f, Rx, dfdx, ydfdy); + CONTEXT_STORE(f, generated_vars.f); + } + + // init Z coordinate + if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) { + parts.z = reg_t(obtainReg()); + comment("compute initial Z coordinate"); + Scratch scratches(registerFile()); + int dzdx = scratches.obtain(); + int ydzdy = parts.z.reg; + CONTEXT_LOAD(dzdx, generated_vars.dzdx); // 1.31 fixed-point + CONTEXT_LOAD(ydzdy, iterators.ydzdy); // 1.31 fixed-point + MLA(AL, 0, parts.z.reg, Rx, dzdx, ydzdy); + + // we're going to index zbase of parts.count + // zbase = base + (xl-count + stride*y)*2 + int Rs = dzdx; + int zbase = scratches.obtain(); + CONTEXT_LOAD(Rs, state.buffers.depth.stride); + CONTEXT_LOAD(zbase, state.buffers.depth.data); + SMLABB(AL, Rs, Ry, Rs, Rx); + ADD(AL, 0, Rs, Rs, reg_imm(parts.count.reg, LSR, 16)); + ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1)); + CONTEXT_STORE(zbase, generated_vars.zbase); + } + + // init texture coordinates + init_textures(parts.coords, reg_t(Rx), reg_t(Ry)); + scratches.recycle(Ry); + + // iterated color + init_iterated_color(parts, reg_t(Rx)); + + // init coverage factor application (anti-aliasing) + if (mAA) { + parts.covPtr.setTo(obtainReg(), 16); + CONTEXT_LOAD(parts.covPtr.reg, state.buffers.coverage); + ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1)); + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_component( pixel_t& pixel, + const fragment_parts_t& parts, + int component, + Scratch& regs) +{ + static char const * comments[] = {"alpha", "red", "green", "blue"}; + comment(comments[component]); + + // local register file + Scratch scratches(registerFile()); + const int dst_component_size = pixel.component_size(component); + + component_t temp(-1); + build_incoming_component( temp, dst_component_size, + parts, component, scratches, regs); + + if (mInfo[component].inDest) { + + // blending... + build_blending( temp, mDstPixel, component, scratches ); + + // downshift component and rebuild pixel... + downshift(pixel, component, temp, parts.dither); + } +} + +void GGLAssembler::build_incoming_component( + component_t& temp, + int dst_size, + const fragment_parts_t& parts, + int component, + Scratch& scratches, + Scratch& global_regs) +{ + const uint32_t component_mask = 1<<component; + + // Figure out what we need for the blending stage... + int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc; + int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst; + if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA) { + fs = GGL_ONE; + } + + // Figure out what we need to extract and for what reason + const int blending = blending_codes(fs, fd); + + // Are we actually going to blend? + const int need_blending = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO)); + + // expand the source if the destination has more bits + int need_expander = false; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT-1 ; i++) { + texture_unit_t& tmu = mTextureMachine.tmu[i]; + if ((tmu.format_idx) && + (parts.texel[i].component_size(component) < dst_size)) { + need_expander = true; + } + } + + // do we need to extract this component? + const bool multiTexture = mTextureMachine.activeUnits > 1; + const int blend_needs_alpha_source = (component==GGLFormat::ALPHA) && + (isAlphaSourceNeeded()); + int need_extract = mInfo[component].needed; + if (mInfo[component].inDest) + { + need_extract |= ((need_blending ? + (blending & (BLEND_SRC|FACTOR_SRC)) : need_expander)); + need_extract |= (mTextureMachine.mask != mTextureMachine.replaced); + need_extract |= mInfo[component].smooth; + need_extract |= mInfo[component].fog; + need_extract |= mDithering; + need_extract |= multiTexture; + } + + if (need_extract) { + Scratch& regs = blend_needs_alpha_source ? global_regs : scratches; + component_t fragment; + + // iterated color + build_iterated_color(fragment, parts, component, regs); + + // texture environement (decal, modulate, replace) + build_texture_environment(fragment, parts, component, regs); + + // expand the source if the destination has more bits + if (need_expander && (fragment.size() < dst_size)) { + // we're here only if we fetched a texel + // (so we know for sure fragment is CORRUPTIBLE) + expand(fragment, fragment, dst_size); + } + + // We have a few specific things to do for the alpha-channel + if ((component==GGLFormat::ALPHA) && + (mInfo[component].needed || fragment.size()<dst_size)) + { + // convert to integer_t first and make sure + // we don't corrupt a needed register + if (fragment.l) { + component_t incoming(fragment); + modify(fragment, regs); + MOV(AL, 0, fragment.reg, reg_imm(incoming.reg, LSR, incoming.l)); + fragment.h -= fragment.l; + fragment.l = 0; + } + + // coverage factor application + build_coverage_application(fragment, parts, regs); + + // alpha-test + build_alpha_test(fragment, parts); + + if (blend_needs_alpha_source) { + // We keep only 8 bits for the blending stage + const int shift = fragment.h <= 8 ? 0 : fragment.h-8; + if (fragment.flags & CORRUPTIBLE) { + fragment.flags &= ~CORRUPTIBLE; + mAlphaSource.setTo(fragment.reg, + fragment.size(), fragment.flags); + if (shift) { + MOV(AL, 0, mAlphaSource.reg, + reg_imm(mAlphaSource.reg, LSR, shift)); + } + } else { + // XXX: it would better to do this in build_blend_factor() + // so we can avoid the extra MOV below. + mAlphaSource.setTo(regs.obtain(), + fragment.size(), CORRUPTIBLE); + if (shift) { + MOV(AL, 0, mAlphaSource.reg, + reg_imm(fragment.reg, LSR, shift)); + } else { + MOV(AL, 0, mAlphaSource.reg, fragment.reg); + } + } + mAlphaSource.s -= shift; + } + } + + // fog... + build_fog( fragment, component, regs ); + + temp = fragment; + } else { + if (mInfo[component].inDest) { + // extraction not needed and replace + // we just select the right component + if ((mTextureMachine.replaced & component_mask) == 0) { + // component wasn't replaced, so use it! + temp = component_t(parts.iterated, component); + } + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if ((tmu.mask & component_mask) && + ((tmu.replaced & component_mask) == 0)) { + temp = component_t(parts.texel[i], component); + } + } + } + } +} + +bool GGLAssembler::isAlphaSourceNeeded() const +{ + // XXX: also needed for alpha-test + const int bs = mBlendSrc; + const int bd = mBlendDst; + return bs==GGL_SRC_ALPHA_SATURATE || + bs==GGL_SRC_ALPHA || bs==GGL_ONE_MINUS_SRC_ALPHA || + bd==GGL_SRC_ALPHA || bd==GGL_ONE_MINUS_SRC_ALPHA ; +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_smooth_shade(const fragment_parts_t& parts) +{ + if (mSmooth && !parts.iterated_packed) { + // update the iterated color in a pipelined way... + comment("update iterated color"); + Scratch scratches(registerFile()); + + const int reload = parts.reload; + for (int i=0 ; i<4 ; i++) { + if (!mInfo[i].iterated) + continue; + + int c = parts.argb[i].reg; + int dx = parts.argb_dx[i].reg; + + if (reload & 1) { + c = scratches.obtain(); + CONTEXT_LOAD(c, generated_vars.argb[i].c); + } + if (reload & 2) { + dx = scratches.obtain(); + CONTEXT_LOAD(dx, generated_vars.argb[i].dx); + } + + if (mSmooth) { + ADD(AL, 0, c, c, dx); + } + + if (reload & 1) { + CONTEXT_STORE(c, generated_vars.argb[i].c); + scratches.recycle(c); + } + if (reload & 2) { + scratches.recycle(dx); + } + } + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_coverage_application(component_t& fragment, + const fragment_parts_t& parts, Scratch& regs) +{ + // here fragment.l is guarenteed to be 0 + if (mAA) { + // coverages are 1.15 fixed-point numbers + comment("coverage application"); + + component_t incoming(fragment); + modify(fragment, regs); + + Scratch scratches(registerFile()); + int cf = scratches.obtain(); + LDRH(AL, cf, parts.covPtr.reg, immed8_post(2)); + if (fragment.h > 31) { + fragment.h--; + SMULWB(AL, fragment.reg, incoming.reg, cf); + } else { + MOV(AL, 0, fragment.reg, reg_imm(incoming.reg, LSL, 1)); + SMULWB(AL, fragment.reg, fragment.reg, cf); + } + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_alpha_test(component_t& fragment, + const fragment_parts_t& parts) +{ + if (mAlphaTest != GGL_ALWAYS) { + comment("Alpha Test"); + Scratch scratches(registerFile()); + int ref = scratches.obtain(); + const int shift = GGL_COLOR_BITS-fragment.size(); + CONTEXT_LOAD(ref, state.alpha_test.ref); + if (shift) CMP(AL, fragment.reg, reg_imm(ref, LSR, shift)); + else CMP(AL, fragment.reg, ref); + int cc = NV; + switch (mAlphaTest) { + case GGL_NEVER: cc = NV; break; + case GGL_LESS: cc = LT; break; + case GGL_EQUAL: cc = EQ; break; + case GGL_LEQUAL: cc = LS; break; + case GGL_GREATER: cc = HI; break; + case GGL_NOTEQUAL: cc = NE; break; + case GGL_GEQUAL: cc = HS; break; + } + B(cc^1, "discard_after_textures"); + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_depth_test( + const fragment_parts_t& parts, uint32_t mask) +{ + mask &= Z_TEST|Z_WRITE; + const needs_t& needs = mBuilderContext.needs; + const int zmask = GGL_READ_NEEDS(P_MASK_Z, needs.p); + Scratch scratches(registerFile()); + + if (mDepthTest != GGL_ALWAYS || zmask) { + int cc=AL, ic=AL; + switch (mDepthTest) { + case GGL_LESS: ic = HI; break; + case GGL_EQUAL: ic = EQ; break; + case GGL_LEQUAL: ic = HS; break; + case GGL_GREATER: ic = LT; break; + case GGL_NOTEQUAL: ic = NE; break; + case GGL_GEQUAL: ic = LS; break; + case GGL_NEVER: + // this never happens, because it's taken care of when + // computing the needs. but we keep it for completness. + comment("Depth Test (NEVER)"); + B(AL, "discard_before_textures"); + return; + case GGL_ALWAYS: + // we're here because zmask is enabled + mask &= ~Z_TEST; // test always passes. + break; + } + + // inverse the condition + cc = ic^1; + + if ((mask & Z_WRITE) && !zmask) { + mask &= ~Z_WRITE; + } + + if (!mask) + return; + + comment("Depth Test"); + + int zbase = scratches.obtain(); + int depth = scratches.obtain(); + int z = parts.z.reg; + + CONTEXT_LOAD(zbase, generated_vars.zbase); // stall + SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15)); + // above does zbase = zbase + ((count >> 16) << 1) + + if (mask & Z_TEST) { + LDRH(AL, depth, zbase); // stall + CMP(AL, depth, reg_imm(z, LSR, 16)); + B(cc, "discard_before_textures"); + } + if (mask & Z_WRITE) { + if (mask == Z_WRITE) { + // only z-write asked, cc is meaningless + ic = AL; + } + MOV(AL, 0, depth, reg_imm(z, LSR, 16)); + STRH(ic, depth, zbase); + } + } +} + +void GGLAssembler::build_iterate_z(const fragment_parts_t& parts) +{ + const needs_t& needs = mBuilderContext.needs; + if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) { + Scratch scratches(registerFile()); + int dzdx = scratches.obtain(); + CONTEXT_LOAD(dzdx, generated_vars.dzdx); // stall + ADD(AL, 0, parts.z.reg, parts.z.reg, dzdx); + } +} + +void GGLAssembler::build_iterate_f(const fragment_parts_t& parts) +{ + const needs_t& needs = mBuilderContext.needs; + if (GGL_READ_NEEDS(P_FOG, needs.p)) { + Scratch scratches(registerFile()); + int dfdx = scratches.obtain(); + int f = scratches.obtain(); + CONTEXT_LOAD(f, generated_vars.f); + CONTEXT_LOAD(dfdx, generated_vars.dfdx); // stall + ADD(AL, 0, f, f, dfdx); + CONTEXT_STORE(f, generated_vars.f); + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_logic_op(pixel_t& pixel, Scratch& regs) +{ + const needs_t& needs = mBuilderContext.needs; + const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR; + if (opcode == GGL_COPY) + return; + + comment("logic operation"); + + pixel_t s(pixel); + if (!(pixel.flags & CORRUPTIBLE)) { + pixel.reg = regs.obtain(); + pixel.flags |= CORRUPTIBLE; + } + + pixel_t d(mDstPixel); + switch(opcode) { + case GGL_CLEAR: MOV(AL, 0, pixel.reg, imm(0)); break; + case GGL_AND: AND(AL, 0, pixel.reg, s.reg, d.reg); break; + case GGL_AND_REVERSE: BIC(AL, 0, pixel.reg, s.reg, d.reg); break; + case GGL_COPY: break; + case GGL_AND_INVERTED: BIC(AL, 0, pixel.reg, d.reg, s.reg); break; + case GGL_NOOP: MOV(AL, 0, pixel.reg, d.reg); break; + case GGL_XOR: EOR(AL, 0, pixel.reg, s.reg, d.reg); break; + case GGL_OR: ORR(AL, 0, pixel.reg, s.reg, d.reg); break; + case GGL_NOR: ORR(AL, 0, pixel.reg, s.reg, d.reg); + MVN(AL, 0, pixel.reg, pixel.reg); break; + case GGL_EQUIV: EOR(AL, 0, pixel.reg, s.reg, d.reg); + MVN(AL, 0, pixel.reg, pixel.reg); break; + case GGL_INVERT: MVN(AL, 0, pixel.reg, d.reg); break; + case GGL_OR_REVERSE: // s | ~d == ~(~s & d) + BIC(AL, 0, pixel.reg, d.reg, s.reg); + MVN(AL, 0, pixel.reg, pixel.reg); break; + case GGL_COPY_INVERTED: MVN(AL, 0, pixel.reg, s.reg); break; + case GGL_OR_INVERTED: // ~s | d == ~(s & ~d) + BIC(AL, 0, pixel.reg, s.reg, d.reg); + MVN(AL, 0, pixel.reg, pixel.reg); break; + case GGL_NAND: AND(AL, 0, pixel.reg, s.reg, d.reg); + MVN(AL, 0, pixel.reg, pixel.reg); break; + case GGL_SET: MVN(AL, 0, pixel.reg, imm(0)); break; + }; +} + +// --------------------------------------------------------------------------- + +static uint32_t find_bottom(uint32_t val) +{ + uint32_t i = 0; + while (!(val & (3<<i))) + i+= 2; + return i; +} + +static void normalize(uint32_t& val, uint32_t& rot) +{ + rot = 0; + while (!(val&3) || (val & 0xFC000000)) { + uint32_t newval; + newval = val >> 2; + newval |= (val&3) << 30; + val = newval; + rot += 2; + if (rot == 32) { + rot = 0; + break; + } + } +} + +void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits) +{ + uint32_t rot; + uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1; + mask &= size; + + if (mask == size) { + if (d != s) + MOV( AL, 0, d, s); + return; + } + + int negative_logic = !isValidImmediate(mask); + if (negative_logic) { + mask = ~mask & size; + } + normalize(mask, rot); + + if (mask) { + while (mask) { + uint32_t bitpos = find_bottom(mask); + int shift = rot + bitpos; + uint32_t m = mask & (0xff << bitpos); + mask &= ~m; + m >>= bitpos; + int32_t newMask = (m<<shift) | (m>>(32-shift)); + if (!negative_logic) { + AND( AL, 0, d, s, imm(newMask) ); + } else { + BIC( AL, 0, d, s, imm(newMask) ); + } + s = d; + } + } else { + MOV( AL, 0, d, imm(0)); + } +} + +void GGLAssembler::build_masking(pixel_t& pixel, Scratch& regs) +{ + if (!mMasking || mAllMasked) { + return; + } + + comment("color mask"); + + pixel_t fb(mDstPixel); + pixel_t s(pixel); + if (!(pixel.flags & CORRUPTIBLE)) { + pixel.reg = regs.obtain(); + pixel.flags |= CORRUPTIBLE; + } + + int mask = 0; + for (int i=0 ; i<4 ; i++) { + const int component_mask = 1<<i; + const int h = fb.format.c[i].h; + const int l = fb.format.c[i].l; + if (h && (!(mMasking & component_mask))) { + mask |= ((1<<(h-l))-1) << l; + } + } + + // There is no need to clear the masked components of the source + // (unless we applied a logic op), because they're already zeroed + // by construction (masked components are not computed) + + if (mLogicOp) { + const needs_t& needs = mBuilderContext.needs; + const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR; + if (opcode != GGL_CLEAR) { + // clear masked component of source + build_and_immediate(pixel.reg, s.reg, mask, fb.size()); + s = pixel; + } + } + + // clear non masked components of destination + build_and_immediate(fb.reg, fb.reg, ~mask, fb.size()); + + // or back the channels that were masked + if (s.reg == fb.reg) { + // this is in fact a MOV + if (s.reg == pixel.reg) { + // ugh. this in in fact a nop + } else { + MOV(AL, 0, pixel.reg, fb.reg); + } + } else { + ORR(AL, 0, pixel.reg, s.reg, fb.reg); + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::base_offset( + const pointer_t& d, const pointer_t& b, const reg_t& o) +{ + switch (b.size) { + case 32: + ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2)); + break; + case 24: + if (d.reg == b.reg) { + ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1)); + ADD(AL, 0, d.reg, d.reg, o.reg); + } else { + ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1)); + ADD(AL, 0, d.reg, d.reg, b.reg); + } + break; + case 16: + ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1)); + break; + case 8: + ADD(AL, 0, d.reg, b.reg, o.reg); + break; + } +} + +// ---------------------------------------------------------------------------- +// cheezy register allocator... +// ---------------------------------------------------------------------------- + +void RegisterAllocator::reset() +{ + mRegs.reset(); +} + +int RegisterAllocator::reserveReg(int reg) +{ + return mRegs.reserve(reg); +} + +int RegisterAllocator::obtainReg() +{ + return mRegs.obtain(); +} + +void RegisterAllocator::recycleReg(int reg) +{ + mRegs.recycle(reg); +} + +RegisterAllocator::RegisterFile& RegisterAllocator::registerFile() +{ + return mRegs; +} + +// ---------------------------------------------------------------------------- + +RegisterAllocator::RegisterFile::RegisterFile() + : mRegs(0), mTouched(0), mStatus(0) +{ + reserve(ARMAssemblerInterface::SP); + reserve(ARMAssemblerInterface::PC); +} + +RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs) + : mRegs(rhs.mRegs), mTouched(rhs.mTouched) +{ +} + +RegisterAllocator::RegisterFile::~RegisterFile() +{ +} + +bool RegisterAllocator::RegisterFile::operator == (const RegisterFile& rhs) const +{ + return (mRegs == rhs.mRegs); +} + +void RegisterAllocator::RegisterFile::reset() +{ + mRegs = mTouched = mStatus = 0; + reserve(ARMAssemblerInterface::SP); + reserve(ARMAssemblerInterface::PC); +} + +int RegisterAllocator::RegisterFile::reserve(int reg) +{ + LOG_ALWAYS_FATAL_IF(isUsed(reg), + "reserving register %d, but already in use", + reg); + mRegs |= (1<<reg); + mTouched |= mRegs; + return reg; +} + +void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask) +{ + mRegs |= regMask; + mTouched |= regMask; +} + +int RegisterAllocator::RegisterFile::isUsed(int reg) const +{ + LOG_ALWAYS_FATAL_IF(reg>=16, "invalid register %d", reg); + return mRegs & (1<<reg); +} + +int RegisterAllocator::RegisterFile::obtain() +{ + const char priorityList[14] = { 0, 1, 2, 3, + 12, 14, 4, 5, + 6, 7, 8, 9, + 10, 11 }; + const int nbreg = sizeof(priorityList); + int i, r; + for (i=0 ; i<nbreg ; i++) { + r = priorityList[i]; + if (!isUsed(r)) { + break; + } + } + // this is not an error anymore because, we'll try again with + // a lower optimization level. + //LOGE_IF(i >= nbreg, "pixelflinger ran out of registers\n"); + if (i >= nbreg) { + mStatus |= OUT_OF_REGISTERS; + // we return SP so we can more easily debug things + // the code will never be run anyway. + return ARMAssemblerInterface::SP; + } + reserve(r); + return r; +} + +bool RegisterAllocator::RegisterFile::hasFreeRegs() const +{ + return ((mRegs & 0xFFFF) == 0xFFFF) ? false : true; +} + +int RegisterAllocator::RegisterFile::countFreeRegs() const +{ + int f = ~mRegs & 0xFFFF; + // now count number of 1 + f = (f & 0x5555) + ((f>>1) & 0x5555); + f = (f & 0x3333) + ((f>>2) & 0x3333); + f = (f & 0x0F0F) + ((f>>4) & 0x0F0F); + f = (f & 0x00FF) + ((f>>8) & 0x00FF); + return f; +} + +void RegisterAllocator::RegisterFile::recycle(int reg) +{ + LOG_FATAL_IF(!isUsed(reg), + "recycling unallocated register %d", + reg); + mRegs &= ~(1<<reg); +} + +void RegisterAllocator::RegisterFile::recycleSeveral(uint32_t regMask) +{ + LOG_FATAL_IF((mRegs & regMask)!=regMask, + "recycling unallocated registers " + "(recycle=%08x, allocated=%08x, unallocated=%08x)", + regMask, mRegs, mRegs®Mask); + mRegs &= ~regMask; +} + +uint32_t RegisterAllocator::RegisterFile::touched() const +{ + return mTouched; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h new file mode 100644 index 0000000..d1d29f0 --- /dev/null +++ b/libpixelflinger/codeflinger/GGLAssembler.h @@ -0,0 +1,554 @@ +/* libs/pixelflinger/codeflinger/GGLAssembler.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_GGLASSEMBLER_H +#define ANDROID_GGLASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/ARMAssemblerProxy.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +#define CONTEXT_LOAD(REG, FIELD) \ + LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD))) + +#define CONTEXT_STORE(REG, FIELD) \ + STR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD))) + + +class RegisterAllocator +{ +public: + class RegisterFile; + + RegisterFile& registerFile(); + int reserveReg(int reg); + int obtainReg(); + void recycleReg(int reg); + void reset(); + + class RegisterFile + { + public: + RegisterFile(); + RegisterFile(const RegisterFile& rhs); + ~RegisterFile(); + + void reset(); + + bool operator == (const RegisterFile& rhs) const; + bool operator != (const RegisterFile& rhs) const { + return !operator == (rhs); + } + + int reserve(int reg); + void reserveSeveral(uint32_t regMask); + + void recycle(int reg); + void recycleSeveral(uint32_t regMask); + + int obtain(); + inline int isUsed(int reg) const; + + bool hasFreeRegs() const; + int countFreeRegs() const; + + uint32_t touched() const; + inline uint32_t status() const { return mStatus; } + + enum { + OUT_OF_REGISTERS = 0x1 + }; + + private: + uint32_t mRegs; + uint32_t mTouched; + uint32_t mStatus; + }; + + class Scratch + { + public: + Scratch(RegisterFile& regFile) + : mRegFile(regFile), mScratch(0) { + } + ~Scratch() { + mRegFile.recycleSeveral(mScratch); + } + int obtain() { + int reg = mRegFile.obtain(); + mScratch |= 1<<reg; + return reg; + } + void recycle(int reg) { + mRegFile.recycle(reg); + mScratch &= ~(1<<reg); + } + bool isUsed(int reg) { + return (mScratch & (1<<reg)); + } + int countFreeRegs() { + return mRegFile.countFreeRegs(); + } + private: + RegisterFile& mRegFile; + uint32_t mScratch; + }; + + class Spill + { + public: + Spill(RegisterFile& regFile, ARMAssemblerInterface& gen, uint32_t reglist) + : mRegFile(regFile), mGen(gen), mRegList(reglist), mCount(0) + { + if (reglist) { + int count = 0; + while (reglist) { + count++; + reglist &= ~(1 << (31 - __builtin_clz(reglist))); + } + if (count == 1) { + int reg = 31 - __builtin_clz(mRegList); + mGen.STR(mGen.AL, reg, mGen.SP, mGen.immed12_pre(-4, 1)); + } else { + mGen.STM(mGen.AL, mGen.DB, mGen.SP, 1, mRegList); + } + mRegFile.recycleSeveral(mRegList); + mCount = count; + } + } + ~Spill() { + if (mRegList) { + if (mCount == 1) { + int reg = 31 - __builtin_clz(mRegList); + mGen.LDR(mGen.AL, reg, mGen.SP, mGen.immed12_post(4)); + } else { + mGen.LDM(mGen.AL, mGen.IA, mGen.SP, 1, mRegList); + } + mRegFile.reserveSeveral(mRegList); + } + } + private: + RegisterFile& mRegFile; + ARMAssemblerInterface& mGen; + uint32_t mRegList; + int mCount; + }; + +private: + RegisterFile mRegs; +}; + +// ---------------------------------------------------------------------------- + +class GGLAssembler : public ARMAssemblerProxy, public RegisterAllocator +{ +public: + + GGLAssembler(ARMAssemblerInterface* target); + virtual ~GGLAssembler(); + + uint32_t* base() const { return 0; } // XXX + uint32_t* pc() const { return 0; } // XXX + + void reset(int opt_level); + + virtual void prolog(); + virtual void epilog(uint32_t touched); + + // generate scanline code for given needs + int scanline(const needs_t& needs, context_t const* c); + int scanline_core(const needs_t& needs, context_t const* c); + + enum { + CLEAR_LO = 0x0001, + CLEAR_HI = 0x0002, + CORRUPTIBLE = 0x0004, + FIRST = 0x0008 + }; + + enum { //load/store flags + WRITE_BACK = 0x0001 + }; + + struct reg_t { + reg_t() : reg(-1), flags(0) { + } + reg_t(int r, int f=0) + : reg(r), flags(f) { + } + void setTo(int r, int f=0) { + reg=r; flags=f; + } + int reg; + uint16_t flags; + }; + + struct integer_t : public reg_t { + integer_t() : reg_t(), s(0) { + } + integer_t(int r, int sz=32, int f=0) + : reg_t(r, f), s(sz) { + } + void setTo(int r, int sz=32, int f=0) { + reg_t::setTo(r, f); s=sz; + } + int8_t s; + inline int size() const { return s; } + }; + + struct pixel_t : public reg_t { + pixel_t() : reg_t() { + memset(&format, 0, sizeof(GGLFormat)); + } + pixel_t(int r, const GGLFormat* fmt, int f=0) + : reg_t(r, f), format(*fmt) { + } + void setTo(int r, const GGLFormat* fmt, int f=0) { + reg_t::setTo(r, f); format = *fmt; + } + GGLFormat format; + inline int hi(int c) const { return format.c[c].h; } + inline int low(int c) const { return format.c[c].l; } + inline int mask(int c) const { return ((1<<size(c))-1) << low(c); } + inline int size() const { return format.size*8; } + inline int size(int c) const { return component_size(c); } + inline int component_size(int c) const { return hi(c) - low(c); } + }; + + struct component_t : public reg_t { + component_t() : reg_t(), h(0), l(0) { + } + component_t(int r, int f=0) + : reg_t(r, f), h(0), l(0) { + } + component_t(int r, int lo, int hi, int f=0) + : reg_t(r, f), h(hi), l(lo) { + } + explicit component_t(const integer_t& rhs) + : reg_t(rhs.reg, rhs.flags), h(rhs.s), l(0) { + } + explicit component_t(const pixel_t& rhs, int component) { + setTo( rhs.reg, + rhs.format.c[component].l, + rhs.format.c[component].h, + rhs.flags|CLEAR_LO|CLEAR_HI); + } + void setTo(int r, int lo=0, int hi=0, int f=0) { + reg_t::setTo(r, f); h=hi; l=lo; + } + int8_t h; + int8_t l; + inline int size() const { return h-l; } + }; + + struct pointer_t : public reg_t { + pointer_t() : reg_t(), size(0) { + } + pointer_t(int r, int s, int f=0) + : reg_t(r, f), size(s) { + } + void setTo(int r, int s, int f=0) { + reg_t::setTo(r, f); size=s; + } + int8_t size; + }; + + +private: + struct tex_coord_t { + reg_t s; + reg_t t; + pointer_t ptr; + }; + + struct fragment_parts_t { + uint32_t packed : 1; + uint32_t reload : 2; + uint32_t iterated_packed : 1; + pixel_t iterated; + pointer_t cbPtr; + pointer_t covPtr; + reg_t count; + reg_t argb[4]; + reg_t argb_dx[4]; + reg_t z; + reg_t dither; + pixel_t texel[GGL_TEXTURE_UNIT_COUNT]; + tex_coord_t coords[GGL_TEXTURE_UNIT_COUNT]; + }; + + struct texture_unit_t { + int format_idx; + GGLFormat format; + int bits; + int swrap; + int twrap; + int env; + int pot; + int linear; + uint8_t mask; + uint8_t replaced; + }; + + struct texture_machine_t { + texture_unit_t tmu[GGL_TEXTURE_UNIT_COUNT]; + uint8_t mask; + uint8_t replaced; + uint8_t directTexture; + uint8_t activeUnits; + }; + + struct component_info_t { + bool masked : 1; + bool inDest : 1; + bool needed : 1; + bool replaced : 1; + bool iterated : 1; + bool smooth : 1; + bool blend : 1; + bool fog : 1; + }; + + struct builder_context_t { + context_t const* c; + needs_t needs; + int Rctx; + }; + + template <typename T> + void modify(T& r, Scratch& regs) + { + if (!(r.flags & CORRUPTIBLE)) { + r.reg = regs.obtain(); + r.flags |= CORRUPTIBLE; + } + } + + // helpers + void base_offset(const pointer_t& d, const pointer_t& b, const reg_t& o); + + // texture environement + void modulate( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + + void decal( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + + void blend( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component, int tmu); + + void add( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + + // load/store stuff + void store(const pointer_t& addr, const pixel_t& src, uint32_t flags=0); + void load(const pointer_t& addr, const pixel_t& dest, uint32_t flags=0); + void extract(integer_t& d, const pixel_t& s, int component); + void extract(component_t& d, const pixel_t& s, int component); + void extract(integer_t& d, int s, int h, int l, int bits=32); + void expand(integer_t& d, const integer_t& s, int dbits); + void expand(integer_t& d, const component_t& s, int dbits); + void expand(component_t& d, const component_t& s, int dbits); + void downshift(pixel_t& d, int component, component_t s, const reg_t& dither); + + + void mul_factor( component_t& d, + const integer_t& v, + const integer_t& f); + + void mul_factor_add( component_t& d, + const integer_t& v, + const integer_t& f, + const component_t& a); + + void component_add( component_t& d, + const integer_t& dst, + const integer_t& src); + + void component_sat( const component_t& v); + + + void build_scanline_prolog( fragment_parts_t& parts, + const needs_t& needs); + + void build_smooth_shade(const fragment_parts_t& parts); + + void build_component( pixel_t& pixel, + const fragment_parts_t& parts, + int component, + Scratch& global_scratches); + + void build_incoming_component( + component_t& temp, + int dst_size, + const fragment_parts_t& parts, + int component, + Scratch& scratches, + Scratch& global_scratches); + + void init_iterated_color(fragment_parts_t& parts, const reg_t& x); + + void build_iterated_color( component_t& fragment, + const fragment_parts_t& parts, + int component, + Scratch& regs); + + void decodeLogicOpNeeds(const needs_t& needs); + + void decodeTMUNeeds(const needs_t& needs, context_t const* c); + + void init_textures( tex_coord_t* coords, + const reg_t& x, + const reg_t& y); + + void build_textures( fragment_parts_t& parts, + Scratch& regs); + + void filter8( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS); + + void filter16( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS); + + void filter24( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS); + + void filter32( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS); + + void build_texture_environment( component_t& fragment, + const fragment_parts_t& parts, + int component, + Scratch& regs); + + void wrapping( int d, + int coord, int size, + int tx_wrap, int tx_linear); + + void build_fog( component_t& temp, + int component, + Scratch& parent_scratches); + + void build_blending( component_t& in_out, + const pixel_t& pixel, + int component, + Scratch& parent_scratches); + + void build_blend_factor( + integer_t& factor, int f, int component, + const pixel_t& dst_pixel, + integer_t& fragment, + integer_t& fb, + Scratch& scratches); + + void build_blendFOneMinusF( component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb); + + void build_blendOneMinusFF( component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb); + + void build_coverage_application(component_t& fragment, + const fragment_parts_t& parts, + Scratch& regs); + + void build_alpha_test(component_t& fragment, const fragment_parts_t& parts); + + enum { Z_TEST=1, Z_WRITE=2 }; + void build_depth_test(const fragment_parts_t& parts, uint32_t mask); + void build_iterate_z(const fragment_parts_t& parts); + void build_iterate_f(const fragment_parts_t& parts); + void build_iterate_texture_coordinates(const fragment_parts_t& parts); + + void build_logic_op(pixel_t& pixel, Scratch& regs); + + void build_masking(pixel_t& pixel, Scratch& regs); + + void build_and_immediate(int d, int s, uint32_t mask, int bits); + + bool isAlphaSourceNeeded() const; + + enum { + FACTOR_SRC=1, FACTOR_DST=2, BLEND_SRC=4, BLEND_DST=8 + }; + + enum { + LOGIC_OP=1, LOGIC_OP_SRC=2, LOGIC_OP_DST=4 + }; + + static int blending_codes(int fs, int fd); + + builder_context_t mBuilderContext; + texture_machine_t mTextureMachine; + component_info_t mInfo[4]; + int mBlending; + int mMasking; + int mAllMasked; + int mLogicOp; + int mAlphaTest; + int mAA; + int mDithering; + int mDepthTest; + + int mSmooth; + int mFog; + pixel_t mDstPixel; + + GGLFormat mCbFormat; + + int mBlendFactorCached; + integer_t mAlphaSource; + + int mBaseRegister; + + int mBlendSrc; + int mBlendDst; + int mBlendSrcA; + int mBlendDstA; + + int mOptLevel; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_GGLASSEMBLER_H diff --git a/libpixelflinger/codeflinger/armreg.h b/libpixelflinger/codeflinger/armreg.h new file mode 100644 index 0000000..fde81ba --- /dev/null +++ b/libpixelflinger/codeflinger/armreg.h @@ -0,0 +1,300 @@ +/* $NetBSD: armreg.h,v 1.28 2003/10/31 16:30:15 scw Exp $ */ + +/*- + * Copyright (c) 1998, 2001 Ben Harris + * Copyright (c) 1994-1996 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/armreg.h,v 1.3 2005/11/21 19:06:25 cognet Exp $ + */ + +#ifndef MACHINE_ARMREG_H +#define MACHINE_ARMREG_H +#define INSN_SIZE 4 +#define INSN_COND_MASK 0xf0000000 /* Condition mask */ +#define PSR_MODE 0x0000001f /* mode mask */ +#define PSR_USR26_MODE 0x00000000 +#define PSR_FIQ26_MODE 0x00000001 +#define PSR_IRQ26_MODE 0x00000002 +#define PSR_SVC26_MODE 0x00000003 +#define PSR_USR32_MODE 0x00000010 +#define PSR_FIQ32_MODE 0x00000011 +#define PSR_IRQ32_MODE 0x00000012 +#define PSR_SVC32_MODE 0x00000013 +#define PSR_ABT32_MODE 0x00000017 +#define PSR_UND32_MODE 0x0000001b +#define PSR_SYS32_MODE 0x0000001f +#define PSR_32_MODE 0x00000010 +#define PSR_FLAGS 0xf0000000 /* flags */ + +#define PSR_C_bit (1 << 29) /* carry */ + +/* The high-order byte is always the implementor */ +#define CPU_ID_IMPLEMENTOR_MASK 0xff000000 +#define CPU_ID_ARM_LTD 0x41000000 /* 'A' */ +#define CPU_ID_DEC 0x44000000 /* 'D' */ +#define CPU_ID_INTEL 0x69000000 /* 'i' */ +#define CPU_ID_TI 0x54000000 /* 'T' */ + +/* How to decide what format the CPUID is in. */ +#define CPU_ID_ISOLD(x) (((x) & 0x0000f000) == 0x00000000) +#define CPU_ID_IS7(x) (((x) & 0x0000f000) == 0x00007000) +#define CPU_ID_ISNEW(x) (!CPU_ID_ISOLD(x) && !CPU_ID_IS7(x)) + +/* On ARM3 and ARM6, this byte holds the foundry ID. */ +#define CPU_ID_FOUNDRY_MASK 0x00ff0000 +#define CPU_ID_FOUNDRY_VLSI 0x00560000 + +/* On ARM7 it holds the architecture and variant (sub-model) */ +#define CPU_ID_7ARCH_MASK 0x00800000 +#define CPU_ID_7ARCH_V3 0x00000000 +#define CPU_ID_7ARCH_V4T 0x00800000 +#define CPU_ID_7VARIANT_MASK 0x007f0000 + +/* On more recent ARMs, it does the same, but in a different format */ +#define CPU_ID_ARCH_MASK 0x000f0000 +#define CPU_ID_ARCH_V3 0x00000000 +#define CPU_ID_ARCH_V4 0x00010000 +#define CPU_ID_ARCH_V4T 0x00020000 +#define CPU_ID_ARCH_V5 0x00030000 +#define CPU_ID_ARCH_V5T 0x00040000 +#define CPU_ID_ARCH_V5TE 0x00050000 +#define CPU_ID_VARIANT_MASK 0x00f00000 + +/* Next three nybbles are part number */ +#define CPU_ID_PARTNO_MASK 0x0000fff0 + +/* Intel XScale has sub fields in part number */ +#define CPU_ID_XSCALE_COREGEN_MASK 0x0000e000 /* core generation */ +#define CPU_ID_XSCALE_COREREV_MASK 0x00001c00 /* core revision */ +#define CPU_ID_XSCALE_PRODUCT_MASK 0x000003f0 /* product number */ + +/* And finally, the revision number. */ +#define CPU_ID_REVISION_MASK 0x0000000f + +/* Individual CPUs are probably best IDed by everything but the revision. */ +#define CPU_ID_CPU_MASK 0xfffffff0 + +/* Fake CPU IDs for ARMs without CP15 */ +#define CPU_ID_ARM2 0x41560200 +#define CPU_ID_ARM250 0x41560250 + +/* Pre-ARM7 CPUs -- [15:12] == 0 */ +#define CPU_ID_ARM3 0x41560300 +#define CPU_ID_ARM600 0x41560600 +#define CPU_ID_ARM610 0x41560610 +#define CPU_ID_ARM620 0x41560620 + +/* ARM7 CPUs -- [15:12] == 7 */ +#define CPU_ID_ARM700 0x41007000 /* XXX This is a guess. */ +#define CPU_ID_ARM710 0x41007100 +#define CPU_ID_ARM7500 0x41027100 /* XXX This is a guess. */ +#define CPU_ID_ARM710A 0x41047100 /* inc ARM7100 */ +#define CPU_ID_ARM7500FE 0x41077100 +#define CPU_ID_ARM710T 0x41807100 +#define CPU_ID_ARM720T 0x41807200 +#define CPU_ID_ARM740T8K 0x41807400 /* XXX no MMU, 8KB cache */ +#define CPU_ID_ARM740T4K 0x41817400 /* XXX no MMU, 4KB cache */ + +/* Post-ARM7 CPUs */ +#define CPU_ID_ARM810 0x41018100 +#define CPU_ID_ARM920T 0x41129200 +#define CPU_ID_ARM920T_ALT 0x41009200 +#define CPU_ID_ARM922T 0x41029220 +#define CPU_ID_ARM940T 0x41029400 /* XXX no MMU */ +#define CPU_ID_ARM946ES 0x41049460 /* XXX no MMU */ +#define CPU_ID_ARM966ES 0x41049660 /* XXX no MMU */ +#define CPU_ID_ARM966ESR1 0x41059660 /* XXX no MMU */ +#define CPU_ID_ARM1020E 0x4115a200 /* (AKA arm10 rev 1) */ +#define CPU_ID_ARM1022ES 0x4105a220 +#define CPU_ID_SA110 0x4401a100 +#define CPU_ID_SA1100 0x4401a110 +#define CPU_ID_TI925T 0x54029250 +#define CPU_ID_SA1110 0x6901b110 +#define CPU_ID_IXP1200 0x6901c120 +#define CPU_ID_80200 0x69052000 +#define CPU_ID_PXA250 0x69052100 /* sans core revision */ +#define CPU_ID_PXA210 0x69052120 +#define CPU_ID_PXA250A 0x69052100 /* 1st version Core */ +#define CPU_ID_PXA210A 0x69052120 /* 1st version Core */ +#define CPU_ID_PXA250B 0x69052900 /* 3rd version Core */ +#define CPU_ID_PXA210B 0x69052920 /* 3rd version Core */ +#define CPU_ID_PXA250C 0x69052d00 /* 4th version Core */ +#define CPU_ID_PXA210C 0x69052d20 /* 4th version Core */ +#define CPU_ID_80321_400 0x69052420 +#define CPU_ID_80321_600 0x69052430 +#define CPU_ID_80321_400_B0 0x69052c20 +#define CPU_ID_80321_600_B0 0x69052c30 +#define CPU_ID_IXP425_533 0x690541c0 +#define CPU_ID_IXP425_400 0x690541d0 +#define CPU_ID_IXP425_266 0x690541f0 + +/* ARM3-specific coprocessor 15 registers */ +#define ARM3_CP15_FLUSH 1 +#define ARM3_CP15_CONTROL 2 +#define ARM3_CP15_CACHEABLE 3 +#define ARM3_CP15_UPDATEABLE 4 +#define ARM3_CP15_DISRUPTIVE 5 + +/* ARM3 Control register bits */ +#define ARM3_CTL_CACHE_ON 0x00000001 +#define ARM3_CTL_SHARED 0x00000002 +#define ARM3_CTL_MONITOR 0x00000004 + +/* + * Post-ARM3 CP15 registers: + * + * 1 Control register + * + * 2 Translation Table Base + * + * 3 Domain Access Control + * + * 4 Reserved + * + * 5 Fault Status + * + * 6 Fault Address + * + * 7 Cache/write-buffer Control + * + * 8 TLB Control + * + * 9 Cache Lockdown + * + * 10 TLB Lockdown + * + * 11 Reserved + * + * 12 Reserved + * + * 13 Process ID (for FCSE) + * + * 14 Reserved + * + * 15 Implementation Dependent + */ + +/* Some of the definitions below need cleaning up for V3/V4 architectures */ + +/* CPU control register (CP15 register 1) */ +#define CPU_CONTROL_MMU_ENABLE 0x00000001 /* M: MMU/Protection unit enable */ +#define CPU_CONTROL_AFLT_ENABLE 0x00000002 /* A: Alignment fault enable */ +#define CPU_CONTROL_DC_ENABLE 0x00000004 /* C: IDC/DC enable */ +#define CPU_CONTROL_WBUF_ENABLE 0x00000008 /* W: Write buffer enable */ +#define CPU_CONTROL_32BP_ENABLE 0x00000010 /* P: 32-bit exception handlers */ +#define CPU_CONTROL_32BD_ENABLE 0x00000020 /* D: 32-bit addressing */ +#define CPU_CONTROL_LABT_ENABLE 0x00000040 /* L: Late abort enable */ +#define CPU_CONTROL_BEND_ENABLE 0x00000080 /* B: Big-endian mode */ +#define CPU_CONTROL_SYST_ENABLE 0x00000100 /* S: System protection bit */ +#define CPU_CONTROL_ROM_ENABLE 0x00000200 /* R: ROM protection bit */ +#define CPU_CONTROL_CPCLK 0x00000400 /* F: Implementation defined */ +#define CPU_CONTROL_BPRD_ENABLE 0x00000800 /* Z: Branch prediction enable */ +#define CPU_CONTROL_IC_ENABLE 0x00001000 /* I: IC enable */ +#define CPU_CONTROL_VECRELOC 0x00002000 /* V: Vector relocation */ +#define CPU_CONTROL_ROUNDROBIN 0x00004000 /* RR: Predictable replacement */ +#define CPU_CONTROL_V4COMPAT 0x00008000 /* L4: ARMv4 compat LDR R15 etc */ + +#define CPU_CONTROL_IDC_ENABLE CPU_CONTROL_DC_ENABLE + +/* XScale Auxillary Control Register (CP15 register 1, opcode2 1) */ +#define XSCALE_AUXCTL_K 0x00000001 /* dis. write buffer coalescing */ +#define XSCALE_AUXCTL_P 0x00000002 /* ECC protect page table access */ +#define XSCALE_AUXCTL_MD_WB_RA 0x00000000 /* mini-D$ wb, read-allocate */ +#define XSCALE_AUXCTL_MD_WB_RWA 0x00000010 /* mini-D$ wb, read/write-allocate */ +#define XSCALE_AUXCTL_MD_WT 0x00000020 /* mini-D$ wt, read-allocate */ +#define XSCALE_AUXCTL_MD_MASK 0x00000030 + +/* Cache type register definitions */ +#define CPU_CT_ISIZE(x) ((x) & 0xfff) /* I$ info */ +#define CPU_CT_DSIZE(x) (((x) >> 12) & 0xfff) /* D$ info */ +#define CPU_CT_S (1U << 24) /* split cache */ +#define CPU_CT_CTYPE(x) (((x) >> 25) & 0xf) /* cache type */ + +#define CPU_CT_CTYPE_WT 0 /* write-through */ +#define CPU_CT_CTYPE_WB1 1 /* write-back, clean w/ read */ +#define CPU_CT_CTYPE_WB2 2 /* w/b, clean w/ cp15,7 */ +#define CPU_CT_CTYPE_WB6 6 /* w/b, cp15,7, lockdown fmt A */ +#define CPU_CT_CTYPE_WB7 7 /* w/b, cp15,7, lockdown fmt B */ + +#define CPU_CT_xSIZE_LEN(x) ((x) & 0x3) /* line size */ +#define CPU_CT_xSIZE_M (1U << 2) /* multiplier */ +#define CPU_CT_xSIZE_ASSOC(x) (((x) >> 3) & 0x7) /* associativity */ +#define CPU_CT_xSIZE_SIZE(x) (((x) >> 6) & 0x7) /* size */ + +/* Fault status register definitions */ + +#define FAULT_TYPE_MASK 0x0f +#define FAULT_USER 0x10 + +#define FAULT_WRTBUF_0 0x00 /* Vector Exception */ +#define FAULT_WRTBUF_1 0x02 /* Terminal Exception */ +#define FAULT_BUSERR_0 0x04 /* External Abort on Linefetch -- Section */ +#define FAULT_BUSERR_1 0x06 /* External Abort on Linefetch -- Page */ +#define FAULT_BUSERR_2 0x08 /* External Abort on Non-linefetch -- Section */ +#define FAULT_BUSERR_3 0x0a /* External Abort on Non-linefetch -- Page */ +#define FAULT_BUSTRNL1 0x0c /* External abort on Translation -- Level 1 */ +#define FAULT_BUSTRNL2 0x0e /* External abort on Translation -- Level 2 */ +#define FAULT_ALIGN_0 0x01 /* Alignment */ +#define FAULT_ALIGN_1 0x03 /* Alignment */ +#define FAULT_TRANS_S 0x05 /* Translation -- Section */ +#define FAULT_TRANS_P 0x07 /* Translation -- Page */ +#define FAULT_DOMAIN_S 0x09 /* Domain -- Section */ +#define FAULT_DOMAIN_P 0x0b /* Domain -- Page */ +#define FAULT_PERM_S 0x0d /* Permission -- Section */ +#define FAULT_PERM_P 0x0f /* Permission -- Page */ + +#define FAULT_IMPRECISE 0x400 /* Imprecise exception (XSCALE) */ + +/* + * Address of the vector page, low and high versions. + */ +#define ARM_VECTORS_LOW 0x00000000U +#define ARM_VECTORS_HIGH 0xffff0000U + +/* + * ARM Instructions + * + * 3 3 2 2 2 + * 1 0 9 8 7 0 + * +-------+-------------------------------------------------------+ + * | cond | instruction dependant | + * |c c c c| | + * +-------+-------------------------------------------------------+ + */ + +#define INSN_SIZE 4 /* Always 4 bytes */ +#define INSN_COND_MASK 0xf0000000 /* Condition mask */ +#define INSN_COND_AL 0xe0000000 /* Always condition */ + +#endif /* !MACHINE_ARMREG_H */ diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp new file mode 100644 index 0000000..f10217b --- /dev/null +++ b/libpixelflinger/codeflinger/blending.cpp @@ -0,0 +1,682 @@ +/* libs/pixelflinger/codeflinger/blending.cpp +** +** 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 <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include "codeflinger/GGLAssembler.h" + + +namespace android { + +void GGLAssembler::build_fog( + component_t& temp, // incomming fragment / output + int component, + Scratch& regs) +{ + if (mInfo[component].fog) { + Scratch scratches(registerFile()); + comment("fog"); + + integer_t fragment(temp.reg, temp.h, temp.flags); + if (!(temp.flags & CORRUPTIBLE)) { + temp.reg = regs.obtain(); + temp.flags |= CORRUPTIBLE; + } + + integer_t fogColor(scratches.obtain(), 8, CORRUPTIBLE); + LDRB(AL, fogColor.reg, mBuilderContext.Rctx, + immed12_pre(GGL_OFFSETOF(state.fog.color[component]))); + + integer_t factor(scratches.obtain(), 16, CORRUPTIBLE); + CONTEXT_LOAD(factor.reg, generated_vars.f); + + // clamp fog factor (TODO: see if there is a way to guarantee + // we won't overflow, when setting the iterators) + BIC(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, ASR, 31)); + CMP(AL, factor.reg, imm( 0x10000 )); + MOV(HS, 0, factor.reg, imm( 0x10000 )); + + build_blendFOneMinusF(temp, factor, fragment, fogColor); + } +} + +void GGLAssembler::build_blending( + component_t& temp, // incomming fragment / output + const pixel_t& pixel, // framebuffer + int component, + Scratch& regs) +{ + if (!mInfo[component].blend) + return; + + int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc; + int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst; + if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA) + fs = GGL_ONE; + const int blending = blending_codes(fs, fd); + if (!temp.size()) { + // here, blending will produce something which doesn't depend on + // that component (eg: GL_ZERO:GL_*), so the register has not been + // allocated yet. Will never be used as a source. + temp = component_t(regs.obtain(), CORRUPTIBLE); + } + + // we are doing real blending... + // fb: extracted dst + // fragment: extracted src + // temp: component_t(fragment) and result + + // scoped register allocator + Scratch scratches(registerFile()); + comment("blending"); + + // we can optimize these cases a bit... + // (1) saturation is not needed + // (2) we can use only one multiply instead of 2 + // (3) we can reduce the register pressure + // R = S*f + D*(1-f) = (S-D)*f + D + // R = S*(1-f) + D*f = (D-S)*f + S + + const bool same_factor_opt1 = + (fs==GGL_DST_COLOR && fd==GGL_ONE_MINUS_DST_COLOR) || + (fs==GGL_SRC_COLOR && fd==GGL_ONE_MINUS_SRC_COLOR) || + (fs==GGL_DST_ALPHA && fd==GGL_ONE_MINUS_DST_ALPHA) || + (fs==GGL_SRC_ALPHA && fd==GGL_ONE_MINUS_SRC_ALPHA); + + const bool same_factor_opt2 = + (fs==GGL_ONE_MINUS_DST_COLOR && fd==GGL_DST_COLOR) || + (fs==GGL_ONE_MINUS_SRC_COLOR && fd==GGL_SRC_COLOR) || + (fs==GGL_ONE_MINUS_DST_ALPHA && fd==GGL_DST_ALPHA) || + (fs==GGL_ONE_MINUS_SRC_ALPHA && fd==GGL_SRC_ALPHA); + + + // XXX: we could also optimize these cases: + // R = S*f + D*f = (S+D)*f + // R = S*(1-f) + D*(1-f) = (S+D)*(1-f) + // R = S*D + D*S = 2*S*D + + + // see if we need to extract 'component' from the destination (fb) + integer_t fb; + if (blending & (BLEND_DST|FACTOR_DST)) { + fb.setTo(scratches.obtain(), 32); + extract(fb, pixel, component); + if (mDithering) { + // XXX: maybe what we should do instead, is simply + // expand fb -or- fragment to the larger of the two + if (fb.size() < temp.size()) { + // for now we expand 'fb' to min(fragment, 8) + int new_size = temp.size() < 8 ? temp.size() : 8; + expand(fb, fb, new_size); + } + } + } + + + // convert input fragment to integer_t + if (temp.l && (temp.flags & CORRUPTIBLE)) { + MOV(AL, 0, temp.reg, reg_imm(temp.reg, LSR, temp.l)); + temp.h -= temp.l; + temp.l = 0; + } + integer_t fragment(temp.reg, temp.size(), temp.flags); + + // if not done yet, convert input fragment to integer_t + if (temp.l) { + // here we know temp is not CORRUPTIBLE + fragment.reg = scratches.obtain(); + MOV(AL, 0, fragment.reg, reg_imm(temp.reg, LSR, temp.l)); + fragment.flags |= CORRUPTIBLE; + } + + if (!(temp.flags & CORRUPTIBLE)) { + // temp is not corruptible, but since it's the destination it + // will be modified, so we need to allocate a new register. + temp.reg = regs.obtain(); + temp.flags &= ~CORRUPTIBLE; + fragment.flags &= ~CORRUPTIBLE; + } + + if ((blending & BLEND_SRC) && !same_factor_opt1) { + // source (fragment) is needed for the blending stage + // so it's not CORRUPTIBLE (unless we're doing same_factor_opt1) + fragment.flags &= ~CORRUPTIBLE; + } + + + if (same_factor_opt1) { + // R = S*f + D*(1-f) = (S-D)*f + D + integer_t factor; + build_blend_factor(factor, fs, + component, pixel, fragment, fb, scratches); + // fb is always corruptible from this point + fb.flags |= CORRUPTIBLE; + build_blendFOneMinusF(temp, factor, fragment, fb); + } else if (same_factor_opt2) { + // R = S*(1-f) + D*f = (D-S)*f + S + integer_t factor; + // fb is always corrruptible here + fb.flags |= CORRUPTIBLE; + build_blend_factor(factor, fd, + component, pixel, fragment, fb, scratches); + build_blendOneMinusFF(temp, factor, fragment, fb); + } else { + integer_t src_factor; + integer_t dst_factor; + + // if destination (fb) is not needed for the blending stage, + // then it can be marked as CORRUPTIBLE + if (!(blending & BLEND_DST)) { + fb.flags |= CORRUPTIBLE; + } + + // XXX: try to mark some registers as CORRUPTIBLE + // in most case we could make those corruptible + // when we're processing the last component + // but not always, for instance + // when fragment is constant and not reloaded + // when fb is needed for logic-ops or masking + // when a register is aliased (for instance with mAlphaSource) + + // blend away... + if (fs==GGL_ZERO) { + if (fd==GGL_ZERO) { // R = 0 + // already taken care of + } else if (fd==GGL_ONE) { // R = D + // already taken care of + } else { // R = D*fd + // compute fd + build_blend_factor(dst_factor, fd, + component, pixel, fragment, fb, scratches); + mul_factor(temp, fb, dst_factor); + } + } else if (fs==GGL_ONE) { + if (fd==GGL_ZERO) { // R = S + // NOP, taken care of + } else if (fd==GGL_ONE) { // R = S + D + component_add(temp, fb, fragment); // args order matters + component_sat(temp); + } else { // R = S + D*fd + // compute fd + build_blend_factor(dst_factor, fd, + component, pixel, fragment, fb, scratches); + mul_factor_add(temp, fb, dst_factor, component_t(fragment)); + if (fd==GGL_ONE_MINUS_SRC_ALPHA) { + // XXX: in theory this is not correct, we should + // saturate here. However, this mode is often + // used for displaying alpha-premultiplied graphics, + // in which case, saturation is not necessary. + // unfortunatelly, we have no way to know. + // This is a case, where we sacrifice correctness for + // performance. we should probably have some heuristics. + } else { + component_sat(temp); + } + } + } else { + // compute fs + build_blend_factor(src_factor, fs, + component, pixel, fragment, fb, scratches); + if (fd==GGL_ZERO) { // R = S*fs + mul_factor(temp, fragment, src_factor); + } else if (fd==GGL_ONE) { // R = S*fs + D + mul_factor_add(temp, fragment, src_factor, component_t(fb)); + component_sat(temp); + } else { // R = S*fs + D*fd + mul_factor(temp, fragment, src_factor); + if (scratches.isUsed(src_factor.reg)) + scratches.recycle(src_factor.reg); + // compute fd + build_blend_factor(dst_factor, fd, + component, pixel, fragment, fb, scratches); + mul_factor_add(temp, fb, dst_factor, temp); + if (!same_factor_opt1 && !same_factor_opt2) { + component_sat(temp); + } + } + } + } + + // now we can be corrupted (it's the dest) + temp.flags |= CORRUPTIBLE; +} + +void GGLAssembler::build_blend_factor( + integer_t& factor, int f, int component, + const pixel_t& dst_pixel, + integer_t& fragment, + integer_t& fb, + Scratch& scratches) +{ + integer_t src_alpha(fragment); + + // src_factor/dst_factor won't be used after blending, + // so it's fine to mark them as CORRUPTIBLE (if not aliased) + factor.flags |= CORRUPTIBLE; + + switch(f) { + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + if (component==GGLFormat::ALPHA && !isAlphaSourceNeeded()) { + // we're processing alpha, so we already have + // src-alpha in fragment, and we need src-alpha just this time. + } else { + // alpha-src will be needed for other components + if (!mBlendFactorCached || mBlendFactorCached==f) { + src_alpha = mAlphaSource; + factor = mAlphaSource; + factor.flags &= ~CORRUPTIBLE; + // we already computed the blend factor before, nothing to do. + if (mBlendFactorCached) + return; + // this is the first time, make sure to compute the blend + // factor properly. + mBlendFactorCached = f; + break; + } else { + // we have a cached alpha blend factor, but we want another one, + // this should really not happen because by construction, + // we cannot have BOTH source and destination + // blend factors use ALPHA *and* ONE_MINUS_ALPHA (because + // the blending stage uses the f/(1-f) optimization + + // for completeness, we handle this case though. Since there + // are only 2 choices, this meens we want "the other one" + // (1-factor) + factor = mAlphaSource; + factor.flags &= ~CORRUPTIBLE; + RSB(AL, 0, factor.reg, factor.reg, imm((1<<factor.s))); + mBlendFactorCached = f; + return; + } + } + // fall-through... + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + case GGL_SRC_ALPHA_SATURATE: + // help us find out what register we can use for the blend-factor + // CORRUPTIBLE registers are chosen first, or a new one is allocated. + if (fragment.flags & CORRUPTIBLE) { + factor.setTo(fragment.reg, 32, CORRUPTIBLE); + fragment.flags &= ~CORRUPTIBLE; + } else if (fb.flags & CORRUPTIBLE) { + factor.setTo(fb.reg, 32, CORRUPTIBLE); + fb.flags &= ~CORRUPTIBLE; + } else { + factor.setTo(scratches.obtain(), 32, CORRUPTIBLE); + } + break; + } + + // XXX: doesn't work if size==1 + + switch(f) { + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + factor.s = fb.s; + ADD(AL, 0, factor.reg, fb.reg, reg_imm(fb.reg, LSR, fb.s-1)); + break; + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + factor.s = fragment.s; + ADD(AL, 0, factor.reg, fragment.reg, + reg_imm(fragment.reg, LSR, fragment.s-1)); + break; + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + factor.s = src_alpha.s; + ADD(AL, 0, factor.reg, src_alpha.reg, + reg_imm(src_alpha.reg, LSR, src_alpha.s-1)); + break; + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + // XXX: should be precomputed + extract(factor, dst_pixel, GGLFormat::ALPHA); + ADD(AL, 0, factor.reg, factor.reg, + reg_imm(factor.reg, LSR, factor.s-1)); + break; + case GGL_SRC_ALPHA_SATURATE: + // XXX: should be precomputed + // XXX: f = min(As, 1-Ad) + // btw, we're guaranteed that Ad's size is <= 8, because + // it's extracted from the framebuffer + break; + } + + switch(f) { + case GGL_ONE_MINUS_DST_COLOR: + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_ONE_MINUS_SRC_ALPHA: + RSB(AL, 0, factor.reg, factor.reg, imm((1<<factor.s))); + } + + // don't need more than 8-bits for the blend factor + // and this will prevent overflows in the multiplies later + if (factor.s > 8) { + MOV(AL, 0, factor.reg, reg_imm(factor.reg, LSR, factor.s-8)); + factor.s = 8; + } +} + +int GGLAssembler::blending_codes(int fs, int fd) +{ + int blending = 0; + switch(fs) { + case GGL_ONE: + blending |= BLEND_SRC; + break; + + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + blending |= FACTOR_DST|BLEND_SRC; + break; + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + // no need to extract 'component' from the destination + // for the blend factor, because we need ALPHA only. + blending |= BLEND_SRC; + break; + + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + blending |= FACTOR_SRC|BLEND_SRC; + break; + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + case GGL_SRC_ALPHA_SATURATE: + blending |= FACTOR_SRC|BLEND_SRC; + break; + } + switch(fd) { + case GGL_ONE: + blending |= BLEND_DST; + break; + + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + blending |= FACTOR_DST|BLEND_DST; + break; + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + blending |= FACTOR_DST|BLEND_DST; + break; + + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + blending |= FACTOR_SRC|BLEND_DST; + break; + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + // no need to extract 'component' from the source + // for the blend factor, because we need ALPHA only. + blending |= BLEND_DST; + break; + } + return blending; +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::build_blendFOneMinusF( + component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb) +{ + // R = S*f + D*(1-f) = (S-D)*f + D + Scratch scratches(registerFile()); + // compute S-D + integer_t diff(fragment.flags & CORRUPTIBLE ? + fragment.reg : scratches.obtain(), fb.size(), CORRUPTIBLE); + const int shift = fragment.size() - fb.size(); + if (shift>0) RSB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSR, shift)); + else if (shift<0) RSB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSL,-shift)); + else RSB(AL, 0, diff.reg, fb.reg, fragment.reg); + mul_factor_add(temp, diff, factor, component_t(fb)); +} + +void GGLAssembler::build_blendOneMinusFF( + component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb) +{ + // R = S*f + D*(1-f) = (S-D)*f + D + Scratch scratches(registerFile()); + // compute D-S + integer_t diff(fb.flags & CORRUPTIBLE ? + fb.reg : scratches.obtain(), fb.size(), CORRUPTIBLE); + const int shift = fragment.size() - fb.size(); + if (shift>0) SUB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSR, shift)); + else if (shift<0) SUB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSL,-shift)); + else SUB(AL, 0, diff.reg, fb.reg, fragment.reg); + mul_factor_add(temp, diff, factor, component_t(fragment)); +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::mul_factor( component_t& d, + const integer_t& v, + const integer_t& f) +{ + int vs = v.size(); + int fs = f.size(); + int ms = vs+fs; + + // XXX: we could have special cases for 1 bit mul + + // all this code below to use the best multiply instruction + // wrt the parameters size. We take advantage of the fact + // that the 16-bits multiplies allow a 16-bit shift + // The trick is that we just make sure that we have at least 8-bits + // per component (which is enough for a 8 bits display). + + int xy; + int vshift = 0; + int fshift = 0; + int smulw = 0; + + if (vs<16) { + if (fs<16) { + xy = xyBB; + } else if (GGL_BETWEEN(fs, 24, 31)) { + ms -= 16; + xy = xyTB; + } else { + // eg: 15 * 18 -> 15 * 15 + fshift = fs - 15; + ms -= fshift; + xy = xyBB; + } + } else if (GGL_BETWEEN(vs, 24, 31)) { + if (fs<16) { + ms -= 16; + xy = xyTB; + } else if (GGL_BETWEEN(fs, 24, 31)) { + ms -= 32; + xy = xyTT; + } else { + // eg: 24 * 18 -> 8 * 18 + fshift = fs - 15; + ms -= 16 + fshift; + xy = xyTB; + } + } else { + if (fs<16) { + // eg: 18 * 15 -> 15 * 15 + vshift = vs - 15; + ms -= vshift; + xy = xyBB; + } else if (GGL_BETWEEN(fs, 24, 31)) { + // eg: 18 * 24 -> 15 * 8 + vshift = vs - 15; + ms -= 16 + vshift; + xy = xyBT; + } else { + // eg: 18 * 18 -> (15 * 18)>>16 + fshift = fs - 15; + ms -= 16 + fshift; + xy = yB; //XXX SMULWB + smulw = 1; + } + } + + LOGE_IF(ms>=32, "mul_factor overflow vs=%d, fs=%d", vs, fs); + + int vreg = v.reg; + int freg = f.reg; + if (vshift) { + MOV(AL, 0, d.reg, reg_imm(vreg, LSR, vshift)); + vreg = d.reg; + } + if (fshift) { + MOV(AL, 0, d.reg, reg_imm(vreg, LSR, fshift)); + freg = d.reg; + } + if (smulw) SMULW(AL, xy, d.reg, vreg, freg); + else SMUL(AL, xy, d.reg, vreg, freg); + + + d.h = ms; + if (mDithering) { + d.l = 0; + } else { + d.l = fs; + d.flags |= CLEAR_LO; + } +} + +void GGLAssembler::mul_factor_add( component_t& d, + const integer_t& v, + const integer_t& f, + const component_t& a) +{ + // XXX: we could have special cases for 1 bit mul + Scratch scratches(registerFile()); + + int vs = v.size(); + int fs = f.size(); + int as = a.h; + int ms = vs+fs; + + LOGE_IF(ms>=32, "mul_factor_add overflow vs=%d, fs=%d, as=%d", vs, fs, as); + + integer_t add(a.reg, a.h, a.flags); + + // 'a' is a component_t but it is guaranteed to have + // its high bits set to 0. However in the dithering case, + // we can't get away with truncating the potentially bad bits + // so extraction is needed. + + if ((mDithering) && (a.size() < ms)) { + // we need to expand a + if (!(a.flags & CORRUPTIBLE)) { + // ... but it's not corruptible, so we need to pick a + // temporary register. + // Try to uses the destination register first (it's likely + // to be usable, unless it aliases an input). + if (d.reg!=a.reg && d.reg!=v.reg && d.reg!=f.reg) { + add.reg = d.reg; + } else { + add.reg = scratches.obtain(); + } + } + expand(add, a, ms); // extracts and expands + as = ms; + } + + if (ms == as) { + if (vs<16 && fs<16) SMLABB(AL, d.reg, v.reg, f.reg, add.reg); + else MLA(AL, 0, d.reg, v.reg, f.reg, add.reg); + } else { + int temp = d.reg; + if (temp == add.reg) { + // the mul will modify add.reg, we need an intermediary reg + if (v.flags & CORRUPTIBLE) temp = v.reg; + else if (f.flags & CORRUPTIBLE) temp = f.reg; + else temp = scratches.obtain(); + } + + if (vs<16 && fs<16) SMULBB(AL, temp, v.reg, f.reg); + else MUL(AL, 0, temp, v.reg, f.reg); + + if (ms>as) { + ADD(AL, 0, d.reg, temp, reg_imm(add.reg, LSL, ms-as)); + } else if (ms<as) { + // not sure if we should expand the mul instead? + ADD(AL, 0, d.reg, temp, reg_imm(add.reg, LSR, as-ms)); + } + } + + d.h = ms; + if (mDithering) { + d.l = a.l; + } else { + d.l = fs>a.l ? fs : a.l; + d.flags |= CLEAR_LO; + } +} + +void GGLAssembler::component_add(component_t& d, + const integer_t& dst, const integer_t& src) +{ + // here we're guaranteed that fragment.size() >= fb.size() + const int shift = src.size() - dst.size(); + if (!shift) { + ADD(AL, 0, d.reg, src.reg, dst.reg); + } else { + ADD(AL, 0, d.reg, src.reg, reg_imm(dst.reg, LSL, shift)); + } + + d.h = src.size(); + if (mDithering) { + d.l = 0; + } else { + d.l = shift; + d.flags |= CLEAR_LO; + } +} + +void GGLAssembler::component_sat(const component_t& v) +{ + const int one = ((1<<v.size())-1)<<v.l; + CMP(AL, v.reg, imm( 1<<v.h )); + if (isValidImmediate(one)) { + MOV(HS, 0, v.reg, imm( one )); + } else if (isValidImmediate(~one)) { + MVN(HS, 0, v.reg, imm( ~one )); + } else { + MOV(HS, 0, v.reg, imm( 1<<v.h )); + SUB(HS, 0, v.reg, v.reg, imm( 1<<v.l )); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c new file mode 100644 index 0000000..4676da0 --- /dev/null +++ b/libpixelflinger/codeflinger/disassem.c @@ -0,0 +1,702 @@ +/* $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $ */ + +/*- + * Copyright (c) 1996 Mark Brinicombe. + * Copyright (c) 1996 Brini. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * RiscBSD kernel project + * + * db_disasm.c + * + * Kernel disassembler + * + * Created : 10/02/96 + * + * Structured after the sparc/sparc/db_disasm.c by David S. Miller & + * Paul Kranenburg + * + * This code is not complete. Not all instructions are disassembled. + */ + +#include <sys/cdefs.h> +//__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/arm/arm/disassem.c,v 1.2 2005/01/05 21:58:47 imp Exp $"); +#include <sys/param.h> +#include <stdio.h> + +#include "disassem.h" +#include "armreg.h" +//#include <ddb/ddb.h> + +/* + * General instruction format + * + * insn[cc][mod] [operands] + * + * Those fields with an uppercase format code indicate that the field + * follows directly after the instruction before the separator i.e. + * they modify the instruction rather than just being an operand to + * the instruction. The only exception is the writeback flag which + * follows a operand. + * + * + * 2 - print Operand 2 of a data processing instruction + * d - destination register (bits 12-15) + * n - n register (bits 16-19) + * s - s register (bits 8-11) + * o - indirect register rn (bits 16-19) (used by swap) + * m - m register (bits 0-3) + * a - address operand of ldr/str instruction + * e - address operand of ldrh/strh instruction + * l - register list for ldm/stm instruction + * f - 1st fp operand (register) (bits 12-14) + * g - 2nd fp operand (register) (bits 16-18) + * h - 3rd fp operand (register/immediate) (bits 0-4) + * b - branch address + * t - thumb branch address (bits 24, 0-23) + * k - breakpoint comment (bits 0-3, 8-19) + * X - block transfer type + * Y - block transfer type (r13 base) + * c - comment field bits(0-23) + * p - saved or current status register + * F - PSR transfer fields + * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN + * L - co-processor transfer size + * S - set status flag + * P - fp precision + * Q - fp precision (for ldf/stf) + * R - fp rounding + * v - co-processor data transfer registers + addressing mode + * W - writeback flag + * x - instruction in hex + * # - co-processor number + * y - co-processor data processing registers + * z - co-processor register transfer registers + */ + +struct arm32_insn { + u_int mask; + u_int pattern; + char* name; + char* format; +}; + +static const struct arm32_insn arm32_i[] = { + { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */ + { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */ + { 0x0f000000, 0x0f000000, "swi", "c" }, + { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */ + { 0x0f000000, 0x0a000000, "b", "b" }, + { 0x0f000000, 0x0b000000, "bl", "b" }, + { 0x0fe000f0, 0x00000090, "mul", "Snms" }, + { 0x0fe000f0, 0x00200090, "mla", "Snmsd" }, + { 0x0fe000f0, 0x00800090, "umull", "Sdnms" }, + { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" }, + { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" }, + { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" }, + { 0x0d700000, 0x04200000, "strt", "daW" }, + { 0x0d700000, 0x04300000, "ldrt", "daW" }, + { 0x0d700000, 0x04600000, "strbt", "daW" }, + { 0x0d700000, 0x04700000, "ldrbt", "daW" }, + { 0x0c500000, 0x04000000, "str", "daW" }, + { 0x0c500000, 0x04100000, "ldr", "daW" }, + { 0x0c500000, 0x04400000, "strb", "daW" }, + { 0x0c500000, 0x04500000, "ldrb", "daW" }, + { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */ + { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */ + { 0x0e100000, 0x08000000, "stm", "XnWl" }, + { 0x0e100000, 0x08100000, "ldm", "XnWl" }, + { 0x0e1000f0, 0x00100090, "ldrb", "deW" }, + { 0x0e1000f0, 0x00000090, "strb", "deW" }, + { 0x0e1000f0, 0x001000d0, "ldrsb", "deW" }, + { 0x0e1000f0, 0x001000b0, "ldrh", "deW" }, + { 0x0e1000f0, 0x000000b0, "strh", "deW" }, + { 0x0e1000f0, 0x001000f0, "ldrsh", "deW" }, + { 0x0f200090, 0x00200090, "und", "x" }, /* Before data processing */ + { 0x0e1000d0, 0x000000d0, "und", "x" }, /* Before data processing */ + { 0x0ff00ff0, 0x01000090, "swp", "dmo" }, + { 0x0ff00ff0, 0x01400090, "swpb", "dmo" }, + { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* Before data processing */ + { 0x0fb0fff0, 0x0120f000, "msr", "pFm" },/* Before data processing */ + { 0x0fb0f000, 0x0320f000, "msr", "pF2" },/* Before data processing */ + { 0x0ffffff0, 0x012fff10, "bx", "m" }, + { 0x0fff0ff0, 0x016f0f10, "clz", "dm" }, + { 0x0ffffff0, 0x012fff30, "blx", "m" }, + { 0xfff000f0, 0xe1200070, "bkpt", "k" }, + { 0x0de00000, 0x00000000, "and", "Sdn2" }, + { 0x0de00000, 0x00200000, "eor", "Sdn2" }, + { 0x0de00000, 0x00400000, "sub", "Sdn2" }, + { 0x0de00000, 0x00600000, "rsb", "Sdn2" }, + { 0x0de00000, 0x00800000, "add", "Sdn2" }, + { 0x0de00000, 0x00a00000, "adc", "Sdn2" }, + { 0x0de00000, 0x00c00000, "sbc", "Sdn2" }, + { 0x0de00000, 0x00e00000, "rsc", "Sdn2" }, + { 0x0df00000, 0x01100000, "tst", "Dn2" }, + { 0x0df00000, 0x01300000, "teq", "Dn2" }, + { 0x0df00000, 0x01500000, "cmp", "Dn2" }, + { 0x0df00000, 0x01700000, "cmn", "Dn2" }, + { 0x0de00000, 0x01800000, "orr", "Sdn2" }, + { 0x0de00000, 0x01a00000, "mov", "Sd2" }, + { 0x0de00000, 0x01c00000, "bic", "Sdn2" }, + { 0x0de00000, 0x01e00000, "mvn", "Sd2" }, + { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" }, + { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" }, + { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" }, + { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" }, + { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" }, + { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" }, + { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" }, + { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" }, + { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" }, + { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" }, + { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" }, + { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" }, + { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" }, + { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" }, + { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" }, + { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" }, + { 0x0ff08f10, 0x0e208100, "abs", "PRfh" }, + { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" }, + { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" }, + { 0x0ff08f10, 0x0e508100, "log", "PRfh" }, + { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" }, + { 0x0ff08f10, 0x0e708100, "exp", "PRfh" }, + { 0x0ff08f10, 0x0e808100, "sin", "PRfh" }, + { 0x0ff08f10, 0x0e908100, "cos", "PRfh" }, + { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" }, + { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" }, + { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" }, + { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" }, + { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" }, + { 0x0e100f00, 0x0c000100, "stf", "QLv" }, + { 0x0e100f00, 0x0c100100, "ldf", "QLv" }, + { 0x0ff00f10, 0x0e000110, "flt", "PRgd" }, + { 0x0ff00f10, 0x0e100110, "fix", "PRdh" }, + { 0x0ff00f10, 0x0e200110, "wfs", "d" }, + { 0x0ff00f10, 0x0e300110, "rfs", "d" }, + { 0x0ff00f10, 0x0e400110, "wfc", "d" }, + { 0x0ff00f10, 0x0e500110, "rfc", "d" }, + { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" }, + { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" }, + { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" }, + { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" }, + { 0xff100010, 0xfe000010, "mcr2", "#z" }, + { 0x0f100010, 0x0e000010, "mcr", "#z" }, + { 0xff100010, 0xfe100010, "mrc2", "#z" }, + { 0x0f100010, 0x0e100010, "mrc", "#z" }, + { 0xff000010, 0xfe000000, "cdp2", "#y" }, + { 0x0f000010, 0x0e000000, "cdp", "#y" }, + { 0xfe100090, 0xfc100000, "ldc2", "L#v" }, + { 0x0e100090, 0x0c100000, "ldc", "L#v" }, + { 0xfe100090, 0xfc000000, "stc2", "L#v" }, + { 0x0e100090, 0x0c000000, "stc", "L#v" }, + { 0xf550f000, 0xf550f000, "pld", "ne" }, + { 0x0ff00ff0, 0x01000050, "qaad", "dmn" }, + { 0x0ff00ff0, 0x01400050, "qdaad", "dmn" }, + { 0x0ff00ff0, 0x01600050, "qdsub", "dmn" }, + { 0x0ff00ff0, 0x01200050, "dsub", "dmn" }, + { 0x0ff000f0, 0x01000080, "smlabb", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x010000a0, "smlatb", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x010000c0, "smlabt", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x010000e0, "smlatt", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x01400080, "smlalbb","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x014000a0, "smlaltb","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x014000c0, "smlalbt","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x014000e0, "smlaltt","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x01200080, "smlawb", "nmsd" }, // d & n inverted!! + { 0x0ff0f0f0, 0x012000a0, "smulwb","nms" }, // d & n inverted!! + { 0x0ff000f0, 0x012000c0, "smlawt", "nmsd" }, // d & n inverted!! + { 0x0ff0f0f0, 0x012000e0, "smulwt","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x01600080, "smulbb","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x016000a0, "smultb","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x016000c0, "smulbt","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x016000e0, "smultt","nms" }, // d & n inverted!! + { 0x00000000, 0x00000000, NULL, NULL } +}; + +static char const arm32_insn_conditions[][4] = { + "eq", "ne", "cs", "cc", + "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", + "gt", "le", "", "nv" +}; + +static char const insn_block_transfers[][4] = { + "da", "ia", "db", "ib" +}; + +static char const insn_stack_block_transfers[][4] = { + "ed", "ea", "fd", "fa" +}; + +static char const op_shifts[][4] = { + "lsl", "lsr", "asr", "ror" +}; + +static char const insn_fpa_rounding[][2] = { + "", "p", "m", "z" +}; + +static char const insn_fpa_precision[][2] = { + "s", "d", "e", "p" +}; + +static char const insn_fpaconstants[][8] = { + "0.0", "1.0", "2.0", "3.0", + "4.0", "5.0", "0.5", "10.0" +}; + +#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f] +#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3] +#define insn_stkblktrans(x) insn_stack_block_transfers[(x >> 23) & 3] +#define op2_shift(x) op_shifts[(x >> 5) & 3] +#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03] +#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1] +#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1] +#define insn_fpaimm(x) insn_fpaconstants[x & 0x07] + +/* Local prototypes */ +static void disasm_register_shift(const disasm_interface_t *di, u_int insn); +static void disasm_print_reglist(const disasm_interface_t *di, u_int insn); +static void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, + u_int loc); +static void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, + u_int loc); +static void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, + u_int loc); +static u_int disassemble_readword(u_int address); +static void disassemble_printaddr(u_int address); + +u_int +disasm(const disasm_interface_t *di, u_int loc, int altfmt) +{ + const struct arm32_insn *i_ptr = &arm32_i[0]; + + u_int insn; + int matchp; + int branch; + char* f_ptr; + int fmt; + + fmt = 0; + matchp = 0; + insn = di->di_readword(loc); + +/* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ + + while (i_ptr->name) { + if ((insn & i_ptr->mask) == i_ptr->pattern) { + matchp = 1; + break; + } + i_ptr++; + } + + if (!matchp) { + di->di_printf("und%s\t%08x\n", insn_condition(insn), insn); + return(loc + INSN_SIZE); + } + + /* If instruction forces condition code, don't print it. */ + if ((i_ptr->mask & 0xf0000000) == 0xf0000000) + di->di_printf("%s", i_ptr->name); + else + di->di_printf("%s%s", i_ptr->name, insn_condition(insn)); + + f_ptr = i_ptr->format; + + /* Insert tab if there are no instruction modifiers */ + + if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') { + ++fmt; + di->di_printf("\t"); + } + + while (*f_ptr) { + switch (*f_ptr) { + /* 2 - print Operand 2 of a data processing instruction */ + case '2': + if (insn & 0x02000000) { + int rotate= ((insn >> 7) & 0x1e); + + di->di_printf("#0x%08x", + (insn & 0xff) << (32 - rotate) | + (insn & 0xff) >> rotate); + } else { + disasm_register_shift(di, insn); + } + break; + /* d - destination register (bits 12-15) */ + case 'd': + di->di_printf("r%d", ((insn >> 12) & 0x0f)); + break; + /* D - insert 'p' if Rd is R15 */ + case 'D': + if (((insn >> 12) & 0x0f) == 15) + di->di_printf("p"); + break; + /* n - n register (bits 16-19) */ + case 'n': + di->di_printf("r%d", ((insn >> 16) & 0x0f)); + break; + /* s - s register (bits 8-11) */ + case 's': + di->di_printf("r%d", ((insn >> 8) & 0x0f)); + break; + /* o - indirect register rn (bits 16-19) (used by swap) */ + case 'o': + di->di_printf("[r%d]", ((insn >> 16) & 0x0f)); + break; + /* m - m register (bits 0-4) */ + case 'm': + di->di_printf("r%d", ((insn >> 0) & 0x0f)); + break; + /* a - address operand of ldr/str instruction */ + case 'a': + disasm_insn_ldrstr(di, insn, loc); + break; + /* e - address operand of ldrh/strh instruction */ + case 'e': + disasm_insn_ldrhstrh(di, insn, loc); + break; + /* l - register list for ldm/stm instruction */ + case 'l': + disasm_print_reglist(di, insn); + break; + /* f - 1st fp operand (register) (bits 12-14) */ + case 'f': + di->di_printf("f%d", (insn >> 12) & 7); + break; + /* g - 2nd fp operand (register) (bits 16-18) */ + case 'g': + di->di_printf("f%d", (insn >> 16) & 7); + break; + /* h - 3rd fp operand (register/immediate) (bits 0-4) */ + case 'h': + if (insn & (1 << 3)) + di->di_printf("#%s", insn_fpaimm(insn)); + else + di->di_printf("f%d", insn & 7); + break; + /* b - branch address */ + case 'b': + branch = ((insn << 2) & 0x03ffffff); + if (branch & 0x02000000) + branch |= 0xfc000000; + di->di_printaddr(loc + 8 + branch); + break; + /* t - blx address */ + case 't': + branch = ((insn << 2) & 0x03ffffff) | + (insn >> 23 & 0x00000002); + if (branch & 0x02000000) + branch |= 0xfc000000; + di->di_printaddr(loc + 8 + branch); + break; + /* X - block transfer type */ + case 'X': + di->di_printf("%s", insn_blktrans(insn)); + break; + /* Y - block transfer type (r13 base) */ + case 'Y': + di->di_printf("%s", insn_stkblktrans(insn)); + break; + /* c - comment field bits(0-23) */ + case 'c': + di->di_printf("0x%08x", (insn & 0x00ffffff)); + break; + /* k - breakpoint comment (bits 0-3, 8-19) */ + case 'k': + di->di_printf("0x%04x", + (insn & 0x000fff00) >> 4 | (insn & 0x0000000f)); + break; + /* p - saved or current status register */ + case 'p': + if (insn & 0x00400000) + di->di_printf("spsr"); + else + di->di_printf("cpsr"); + break; + /* F - PSR transfer fields */ + case 'F': + di->di_printf("_"); + if (insn & (1 << 16)) + di->di_printf("c"); + if (insn & (1 << 17)) + di->di_printf("x"); + if (insn & (1 << 18)) + di->di_printf("s"); + if (insn & (1 << 19)) + di->di_printf("f"); + break; + /* B - byte transfer flag */ + case 'B': + if (insn & 0x00400000) + di->di_printf("b"); + break; + /* L - co-processor transfer size */ + case 'L': + if (insn & (1 << 22)) + di->di_printf("l"); + break; + /* S - set status flag */ + case 'S': + if (insn & 0x00100000) + di->di_printf("s"); + break; + /* P - fp precision */ + case 'P': + di->di_printf("%s", insn_fpaprec(insn)); + break; + /* Q - fp precision (for ldf/stf) */ + case 'Q': + break; + /* R - fp rounding */ + case 'R': + di->di_printf("%s", insn_fparnd(insn)); + break; + /* W - writeback flag */ + case 'W': + if (insn & (1 << 21)) + di->di_printf("!"); + break; + /* # - co-processor number */ + case '#': + di->di_printf("p%d", (insn >> 8) & 0x0f); + break; + /* v - co-processor data transfer registers+addressing mode */ + case 'v': + disasm_insn_ldcstc(di, insn, loc); + break; + /* x - instruction in hex */ + case 'x': + di->di_printf("0x%08x", insn); + break; + /* y - co-processor data processing registers */ + case 'y': + di->di_printf("%d, ", (insn >> 20) & 0x0f); + + di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f, + (insn >> 16) & 0x0f, insn & 0x0f); + + di->di_printf(", %d", (insn >> 5) & 0x07); + break; + /* z - co-processor register transfer registers */ + case 'z': + di->di_printf("%d, ", (insn >> 21) & 0x07); + di->di_printf("r%d, c%d, c%d, %d", + (insn >> 12) & 0x0f, (insn >> 16) & 0x0f, + insn & 0x0f, (insn >> 5) & 0x07); + +/* if (((insn >> 5) & 0x07) != 0) + di->di_printf(", %d", (insn >> 5) & 0x07);*/ + break; + default: + di->di_printf("[%c - unknown]", *f_ptr); + break; + } + if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z') + ++f_ptr; + else if (*(++f_ptr)) { + ++fmt; + if (fmt == 1) + di->di_printf("\t"); + else + di->di_printf(", "); + } + }; + + di->di_printf("\n"); + + return(loc + INSN_SIZE); +} + + +static void +disasm_register_shift(const disasm_interface_t *di, u_int insn) +{ + di->di_printf("r%d", (insn & 0x0f)); + if ((insn & 0x00000ff0) == 0) + ; + else if ((insn & 0x00000ff0) == 0x00000060) + di->di_printf(", rrx"); + else { + if (insn & 0x10) + di->di_printf(", %s r%d", op2_shift(insn), + (insn >> 8) & 0x0f); + else + di->di_printf(", %s #%d", op2_shift(insn), + (insn >> 7) & 0x1f); + } +} + + +static void +disasm_print_reglist(const disasm_interface_t *di, u_int insn) +{ + int loop; + int start; + int comma; + + di->di_printf("{"); + start = -1; + comma = 0; + + for (loop = 0; loop < 17; ++loop) { + if (start != -1) { + if (loop == 16 || !(insn & (1 << loop))) { + if (comma) + di->di_printf(", "); + else + comma = 1; + if (start == loop - 1) + di->di_printf("r%d", start); + else + di->di_printf("r%d-r%d", start, loop - 1); + start = -1; + } + } else { + if (insn & (1 << loop)) + start = loop; + } + } + di->di_printf("}"); + + if (insn & (1 << 22)) + di->di_printf("^"); +} + +static void +disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc) +{ + int offset; + + offset = insn & 0xfff; + if ((insn & 0x032f0000) == 0x010f0000) { + /* rA = pc, immediate index */ + if (insn & 0x00800000) + loc += offset; + else + loc -= offset; + di->di_printaddr(loc + 8); + } else { + di->di_printf("[r%d", (insn >> 16) & 0x0f); + if ((insn & 0x03000fff) != 0x01000000) { + di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); + if (!(insn & 0x00800000)) + di->di_printf("-"); + if (insn & (1 << 25)) + disasm_register_shift(di, insn); + else + di->di_printf("#0x%03x", offset); + } + if (insn & (1 << 24)) + di->di_printf("]"); + } +} + +static void +disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc) +{ + int offset; + + offset = ((insn & 0xf00) >> 4) | (insn & 0xf); + if ((insn & 0x004f0000) == 0x004f0000) { + /* rA = pc, immediate index */ + if (insn & 0x00800000) + loc += offset; + else + loc -= offset; + di->di_printaddr(loc + 8); + } else { + di->di_printf("[r%d", (insn >> 16) & 0x0f); + if ((insn & 0x01400f0f) != 0x01400000) { + di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); + if (!(insn & 0x00800000)) + di->di_printf("-"); + if (insn & (1 << 22)) + di->di_printf("#0x%02x", offset); + else + di->di_printf("r%d", (insn & 0x0f)); + } + if (insn & (1 << 24)) + di->di_printf("]"); + } +} + +static void +disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) +{ + if (((insn >> 8) & 0xf) == 1) + di->di_printf("f%d, ", (insn >> 12) & 0x07); + else + di->di_printf("c%d, ", (insn >> 12) & 0x0f); + + di->di_printf("[r%d", (insn >> 16) & 0x0f); + + di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); + + if (!(insn & (1 << 23))) + di->di_printf("-"); + + di->di_printf("#0x%03x", (insn & 0xff) << 2); + + if (insn & (1 << 24)) + di->di_printf("]"); + + if (insn & (1 << 21)) + di->di_printf("!"); +} + +static u_int +disassemble_readword(u_int address) +{ + return(*((u_int *)address)); +} + +static void +disassemble_printaddr(u_int address) +{ + printf("0x%08x", address); +} + +static const disasm_interface_t disassemble_di = { + disassemble_readword, disassemble_printaddr, printf +}; + +void +disassemble(u_int address) +{ + + (void)disasm(&disassemble_di, address, 0); +} + +/* End of disassem.c */ diff --git a/libpixelflinger/codeflinger/disassem.h b/libpixelflinger/codeflinger/disassem.h new file mode 100644 index 0000000..02747cd --- /dev/null +++ b/libpixelflinger/codeflinger/disassem.h @@ -0,0 +1,65 @@ +/* $NetBSD: disassem.h,v 1.4 2001/03/04 04:15:58 matt Exp $ */ + +/*- + * Copyright (c) 1997 Mark Brinicombe. + * Copyright (c) 1997 Causality Limited. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Brinicombe. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Define the interface structure required by the disassembler. + * + * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/disassem.h,v 1.2 2005/01/05 21:58:48 imp Exp $ + */ + +#ifndef ANDROID_MACHINE_DISASSEM_H +#define ANDROID_MACHINE_DISASSEM_H + +#include <sys/types.h> + +#if __cplusplus +extern "C" { +#endif + +typedef struct { + u_int (*di_readword)(u_int); + void (*di_printaddr)(u_int); + void (*di_printf)(const char *, ...); +} disasm_interface_t; + +/* Prototypes for callable functions */ + +u_int disasm(const disasm_interface_t *, u_int, int); +void disassemble(u_int); + +#if __cplusplus +} +#endif + +#endif /* !ANDROID_MACHINE_DISASSEM_H */ diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp new file mode 100644 index 0000000..93c5825 --- /dev/null +++ b/libpixelflinger/codeflinger/load_store.cpp @@ -0,0 +1,378 @@ +/* libs/pixelflinger/codeflinger/load_store.cpp +** +** 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 <assert.h> +#include <stdio.h> +#include <cutils/log.h> + +#include "codeflinger/GGLAssembler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +void GGLAssembler::store(const pointer_t& addr, const pixel_t& s, uint32_t flags) +{ + const int bits = addr.size; + const int inc = (flags & WRITE_BACK)?1:0; + switch (bits) { + case 32: + if (inc) STR(AL, s.reg, addr.reg, immed12_post(4)); + else STR(AL, s.reg, addr.reg); + break; + case 24: + // 24 bits formats are a little special and used only for RGB + // 0x00BBGGRR is unpacked as R,G,B + STRB(AL, s.reg, addr.reg, immed12_pre(0)); + MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 8)); + STRB(AL, s.reg, addr.reg, immed12_pre(1)); + MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 8)); + STRB(AL, s.reg, addr.reg, immed12_pre(2)); + if (!(s.flags & CORRUPTIBLE)) { + MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 16)); + } + if (inc) + ADD(AL, 0, addr.reg, addr.reg, imm(3)); + break; + case 16: + if (inc) STRH(AL, s.reg, addr.reg, immed8_post(2)); + else STRH(AL, s.reg, addr.reg); + break; + case 8: + if (inc) STRB(AL, s.reg, addr.reg, immed12_post(1)); + else STRB(AL, s.reg, addr.reg); + break; + } +} + +void GGLAssembler::load(const pointer_t& addr, const pixel_t& s, uint32_t flags) +{ + Scratch scratches(registerFile()); + int s0; + + const int bits = addr.size; + const int inc = (flags & WRITE_BACK)?1:0; + switch (bits) { + case 32: + if (inc) LDR(AL, s.reg, addr.reg, immed12_post(4)); + else LDR(AL, s.reg, addr.reg); + break; + case 24: + // 24 bits formats are a little special and used only for RGB + // R,G,B is packed as 0x00BBGGRR + s0 = scratches.obtain(); + if (s.reg != addr.reg) { + LDRB(AL, s.reg, addr.reg, immed12_pre(0)); // R + LDRB(AL, s0, addr.reg, immed12_pre(1)); // G + ORR(AL, 0, s.reg, s.reg, reg_imm(s0, LSL, 8)); + LDRB(AL, s0, addr.reg, immed12_pre(2)); // B + ORR(AL, 0, s.reg, s.reg, reg_imm(s0, LSL, 16)); + } else { + int s1 = scratches.obtain(); + LDRB(AL, s1, addr.reg, immed12_pre(0)); // R + LDRB(AL, s0, addr.reg, immed12_pre(1)); // G + ORR(AL, 0, s1, s1, reg_imm(s0, LSL, 8)); + LDRB(AL, s0, addr.reg, immed12_pre(2)); // B + ORR(AL, 0, s.reg, s1, reg_imm(s0, LSL, 16)); + } + if (inc) + ADD(AL, 0, addr.reg, addr.reg, imm(3)); + break; + case 16: + if (inc) LDRH(AL, s.reg, addr.reg, immed8_post(2)); + else LDRH(AL, s.reg, addr.reg); + break; + case 8: + if (inc) LDRB(AL, s.reg, addr.reg, immed12_post(1)); + else LDRB(AL, s.reg, addr.reg); + break; + } +} + +void GGLAssembler::extract(integer_t& d, int s, int h, int l, int bits) +{ + const int maskLen = h-l; + + assert(maskLen<=8); + assert(h); + + if (h != bits) { + const int mask = ((1<<maskLen)-1) << l; + if (isValidImmediate(mask)) { + AND(AL, 0, d.reg, s, imm(mask)); // component = packed & mask; + } else if (isValidImmediate(~mask)) { + BIC(AL, 0, d.reg, s, imm(~mask)); // component = packed & mask; + } else { + MOV(AL, 0, d.reg, reg_imm(s, LSL, 32-h)); + l += 32-h; + h = 32; + } + s = d.reg; + } + + if (l) { + MOV(AL, 0, d.reg, reg_imm(s, LSR, l)); // component = packed >> l; + s = d.reg; + } + + if (s != d.reg) { + MOV(AL, 0, d.reg, s); + } + + d.s = maskLen; +} + +void GGLAssembler::extract(integer_t& d, const pixel_t& s, int component) +{ + extract(d, s.reg, + s.format.c[component].h, + s.format.c[component].l, + s.size()); +} + +void GGLAssembler::extract(component_t& d, const pixel_t& s, int component) +{ + integer_t r(d.reg, 32, d.flags); + extract(r, s.reg, + s.format.c[component].h, + s.format.c[component].l, + s.size()); + d = component_t(r); +} + + +void GGLAssembler::expand(integer_t& d, const component_t& s, int dbits) +{ + if (s.l || (s.flags & CLEAR_HI)) { + extract(d, s.reg, s.h, s.l, 32); + expand(d, d, dbits); + } else { + expand(d, integer_t(s.reg, s.size(), s.flags), dbits); + } +} + +void GGLAssembler::expand(component_t& d, const component_t& s, int dbits) +{ + integer_t r(d.reg, 32, d.flags); + expand(r, s, dbits); + d = component_t(r); +} + +void GGLAssembler::expand(integer_t& dst, const integer_t& src, int dbits) +{ + assert(src.size()); + + int sbits = src.size(); + int s = src.reg; + int d = dst.reg; + + // be sure to set 'dst' after we read 'src' as they may be identical + dst.s = dbits; + dst.flags = 0; + + if (dbits<=sbits) { + if (s != d) { + MOV(AL, 0, d, s); + } + return; + } + + if (sbits == 1) { + RSB(AL, 0, d, s, reg_imm(s, LSL, dbits)); + // d = (s<<dbits) - s; + return; + } + + if (dbits % sbits) { + MOV(AL, 0, d, reg_imm(s, LSL, dbits-sbits)); + // d = s << (dbits-sbits); + dbits -= sbits; + do { + ORR(AL, 0, d, d, reg_imm(d, LSR, sbits)); + // d |= d >> sbits; + dbits -= sbits; + sbits *= 2; + } while(dbits>0); + return; + } + + dbits -= sbits; + do { + ORR(AL, 0, d, s, reg_imm(s, LSL, sbits)); + // d |= d<<sbits; + s = d; + dbits -= sbits; + if (sbits*2 < dbits) { + sbits *= 2; + } + } while(dbits>0); +} + +void GGLAssembler::downshift( + pixel_t& d, int component, component_t s, const reg_t& dither) +{ + const needs_t& needs = mBuilderContext.needs; + Scratch scratches(registerFile()); + + int sh = s.h; + int sl = s.l; + int maskHiBits = (sh!=32) ? ((s.flags & CLEAR_HI)?1:0) : 0; + int maskLoBits = (sl!=0) ? ((s.flags & CLEAR_LO)?1:0) : 0; + int sbits = sh - sl; + + int dh = d.format.c[component].h; + int dl = d.format.c[component].l; + int dbits = dh - dl; + int dithering = 0; + + LOGE_IF(sbits<dbits, "sbits (%d) < dbits (%d) in downshift", sbits, dbits); + + if (sbits>dbits) { + // see if we need to dither + dithering = mDithering; + } + + int ireg = d.reg; + if (!(d.flags & FIRST)) { + if (s.flags & CORRUPTIBLE) { + ireg = s.reg; + } else { + ireg = scratches.obtain(); + } + } + d.flags &= ~FIRST; + + if (maskHiBits) { + // we need to mask the high bits (and possibly the lowbits too) + // and we might be able to use immediate mask. + if (!dithering) { + // we don't do this if we only have maskLoBits because we can + // do it more efficiently below (in the case where dl=0) + const int offset = sh - dbits; + if (dbits<=8 && offset >= 0) { + const uint32_t mask = ((1<<dbits)-1) << offset; + if (isValidImmediate(mask) || isValidImmediate(~mask)) { + build_and_immediate(ireg, s.reg, mask, 32); + sl = offset; + s.reg = ireg; + sbits = dbits; + maskLoBits = maskHiBits = 0; + } + } + } else { + // in the dithering case though, we need to preserve the lower bits + const uint32_t mask = ((1<<sbits)-1) << sl; + if (isValidImmediate(mask) || isValidImmediate(~mask)) { + build_and_immediate(ireg, s.reg, mask, 32); + s.reg = ireg; + maskLoBits = maskHiBits = 0; + } + } + } + + // XXX: we could special case (maskHiBits & !maskLoBits) + // like we do for maskLoBits below, but it happens very rarely + // that we have maskHiBits only and the conditions necessary to lead + // to better code (like doing d |= s << 24) + + if (maskHiBits) { + MOV(AL, 0, ireg, reg_imm(s.reg, LSL, 32-sh)); + sl += 32-sh; + sh = 32; + s.reg = ireg; + maskHiBits = 0; + } + + // Downsampling should be performed as follows: + // V * ((1<<dbits)-1) / ((1<<sbits)-1) + // V * [(1<<dbits)/((1<<sbits)-1) - 1/((1<<sbits)-1)] + // V * [1/((1<<sbits)-1)>>dbits - 1/((1<<sbits)-1)] + // V/((1<<(sbits-dbits))-(1>>dbits)) - (V>>sbits)/((1<<sbits)-1)>>sbits + // V/((1<<(sbits-dbits))-(1>>dbits)) - (V>>sbits)/(1-(1>>sbits)) + // + // By approximating (1>>dbits) and (1>>sbits) to 0: + // + // V>>(sbits-dbits) - V>>sbits + // + // A good approximation is V>>(sbits-dbits), + // but better one (needed for dithering) is: + // + // (V>>(sbits-dbits)<<sbits - V)>>sbits + // (V<<dbits - V)>>sbits + // (V - V>>dbits)>>(sbits-dbits) + + // Dithering is done here + if (dithering) { + comment("dithering"); + if (sl) { + MOV(AL, 0, ireg, reg_imm(s.reg, LSR, sl)); + sh -= sl; + sl = 0; + s.reg = ireg; + } + // scaling (V-V>>dbits) + SUB(AL, 0, ireg, s.reg, reg_imm(s.reg, LSR, dbits)); + const int shift = (GGL_DITHER_BITS - (sbits-dbits)); + if (shift>0) ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSR, shift)); + else if (shift<0) ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSL,-shift)); + else ADD(AL, 0, ireg, ireg, dither.reg); + s.reg = ireg; + } + + if ((maskLoBits|dithering) && (sh > dbits)) { + int shift = sh-dbits; + if (dl) { + MOV(AL, 0, ireg, reg_imm(s.reg, LSR, shift)); + if (ireg == d.reg) { + MOV(AL, 0, d.reg, reg_imm(ireg, LSL, dl)); + } else { + ORR(AL, 0, d.reg, d.reg, reg_imm(ireg, LSL, dl)); + } + } else { + if (ireg == d.reg) { + MOV(AL, 0, d.reg, reg_imm(s.reg, LSR, shift)); + } else { + ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSR, shift)); + } + } + } else { + int shift = sh-dh; + if (shift>0) { + if (ireg == d.reg) { + MOV(AL, 0, d.reg, reg_imm(s.reg, LSR, shift)); + } else { + ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSR, shift)); + } + } else if (shift<0) { + if (ireg == d.reg) { + MOV(AL, 0, d.reg, reg_imm(s.reg, LSL, -shift)); + } else { + ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSL, -shift)); + } + } else { + if (ireg == d.reg) { + if (s.reg != d.reg) { + MOV(AL, 0, d.reg, s.reg); + } + } else { + ORR(AL, 0, d.reg, d.reg, s.reg); + } + } + } +} + +}; // namespace android diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp new file mode 100644 index 0000000..90e6584 --- /dev/null +++ b/libpixelflinger/codeflinger/texturing.cpp @@ -0,0 +1,1251 @@ +/* libs/pixelflinger/codeflinger/texturing.cpp +** +** 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 <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include "codeflinger/GGLAssembler.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +// iterators are initialized like this: +// (intToFixedCenter(x) * dx)>>16 + x0 +// ((x<<16 + 0x8000) * dx)>>16 + x0 +// ((x<<16)*dx + (0x8000*dx))>>16 + x0 +// ( (x*dx) + dx>>1 ) + x0 +// (x*dx) + (dx>>1 + x0) + +void GGLAssembler::init_iterated_color(fragment_parts_t& parts, const reg_t& x) +{ + context_t const* c = mBuilderContext.c; + const needs_t& needs = mBuilderContext.needs; + + if (mSmooth) { + // NOTE: we could take this case in the mDithering + !mSmooth case, + // but this would use up to 4 more registers for the color components + // for only a little added quality. + // Currently, this causes the system to run out of registers in + // some case (see issue #719496) + + comment("compute initial iterated color (smooth and/or dither case)"); + + parts.iterated_packed = 0; + parts.packed = 0; + + // 0x1: color component + // 0x2: iterators + const int optReload = mOptLevel >> 1; + if (optReload >= 3) parts.reload = 0; // reload nothing + else if (optReload == 2) parts.reload = 2; // reload iterators + else if (optReload == 1) parts.reload = 1; // reload colors + else if (optReload <= 0) parts.reload = 3; // reload both + + if (!mSmooth) { + // we're not smoothing (just dithering), we never have to + // reload the iterators + parts.reload &= ~2; + } + + Scratch scratches(registerFile()); + const int t0 = (parts.reload & 1) ? scratches.obtain() : 0; + const int t1 = (parts.reload & 2) ? scratches.obtain() : 0; + for (int i=0 ; i<4 ; i++) { + if (!mInfo[i].iterated) + continue; + + // this component exists in the destination and is not replaced + // by a texture unit. + const int c = (parts.reload & 1) ? t0 : obtainReg(); + if (i==0) CONTEXT_LOAD(c, iterators.ydady); + if (i==1) CONTEXT_LOAD(c, iterators.ydrdy); + if (i==2) CONTEXT_LOAD(c, iterators.ydgdy); + if (i==3) CONTEXT_LOAD(c, iterators.ydbdy); + parts.argb[i].reg = c; + + if (mInfo[i].smooth) { + parts.argb_dx[i].reg = (parts.reload & 2) ? t1 : obtainReg(); + const int dvdx = parts.argb_dx[i].reg; + CONTEXT_LOAD(dvdx, generated_vars.argb[i].dx); + MLA(AL, 0, c, x.reg, dvdx, c); + + // adjust the color iterator to make sure it won't overflow + if (!mAA) { + // this is not needed when we're using anti-aliasing + // because we will (have to) clamp the components + // anyway. + int end = scratches.obtain(); + MOV(AL, 0, end, reg_imm(parts.count.reg, LSR, 16)); + MLA(AL, 1, end, dvdx, end, c); + SUB(MI, 0, c, c, end); + BIC(AL, 0, c, c, reg_imm(c, ASR, 31)); + scratches.recycle(end); + } + } + + if (parts.reload & 1) { + CONTEXT_STORE(c, generated_vars.argb[i].c); + } + } + } else { + // We're not smoothed, so we can + // just use a packed version of the color and extract the + // components as needed (or not at all if we don't blend) + + // figure out if we need the iterated color + int load = 0; + for (int i=0 ; i<4 ; i++) { + component_info_t& info = mInfo[i]; + if ((info.inDest || info.needed) && !info.replaced) + load |= 1; + } + + parts.iterated_packed = 1; + parts.packed = (!mTextureMachine.mask && !mBlending + && !mFog && !mDithering); + parts.reload = 0; + if (load || parts.packed) { + if (mBlending || mDithering || mInfo[GGLFormat::ALPHA].needed) { + comment("load initial iterated color (8888 packed)"); + parts.iterated.setTo(obtainReg(), + &(c->formats[GGL_PIXEL_FORMAT_RGBA_8888])); + CONTEXT_LOAD(parts.iterated.reg, packed8888); + } else { + comment("load initial iterated color (dest format packed)"); + + parts.iterated.setTo(obtainReg(), &mCbFormat); + + // pre-mask the iterated color + const int bits = parts.iterated.size(); + const uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1; + uint32_t mask = 0; + if (mMasking) { + for (int i=0 ; i<4 ; i++) { + const int component_mask = 1<<i; + const int h = parts.iterated.format.c[i].h; + const int l = parts.iterated.format.c[i].l; + if (h && (!(mMasking & component_mask))) { + mask |= ((1<<(h-l))-1) << l; + } + } + } + + if (mMasking && ((mask & size)==0)) { + // none of the components are present in the mask + } else { + CONTEXT_LOAD(parts.iterated.reg, packed); + if (mCbFormat.size == 1) { + AND(AL, 0, parts.iterated.reg, + parts.iterated.reg, imm(0xFF)); + } else if (mCbFormat.size == 2) { + MOV(AL, 0, parts.iterated.reg, + reg_imm(parts.iterated.reg, LSR, 16)); + } + } + + // pre-mask the iterated color + if (mMasking) { + build_and_immediate(parts.iterated.reg, parts.iterated.reg, + mask, bits); + } + } + } + } +} + +void GGLAssembler::build_iterated_color( + component_t& fragment, + const fragment_parts_t& parts, + int component, + Scratch& regs) +{ + fragment.setTo( regs.obtain(), 0, 32, CORRUPTIBLE); + + if (!mInfo[component].iterated) + return; + + if (parts.iterated_packed) { + // iterated colors are packed, extract the one we need + extract(fragment, parts.iterated, component); + } else { + fragment.h = GGL_COLOR_BITS; + fragment.l = GGL_COLOR_BITS - 8; + fragment.flags |= CLEAR_LO; + // iterated colors are held in their own register, + // (smooth and/or dithering case) + if (parts.reload==3) { + // this implies mSmooth + Scratch scratches(registerFile()); + int dx = scratches.obtain(); + CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c); + CONTEXT_LOAD(dx, generated_vars.argb[component].dx); + ADD(AL, 0, dx, fragment.reg, dx); + CONTEXT_STORE(dx, generated_vars.argb[component].c); + } else if (parts.reload & 1) { + CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c); + } else { + // we don't reload, so simply rename the register and mark as + // non CORRUPTIBLE so that the texture env or blending code + // won't modify this (renamed) register + regs.recycle(fragment.reg); + fragment.reg = parts.argb[component].reg; + fragment.flags &= ~CORRUPTIBLE; + } + if (mInfo[component].smooth && mAA) { + // when using smooth shading AND anti-aliasing, we need to clamp + // the iterators because there is always an extra pixel on the + // edges, which most of the time will cause an overflow + // (since technically its outside of the domain). + BIC(AL, 0, fragment.reg, fragment.reg, + reg_imm(fragment.reg, ASR, 31)); + component_sat(fragment); + } + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::decodeLogicOpNeeds(const needs_t& needs) +{ + // gather some informations about the components we need to process... + const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR; + switch(opcode) { + case GGL_COPY: + mLogicOp = 0; + break; + case GGL_CLEAR: + case GGL_SET: + mLogicOp = LOGIC_OP; + break; + case GGL_AND: + case GGL_AND_REVERSE: + case GGL_AND_INVERTED: + case GGL_XOR: + case GGL_OR: + case GGL_NOR: + case GGL_EQUIV: + case GGL_OR_REVERSE: + case GGL_OR_INVERTED: + case GGL_NAND: + mLogicOp = LOGIC_OP|LOGIC_OP_SRC|LOGIC_OP_DST; + break; + case GGL_NOOP: + case GGL_INVERT: + mLogicOp = LOGIC_OP|LOGIC_OP_DST; + break; + case GGL_COPY_INVERTED: + mLogicOp = LOGIC_OP|LOGIC_OP_SRC; + break; + }; +} + +void GGLAssembler::decodeTMUNeeds(const needs_t& needs, context_t const* c) +{ + uint8_t replaced=0; + mTextureMachine.mask = 0; + mTextureMachine.activeUnits = 0; + for (int i=GGL_TEXTURE_UNIT_COUNT-1 ; i>=0 ; i--) { + texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (replaced == 0xF) { + // all components are replaced, skip this TMU. + tmu.format_idx = 0; + tmu.mask = 0; + tmu.replaced = replaced; + continue; + } + tmu.format_idx = GGL_READ_NEEDS(T_FORMAT, needs.t[i]); + tmu.format = c->formats[tmu.format_idx]; + tmu.bits = tmu.format.size*8; + tmu.swrap = GGL_READ_NEEDS(T_S_WRAP, needs.t[i]); + tmu.twrap = GGL_READ_NEEDS(T_T_WRAP, needs.t[i]); + tmu.env = ggl_needs_to_env(GGL_READ_NEEDS(T_ENV, needs.t[i])); + tmu.pot = GGL_READ_NEEDS(T_POT, needs.t[i]); + tmu.linear = GGL_READ_NEEDS(T_LINEAR, needs.t[i]) + && tmu.format.size!=3; // XXX: only 8, 16 and 32 modes for now + + // 5551 linear filtering is not supported + if (tmu.format_idx == GGL_PIXEL_FORMAT_RGBA_5551) + tmu.linear = 0; + + tmu.mask = 0; + tmu.replaced = replaced; + + if (tmu.format_idx) { + mTextureMachine.activeUnits++; + if (tmu.format.c[0].h) tmu.mask |= 0x1; + if (tmu.format.c[1].h) tmu.mask |= 0x2; + if (tmu.format.c[2].h) tmu.mask |= 0x4; + if (tmu.format.c[3].h) tmu.mask |= 0x8; + if (tmu.env == GGL_REPLACE) { + replaced |= tmu.mask; + } else if (tmu.env == GGL_DECAL) { + if (!tmu.format.c[GGLFormat::ALPHA].h) { + // if we don't have alpha, decal does nothing + tmu.mask = 0; + } else { + // decal always ignores At + tmu.mask &= ~(1<<GGLFormat::ALPHA); + } + } + } + mTextureMachine.mask |= tmu.mask; + //printf("%d: mask=%08lx, replaced=%08lx\n", + // i, int(tmu.mask), int(tmu.replaced)); + } + mTextureMachine.replaced = replaced; + mTextureMachine.directTexture = 0; + //printf("replaced=%08lx\n", mTextureMachine.replaced); +} + + +void GGLAssembler::init_textures( + tex_coord_t* coords, + const reg_t& x, const reg_t& y) +{ + context_t const* c = mBuilderContext.c; + const needs_t& needs = mBuilderContext.needs; + int Rctx = mBuilderContext.Rctx; + int Rx = x.reg; + int Ry = y.reg; + + if (mTextureMachine.mask) { + comment("compute texture coordinates"); + } + + // init texture coordinates for each tmu + const int cb_format_idx = GGL_READ_NEEDS(CB_FORMAT, needs.n); + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + if ((tmu.swrap == GGL_NEEDS_WRAP_11) && + (tmu.twrap == GGL_NEEDS_WRAP_11)) + { + // 1:1 texture + pointer_t& txPtr = coords[i].ptr; + txPtr.setTo(obtainReg(), tmu.bits); + CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydsdy); + ADD(AL, 0, Rx, Rx, reg_imm(txPtr.reg, ASR, 16)); // x += (s>>16) + CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydtdy); + ADD(AL, 0, Ry, Ry, reg_imm(txPtr.reg, ASR, 16)); // y += (t>>16) + // merge base & offset + CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].stride); + SMLABB(AL, Rx, Ry, txPtr.reg, Rx); // x+y*stride + CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); + base_offset(txPtr, txPtr, Rx); + } else { + Scratch scratches(registerFile()); + reg_t& s = coords[i].s; + reg_t& t = coords[i].t; + // s = (x * dsdx)>>16 + ydsdy + // s = (x * dsdx)>>16 + (y*dsdy)>>16 + s0 + // t = (x * dtdx)>>16 + ydtdy + // t = (x * dtdx)>>16 + (y*dtdy)>>16 + t0 + s.setTo(obtainReg()); + t.setTo(obtainReg()); + const int need_w = GGL_READ_NEEDS(W, needs.n); + if (need_w) { + CONTEXT_LOAD(s.reg, state.texture[i].iterators.ydsdy); + CONTEXT_LOAD(t.reg, state.texture[i].iterators.ydtdy); + } else { + int ydsdy = scratches.obtain(); + int ydtdy = scratches.obtain(); + CONTEXT_LOAD(s.reg, generated_vars.texture[i].dsdx); + CONTEXT_LOAD(ydsdy, state.texture[i].iterators.ydsdy); + CONTEXT_LOAD(t.reg, generated_vars.texture[i].dtdx); + CONTEXT_LOAD(ydtdy, state.texture[i].iterators.ydtdy); + MLA(AL, 0, s.reg, Rx, s.reg, ydsdy); + MLA(AL, 0, t.reg, Rx, t.reg, ydtdy); + } + + if ((mOptLevel&1)==0) { + CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]); + CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]); + recycleReg(s.reg); + recycleReg(t.reg); + } + } + + // direct texture? + if (!multiTexture && !mBlending && !mDithering && !mFog && + cb_format_idx == tmu.format_idx && !tmu.linear && + mTextureMachine.replaced == tmu.mask) + { + mTextureMachine.directTexture = i + 1; + } + } +} + +void GGLAssembler::build_textures( fragment_parts_t& parts, + Scratch& regs) +{ + context_t const* c = mBuilderContext.c; + const needs_t& needs = mBuilderContext.needs; + int Rctx = mBuilderContext.Rctx; + + // We don't have a way to spill registers automatically + // spill depth and AA regs, when we know we may have to. + // build the spill list... + uint32_t spill_list = 0; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + if (tmu.linear) { + // we may run out of register if we have linear filtering + // at 1 or 4 bytes / pixel on any texture unit. + if (tmu.format.size == 1) { + // if depth and AA enabled, we'll run out of 1 register + if (parts.z.reg > 0 && parts.covPtr.reg > 0) + spill_list |= 1<<parts.covPtr.reg; + } + if (tmu.format.size == 4) { + // if depth or AA enabled, we'll run out of 1 or 2 registers + if (parts.z.reg > 0) + spill_list |= 1<<parts.z.reg; + if (parts.covPtr.reg > 0) + spill_list |= 1<<parts.covPtr.reg; + } + } + } + + Spill spill(registerFile(), *this, spill_list); + + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + + pointer_t& txPtr = parts.coords[i].ptr; + pixel_t& texel = parts.texel[i]; + + // repeat... + if ((tmu.swrap == GGL_NEEDS_WRAP_11) && + (tmu.twrap == GGL_NEEDS_WRAP_11)) + { // 1:1 textures + comment("fetch texel"); + texel.setTo(regs.obtain(), &tmu.format); + load(txPtr, texel, WRITE_BACK); + } else { + Scratch scratches(registerFile()); + reg_t& s = parts.coords[i].s; + reg_t& t = parts.coords[i].t; + if ((mOptLevel&1)==0) { + comment("reload s/t (multitexture or linear filtering)"); + s.reg = scratches.obtain(); + t.reg = scratches.obtain(); + CONTEXT_LOAD(s.reg, generated_vars.texture[i].spill[0]); + CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]); + } + + comment("compute repeat/clamp"); + int u = scratches.obtain(); + int v = scratches.obtain(); + int width = scratches.obtain(); + int height = scratches.obtain(); + int U = 0; + int V = 0; + + CONTEXT_LOAD(width, generated_vars.texture[i].width); + CONTEXT_LOAD(height, generated_vars.texture[i].height); + + int FRAC_BITS = 0; + if (tmu.linear) { + // linear interpolation + if (tmu.format.size == 1) { + // for 8-bits textures, we can afford + // 7 bits of fractional precision at no + // additional cost (we can't do 8 bits + // because filter8 uses signed 16 bits muls) + FRAC_BITS = 7; + } else if (tmu.format.size == 2) { + // filter16() is internally limited to 4 bits, so: + // FRAC_BITS=2 generates less instructions, + // FRAC_BITS=3,4,5 creates unpleasant artifacts, + // FRAC_BITS=6+ looks good + FRAC_BITS = 6; + } else if (tmu.format.size == 4) { + // filter32() is internally limited to 8 bits, so: + // FRAC_BITS=4 looks good + // FRAC_BITS=5+ looks better, but generates 3 extra ipp + FRAC_BITS = 6; + } else { + // for all other cases we use 4 bits. + FRAC_BITS = 4; + } + } + wrapping(u, s.reg, width, tmu.swrap, FRAC_BITS); + wrapping(v, t.reg, height, tmu.twrap, FRAC_BITS); + + if (tmu.linear) { + comment("compute linear filtering offsets"); + // pixel size scale + const int shift = 31 - gglClz(tmu.format.size); + U = scratches.obtain(); + V = scratches.obtain(); + + // sample the texel center + SUB(AL, 0, u, u, imm(1<<(FRAC_BITS-1))); + SUB(AL, 0, v, v, imm(1<<(FRAC_BITS-1))); + + // get the fractionnal part of U,V + AND(AL, 0, U, u, imm((1<<FRAC_BITS)-1)); + AND(AL, 0, V, v, imm((1<<FRAC_BITS)-1)); + + // compute width-1 and height-1 + SUB(AL, 0, width, width, imm(1)); + SUB(AL, 0, height, height, imm(1)); + + // get the integer part of U,V and clamp/wrap + // and compute offset to the next texel + if (tmu.swrap == GGL_NEEDS_WRAP_REPEAT) { + // u has already been REPEATed + MOV(AL, 1, u, reg_imm(u, ASR, FRAC_BITS)); + MOV(MI, 0, u, width); + CMP(AL, u, width); + MOV(LT, 0, width, imm(1 << shift)); + if (shift) + MOV(GE, 0, width, reg_imm(width, LSL, shift)); + RSB(GE, 0, width, width, imm(0)); + } else { + // u has not been CLAMPed yet + // algorithm: + // if ((u>>4) >= width) + // u = width<<4 + // width = 0 + // else + // width = 1<<shift + // u = u>>4; // get integer part + // if (u<0) + // u = 0 + // width = 0 + // generated_vars.rt = width + + CMP(AL, width, reg_imm(u, ASR, FRAC_BITS)); + MOV(LE, 0, u, reg_imm(width, LSL, FRAC_BITS)); + MOV(LE, 0, width, imm(0)); + MOV(GT, 0, width, imm(1 << shift)); + MOV(AL, 1, u, reg_imm(u, ASR, FRAC_BITS)); + MOV(MI, 0, u, imm(0)); + MOV(MI, 0, width, imm(0)); + } + CONTEXT_STORE(width, generated_vars.rt); + + const int stride = width; + CONTEXT_LOAD(stride, generated_vars.texture[i].stride); + if (tmu.twrap == GGL_NEEDS_WRAP_REPEAT) { + // v has already been REPEATed + MOV(AL, 1, v, reg_imm(v, ASR, FRAC_BITS)); + MOV(MI, 0, v, height); + CMP(AL, v, height); + MOV(LT, 0, height, imm(1 << shift)); + if (shift) + MOV(GE, 0, height, reg_imm(height, LSL, shift)); + RSB(GE, 0, height, height, imm(0)); + MUL(AL, 0, height, stride, height); + } else { + // u has not been CLAMPed yet + CMP(AL, height, reg_imm(v, ASR, FRAC_BITS)); + MOV(LE, 0, v, reg_imm(height, LSL, FRAC_BITS)); + MOV(LE, 0, height, imm(0)); + if (shift) { + MOV(GT, 0, height, reg_imm(stride, LSL, shift)); + } else { + MOV(GT, 0, height, stride); + } + MOV(AL, 1, v, reg_imm(v, ASR, FRAC_BITS)); + MOV(MI, 0, v, imm(0)); + MOV(MI, 0, height, imm(0)); + } + CONTEXT_STORE(height, generated_vars.lb); + } + + scratches.recycle(width); + scratches.recycle(height); + + // iterate texture coordinates... + comment("iterate s,t"); + int dsdx = scratches.obtain(); + int dtdx = scratches.obtain(); + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); + CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); + ADD(AL, 0, s.reg, s.reg, dsdx); + ADD(AL, 0, t.reg, t.reg, dtdx); + if ((mOptLevel&1)==0) { + CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]); + CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]); + scratches.recycle(s.reg); + scratches.recycle(t.reg); + } + scratches.recycle(dsdx); + scratches.recycle(dtdx); + + // merge base & offset... + comment("merge base & offset"); + texel.setTo(regs.obtain(), &tmu.format); + txPtr.setTo(texel.reg, tmu.bits); + int stride = scratches.obtain(); + CONTEXT_LOAD(stride, generated_vars.texture[i].stride); + CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); + SMLABB(AL, u, v, stride, u); // u+v*stride + base_offset(txPtr, txPtr, u); + + // load texel + if (!tmu.linear) { + comment("fetch texel"); + load(txPtr, texel, 0); + } else { + // recycle registers we don't need anymore + scratches.recycle(u); + scratches.recycle(v); + scratches.recycle(stride); + + comment("fetch texel, bilinear"); + switch (tmu.format.size) { + case 1: filter8(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break; + case 2: filter16(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break; + case 3: filter24(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break; + case 4: filter32(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break; + } + } + } + } +} + +void GGLAssembler::build_iterate_texture_coordinates( + const fragment_parts_t& parts) +{ + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + + if ((tmu.swrap == GGL_NEEDS_WRAP_11) && + (tmu.twrap == GGL_NEEDS_WRAP_11)) + { // 1:1 textures + const pointer_t& txPtr = parts.coords[i].ptr; + ADD(AL, 0, txPtr.reg, txPtr.reg, imm(txPtr.size>>3)); + } else { + Scratch scratches(registerFile()); + int s = parts.coords[i].s.reg; + int t = parts.coords[i].t.reg; + if ((mOptLevel&1)==0) { + s = scratches.obtain(); + t = scratches.obtain(); + CONTEXT_LOAD(s, generated_vars.texture[i].spill[0]); + CONTEXT_LOAD(t, generated_vars.texture[i].spill[1]); + } + int dsdx = scratches.obtain(); + int dtdx = scratches.obtain(); + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); + CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); + ADD(AL, 0, s, s, dsdx); + ADD(AL, 0, t, t, dtdx); + if ((mOptLevel&1)==0) { + CONTEXT_STORE(s, generated_vars.texture[i].spill[0]); + CONTEXT_STORE(t, generated_vars.texture[i].spill[1]); + } + } + } +} + +void GGLAssembler::filter8( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS) +{ + if (tmu.format.components != GGL_ALPHA && + tmu.format.components != GGL_LUMINANCE) + { + // this is a packed format, and we don't support + // linear filtering (it's probably RGB 332) + // Should not happen with OpenGL|ES + LDRB(AL, texel.reg, txPtr.reg); + return; + } + + // ------------------------ + // about ~22 cycles / pixel + Scratch scratches(registerFile()); + + int pixel= scratches.obtain(); + int d = scratches.obtain(); + int u = scratches.obtain(); + int k = scratches.obtain(); + int rt = scratches.obtain(); + int lb = scratches.obtain(); + + // RB -> U * V + + CONTEXT_LOAD(rt, generated_vars.rt); + CONTEXT_LOAD(lb, generated_vars.lb); + + int offset = pixel; + ADD(AL, 0, offset, lb, rt); + LDRB(AL, pixel, txPtr.reg, reg_scale_pre(offset)); + SMULBB(AL, u, U, V); + SMULBB(AL, d, pixel, u); + RSB(AL, 0, k, u, imm(1<<(FRAC_BITS*2))); + + // LB -> (1-U) * V + RSB(AL, 0, U, U, imm(1<<FRAC_BITS)); + LDRB(AL, pixel, txPtr.reg, reg_scale_pre(lb)); + SMULBB(AL, u, U, V); + SMLABB(AL, d, pixel, u, d); + SUB(AL, 0, k, k, u); + + // LT -> (1-U)*(1-V) + RSB(AL, 0, V, V, imm(1<<FRAC_BITS)); + LDRB(AL, pixel, txPtr.reg); + SMULBB(AL, u, U, V); + SMLABB(AL, d, pixel, u, d); + + // RT -> U*(1-V) + LDRB(AL, pixel, txPtr.reg, reg_scale_pre(rt)); + SUB(AL, 0, u, k, u); + SMLABB(AL, texel.reg, pixel, u, d); + + for (int i=0 ; i<4 ; i++) { + if (!texel.format.c[i].h) continue; + texel.format.c[i].h = FRAC_BITS*2+8; + texel.format.c[i].l = FRAC_BITS*2; // keeping 8 bits in enough + } + texel.format.size = 4; + texel.format.bitsPerPixel = 32; + texel.flags |= CLEAR_LO; +} + +void GGLAssembler::filter16( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS) +{ + // compute the mask + // XXX: it would be nice if the mask below could be computed + // automatically. + uint32_t mask = 0; + int shift = 0; + int prec = 0; + switch (tmu.format_idx) { + case GGL_PIXEL_FORMAT_RGB_565: + // source: 00000ggg.ggg00000 | rrrrr000.000bbbbb + // result: gggggggg.gggrrrrr | rrrrr0bb.bbbbbbbb + mask = 0x07E0F81F; + shift = 16; + prec = 5; + break; + case GGL_PIXEL_FORMAT_RGBA_4444: + // 0000,1111,0000,1111 | 0000,1111,0000,1111 + mask = 0x0F0F0F0F; + shift = 12; + prec = 4; + break; + case GGL_PIXEL_FORMAT_LA_88: + // 0000,0000,1111,1111 | 0000,0000,1111,1111 + // AALL -> 00AA | 00LL + mask = 0x00FF00FF; + shift = 8; + prec = 8; + break; + default: + // unsupported format, do something sensical... + LOGE("Unsupported 16-bits texture format (%d)", tmu.format_idx); + LDRH(AL, texel.reg, txPtr.reg); + return; + } + + const int adjust = FRAC_BITS*2 - prec; + const int round = 0; + + // update the texel format + texel.format.size = 4; + texel.format.bitsPerPixel = 32; + texel.flags |= CLEAR_HI|CLEAR_LO; + for (int i=0 ; i<4 ; i++) { + if (!texel.format.c[i].h) continue; + const uint32_t offset = (mask & tmu.format.mask(i)) ? 0 : shift; + texel.format.c[i].h = tmu.format.c[i].h + offset + prec; + texel.format.c[i].l = texel.format.c[i].h - (tmu.format.bits(i) + prec); + } + + // ------------------------ + // about ~40 cycles / pixel + Scratch scratches(registerFile()); + + int pixel= scratches.obtain(); + int d = scratches.obtain(); + int u = scratches.obtain(); + int k = scratches.obtain(); + + // RB -> U * V + int offset = pixel; + CONTEXT_LOAD(offset, generated_vars.rt); + CONTEXT_LOAD(u, generated_vars.lb); + ADD(AL, 0, offset, offset, u); + + LDRH(AL, pixel, txPtr.reg, reg_pre(offset)); + SMULBB(AL, u, U, V); + ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift)); + build_and_immediate(pixel, pixel, mask, 32); + if (adjust) { + if (round) + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MUL(AL, 0, d, pixel, u); + RSB(AL, 0, k, u, imm(1<<prec)); + + // LB -> (1-U) * V + CONTEXT_LOAD(offset, generated_vars.lb); + RSB(AL, 0, U, U, imm(1<<FRAC_BITS)); + LDRH(AL, pixel, txPtr.reg, reg_pre(offset)); + SMULBB(AL, u, U, V); + ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift)); + build_and_immediate(pixel, pixel, mask, 32); + if (adjust) { + if (round) + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MLA(AL, 0, d, pixel, u, d); + SUB(AL, 0, k, k, u); + + // LT -> (1-U)*(1-V) + RSB(AL, 0, V, V, imm(1<<FRAC_BITS)); + LDRH(AL, pixel, txPtr.reg); + SMULBB(AL, u, U, V); + ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift)); + build_and_immediate(pixel, pixel, mask, 32); + if (adjust) { + if (round) + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MLA(AL, 0, d, pixel, u, d); + + // RT -> U*(1-V) + CONTEXT_LOAD(offset, generated_vars.rt); + LDRH(AL, pixel, txPtr.reg, reg_pre(offset)); + SUB(AL, 0, u, k, u); + ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift)); + build_and_immediate(pixel, pixel, mask, 32); + MLA(AL, 0, texel.reg, pixel, u, d); +} + +void GGLAssembler::filter24( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS) +{ + // not supported yet (currently disabled) + load(txPtr, texel, 0); +} + +void GGLAssembler::filter32( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS) +{ + const int adjust = FRAC_BITS*2 - 8; + const int round = 0; + + // ------------------------ + // about ~38 cycles / pixel + Scratch scratches(registerFile()); + + int pixel= scratches.obtain(); + int dh = scratches.obtain(); + int u = scratches.obtain(); + int k = scratches.obtain(); + + int temp = scratches.obtain(); + int dl = scratches.obtain(); + int mask = scratches.obtain(); + + MOV(AL, 0, mask, imm(0xFF)); + ORR(AL, 0, mask, mask, imm(0xFF0000)); + + // RB -> U * V + int offset = pixel; + CONTEXT_LOAD(offset, generated_vars.rt); + CONTEXT_LOAD(u, generated_vars.lb); + ADD(AL, 0, offset, offset, u); + + LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset)); + SMULBB(AL, u, U, V); + AND(AL, 0, temp, mask, pixel); + if (adjust) { + if (round) + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MUL(AL, 0, dh, temp, u); + AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8)); + MUL(AL, 0, dl, temp, u); + RSB(AL, 0, k, u, imm(0x100)); + + // LB -> (1-U) * V + CONTEXT_LOAD(offset, generated_vars.lb); + RSB(AL, 0, U, U, imm(1<<FRAC_BITS)); + LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset)); + SMULBB(AL, u, U, V); + AND(AL, 0, temp, mask, pixel); + if (adjust) { + if (round) + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MLA(AL, 0, dh, temp, u, dh); + AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8)); + MLA(AL, 0, dl, temp, u, dl); + SUB(AL, 0, k, k, u); + + // LT -> (1-U)*(1-V) + RSB(AL, 0, V, V, imm(1<<FRAC_BITS)); + LDR(AL, pixel, txPtr.reg); + SMULBB(AL, u, U, V); + AND(AL, 0, temp, mask, pixel); + if (adjust) { + if (round) + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MLA(AL, 0, dh, temp, u, dh); + AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8)); + MLA(AL, 0, dl, temp, u, dl); + + // RT -> U*(1-V) + CONTEXT_LOAD(offset, generated_vars.rt); + LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset)); + SUB(AL, 0, u, k, u); + AND(AL, 0, temp, mask, pixel); + MLA(AL, 0, dh, temp, u, dh); + AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8)); + MLA(AL, 0, dl, temp, u, dl); + + AND(AL, 0, dh, mask, reg_imm(dh, LSR, 8)); + AND(AL, 0, dl, dl, reg_imm(mask, LSL, 8)); + ORR(AL, 0, texel.reg, dh, dl); +} + +void GGLAssembler::build_texture_environment( + component_t& fragment, + const fragment_parts_t& parts, + int component, + Scratch& regs) +{ + const uint32_t component_mask = 1<<component; + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + texture_unit_t& tmu = mTextureMachine.tmu[i]; + + if (tmu.mask & component_mask) { + // replace or modulate with this texture + if ((tmu.replaced & component_mask) == 0) { + // not replaced by a later tmu... + + Scratch scratches(registerFile()); + pixel_t texel(parts.texel[i]); + if (multiTexture && + tmu.swrap == GGL_NEEDS_WRAP_11 && + tmu.twrap == GGL_NEEDS_WRAP_11) + { + texel.reg = scratches.obtain(); + texel.flags |= CORRUPTIBLE; + comment("fetch texel (multitexture 1:1)"); + load(parts.coords[i].ptr, texel, WRITE_BACK); + } + + component_t incoming(fragment); + modify(fragment, regs); + + switch (tmu.env) { + case GGL_REPLACE: + extract(fragment, texel, component); + break; + case GGL_MODULATE: + modulate(fragment, incoming, texel, component); + break; + case GGL_DECAL: + decal(fragment, incoming, texel, component); + break; + case GGL_BLEND: + blend(fragment, incoming, texel, component, i); + break; + case GGL_ADD: + add(fragment, incoming, texel, component); + break; + } + } + } + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::wrapping( + int d, + int coord, int size, + int tx_wrap, int tx_linear) +{ + // notes: + // if tx_linear is set, we need 4 extra bits of precision on the result + // SMULL/UMULL is 3 cycles + Scratch scratches(registerFile()); + int c = coord; + if (tx_wrap == GGL_NEEDS_WRAP_REPEAT) { + // UMULL takes 4 cycles (interlocked), and we can get away with + // 2 cycles using SMULWB, but we're loosing 16 bits of precision + // out of 32 (this is not a problem because the iterator keeps + // its full precision) + // UMULL(AL, 0, size, d, c, size); + // note: we can't use SMULTB because it's signed. + MOV(AL, 0, d, reg_imm(c, LSR, 16-tx_linear)); + SMULWB(AL, d, d, size); + } else if (tx_wrap == GGL_NEEDS_WRAP_CLAMP_TO_EDGE) { + if (tx_linear) { + // 1 cycle + MOV(AL, 0, d, reg_imm(coord, ASR, 16-tx_linear)); + } else { + // 4 cycles (common case) + MOV(AL, 0, d, reg_imm(coord, ASR, 16)); + BIC(AL, 0, d, d, reg_imm(d, ASR, 31)); + CMP(AL, d, size); + SUB(GE, 0, d, size, imm(1)); + } + } +} + +// --------------------------------------------------------------------------- + +void GGLAssembler::modulate( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + Scratch locals(registerFile()); + integer_t texel(locals.obtain(), 32, CORRUPTIBLE); + extract(texel, incomingTexel, component); + + const int Nt = texel.size(); + // Nt should always be less than 10 bits because it comes + // from the TMU. + + int Ni = incoming.size(); + // Ni could be big because it comes from previous MODULATEs + + if (Nt == 1) { + // texel acts as a bit-mask + // dest = incoming & ((texel << incoming.h)-texel) + RSB(AL, 0, dest.reg, texel.reg, reg_imm(texel.reg, LSL, incoming.h)); + AND(AL, 0, dest.reg, dest.reg, incoming.reg); + dest.l = incoming.l; + dest.h = incoming.h; + dest.flags |= (incoming.flags & CLEAR_LO); + } else if (Ni == 1) { + MOV(AL, 0, dest.reg, reg_imm(incoming.reg, LSL, 31-incoming.h)); + AND(AL, 0, dest.reg, texel.reg, reg_imm(dest.reg, ASR, 31)); + dest.l = 0; + dest.h = Nt; + } else { + int inReg = incoming.reg; + int shift = incoming.l; + if ((Nt + Ni) > 32) { + // we will overflow, reduce the precision of Ni to 8 bits + // (Note Nt cannot be more than 10 bits which happens with + // 565 textures and GGL_LINEAR) + shift += Ni-8; + Ni = 8; + } + + // modulate by the component with the lowest precision + if (Nt >= Ni) { + if (shift) { + // XXX: we should be able to avoid this shift + // when shift==16 && Nt<16 && Ni<16, in which + // we could use SMULBT below. + MOV(AL, 0, dest.reg, reg_imm(inReg, LSR, shift)); + inReg = dest.reg; + shift = 0; + } + // operation: (Cf*Ct)/((1<<Ni)-1) + // approximated with: Cf*(Ct + Ct>>(Ni-1))>>Ni + // this operation doesn't change texel's size + ADD(AL, 0, dest.reg, inReg, reg_imm(inReg, LSR, Ni-1)); + if (Nt<16 && Ni<16) SMULBB(AL, dest.reg, texel.reg, dest.reg); + else MUL(AL, 0, dest.reg, texel.reg, dest.reg); + dest.l = Ni; + dest.h = Nt + Ni; + } else { + if (shift && (shift != 16)) { + // if shift==16, we can use 16-bits mul instructions later + MOV(AL, 0, dest.reg, reg_imm(inReg, LSR, shift)); + inReg = dest.reg; + shift = 0; + } + // operation: (Cf*Ct)/((1<<Nt)-1) + // approximated with: Ct*(Cf + Cf>>(Nt-1))>>Nt + // this operation doesn't change incoming's size + Scratch scratches(registerFile()); + int t = (texel.flags & CORRUPTIBLE) ? texel.reg : dest.reg; + if (t == inReg) + t = scratches.obtain(); + ADD(AL, 0, t, texel.reg, reg_imm(texel.reg, LSR, Nt-1)); + if (Nt<16 && Ni<16) { + if (shift==16) SMULBT(AL, dest.reg, t, inReg); + else SMULBB(AL, dest.reg, t, inReg); + } else MUL(AL, 0, dest.reg, t, inReg); + dest.l = Nt; + dest.h = Nt + Ni; + } + + // low bits are not valid + dest.flags |= CLEAR_LO; + + // no need to keep more than 8 bits/component + if (dest.size() > 8) + dest.l = dest.h-8; + } +} + +void GGLAssembler::decal( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + // RGBA: + // Cv = Cf*(1 - At) + Ct*At = Cf + (Ct - Cf)*At + // Av = Af + Scratch locals(registerFile()); + integer_t texel(locals.obtain(), 32, CORRUPTIBLE); + integer_t factor(locals.obtain(), 32, CORRUPTIBLE); + extract(texel, incomingTexel, component); + extract(factor, incomingTexel, GGLFormat::ALPHA); + + // no need to keep more than 8-bits for decal + int Ni = incoming.size(); + int shift = incoming.l; + if (Ni > 8) { + shift += Ni-8; + Ni = 8; + } + integer_t incomingNorm(incoming.reg, Ni, incoming.flags); + if (shift) { + MOV(AL, 0, dest.reg, reg_imm(incomingNorm.reg, LSR, shift)); + incomingNorm.reg = dest.reg; + incomingNorm.flags |= CORRUPTIBLE; + } + ADD(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, LSR, factor.s-1)); + build_blendOneMinusFF(dest, factor, incomingNorm, texel); +} + +void GGLAssembler::blend( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component, int tmu) +{ + // RGBA: + // Cv = (1 - Ct)*Cf + Ct*Cc = Cf + (Cc - Cf)*Ct + // Av = At*Af + + if (component == GGLFormat::ALPHA) { + modulate(dest, incoming, incomingTexel, component); + return; + } + + Scratch locals(registerFile()); + integer_t color(locals.obtain(), 8, CORRUPTIBLE); + integer_t factor(locals.obtain(), 32, CORRUPTIBLE); + LDRB(AL, color.reg, mBuilderContext.Rctx, + immed12_pre(GGL_OFFSETOF(state.texture[tmu].env_color[component]))); + extract(factor, incomingTexel, component); + + // no need to keep more than 8-bits for blend + int Ni = incoming.size(); + int shift = incoming.l; + if (Ni > 8) { + shift += Ni-8; + Ni = 8; + } + integer_t incomingNorm(incoming.reg, Ni, incoming.flags); + if (shift) { + MOV(AL, 0, dest.reg, reg_imm(incomingNorm.reg, LSR, shift)); + incomingNorm.reg = dest.reg; + incomingNorm.flags |= CORRUPTIBLE; + } + ADD(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, LSR, factor.s-1)); + build_blendOneMinusFF(dest, factor, incomingNorm, color); +} + +void GGLAssembler::add( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + // RGBA: + // Cv = Cf + Ct; + Scratch locals(registerFile()); + + component_t incomingTemp(incoming); + + // use "dest" as a temporary for extracting the texel, unless "dest" + // overlaps "incoming". + integer_t texel(dest.reg, 32, CORRUPTIBLE); + if (dest.reg == incomingTemp.reg) + texel.reg = locals.obtain(); + extract(texel, incomingTexel, component); + + if (texel.s < incomingTemp.size()) { + expand(texel, texel, incomingTemp.size()); + } else if (texel.s > incomingTemp.size()) { + if (incomingTemp.flags & CORRUPTIBLE) { + expand(incomingTemp, incomingTemp, texel.s); + } else { + incomingTemp.reg = locals.obtain(); + expand(incomingTemp, incoming, texel.s); + } + } + + if (incomingTemp.l) { + ADD(AL, 0, dest.reg, texel.reg, + reg_imm(incomingTemp.reg, LSR, incomingTemp.l)); + } else { + ADD(AL, 0, dest.reg, texel.reg, incomingTemp.reg); + } + dest.l = 0; + dest.h = texel.size(); + component_sat(dest); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp new file mode 100644 index 0000000..5b92062 --- /dev/null +++ b/libpixelflinger/fixed.cpp @@ -0,0 +1,339 @@ +/* libs/pixelflinger/fixed.cpp +** +** 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 <stdio.h> + +#include <private/pixelflinger/ggl_context.h> +#include <private/pixelflinger/ggl_fixed.h> + + +// ------------------------------------------------------------------------ + +int32_t gglRecipQNormalized(int32_t x, int* exponent) +{ + const int32_t s = x>>31; + uint32_t a = s ? -x : x; + + // the result will overflow, so just set it to the biggest/inf value + if (ggl_unlikely(a <= 2LU)) { + *exponent = 0; + return s ? FIXED_MIN : FIXED_MAX; + } + + // Newton-Raphson iteration: + // x = r*(2 - a*r) + + const int32_t lz = gglClz(a); + a <<= lz; // 0.32 + uint32_t r = a; + // note: if a == 0x80000000, this means x was a power-of-2, in this + // case we don't need to compute anything. We get the reciprocal for + // (almost) free. + if (a != 0x80000000) { + r = (0x2E800 << (30-16)) - (r>>(2-1)); // 2.30, r = 2.90625 - 2*a + // 0.32 + 2.30 = 2.62 -> 2.30 + // 2.30 + 2.30 = 4.60 -> 2.30 + r = (((2LU<<30) - uint32_t((uint64_t(a)*r) >> 32)) * uint64_t(r)) >> 30; + r = (((2LU<<30) - uint32_t((uint64_t(a)*r) >> 32)) * uint64_t(r)) >> 30; + } + + // shift right 1-bit to make room for the sign bit + *exponent = 30-lz-1; + r >>= 1; + return s ? -r : r; +} + +int32_t gglRecipQ(GGLfixed x, int q) +{ + int shift; + x = gglRecipQNormalized(x, &shift); + shift += 16-q; + x += 1L << (shift-1); // rounding + x >>= shift; + return x; +} + +// ------------------------------------------------------------------------ + +GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) +{ + if ((d>>24) && ((d>>24)+1)) { + n >>= 8; + d >>= 8; + } + return gglMulx(n, gglRecip(d)); +} + +// ------------------------------------------------------------------------ + +static const GGLfixed ggl_sqrt_reciproc_approx_tab[8] = { + // 1/sqrt(x) with x = 1-N/16, N=[8...1] + 0x16A09, 0x15555, 0x143D1, 0x134BF, 0x1279A, 0x11C01, 0x111AC, 0x10865 +}; + +GGLfixed gglSqrtRecipx(GGLfixed x) +{ + if (x == 0) return FIXED_MAX; + if (x == FIXED_ONE) return x; + const GGLfixed a = x; + const int32_t lz = gglClz(x); + x = ggl_sqrt_reciproc_approx_tab[(a>>(28-lz))&0x7]; + const int32_t exp = lz - 16; + if (exp <= 0) x >>= -exp>>1; + else x <<= (exp>>1) + (exp & 1); + if (exp & 1) { + x = gglMulx(x, ggl_sqrt_reciproc_approx_tab[0])>>1; + } + // 2 Newton-Raphson iterations: x = x/2*(3-(a*x)*x) + x = gglMulx((x>>1),(0x30000 - gglMulx(gglMulx(a,x),x))); + x = gglMulx((x>>1),(0x30000 - gglMulx(gglMulx(a,x),x))); + return x; +} + +GGLfixed gglSqrtx(GGLfixed a) +{ + // Compute a full precision square-root (24 bits accuracy) + GGLfixed r = 0; + GGLfixed bit = 0x800000; + int32_t bshift = 15; + do { + GGLfixed temp = bit + (r<<1); + if (bshift >= 8) temp <<= (bshift-8); + else temp >>= (8-bshift); + if (a >= temp) { + r += bit; + a -= temp; + } + bshift--; + } while (bit>>=1); + return r; +} + +// ------------------------------------------------------------------------ + +static const GGLfixed ggl_log_approx_tab[] = { + // -ln(x)/ln(2) with x = N/16, N=[8...16] + 0xFFFF, 0xd47f, 0xad96, 0x8a62, 0x6a3f, 0x4caf, 0x3151, 0x17d6, 0x0000 +}; + +static const GGLfixed ggl_alog_approx_tab[] = { // domain [0 - 1.0] + 0xffff, 0xeac0, 0xd744, 0xc567, 0xb504, 0xa5fe, 0x9837, 0x8b95, 0x8000 +}; + +GGLfixed gglPowx(GGLfixed x, GGLfixed y) +{ + // prerequisite: 0 <= x <= 1, and y >=0 + + // pow(x,y) = 2^(y*log2(x)) + // = 2^(y*log2(x*(2^exp)*(2^-exp)))) + // = 2^(y*(log2(X)-exp)) + // = 2^(log2(X)*y - y*exp) + // = 2^( - (-log2(X)*y + y*exp) ) + + int32_t exp = gglClz(x) - 16; + GGLfixed f = x << exp; + x = (f & 0x0FFF)<<4; + f = (f >> 12) & 0x7; + GGLfixed p = gglMulAddx( + ggl_log_approx_tab[f+1] - ggl_log_approx_tab[f], x, + ggl_log_approx_tab[f]); + p = gglMulAddx(p, y, y*exp); + exp = gglFixedToIntFloor(p); + if (exp < 31) { + p = gglFracx(p); + x = (p & 0x1FFF)<<3; + p >>= 13; + p = gglMulAddx( + ggl_alog_approx_tab[p+1] - ggl_alog_approx_tab[p], x, + ggl_alog_approx_tab[p]); + p >>= exp; + } else { + p = 0; + } + return p; + // ( powf((a*65536.0f), (b*65536.0f)) ) * 65536.0f; +} + +// ------------------------------------------------------------------------ + +int32_t gglDivQ(GGLfixed n, GGLfixed d, int32_t i) +{ + //int32_t r =int32_t((int64_t(n)<<i)/d); + const int32_t ds = n^d; + if (n<0) n = -n; + if (d<0) d = -d; + int nd = gglClz(d) - gglClz(n); + i += nd + 1; + if (nd > 0) d <<= nd; + else n <<= -nd; + uint32_t q = 0; + + int j = i & 7; + i >>= 3; + + // gcc deals with the code below pretty well. + // we get 3.75 cycles per bit in the main loop + // and 8 cycles per bit in the termination loop + if (ggl_likely(i)) { + n -= d; + do { + q <<= 8; + if (n>=0) q |= 128; + else n += d; + n = n*2 - d; + if (n>=0) q |= 64; + else n += d; + n = n*2 - d; + if (n>=0) q |= 32; + else n += d; + n = n*2 - d; + if (n>=0) q |= 16; + else n += d; + n = n*2 - d; + if (n>=0) q |= 8; + else n += d; + n = n*2 - d; + if (n>=0) q |= 4; + else n += d; + n = n*2 - d; + if (n>=0) q |= 2; + else n += d; + n = n*2 - d; + if (n>=0) q |= 1; + else n += d; + + if (--i == 0) + goto finish; + + n = n*2 - d; + } while(true); + do { + q <<= 1; + n = n*2 - d; + if (n>=0) q |= 1; + else n += d; + finish: ; + } while (j--); + return (ds<0) ? -q : q; + } + + n -= d; + if (n>=0) q |= 1; + else n += d; + j--; + goto finish; +} + +// ------------------------------------------------------------------------ + +// assumes that the int32_t values of a, b, and c are all positive +// use when both a and b are larger than c + +template <typename T> +static inline void swap(T& a, T& b) { + T t(a); + a = b; + b = t; +} + +static __attribute__((noinline)) +int32_t slow_muldiv(uint32_t a, uint32_t b, uint32_t c) +{ + // first we compute a*b as a 64-bit integer + // (GCC generates umull with the code below) + uint64_t ab = uint64_t(a)*b; + uint32_t hi = ab>>32; + uint32_t lo = ab; + uint32_t result; + + // now perform the division + if (hi >= c) { + overflow: + result = 0x7fffffff; // basic overflow + } else if (hi == 0) { + result = lo/c; // note: c can't be 0 + if ((result >> 31) != 0) // result must fit in 31 bits + goto overflow; + } else { + uint32_t r = hi; + int bits = 31; + result = 0; + do { + r = (r << 1) | (lo >> 31); + lo <<= 1; + result <<= 1; + if (r >= c) { + r -= c; + result |= 1; + } + } while (bits--); + } + return int32_t(result); +} + +// assumes a >= 0 and c >= b >= 0 +static inline +int32_t quick_muldiv(int32_t a, int32_t b, int32_t c) +{ + int32_t r = 0, q = 0, i; + int leading = gglClz(a); + i = 32 - leading; + a <<= leading; + do { + r <<= 1; + if (a < 0) + r += b; + a <<= 1; + q <<= 1; + if (r >= c) { + r -= c; + q++; + } + asm(""::); // gcc generates better code this way + if (r >= c) { + r -= c; + q++; + } + } + while (--i); + return q; +} + +// this function computes a*b/c with 64-bit intermediate accuracy +// overflows (e.g. division by 0) are handled and return INT_MAX + +int32_t gglMulDivi(int32_t a, int32_t b, int32_t c) +{ + int32_t result; + int32_t sign = a^b^c; + + if (a < 0) a = -a; + if (b < 0) b = -b; + if (c < 0) c = -c; + + if (a < b) { + swap(a, b); + } + + if (b <= c) result = quick_muldiv(a, b, c); + else result = slow_muldiv((uint32_t)a, (uint32_t)b, (uint32_t)c); + + if (sign < 0) + result = -result; + + return result; +} diff --git a/libpixelflinger/format.cpp b/libpixelflinger/format.cpp new file mode 100644 index 0000000..161e6d6 --- /dev/null +++ b/libpixelflinger/format.cpp @@ -0,0 +1,67 @@ +/* libs/pixelflinger/format.cpp +** +** 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 <stdio.h> +#include <pixelflinger/format.h> + +namespace android { + +static GGLFormat const gPixelFormatInfos[] = +{ // Alpha Red Green Blue + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_8888 + { 4, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // PIXEL_FORMAT_RGBX_8888 + { 3, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_888 + { 2, 16, {{ 0, 0, 16,11, 11, 5, 5, 0 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_565 + { 4, 32, {{32,24, 24,16, 16, 8, 8, 0 }}, GGL_RGBA }, // PIXEL_FORMAT_BGRA_8888 + { 2, 16, {{ 1, 0, 16,11, 11, 6, 6, 1 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_5551 + { 2, 16, {{ 4, 0, 16,12, 12, 8, 8, 4 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_4444 + { 1, 8, {{ 8, 0, 0, 0, 0, 0, 0, 0 }}, GGL_ALPHA}, // PIXEL_FORMAT_A8 + { 1, 8, {{ 0, 0, 8, 0, 8, 0, 8, 0 }}, GGL_LUMINANCE},//PIXEL_FORMAT_L8 + { 2, 16, {{16, 8, 8, 0, 8, 0, 8, 0 }}, GGL_LUMINANCE_ALPHA},// PIXEL_FORMAT_LA_88 + { 1, 8, {{ 0, 0, 8, 5, 5, 2, 2, 0 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_332 + + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + + { 1, 16, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_SP },// PIXEL_FORMAT_YCbCr_422_SP + { 1, 12, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_SP },// PIXEL_FORMAT_YCbCr_420_SP + { 1, 16, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_P }, // PIXEL_FORMAT_YCbCr_422_P + { 1, 12, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_P }, // PIXEL_FORMAT_YCbCr_420_P + { 1, 16, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_I }, // PIXEL_FORMAT_YCbCr_422_I + { 1, 12, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_I }, // PIXEL_FORMAT_YCbCr_420_I + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + + { 2, 16, {{ 0, 0, 16, 0, 0, 0, 0, 0 }}, GGL_DEPTH_COMPONENT}, + { 1, 8, {{ 8, 0, 0, 0, 0, 0, 0, 0 }}, GGL_STENCIL_INDEX }, + { 4, 24, {{ 0, 0, 24, 0, 0, 0, 0, 0 }}, GGL_DEPTH_COMPONENT}, + { 4, 8, {{ 32,24, 0, 0, 0, 0, 0, 0 }}, GGL_STENCIL_INDEX }, +}; + +}; // namespace android + + +const GGLFormat* gglGetPixelFormatTable(size_t* numEntries) +{ + if (numEntries) { + *numEntries = sizeof(android::gPixelFormatInfos)/sizeof(GGLFormat); + } + return android::gPixelFormatInfos; +} diff --git a/libpixelflinger/picker.cpp b/libpixelflinger/picker.cpp new file mode 100644 index 0000000..030ef19 --- /dev/null +++ b/libpixelflinger/picker.cpp @@ -0,0 +1,173 @@ +/* libs/pixelflinger/picker.cpp +** +** 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 <stdio.h> + +#include "buffer.h" +#include "scanline.h" +#include "picker.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +void ggl_init_picker(context_t* c) +{ +} + +void ggl_pick(context_t* c) +{ + if (ggl_likely(!c->dirty)) + return; + + // compute needs, see if they changed... + const uint32_t enables = c->state.enables; + needs_t new_needs(c->state.needs); + + if (c->dirty & GGL_CB_STATE) { + new_needs.n &= ~GGL_NEEDS_CB_FORMAT_MASK; + new_needs.n |= GGL_BUILD_NEEDS(c->state.buffers.color.format, CB_FORMAT); + if (enables & GGL_ENABLE_BLENDING) + c->dirty |= GGL_PIXEL_PIPELINE_STATE; + } + + if (c->dirty & GGL_PIXEL_PIPELINE_STATE) { + uint32_t n = GGL_BUILD_NEEDS(c->state.buffers.color.format, CB_FORMAT); + uint32_t p = 0; + if (enables & GGL_ENABLE_BLENDING) { + uint32_t src = c->state.blend.src; + uint32_t dst = c->state.blend.dst; + uint32_t src_alpha = c->state.blend.src_alpha; + uint32_t dst_alpha = c->state.blend.dst_alpha; + const GGLFormat& cbf = c->formats[ c->state.buffers.color.format ]; + if (!cbf.c[GGLFormat::ALPHA].h) { + if ((src == GGL_ONE_MINUS_DST_ALPHA) || + (src == GGL_DST_ALPHA)) { + src = GGL_ONE; + } + if ((src_alpha == GGL_ONE_MINUS_DST_ALPHA) || + (src_alpha == GGL_DST_ALPHA)) { + src_alpha = GGL_ONE; + } + if ((dst == GGL_ONE_MINUS_DST_ALPHA) || + (dst == GGL_DST_ALPHA)) { + dst = GGL_ONE; + } + if ((dst_alpha == GGL_ONE_MINUS_DST_ALPHA) || + (dst_alpha == GGL_DST_ALPHA)) { + dst_alpha = GGL_ONE; + } + } + + src = ggl_blendfactor_to_needs(src); + dst = ggl_blendfactor_to_needs(dst); + src_alpha = ggl_blendfactor_to_needs(src_alpha); + dst_alpha = ggl_blendfactor_to_needs(dst_alpha); + + n |= GGL_BUILD_NEEDS( src, BLEND_SRC ); + n |= GGL_BUILD_NEEDS( dst, BLEND_DST ); + if (c->state.blend.alpha_separate) { + n |= GGL_BUILD_NEEDS( src_alpha, BLEND_SRCA ); + n |= GGL_BUILD_NEEDS( dst_alpha, BLEND_DSTA ); + } else { + n |= GGL_BUILD_NEEDS( src, BLEND_SRCA ); + n |= GGL_BUILD_NEEDS( dst, BLEND_DSTA ); + } + } else { + n |= GGL_BUILD_NEEDS( GGL_ONE, BLEND_SRC ); + n |= GGL_BUILD_NEEDS( GGL_ZERO, BLEND_DST ); + n |= GGL_BUILD_NEEDS( GGL_ONE, BLEND_SRCA ); + n |= GGL_BUILD_NEEDS( GGL_ZERO, BLEND_DSTA ); + } + + + n |= GGL_BUILD_NEEDS(c->state.mask.color^0xF, MASK_ARGB); + n |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_SMOOTH) ?1:0, SHADE); + if (enables & GGL_ENABLE_TMUS) { + n |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_W) ?1:0, W); + } + p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_DITHER) ?1:0, P_DITHER); + p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_AA) ?1:0, P_AA); + p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_FOG) ?1:0, P_FOG); + + if (enables & GGL_ENABLE_LOGIC_OP) { + n |= GGL_BUILD_NEEDS(c->state.logic_op.opcode, LOGIC_OP); + } else { + n |= GGL_BUILD_NEEDS(GGL_COPY, LOGIC_OP); + } + + if (enables & GGL_ENABLE_ALPHA_TEST) { + p |= GGL_BUILD_NEEDS(c->state.alpha_test.func, P_ALPHA_TEST); + } else { + p |= GGL_BUILD_NEEDS(GGL_ALWAYS, P_ALPHA_TEST); + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + p |= GGL_BUILD_NEEDS(c->state.depth_test.func, P_DEPTH_TEST); + p |= GGL_BUILD_NEEDS(c->state.mask.depth&1, P_MASK_Z); + } else { + p |= GGL_BUILD_NEEDS(GGL_ALWAYS, P_DEPTH_TEST); + // writing to the z-buffer is always disabled if depth-test + // is disabled. + } + new_needs.n = n; + new_needs.p = p; + } + + if (c->dirty & GGL_TMU_STATE) { + int idx = 0; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + const texture_t& tx = c->state.texture[i]; + if (tx.enable) { + uint32_t t = 0; + t |= GGL_BUILD_NEEDS(tx.surface.format, T_FORMAT); + t |= GGL_BUILD_NEEDS(ggl_env_to_needs(tx.env), T_ENV); + t |= GGL_BUILD_NEEDS(0, T_POT); // XXX: not used yet + if (tx.s_coord==GGL_ONE_TO_ONE && tx.t_coord==GGL_ONE_TO_ONE) { + // we encode 1-to-1 into the wrap mode + t |= GGL_BUILD_NEEDS(GGL_NEEDS_WRAP_11, T_S_WRAP); + t |= GGL_BUILD_NEEDS(GGL_NEEDS_WRAP_11, T_T_WRAP); + } else { + t |= GGL_BUILD_NEEDS(ggl_wrap_to_needs(tx.s_wrap), T_S_WRAP); + t |= GGL_BUILD_NEEDS(ggl_wrap_to_needs(tx.t_wrap), T_T_WRAP); + } + if (tx.mag_filter == GGL_LINEAR) { + t |= GGL_BUILD_NEEDS(1, T_LINEAR); + } + if (tx.min_filter == GGL_LINEAR) { + t |= GGL_BUILD_NEEDS(1, T_LINEAR); + } + new_needs.t[idx++] = t; + } else { + new_needs.t[i] = 0; + } + } + } + + if (new_needs != c->state.needs) { + c->state.needs = new_needs; + ggl_pick_texture(c); + ggl_pick_cb(c); + ggl_pick_scanline(c); + } + c->dirty = 0; +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libpixelflinger/picker.h b/libpixelflinger/picker.h new file mode 100644 index 0000000..9cdbc3c --- /dev/null +++ b/libpixelflinger/picker.h @@ -0,0 +1,31 @@ +/* libs/pixelflinger/picker.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_PICKER_H +#define ANDROID_PICKER_H + +#include <private/pixelflinger/ggl_context.h> + +namespace android { + +void ggl_init_picker(context_t* c); +void ggl_pick(context_t* c); + +}; // namespace android + +#endif diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp new file mode 100644 index 0000000..b54da0c --- /dev/null +++ b/libpixelflinger/pixelflinger.cpp @@ -0,0 +1,843 @@ +/* libs/pixelflinger/pixelflinger.cpp +** +** 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 <string.h> +#include <assert.h> + +#include <sys/time.h> + +#include <pixelflinger/pixelflinger.h> +#include <private/pixelflinger/ggl_context.h> + +#include "buffer.h" +#include "clear.h" +#include "picker.h" +#include "raster.h" +#include "scanline.h" +#include "trap.h" + +#include "codeflinger/GGLAssembler.h" +#include "codeflinger/CodeCache.h" + +#include <stdio.h> + + +namespace android { + +// ---------------------------------------------------------------------------- + +// 8x8 Bayer dither matrix +static const uint8_t gDitherMatrix[GGL_DITHER_SIZE] = { + 0, 32, 8, 40, 2, 34, 10, 42, + 48, 16, 56, 24, 50, 18, 58, 26, + 12, 44, 4, 36, 14, 46, 6, 38, + 60, 28, 52, 20, 62, 30, 54, 22, + 3, 35, 11, 43, 1, 33, 9, 41, + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +}; + +static void ggl_init_procs(context_t* c); +static void ggl_set_scissor(context_t* c); + +static void ggl_enable_blending(context_t* c, int enable); +static void ggl_enable_scissor_test(context_t* c, int enable); +static void ggl_enable_alpha_test(context_t* c, int enable); +static void ggl_enable_logic_op(context_t* c, int enable); +static void ggl_enable_dither(context_t* c, int enable); +static void ggl_enable_stencil_test(context_t* c, int enable); +static void ggl_enable_depth_test(context_t* c, int enable); +static void ggl_enable_aa(context_t* c, int enable); +static void ggl_enable_point_aa_nice(context_t* c, int enable); +static void ggl_enable_texture2d(context_t* c, int enable); +static void ggl_enable_w_lerp(context_t* c, int enable); +static void ggl_enable_fog(context_t* c, int enable); + +static inline int min(int a, int b) CONST; +static inline int min(int a, int b) { + return a < b ? a : b; +} + +static inline int max(int a, int b) CONST; +static inline int max(int a, int b) { + return a < b ? b : a; +} + +// ---------------------------------------------------------------------------- + +void ggl_error(context_t* c, GGLenum error) +{ + if (c->error == GGL_NO_ERROR) + c->error = error; +} + +// ---------------------------------------------------------------------------- + +static void ggl_bindTexture(void* con, const GGLSurface* surface) +{ + GGL_CONTEXT(c, con); + if (surface->format != c->activeTMU->surface.format) + ggl_state_changed(c, GGL_TMU_STATE); + ggl_set_surface(c, &(c->activeTMU->surface), surface); +} + + +static void ggl_bindTextureLod(void* con, GGLuint tmu,const GGLSurface* surface) +{ + GGL_CONTEXT(c, con); + // All LODs must have the same format + ggl_set_surface(c, &c->state.texture[tmu].surface, surface); +} + +static void ggl_colorBuffer(void* con, const GGLSurface* surface) +{ + GGL_CONTEXT(c, con); + if (surface->format != c->state.buffers.color.format) + ggl_state_changed(c, GGL_CB_STATE); + + if (surface->width > c->state.buffers.coverageBufferSize) { + // allocate the coverage factor buffer + free(c->state.buffers.coverage); + c->state.buffers.coverage = (int16_t*)malloc(surface->width * 2); + c->state.buffers.coverageBufferSize = + c->state.buffers.coverage ? surface->width : 0; + } + ggl_set_surface(c, &(c->state.buffers.color), surface); + if (c->state.buffers.read.format == 0) { + ggl_set_surface(c, &(c->state.buffers.read), surface); + } + ggl_set_scissor(c); +} + +static void ggl_readBuffer(void* con, const GGLSurface* surface) +{ + GGL_CONTEXT(c, con); + ggl_set_surface(c, &(c->state.buffers.read), surface); +} + +static void ggl_depthBuffer(void* con, const GGLSurface* surface) +{ + GGL_CONTEXT(c, con); + if (surface->format == GGL_PIXEL_FORMAT_Z_16) { + ggl_set_surface(c, &(c->state.buffers.depth), surface); + } else { + c->state.buffers.depth.format = GGL_PIXEL_FORMAT_NONE; + ggl_enable_depth_test(c, 0); + } +} + +static void ggl_scissor(void* con, GGLint x, GGLint y, + GGLsizei width, GGLsizei height) +{ + GGL_CONTEXT(c, con); + c->state.scissor.user_left = x; + c->state.scissor.user_top = y; + c->state.scissor.user_right = x + width; + c->state.scissor.user_bottom = y + height; + ggl_set_scissor(c); +} + +// ---------------------------------------------------------------------------- + +static void enable_disable(context_t* c, GGLenum name, int en) +{ + switch (name) { + case GGL_BLEND: ggl_enable_blending(c, en); break; + case GGL_SCISSOR_TEST: ggl_enable_scissor_test(c, en); break; + case GGL_ALPHA_TEST: ggl_enable_alpha_test(c, en); break; + case GGL_COLOR_LOGIC_OP: ggl_enable_logic_op(c, en); break; + case GGL_DITHER: ggl_enable_dither(c, en); break; + case GGL_STENCIL_TEST: ggl_enable_stencil_test(c, en); break; + case GGL_DEPTH_TEST: ggl_enable_depth_test(c, en); break; + case GGL_AA: ggl_enable_aa(c, en); break; + case GGL_TEXTURE_2D: ggl_enable_texture2d(c, en); break; + case GGL_W_LERP: ggl_enable_w_lerp(c, en); break; + case GGL_FOG: ggl_enable_fog(c, en); break; + case GGL_POINT_SMOOTH_NICE: ggl_enable_point_aa_nice(c, en); break; + } +} + +static void ggl_enable(void* con, GGLenum name) +{ + GGL_CONTEXT(c, con); + enable_disable(c, name, 1); +} + +static void ggl_disable(void* con, GGLenum name) +{ + GGL_CONTEXT(c, con); + enable_disable(c, name, 0); +} + +static void ggl_enableDisable(void* con, GGLenum name, GGLboolean en) +{ + GGL_CONTEXT(c, con); + enable_disable(c, name, en ? 1 : 0); +} + +// ---------------------------------------------------------------------------- + +static void ggl_shadeModel(void* con, GGLenum mode) +{ + GGL_CONTEXT(c, con); + switch (mode) { + case GGL_FLAT: + if (c->state.enables & GGL_ENABLE_SMOOTH) { + c->state.enables &= ~GGL_ENABLE_SMOOTH; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } + break; + case GGL_SMOOTH: + if (!(c->state.enables & GGL_ENABLE_SMOOTH)) { + c->state.enables |= GGL_ENABLE_SMOOTH; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } + break; + default: + ggl_error(c, GGL_INVALID_ENUM); + } +} + +static void ggl_color4xv(void* con, const GGLclampx* color) +{ + GGL_CONTEXT(c, con); + c->shade.r0 = gglFixedToIteratedColor(color[0]); + c->shade.g0 = gglFixedToIteratedColor(color[1]); + c->shade.b0 = gglFixedToIteratedColor(color[2]); + c->shade.a0 = gglFixedToIteratedColor(color[3]); +} + +static void ggl_colorGrad12xv(void* con, const GGLcolor* grad) +{ + GGL_CONTEXT(c, con); + // it is very important to round the iterated value here because + // the rasterizer doesn't clamp them, therefore the iterated value + //must absolutely be correct. + // GGLColor is encoded as 8.16 value + const int32_t round = 0x8000; + c->shade.r0 = grad[ 0] + round; + c->shade.drdx = grad[ 1]; + c->shade.drdy = grad[ 2]; + c->shade.g0 = grad[ 3] + round; + c->shade.dgdx = grad[ 4]; + c->shade.dgdy = grad[ 5]; + c->shade.b0 = grad[ 6] + round; + c->shade.dbdx = grad[ 7]; + c->shade.dbdy = grad[ 8]; + c->shade.a0 = grad[ 9] + round; + c->shade.dadx = grad[10]; + c->shade.dady = grad[11]; +} + +static void ggl_zGrad3xv(void* con, const GGLfixed32* grad) +{ + GGL_CONTEXT(c, con); + // z iterators are encoded as 0.32 fixed point and the z-buffer + // holds 16 bits, the rounding value is 0x8000. + const uint32_t round = 0x8000; + c->shade.z0 = grad[0] + round; + c->shade.dzdx = grad[1]; + c->shade.dzdy = grad[2]; +} + +static void ggl_wGrad3xv(void* con, const GGLfixed* grad) +{ + GGL_CONTEXT(c, con); + c->shade.w0 = grad[0]; + c->shade.dwdx = grad[1]; + c->shade.dwdy = grad[2]; +} + +// ---------------------------------------------------------------------------- + +static void ggl_fogGrad3xv(void* con, const GGLfixed* grad) +{ + GGL_CONTEXT(c, con); + c->shade.f0 = grad[0]; + c->shade.dfdx = grad[1]; + c->shade.dfdy = grad[2]; +} + +static void ggl_fogColor3xv(void* con, const GGLclampx* color) +{ + GGL_CONTEXT(c, con); + const int32_t r = gglClampx(color[0]); + const int32_t g = gglClampx(color[1]); + const int32_t b = gglClampx(color[2]); + c->state.fog.color[GGLFormat::RED] = (r - (r>>8))>>8; + c->state.fog.color[GGLFormat::GREEN]= (g - (g>>8))>>8; + c->state.fog.color[GGLFormat::BLUE] = (b - (b>>8))>>8; +} + +static void ggl_enable_fog(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_FOG)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_FOG; + else c->state.enables &= ~GGL_ENABLE_FOG; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +// ---------------------------------------------------------------------------- + +static void ggl_blendFunc(void* con, GGLenum src, GGLenum dst) +{ + GGL_CONTEXT(c, con); + c->state.blend.src = src; + c->state.blend.src_alpha = src; + c->state.blend.dst = dst; + c->state.blend.dst_alpha = dst; + c->state.blend.alpha_separate = 0; + if (c->state.enables & GGL_ENABLE_BLENDING) { + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +static void ggl_blendFuncSeparate(void* con, + GGLenum src, GGLenum dst, + GGLenum srcAlpha, GGLenum dstAplha) +{ + GGL_CONTEXT(c, con); + c->state.blend.src = src; + c->state.blend.src_alpha = srcAlpha; + c->state.blend.dst = dst; + c->state.blend.dst_alpha = dstAplha; + c->state.blend.alpha_separate = 1; + if (c->state.enables & GGL_ENABLE_BLENDING) { + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +// ---------------------------------------------------------------------------- + +static void ggl_texEnvi(void* con, GGLenum target, + GGLenum pname, + GGLint param) +{ + GGL_CONTEXT(c, con); + if (target != GGL_TEXTURE_ENV || pname != GGL_TEXTURE_ENV_MODE) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + switch (param) { + case GGL_REPLACE: + case GGL_MODULATE: + case GGL_DECAL: + case GGL_BLEND: + case GGL_ADD: + if (c->activeTMU->env != param) { + c->activeTMU->env = param; + ggl_state_changed(c, GGL_TMU_STATE); + } + break; + default: + ggl_error(c, GGL_INVALID_ENUM); + } +} + +static void ggl_texEnvxv(void* con, GGLenum target, + GGLenum pname, const GGLfixed* params) +{ + GGL_CONTEXT(c, con); + if (target != GGL_TEXTURE_ENV) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + switch (pname) { + case GGL_TEXTURE_ENV_MODE: + ggl_texEnvi(con, target, pname, params[0]); + break; + case GGL_TEXTURE_ENV_COLOR: { + uint8_t* const color = c->activeTMU->env_color; + const GGLclampx r = gglClampx(params[0]); + const GGLclampx g = gglClampx(params[1]); + const GGLclampx b = gglClampx(params[2]); + const GGLclampx a = gglClampx(params[3]); + color[0] = (a-(a>>8))>>8; + color[1] = (r-(r>>8))>>8; + color[2] = (g-(g>>8))>>8; + color[3] = (b-(b>>8))>>8; + break; + } + default: + ggl_error(c, GGL_INVALID_ENUM); + return; + } +} + + +static void ggl_texParameteri(void* con, + GGLenum target, + GGLenum pname, + GGLint param) +{ + GGL_CONTEXT(c, con); + if (target != GGL_TEXTURE_2D) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + + if (param == GGL_CLAMP_TO_EDGE) + param = GGL_CLAMP; + + uint16_t* what = 0; + switch (pname) { + case GGL_TEXTURE_WRAP_S: + if ((param == GGL_CLAMP) || + (param == GGL_REPEAT)) { + what = &c->activeTMU->s_wrap; + } + break; + case GGL_TEXTURE_WRAP_T: + if ((param == GGL_CLAMP) || + (param == GGL_REPEAT)) { + what = &c->activeTMU->t_wrap; + } + break; + case GGL_TEXTURE_MIN_FILTER: + if ((param == GGL_NEAREST) || + (param == GGL_NEAREST_MIPMAP_NEAREST) || + (param == GGL_NEAREST_MIPMAP_LINEAR)) { + what = &c->activeTMU->min_filter; + param = GGL_NEAREST; + } + if ((param == GGL_LINEAR) || + (param == GGL_LINEAR_MIPMAP_NEAREST) || + (param == GGL_LINEAR_MIPMAP_LINEAR)) { + what = &c->activeTMU->min_filter; + param = GGL_LINEAR; + } + break; + case GGL_TEXTURE_MAG_FILTER: + if ((param == GGL_NEAREST) || + (param == GGL_LINEAR)) { + what = &c->activeTMU->mag_filter; + } + break; + } + + if (!what) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + + if (*what != param) { + *what = param; + ggl_state_changed(c, GGL_TMU_STATE); + } +} + +static void ggl_texCoordGradScale8xv(void* con, GGLint tmu, const int32_t* grad) +{ + GGL_CONTEXT(c, con); + texture_t& u = c->state.texture[tmu]; + u.shade.is0 = grad[0]; + u.shade.idsdx = grad[1]; + u.shade.idsdy = grad[2]; + u.shade.it0 = grad[3]; + u.shade.idtdx = grad[4]; + u.shade.idtdy = grad[5]; + u.shade.sscale= grad[6]; + u.shade.tscale= grad[7]; +} + +static void ggl_texCoord2x(void* con, GGLfixed s, GGLfixed t) +{ + GGL_CONTEXT(c, con); + c->activeTMU->shade.is0 = s; + c->activeTMU->shade.it0 = t; + c->activeTMU->shade.sscale= 0; + c->activeTMU->shade.tscale= 0; +} + +static void ggl_texCoord2i(void* con, GGLint s, GGLint t) +{ + ggl_texCoord2x(con, s<<16, t<<16); +} + +static void ggl_texGeni(void* con, GGLenum coord, GGLenum pname, GGLint param) +{ + GGL_CONTEXT(c, con); + if (pname != GGL_TEXTURE_GEN_MODE) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + + uint32_t* coord_ptr = 0; + if (coord == GGL_S) coord_ptr = &(c->activeTMU->s_coord); + else if (coord == GGL_T) coord_ptr = &(c->activeTMU->t_coord); + + if (coord_ptr) { + if (*coord_ptr != uint32_t(param)) { + *coord_ptr = uint32_t(param); + ggl_state_changed(c, GGL_TMU_STATE); + } + } else { + ggl_error(c, GGL_INVALID_ENUM); + } +} + +static void ggl_activeTexture(void* con, GGLuint tmu) +{ + GGL_CONTEXT(c, con); + if (tmu >= GGLuint(GGL_TEXTURE_UNIT_COUNT)) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + c->activeTMUIndex = tmu; + c->activeTMU = &(c->state.texture[tmu]); +} + +// ---------------------------------------------------------------------------- + +static void ggl_colorMask(void* con, GGLboolean r, + GGLboolean g, + GGLboolean b, + GGLboolean a) +{ + GGL_CONTEXT(c, con); + int mask = 0; + if (a) mask |= 1 << GGLFormat::ALPHA; + if (r) mask |= 1 << GGLFormat::RED; + if (g) mask |= 1 << GGLFormat::GREEN; + if (b) mask |= 1 << GGLFormat::BLUE; + if (c->state.mask.color != mask) { + c->state.mask.color = mask; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +static void ggl_depthMask(void* con, GGLboolean flag) +{ + GGL_CONTEXT(c, con); + if (c->state.mask.depth != flag?1:0) { + c->state.mask.depth = flag?1:0; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +static void ggl_stencilMask(void* con, GGLuint mask) +{ + GGL_CONTEXT(c, con); + if (c->state.mask.stencil != mask) { + c->state.mask.stencil = mask; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +// ---------------------------------------------------------------------------- + +static void ggl_alphaFuncx(void* con, GGLenum func, GGLclampx ref) +{ + GGL_CONTEXT(c, con); + if ((func < GGL_NEVER) || (func > GGL_ALWAYS)) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + c->state.alpha_test.ref = gglFixedToIteratedColor(gglClampx(ref)); + if (c->state.alpha_test.func != func) { + c->state.alpha_test.func = func; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +// ---------------------------------------------------------------------------- + +static void ggl_depthFunc(void* con, GGLenum func) +{ + GGL_CONTEXT(c, con); + if ((func < GGL_NEVER) || (func > GGL_ALWAYS)) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + if (c->state.depth_test.func != func) { + c->state.depth_test.func = func; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +// ---------------------------------------------------------------------------- + +static void ggl_logicOp(void* con, GGLenum opcode) +{ + GGL_CONTEXT(c, con); + if ((opcode < GGL_CLEAR) || (opcode > GGL_SET)) { + ggl_error(c, GGL_INVALID_ENUM); + return; + } + if (c->state.logic_op.opcode != opcode) { + c->state.logic_op.opcode = opcode; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + + +// ---------------------------------------------------------------------------- + +void ggl_set_scissor(context_t* c) +{ + if (c->state.enables & GGL_ENABLE_SCISSOR_TEST) { + const int32_t l = c->state.scissor.user_left; + const int32_t t = c->state.scissor.user_top; + const int32_t r = c->state.scissor.user_right; + const int32_t b = c->state.scissor.user_bottom; + c->state.scissor.left = max(0, l); + c->state.scissor.right = min(c->state.buffers.color.width, r); + c->state.scissor.top = max(0, t); + c->state.scissor.bottom = min(c->state.buffers.color.height, b); + } else { + c->state.scissor.left = 0; + c->state.scissor.top = 0; + c->state.scissor.right = c->state.buffers.color.width; + c->state.scissor.bottom = c->state.buffers.color.height; + } +} + +void ggl_enable_blending(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_BLENDING)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_BLENDING; + else c->state.enables &= ~GGL_ENABLE_BLENDING; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_scissor_test(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_SCISSOR_TEST)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_SCISSOR_TEST; + else c->state.enables &= ~GGL_ENABLE_SCISSOR_TEST; + ggl_set_scissor(c); + } +} + +void ggl_enable_alpha_test(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_ALPHA_TEST)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_ALPHA_TEST; + else c->state.enables &= ~GGL_ENABLE_ALPHA_TEST; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_logic_op(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_LOGIC_OP)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_LOGIC_OP; + else c->state.enables &= ~GGL_ENABLE_LOGIC_OP; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_dither(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_DITHER)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_DITHER; + else c->state.enables &= ~GGL_ENABLE_DITHER; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_stencil_test(context_t* c, int enable) +{ +} + +void ggl_enable_depth_test(context_t* c, int enable) +{ + if (c->state.buffers.depth.format == 0) + enable = 0; + const int e = (c->state.enables & GGL_ENABLE_DEPTH_TEST)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_DEPTH_TEST; + else c->state.enables &= ~GGL_ENABLE_DEPTH_TEST; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_aa(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_AA)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_AA; + else c->state.enables &= ~GGL_ENABLE_AA; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_point_aa_nice(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_POINT_AA_NICE)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_POINT_AA_NICE; + else c->state.enables &= ~GGL_ENABLE_POINT_AA_NICE; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_w_lerp(context_t* c, int enable) +{ + const int e = (c->state.enables & GGL_ENABLE_W)?1:0; + if (e != enable) { + if (enable) c->state.enables |= GGL_ENABLE_W; + else c->state.enables &= ~GGL_ENABLE_W; + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE); + } +} + +void ggl_enable_texture2d(context_t* c, int enable) +{ + if (c->activeTMU->enable != enable) { + const uint32_t tmu = c->activeTMUIndex; + c->activeTMU->enable = enable; + const uint32_t mask = 1UL << tmu; + if (enable) c->state.enabled_tmu |= mask; + else c->state.enabled_tmu &= ~mask; + if (c->state.enabled_tmu) c->state.enables |= GGL_ENABLE_TMUS; + else c->state.enables &= ~GGL_ENABLE_TMUS; + ggl_state_changed(c, GGL_TMU_STATE); + } +} + + +// ---------------------------------------------------------------------------- + +int64_t ggl_system_time() +{ +#if defined(HAVE_POSIX_CLOCKS) + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t); + return int64_t(t.tv_sec)*1000000000LL + t.tv_nsec; +#else + // we don't support the clocks here. + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + return int64_t(t.tv_sec)*1000000000LL + int64_t(t.tv_usec)*1000LL; +#endif +} + +// ---------------------------------------------------------------------------- + +void ggl_init_procs(context_t* c) +{ + GGLContext& procs = *(GGLContext*)c; + GGL_INIT_PROC(procs, scissor); + GGL_INIT_PROC(procs, activeTexture); + GGL_INIT_PROC(procs, bindTexture); + GGL_INIT_PROC(procs, bindTextureLod); + GGL_INIT_PROC(procs, colorBuffer); + GGL_INIT_PROC(procs, readBuffer); + GGL_INIT_PROC(procs, depthBuffer); + GGL_INIT_PROC(procs, enable); + GGL_INIT_PROC(procs, disable); + GGL_INIT_PROC(procs, enableDisable); + GGL_INIT_PROC(procs, shadeModel); + GGL_INIT_PROC(procs, color4xv); + GGL_INIT_PROC(procs, colorGrad12xv); + GGL_INIT_PROC(procs, zGrad3xv); + GGL_INIT_PROC(procs, wGrad3xv); + GGL_INIT_PROC(procs, fogGrad3xv); + GGL_INIT_PROC(procs, fogColor3xv); + GGL_INIT_PROC(procs, blendFunc); + GGL_INIT_PROC(procs, blendFuncSeparate); + GGL_INIT_PROC(procs, texEnvi); + GGL_INIT_PROC(procs, texEnvxv); + GGL_INIT_PROC(procs, texParameteri); + GGL_INIT_PROC(procs, texCoord2i); + GGL_INIT_PROC(procs, texCoord2x); + GGL_INIT_PROC(procs, texCoordGradScale8xv); + GGL_INIT_PROC(procs, texGeni); + GGL_INIT_PROC(procs, colorMask); + GGL_INIT_PROC(procs, depthMask); + GGL_INIT_PROC(procs, stencilMask); + GGL_INIT_PROC(procs, alphaFuncx); + GGL_INIT_PROC(procs, depthFunc); + GGL_INIT_PROC(procs, logicOp); + ggl_init_clear(c); +} + +void ggl_init_context(context_t* c) +{ + memset(c, 0, sizeof(context_t)); + ggl_init_procs(c); + ggl_init_trap(c); + ggl_init_scanline(c); + ggl_init_texture(c); + ggl_init_picker(c); + ggl_init_raster(c); + c->formats = gglGetPixelFormatTable(); + c->state.blend.src = GGL_ONE; + c->state.blend.dst = GGL_ZERO; + c->state.blend.src_alpha = GGL_ONE; + c->state.blend.dst_alpha = GGL_ZERO; + c->state.mask.color = 0xF; + c->state.mask.depth = 0; + c->state.mask.stencil = 0xFFFFFFFF; + c->state.logic_op.opcode = GGL_COPY; + c->state.alpha_test.func = GGL_ALWAYS; + c->state.depth_test.func = GGL_LESS; + c->state.depth_test.clearValue = FIXED_ONE; + c->shade.w0 = FIXED_ONE; + memcpy(c->ditherMatrix, gDitherMatrix, sizeof(gDitherMatrix)); +} + +void ggl_uninit_context(context_t* c) +{ + ggl_uninit_scanline(c); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + + + +using namespace android; + +ssize_t gglInit(GGLContext** context) +{ + void* const base = malloc(sizeof(context_t) + 32); + if (base) { + // always align the context on cache lines + context_t *c = (context_t *)((ptrdiff_t(base)+31) & ~0x1FL); + ggl_init_context(c); + c->base = base; + *context = (GGLContext*)c; + } else { + return -1; + } + return 0; +} + +ssize_t gglUninit(GGLContext* con) +{ + GGL_CONTEXT(c, (void*)con); + ggl_uninit_context(c); + free(c->base); + return 0; +} + diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp new file mode 100644 index 0000000..d751202 --- /dev/null +++ b/libpixelflinger/raster.cpp @@ -0,0 +1,217 @@ +/* libs/pixelflinger/raster.cpp +** +** 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 <string.h> + +#include "raster.h" +#include "trap.h" + +namespace android { + +static void ggl_rasterPos2x(void* con, GGLfixed x, GGLfixed y); +static void ggl_rasterPos2i(void* con, GGLint x, GGLint y); +static void ggl_copyPixels(void* con, GGLint xs, GGLint ys, + GGLsizei width, GGLsizei height, GGLenum type); + +void ggl_init_raster(context_t* c) +{ + GGLContext& procs = *(GGLContext*)c; + GGL_INIT_PROC(procs, copyPixels); + GGL_INIT_PROC(procs, rasterPos2x); + GGL_INIT_PROC(procs, rasterPos2i); +} + +void ggl_rasterPos2x(void* con, GGLfixed x, GGLfixed y) +{ + GGL_CONTEXT(c, con); + // raster pos should be processed just like glVertex + c->state.raster.x = x; + c->state.raster.y = y; +} + +void ggl_rasterPos2i(void* con, GGLint x, GGLint y) +{ + ggl_rasterPos2x(con, gglIntToFixed(x), gglIntToFixed(y)); +} + +void ggl_copyPixels(void* con, GGLint xs, GGLint ys, + GGLsizei width, GGLsizei height, GGLenum type) +{ + GGL_CONTEXT(c, con); + + // color-buffer + surface_t* cb = &(c->state.buffers.color); + + // undefined behaviour if we try to copy from outside the surface + if (uint32_t(xs) > cb->width) + return; + if (uint32_t(ys) > cb->height) + return; + if (uint32_t(xs + width) > cb->width) + return; + if (uint32_t(ys + height) > cb->height) + return; + + // copy to current raster position + GGLint xd = gglFixedToIntRound(c->state.raster.x); + GGLint yd = gglFixedToIntRound(c->state.raster.y); + + // clip to scissor + if (xd < GGLint(c->state.scissor.left)) { + GGLint offset = GGLint(c->state.scissor.left) - xd; + xd = GGLint(c->state.scissor.left); + xs += offset; + width -= offset; + } + if (yd < GGLint(c->state.scissor.top)) { + GGLint offset = GGLint(c->state.scissor.top) - yd; + yd = GGLint(c->state.scissor.top); + ys += offset; + height -= offset; + } + if ((xd + width) > GGLint(c->state.scissor.right)) { + width = GGLint(c->state.scissor.right) - xd; + } + if ((yd + height) > GGLint(c->state.scissor.bottom)) { + height = GGLint(c->state.scissor.bottom) - yd; + } + + if (width<=0 || height<=0) { + return; // nothing to copy + } + + if (xs==xd && ys==yd) { + // nothing to do, but be careful, this might not be true when we support + // gglPixelTransfer, gglPixelMap and gglPixelZoom + return; + } + + const GGLFormat* fp = &(c->formats[cb->format]); + uint8_t* src = reinterpret_cast<uint8_t*>(cb->data) + + (xs + (cb->stride * ys)) * fp->size; + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + + (xd + (cb->stride * yd)) * fp->size; + const size_t bpr = cb->stride * fp->size; + const size_t rowsize = width * fp->size; + size_t yc = height; + + if (ys < yd) { + // bottom to top + src += height * bpr; + dst += height * bpr; + do { + dst -= bpr; + src -= bpr; + memcpy(dst, src, rowsize); + } while (--yc); + } else { + if (ys == yd) { + // might be right to left + do { + memmove(dst, src, rowsize); + dst += bpr; + src += bpr; + } while (--yc); + } else { + // top to bottom + do { + memcpy(dst, src, rowsize); + dst += bpr; + src += bpr; + } while (--yc); + } + } +} + +}; // namespace android + +using namespace android; + +GGLint gglBitBlti(GGLContext* con, int tmu, GGLint crop[4], GGLint where[4]) +{ + GGL_CONTEXT(c, (void*)con); + + GGLint x = where[0]; + GGLint y = where[1]; + GGLint w = where[2]; + GGLint h = where[3]; + + // exclsively enable this tmu + const GGLSurface& cbSurface = c->state.buffers.color.s; + c->procs.activeTexture(c, tmu); + c->procs.disable(c, GGL_W_LERP); + + uint32_t tmus = 1UL<<tmu; + if (c->state.enabled_tmu != tmus) { + c->activeTMU->enable = 1; + c->state.enabled_tmu = tmus; + c->state.enables |= GGL_ENABLE_TMUS; + ggl_state_changed(c, GGL_TMU_STATE); + } + + const GGLint Wcr = crop[2]; + const GGLint Hcr = crop[3]; + if ((w == Wcr) && (h == Hcr)) { + c->procs.texGeni(c, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + c->procs.texGeni(c, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + const GGLint Ucr = crop[0]; + const GGLint Vcr = crop[1]; + const GGLint s0 = Ucr - x; + const GGLint t0 = Vcr - y; + c->procs.texCoord2i(c, s0, t0); + c->procs.recti(c, x, y, x+w, y+h); + } else { + int32_t texcoords[8]; + x = gglIntToFixed(x); + y = gglIntToFixed(y); + + // we CLAMP here, which works with premultiplied (s,t) + c->procs.texParameteri(c, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP); + c->procs.texParameteri(c, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP); + c->procs.texGeni(c, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + c->procs.texGeni(c, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + + const GGLint Ucr = crop[0] << 16; + const GGLint Vcr = crop[1] << 16; + const GGLint Wcr = crop[2] << 16; + const GGLint Hcr = crop[3] << 16; + + // computes texture coordinates (pre-multiplied) + int32_t dsdx = Wcr / w; // dsdx = ((Wcr/w)/Wt)*Wt + int32_t dtdy = Hcr / h; // dtdy = ((Hcr/h)/Ht)*Ht + int32_t s0 = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx + int32_t t0 = Vcr - gglMulx(dtdy, y); // t0 = Vcr - y * dtdy + texcoords[0] = s0; + texcoords[1] = dsdx; + texcoords[2] = 0; + texcoords[3] = t0; + texcoords[4] = 0; + texcoords[5] = dtdy; + texcoords[6] = 0; + texcoords[7] = 0; + c->procs.texCoordGradScale8xv(c, tmu, texcoords); + c->procs.recti(c, + gglFixedToIntRound(x), + gglFixedToIntRound(y), + gglFixedToIntRound(x)+w, + gglFixedToIntRound(y)+h); + } + return 0; +} + diff --git a/libpixelflinger/raster.h b/libpixelflinger/raster.h new file mode 100644 index 0000000..9f0f240 --- /dev/null +++ b/libpixelflinger/raster.h @@ -0,0 +1,33 @@ +/* libs/pixelflinger/raster.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_GGL_RASTER_H +#define ANDROID_GGL_RASTER_H + +#include <private/pixelflinger/ggl_context.h> + +namespace android { + +void ggl_init_raster(context_t* c); + +void gglCopyPixels(void* c, GGLint x, GGLint y, GGLsizei width, GGLsizei height, GGLenum type); +void gglRasterPos2d(void* c, GGLint x, GGLint y); + +}; // namespace android + +#endif // ANDROID_GGL_RASTER_H diff --git a/libpixelflinger/rotate90CW_4x4_16v6.S b/libpixelflinger/rotate90CW_4x4_16v6.S new file mode 100644 index 0000000..8e3e142 --- /dev/null +++ b/libpixelflinger/rotate90CW_4x4_16v6.S @@ -0,0 +1,62 @@ +/* +** +** 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. +*/ + + + .text + .align + + .global rotate90CW_4x4_16v6 + +// Rotates 90deg CW a 4x4 block of 16bpp pixels using ARMv6 +// src and dst must be 4 pixels-aligned (2-pixels aligned might +// actually work) +// +// The code below is complicated by ARM's little endianness. + +rotate90CW_4x4_16v6: + // r0 = dst + // r1 = src + // r2 = dst stride in pixels + // r3 = src stride in pixels + + stmfd sp!, {r4,r5, r6,r7, r8,r9, r10,r11, lr} + add r14, r3, r3 + add r12, r2, r2 + + ldrd r2, r3, [r1], r14 + ldrd r4, r5, [r1], r14 + ldrd r6, r7, [r1], r14 + ldrd r8, r9, [r1] + + pkhbt r10, r8, r6, lsl #16 + pkhbt r11, r4, r2, lsl #16 + strd r10, r11, [r0], r12 + + pkhtb r10, r6, r8, asr #16 + pkhtb r11, r2, r4, asr #16 + + strd r10, r11, [r0], r12 + pkhbt r10, r9, r7, lsl #16 + pkhbt r11, r5, r3, lsl #16 + + strd r10, r11, [r0], r12 + + pkhtb r10, r7, r9, asr #16 + pkhtb r11, r3, r5, asr #16 + strd r10, r11, [r0] + + ldmfd sp!, {r4,r5, r6,r7, r8,r9, r10,r11, pc} diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp new file mode 100644 index 0000000..f700306 --- /dev/null +++ b/libpixelflinger/scanline.cpp @@ -0,0 +1,1496 @@ +/* libs/pixelflinger/scanline.cpp +** +** 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 LOG_TAG "pixelflinger" + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <cutils/memory.h> +#include <cutils/log.h> + +#include "buffer.h" +#include "scanline.h" + +#include "codeflinger/CodeCache.h" +#include "codeflinger/GGLAssembler.h" +#include "codeflinger/ARMAssembler.h" +//#include "codeflinger/ARMAssemblerOptimizer.h" + +// ---------------------------------------------------------------------------- + +#define ANDROID_CODEGEN_GENERIC 0 // force generic pixel pipeline +#define ANDROID_CODEGEN_C 1 // hand-written C, fallback generic +#define ANDROID_CODEGEN_ASM 2 // hand-written asm, fallback generic +#define ANDROID_CODEGEN_GENERATED 3 // hand-written asm, fallback codegen + +#ifdef NDEBUG +# define ANDROID_RELEASE +# define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED +#else +# define ANDROID_DEBUG +# define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED +#endif + +#if defined(__arm__) +# define ANDROID_ARM_CODEGEN 1 +#else +# define ANDROID_ARM_CODEGEN 0 +#endif + +#define DEBUG__CODEGEN_ONLY 0 + + +#define ASSEMBLY_SCRATCH_SIZE 2048 + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +static void init_y(context_t*, int32_t); +static void init_y_noop(context_t*, int32_t); +static void init_y_packed(context_t*, int32_t); +static void init_y_error(context_t*, int32_t); + +static void step_y__generic(context_t* c); +static void step_y__nop(context_t*); +static void step_y__smooth(context_t* c); +static void step_y__tmu(context_t* c); +static void step_y__w(context_t* c); + +static void scanline(context_t* c); +static void scanline_perspective(context_t* c); +static void scanline_perspective_single(context_t* c); +static void scanline_t32cb16blend(context_t* c); +static void scanline_t32cb16(context_t* c); +static void scanline_memcpy(context_t* c); +static void scanline_memset8(context_t* c); +static void scanline_memset16(context_t* c); +static void scanline_memset32(context_t* c); +static void scanline_noop(context_t* c); +static void scanline_set(context_t* c); +static void scanline_clear(context_t* c); + +static void rect_generic(context_t* c, size_t yc); +static void rect_memcpy(context_t* c, size_t yc); + +extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t); +extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct); + +// ---------------------------------------------------------------------------- + +struct shortcut_t { + needs_filter_t filter; + const char* desc; + void (*scanline)(context_t*); + void (*init_y)(context_t*, int32_t); +}; + +// Keep in sync with needs +static shortcut_t shortcuts[] = { + { { { 0x03515104, 0x00000077, { 0x00000A01, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, blend", scanline_t32cb16blend, init_y_noop }, + { { { 0x03010104, 0x00000077, { 0x00000A01, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx", scanline_t32cb16, init_y_noop }, + { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } }, + { 0x00000000, 0x00000007, { 0x00000000, 0x00000000 } } }, + "(nop) alpha test", scanline_noop, init_y_noop }, + { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } }, + { 0x00000000, 0x00000070, { 0x00000000, 0x00000000 } } }, + "(nop) depth test", scanline_noop, init_y_noop }, + { { { 0x05000000, 0x00000000, { 0x00000000, 0x00000000 } }, + { 0x0F000000, 0x00000080, { 0x00000000, 0x00000000 } } }, + "(nop) logic_op", scanline_noop, init_y_noop }, + { { { 0xF0000000, 0x00000000, { 0x00000000, 0x00000000 } }, + { 0xF0000000, 0x00000080, { 0x00000000, 0x00000000 } } }, + "(nop) color mask", scanline_noop, init_y_noop }, + { { { 0x0F000000, 0x00000077, { 0x00000000, 0x00000000 } }, + { 0xFF000000, 0x000000F7, { 0x00000000, 0x00000000 } } }, + "(set) logic_op", scanline_set, init_y_noop }, + { { { 0x00000000, 0x00000077, { 0x00000000, 0x00000000 } }, + { 0xFF000000, 0x000000F7, { 0x00000000, 0x00000000 } } }, + "(clear) logic_op", scanline_clear, init_y_noop }, + { { { 0x03000000, 0x00000077, { 0x00000000, 0x00000000 } }, + { 0xFFFFFF00, 0x000000F7, { 0x00000000, 0x00000000 } } }, + "(clear) blending 0/0", scanline_clear, init_y_noop }, + { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } }, + { 0x0000003F, 0x00000000, { 0x00000000, 0x00000000 } } }, + "(error) invalid color-buffer format", scanline_noop, init_y_error }, +}; +static const needs_filter_t noblend1to1 = { + // (disregard dithering, see below) + { 0x03010100, 0x00000077, { 0x00000A00, 0x00000000 } }, + { 0xFFFFFFC0, 0xFFFFFEFF, { 0xFFFFFFC0, 0x0000003F } } +}; +static const needs_filter_t fill16noblend = { + { 0x03010100, 0x00000077, { 0x00000000, 0x00000000 } }, + { 0xFFFFFFC0, 0xFFFFFFFF, { 0x0000003F, 0x0000003F } } +}; + +// ---------------------------------------------------------------------------- + +#if ANDROID_ARM_CODEGEN +static CodeCache gCodeCache(12 * 1024); + +class ScanlineAssembly : public Assembly { + AssemblyKey<needs_t> mKey; +public: + ScanlineAssembly(needs_t needs, size_t size) + : Assembly(size), mKey(needs) { } + const AssemblyKey<needs_t>& key() const { return mKey; } +}; +#endif + +// ---------------------------------------------------------------------------- + +void ggl_init_scanline(context_t* c) +{ + c->init_y = init_y; + c->step_y = step_y__generic; + c->scanline = scanline; +} + +void ggl_uninit_scanline(context_t* c) +{ + if (c->state.buffers.coverage) + free(c->state.buffers.coverage); +#if ANDROID_ARM_CODEGEN + if (c->scanline_as) + c->scanline_as->decStrong(c); +#endif +} + +// ---------------------------------------------------------------------------- + +static void pick_scanline(context_t* c) +{ +#if (!defined(DEBUG__CODEGEN_ONLY) || (DEBUG__CODEGEN_ONLY == 0)) + +#if ANDROID_CODEGEN == ANDROID_CODEGEN_GENERIC + c->init_y = init_y; + c->step_y = step_y__generic; + c->scanline = scanline; + return; +#endif + + //printf("*** needs [%08lx:%08lx:%08lx:%08lx]\n", + // c->state.needs.n, c->state.needs.p, + // c->state.needs.t[0], c->state.needs.t[1]); + + // first handle the special case that we cannot test with a filter + const uint32_t cb_format = GGL_READ_NEEDS(CB_FORMAT, c->state.needs.n); + if (GGL_READ_NEEDS(T_FORMAT, c->state.needs.t[0]) == cb_format) { + if (c->state.needs.match(noblend1to1)) { + // this will match regardless of dithering state, since both + // src and dest have the same format anyway, there is no dithering + // to be done. + const GGLFormat* f = + &(c->formats[GGL_READ_NEEDS(T_FORMAT, c->state.needs.t[0])]); + if ((f->components == GGL_RGB) || + (f->components == GGL_RGBA) || + (f->components == GGL_LUMINANCE) || + (f->components == GGL_LUMINANCE_ALPHA)) + { + // format must have all of RGB components + // (so the current color doesn't show through) + c->scanline = scanline_memcpy; + c->init_y = init_y_noop; + return; + } + } + } + + if (c->state.needs.match(fill16noblend)) { + c->init_y = init_y_packed; + switch (c->formats[cb_format].size) { + case 1: c->scanline = scanline_memset8; return; + case 2: c->scanline = scanline_memset16; return; + case 4: c->scanline = scanline_memset32; return; + } + } + + const int numFilters = sizeof(shortcuts)/sizeof(shortcut_t); + for (int i=0 ; i<numFilters ; i++) { + if (c->state.needs.match(shortcuts[i].filter)) { + c->scanline = shortcuts[i].scanline; + c->init_y = shortcuts[i].init_y; + return; + } + } + +#endif // DEBUG__CODEGEN_ONLY + + c->init_y = init_y; + c->step_y = step_y__generic; + +#if ANDROID_ARM_CODEGEN + // we're going to have to generate some code... + // here, generate code for our pixel pipeline + const AssemblyKey<needs_t> key(c->state.needs); + sp<Assembly> assembly = gCodeCache.lookup(key); + if (assembly == 0) { + // create a new assembly region + sp<ScanlineAssembly> a = new ScanlineAssembly(c->state.needs, + ASSEMBLY_SCRATCH_SIZE); + // initialize our assembler + GGLAssembler assembler( new ARMAssembler(a) ); + //GGLAssembler assembler( + // new ARMAssemblerOptimizer(new ARMAssembler(a)) ); + // generate the scanline code for the given needs + int err = assembler.scanline(c->state.needs, c); + if (ggl_likely(!err)) { + // finally, cache this assembly + err = gCodeCache.cache(a->key(), a); + } + if (ggl_unlikely(err)) { + LOGE("error generating or caching assembly. Reverting to NOP."); + c->scanline = scanline_noop; + c->init_y = init_y_noop; + c->step_y = step_y__nop; + return; + } + assembly = a; + } + + // release the previous assembly + if (c->scanline_as) { + c->scanline_as->decStrong(c); + } + + //LOGI("using generated pixel-pipeline"); + c->scanline_as = assembly.get(); + c->scanline_as->incStrong(c); // hold on to assembly + c->scanline = (void(*)(context_t* c))assembly->base(); +#else +// LOGW("using generic (slow) pixel-pipeline"); + c->scanline = scanline; +#endif +} + +void ggl_pick_scanline(context_t* c) +{ + pick_scanline(c); + if ((c->state.enables & GGL_ENABLE_W) && + (c->state.enables & GGL_ENABLE_TMUS)) + { + c->span = c->scanline; + c->scanline = scanline_perspective; + if (!(c->state.enabled_tmu & (c->state.enabled_tmu - 1))) { + // only one TMU enabled + c->scanline = scanline_perspective_single; + } + } +} + +// ---------------------------------------------------------------------------- + +static void blending(context_t* c, pixel_t* fragment, pixel_t* fb); +static void blend_factor(context_t* c, pixel_t* r, uint32_t factor, + const pixel_t* src, const pixel_t* dst); +static void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv); + +#if ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED) + +// no need to compile the generic-pipeline, it can't be reached +void scanline(context_t*) +{ +} + +#else + +void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv) +{ + if (su && sv) { + if (su > sv) { + v = ggl_expand(v, sv, su); + sv = su; + } else if (su < sv) { + u = ggl_expand(u, su, sv); + su = sv; + } + } +} + +void blending(context_t* c, pixel_t* fragment, pixel_t* fb) +{ + rescale(fragment->c[0], fragment->s[0], fb->c[0], fb->s[0]); + rescale(fragment->c[1], fragment->s[1], fb->c[1], fb->s[1]); + rescale(fragment->c[2], fragment->s[2], fb->c[2], fb->s[2]); + rescale(fragment->c[3], fragment->s[3], fb->c[3], fb->s[3]); + + pixel_t sf, df; + blend_factor(c, &sf, c->state.blend.src, fragment, fb); + blend_factor(c, &df, c->state.blend.dst, fragment, fb); + + fragment->c[1] = + gglMulAddx(fragment->c[1], sf.c[1], gglMulx(fb->c[1], df.c[1])); + fragment->c[2] = + gglMulAddx(fragment->c[2], sf.c[2], gglMulx(fb->c[2], df.c[2])); + fragment->c[3] = + gglMulAddx(fragment->c[3], sf.c[3], gglMulx(fb->c[3], df.c[3])); + + if (c->state.blend.alpha_separate) { + blend_factor(c, &sf, c->state.blend.src_alpha, fragment, fb); + blend_factor(c, &df, c->state.blend.dst_alpha, fragment, fb); + } + + fragment->c[0] = + gglMulAddx(fragment->c[0], sf.c[0], gglMulx(fb->c[0], df.c[0])); + + // clamp to 1.0 + if (fragment->c[0] >= (1LU<<fragment->s[0])) + fragment->c[0] = (1<<fragment->s[0])-1; + if (fragment->c[1] >= (1LU<<fragment->s[1])) + fragment->c[1] = (1<<fragment->s[1])-1; + if (fragment->c[2] >= (1LU<<fragment->s[2])) + fragment->c[2] = (1<<fragment->s[2])-1; + if (fragment->c[3] >= (1LU<<fragment->s[3])) + fragment->c[3] = (1<<fragment->s[3])-1; +} + +static inline int blendfactor(uint32_t x, uint32_t size, uint32_t def = 0) +{ + if (!size) + return def; + + // scale to 16 bits + if (size > 16) { + x >>= (size - 16); + } else if (size < 16) { + x = ggl_expand(x, size, 16); + } + x += x >> 15; + return x; +} + +void blend_factor(context_t* c, pixel_t* r, + uint32_t factor, const pixel_t* src, const pixel_t* dst) +{ + switch (factor) { + case GGL_ZERO: + r->c[1] = + r->c[2] = + r->c[3] = + r->c[0] = 0; + break; + case GGL_ONE: + r->c[1] = + r->c[2] = + r->c[3] = + r->c[0] = FIXED_ONE; + break; + case GGL_DST_COLOR: + r->c[1] = blendfactor(dst->c[1], dst->s[1]); + r->c[2] = blendfactor(dst->c[2], dst->s[2]); + r->c[3] = blendfactor(dst->c[3], dst->s[3]); + r->c[0] = blendfactor(dst->c[0], dst->s[0]); + break; + case GGL_SRC_COLOR: + r->c[1] = blendfactor(src->c[1], src->s[1]); + r->c[2] = blendfactor(src->c[2], src->s[2]); + r->c[3] = blendfactor(src->c[3], src->s[3]); + r->c[0] = blendfactor(src->c[0], src->s[0]); + break; + case GGL_ONE_MINUS_DST_COLOR: + r->c[1] = FIXED_ONE - blendfactor(dst->c[1], dst->s[1]); + r->c[2] = FIXED_ONE - blendfactor(dst->c[2], dst->s[2]); + r->c[3] = FIXED_ONE - blendfactor(dst->c[3], dst->s[3]); + r->c[0] = FIXED_ONE - blendfactor(dst->c[0], dst->s[0]); + break; + case GGL_ONE_MINUS_SRC_COLOR: + r->c[1] = FIXED_ONE - blendfactor(src->c[1], src->s[1]); + r->c[2] = FIXED_ONE - blendfactor(src->c[2], src->s[2]); + r->c[3] = FIXED_ONE - blendfactor(src->c[3], src->s[3]); + r->c[0] = FIXED_ONE - blendfactor(src->c[0], src->s[0]); + break; + case GGL_SRC_ALPHA: + r->c[1] = + r->c[2] = + r->c[3] = + r->c[0] = blendfactor(src->c[0], src->s[0], FIXED_ONE); + break; + case GGL_ONE_MINUS_SRC_ALPHA: + r->c[1] = + r->c[2] = + r->c[3] = + r->c[0] = FIXED_ONE - blendfactor(src->c[0], src->s[0], FIXED_ONE); + break; + case GGL_DST_ALPHA: + r->c[1] = + r->c[2] = + r->c[3] = + r->c[0] = blendfactor(dst->c[0], dst->s[0], FIXED_ONE); + break; + case GGL_ONE_MINUS_DST_ALPHA: + r->c[1] = + r->c[2] = + r->c[3] = + r->c[0] = FIXED_ONE - blendfactor(dst->c[0], dst->s[0], FIXED_ONE); + break; + case GGL_SRC_ALPHA_SATURATE: + // XXX: GGL_SRC_ALPHA_SATURATE + break; + } +} + +static GGLfixed wrapping(int32_t coord, uint32_t size, int tx_wrap) +{ + GGLfixed d; + if (tx_wrap == GGL_REPEAT) { + d = (uint32_t(coord)>>16) * size; + } else if (tx_wrap == GGL_CLAMP) { // CLAMP_TO_EDGE semantics + const GGLfixed clamp_min = FIXED_HALF; + const GGLfixed clamp_max = (size << 16) - FIXED_HALF; + if (coord < clamp_min) coord = clamp_min; + if (coord > clamp_max) coord = clamp_max; + d = coord; + } else { // 1:1 + const GGLfixed clamp_min = 0; + const GGLfixed clamp_max = (size << 16); + if (coord < clamp_min) coord = clamp_min; + if (coord > clamp_max) coord = clamp_max; + d = coord; + } + return d; +} + +static inline +GGLcolor ADJUST_COLOR_ITERATOR(GGLcolor v, GGLcolor dvdx, int len) +{ + const int32_t end = dvdx * (len-1) + v; + if (end < 0) + v -= end; + v &= ~(v>>31); + return v; +} + +void scanline(context_t* c) +{ + const uint32_t enables = c->state.enables; + const int xs = c->iterators.xl; + const int x1 = c->iterators.xr; + int xc = x1 - xs; + const int16_t* covPtr = c->state.buffers.coverage + xs; + + // All iterated values are sampled at the pixel center + + // reset iterators for that scanline... + GGLcolor r, g, b, a; + iterators_t& ci = c->iterators; + if (enables & GGL_ENABLE_SMOOTH) { + r = (xs * c->shade.drdx) + ci.ydrdy; + g = (xs * c->shade.dgdx) + ci.ydgdy; + b = (xs * c->shade.dbdx) + ci.ydbdy; + a = (xs * c->shade.dadx) + ci.ydady; + r = ADJUST_COLOR_ITERATOR(r, c->shade.drdx, xc); + g = ADJUST_COLOR_ITERATOR(g, c->shade.dgdx, xc); + b = ADJUST_COLOR_ITERATOR(b, c->shade.dbdx, xc); + a = ADJUST_COLOR_ITERATOR(a, c->shade.dadx, xc); + } else { + r = ci.ydrdy; + g = ci.ydgdy; + b = ci.ydbdy; + a = ci.ydady; + } + + // z iterators are 1.31 + GGLfixed z = (xs * c->shade.dzdx) + ci.ydzdy; + GGLfixed f = (xs * c->shade.dfdx) + ci.ydfdy; + + struct { + GGLfixed s, t; + } tc[GGL_TEXTURE_UNIT_COUNT]; + if (enables & GGL_ENABLE_TMUS) { + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + if (c->state.texture[i].enable) { + texture_iterators_t& ti = c->state.texture[i].iterators; + if (enables & GGL_ENABLE_W) { + tc[i].s = ti.ydsdy; + tc[i].t = ti.ydtdy; + } else { + tc[i].s = (xs * ti.dsdx) + ti.ydsdy; + tc[i].t = (xs * ti.dtdx) + ti.ydtdy; + } + } + } + } + + pixel_t fragment; + pixel_t texel; + pixel_t fb; + + uint32_t x = xs; + uint32_t y = c->iterators.y; + + while (xc--) { + + { // just a scope + + // read color (convert to 8 bits by keeping only the integer part) + fragment.s[1] = fragment.s[2] = + fragment.s[3] = fragment.s[0] = 8; + fragment.c[1] = r >> (GGL_COLOR_BITS-8); + fragment.c[2] = g >> (GGL_COLOR_BITS-8); + fragment.c[3] = b >> (GGL_COLOR_BITS-8); + fragment.c[0] = a >> (GGL_COLOR_BITS-8); + + // texturing + if (enables & GGL_ENABLE_TMUS) { + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + texture_t& tx = c->state.texture[i]; + if (!tx.enable) + continue; + texture_iterators_t& ti = tx.iterators; + int32_t u, v; + + // s-coordinate + if (tx.s_coord != GGL_ONE_TO_ONE) { + const int w = tx.surface.width; + u = wrapping(tc[i].s, w, tx.s_wrap); + tc[i].s += ti.dsdx; + } else { + u = (((tx.shade.is0>>16) + x)<<16) + FIXED_HALF; + } + + // t-coordinate + if (tx.t_coord != GGL_ONE_TO_ONE) { + const int h = tx.surface.height; + v = wrapping(tc[i].t, h, tx.t_wrap); + tc[i].t += ti.dtdx; + } else { + v = (((tx.shade.it0>>16) + y)<<16) + FIXED_HALF; + } + + // read texture + if (tx.mag_filter == GGL_NEAREST && + tx.min_filter == GGL_NEAREST) + { + u >>= 16; + v >>= 16; + tx.surface.read(&tx.surface, c, u, v, &texel); + } else { + const int w = tx.surface.width; + const int h = tx.surface.height; + u -= FIXED_HALF; + v -= FIXED_HALF; + int u0 = u >> 16; + int v0 = v >> 16; + int u1 = u0 + 1; + int v1 = v0 + 1; + if (tx.s_wrap == GGL_REPEAT) { + if (u0<0) u0 += w; + if (u1<0) u1 += w; + if (u0>=w) u0 -= w; + if (u1>=w) u1 -= w; + } else { + if (u0<0) u0 = 0; + if (u1<0) u1 = 0; + if (u0>=w) u0 = w-1; + if (u1>=w) u1 = w-1; + } + if (tx.t_wrap == GGL_REPEAT) { + if (v0<0) v0 += h; + if (v1<0) v1 += h; + if (v0>=h) v0 -= h; + if (v1>=h) v1 -= h; + } else { + if (v0<0) v0 = 0; + if (v1<0) v1 = 0; + if (v0>=h) v0 = h-1; + if (v1>=h) v1 = h-1; + } + pixel_t texels[4]; + uint32_t mm[4]; + tx.surface.read(&tx.surface, c, u0, v0, &texels[0]); + tx.surface.read(&tx.surface, c, u0, v1, &texels[1]); + tx.surface.read(&tx.surface, c, u1, v0, &texels[2]); + tx.surface.read(&tx.surface, c, u1, v1, &texels[3]); + u = (u >> 12) & 0xF; + v = (v >> 12) & 0xF; + u += u>>3; + v += v>>3; + mm[0] = (0x10 - u) * (0x10 - v); + mm[1] = (0x10 - u) * v; + mm[2] = u * (0x10 - v); + mm[3] = 0x100 - (mm[0] + mm[1] + mm[2]); + for (int j=0 ; j<4 ; j++) { + texel.s[j] = texels[0].s[j]; + if (!texel.s[j]) continue; + texel.s[j] += 8; + texel.c[j] = texels[0].c[j]*mm[0] + + texels[1].c[j]*mm[1] + + texels[2].c[j]*mm[2] + + texels[3].c[j]*mm[3] ; + } + } + + // Texture environnement... + for (int j=0 ; j<4 ; j++) { + uint32_t& Cf = fragment.c[j]; + uint32_t& Ct = texel.c[j]; + uint8_t& sf = fragment.s[j]; + uint8_t& st = texel.s[j]; + uint32_t At = texel.c[0]; + uint8_t sat = texel.s[0]; + switch (tx.env) { + case GGL_REPLACE: + if (st) { + Cf = Ct; + sf = st; + } + break; + case GGL_MODULATE: + if (st) { + uint32_t factor = Ct + (Ct>>(st-1)); + Cf = (Cf * factor) >> st; + } + break; + case GGL_DECAL: + if (sat) { + rescale(Cf, sf, Ct, st); + Cf += ((Ct - Cf) * (At + (At>>(sat-1)))) >> sat; + } + break; + case GGL_BLEND: + if (st) { + uint32_t Cc = tx.env_color[i]; + if (sf>8) Cc = (Cc * ((1<<sf)-1))>>8; + else if (sf<8) Cc = (Cc - (Cc>>(8-sf)))>>(8-sf); + uint32_t factor = Ct + (Ct>>(st-1)); + Cf = ((((1<<st) - factor) * Cf) + Ct*Cc)>>st; + } + break; + case GGL_ADD: + if (st) { + rescale(Cf, sf, Ct, st); + Cf += Ct; + } + break; + } + } + } + } + + // coverage application + if (enables & GGL_ENABLE_AA) { + int16_t cf = *covPtr++; + fragment.c[0] = (int64_t(fragment.c[0]) * cf) >> 15; + } + + // alpha-test + if (enables & GGL_ENABLE_ALPHA_TEST) { + GGLcolor ref = c->state.alpha_test.ref; + GGLcolor alpha = (uint64_t(fragment.c[0]) * + ((1<<GGL_COLOR_BITS)-1)) / ((1<<fragment.s[0])-1); + switch (c->state.alpha_test.func) { + case GGL_NEVER: goto discard; + case GGL_LESS: if (alpha<ref) break; goto discard; + case GGL_EQUAL: if (alpha==ref) break; goto discard; + case GGL_LEQUAL: if (alpha<=ref) break; goto discard; + case GGL_GREATER: if (alpha>ref) break; goto discard; + case GGL_NOTEQUAL: if (alpha!=ref) break; goto discard; + case GGL_GEQUAL: if (alpha>=ref) break; goto discard; + } + } + + // depth test + if (c->state.buffers.depth.format) { + if (enables & GGL_ENABLE_DEPTH_TEST) { + surface_t* cb = &(c->state.buffers.depth); + uint16_t* p = (uint16_t*)(cb->data)+(x+(cb->stride*y)); + uint16_t zz = uint32_t(z)>>(16); + uint16_t depth = *p; + switch (c->state.depth_test.func) { + case GGL_NEVER: goto discard; + case GGL_LESS: if (zz<depth) break; goto discard; + case GGL_EQUAL: if (zz==depth) break; goto discard; + case GGL_LEQUAL: if (zz<=depth) break; goto discard; + case GGL_GREATER: if (zz>depth) break; goto discard; + case GGL_NOTEQUAL: if (zz!=depth) break; goto discard; + case GGL_GEQUAL: if (zz>=depth) break; goto discard; + } + // depth buffer is not enabled, if depth-test is not enabled +/* + fragment.s[1] = fragment.s[2] = + fragment.s[3] = fragment.s[0] = 8; + fragment.c[1] = + fragment.c[2] = + fragment.c[3] = + fragment.c[0] = 255 - (zz>>8); +*/ + if (c->state.mask.depth) { + *p = zz; + } + } + } + + // fog + if (enables & GGL_ENABLE_FOG) { + for (int i=1 ; i<=3 ; i++) { + GGLfixed fc = (c->state.fog.color[i] * 0x10000) / 0xFF; + uint32_t& c = fragment.c[i]; + uint8_t& s = fragment.s[i]; + c = (c * 0x10000) / ((1<<s)-1); + c = gglMulAddx(c, f, gglMulx(fc, 0x10000 - f)); + s = 16; + } + } + + // blending + if (enables & GGL_ENABLE_BLENDING) { + fb.c[1] = fb.c[2] = fb.c[3] = fb.c[0] = 0; // placate valgrind + fb.s[1] = fb.s[2] = fb.s[3] = fb.s[0] = 0; + c->state.buffers.color.read( + &(c->state.buffers.color), c, x, y, &fb); + blending( c, &fragment, &fb ); + } + + // write + c->state.buffers.color.write( + &(c->state.buffers.color), c, x, y, &fragment); + } + +discard: + // iterate... + x += 1; + if (enables & GGL_ENABLE_SMOOTH) { + r += c->shade.drdx; + g += c->shade.dgdx; + b += c->shade.dbdx; + a += c->shade.dadx; + } + z += c->shade.dzdx; + f += c->shade.dfdx; + } +} + +#endif // ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED) + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Scanline +#endif + +template <typename T, typename U> +static inline __attribute__((const)) +T interpolate(int y, T v0, U dvdx, U dvdy) { + // interpolates in pixel's centers + // v = v0 + (y + 0.5) * dvdy + (0.5 * dvdx) + return (y * dvdy) + (v0 + ((dvdy + dvdx) >> 1)); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void init_y(context_t* c, int32_t ys) +{ + const uint32_t enables = c->state.enables; + + // compute iterators... + iterators_t& ci = c->iterators; + + // sample in the center + ci.y = ys; + + if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_W|GGL_ENABLE_FOG)) { + ci.ydzdy = interpolate(ys, c->shade.z0, c->shade.dzdx, c->shade.dzdy); + ci.ydwdy = interpolate(ys, c->shade.w0, c->shade.dwdx, c->shade.dwdy); + ci.ydfdy = interpolate(ys, c->shade.f0, c->shade.dfdx, c->shade.dfdy); + } + + if (ggl_unlikely(enables & GGL_ENABLE_SMOOTH)) { + ci.ydrdy = interpolate(ys, c->shade.r0, c->shade.drdx, c->shade.drdy); + ci.ydgdy = interpolate(ys, c->shade.g0, c->shade.dgdx, c->shade.dgdy); + ci.ydbdy = interpolate(ys, c->shade.b0, c->shade.dbdx, c->shade.dbdy); + ci.ydady = interpolate(ys, c->shade.a0, c->shade.dadx, c->shade.dady); + c->step_y = step_y__smooth; + } else { + ci.ydrdy = c->shade.r0; + ci.ydgdy = c->shade.g0; + ci.ydbdy = c->shade.b0; + ci.ydady = c->shade.a0; + // XXX: do only if needed, or make sure this is fast + c->packed = ggl_pack_color(c, c->state.buffers.color.format, + ci.ydrdy, ci.ydgdy, ci.ydbdy, ci.ydady); + c->packed8888 = ggl_pack_color(c, GGL_PIXEL_FORMAT_RGBA_8888, + ci.ydrdy, ci.ydgdy, ci.ydbdy, ci.ydady); + } + + // initialize the variables we need in the shader + generated_vars_t& gen = c->generated_vars; + gen.argb[GGLFormat::ALPHA].c = ci.ydady; + gen.argb[GGLFormat::ALPHA].dx = c->shade.dadx; + gen.argb[GGLFormat::RED ].c = ci.ydrdy; + gen.argb[GGLFormat::RED ].dx = c->shade.drdx; + gen.argb[GGLFormat::GREEN].c = ci.ydgdy; + gen.argb[GGLFormat::GREEN].dx = c->shade.dgdx; + gen.argb[GGLFormat::BLUE ].c = ci.ydbdy; + gen.argb[GGLFormat::BLUE ].dx = c->shade.dbdx; + gen.dzdx = c->shade.dzdx; + gen.f = ci.ydfdy; + gen.dfdx = c->shade.dfdx; + + if (enables & GGL_ENABLE_TMUS) { + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + texture_t& t = c->state.texture[i]; + if (!t.enable) continue; + + texture_iterators_t& ti = t.iterators; + if (t.s_coord == GGL_ONE_TO_ONE && t.t_coord == GGL_ONE_TO_ONE) { + // we need to set all of these to 0 because in some cases + // step_y__generic() or step_y__tmu() will be used and + // therefore will update dtdy, however, in 1:1 mode + // this is always done by the scanline rasterizer. + ti.dsdx = ti.dsdy = ti.dtdx = ti.dtdy = 0; + ti.ydsdy = t.shade.is0; + ti.ydtdy = t.shade.it0; + } else { + const int adjustSWrap = ((t.s_wrap==GGL_CLAMP)?0:16); + const int adjustTWrap = ((t.t_wrap==GGL_CLAMP)?0:16); + ti.sscale = t.shade.sscale + adjustSWrap; + ti.tscale = t.shade.tscale + adjustTWrap; + if (!(enables & GGL_ENABLE_W)) { + // S coordinate + const int32_t sscale = ti.sscale; + const int32_t sy = interpolate(ys, + t.shade.is0, t.shade.idsdx, t.shade.idsdy); + if (sscale>=0) { + ti.ydsdy= sy << sscale; + ti.dsdx = t.shade.idsdx << sscale; + ti.dsdy = t.shade.idsdy << sscale; + } else { + ti.ydsdy= sy >> -sscale; + ti.dsdx = t.shade.idsdx >> -sscale; + ti.dsdy = t.shade.idsdy >> -sscale; + } + // T coordinate + const int32_t tscale = ti.tscale; + const int32_t ty = interpolate(ys, + t.shade.it0, t.shade.idtdx, t.shade.idtdy); + if (tscale>=0) { + ti.ydtdy= ty << tscale; + ti.dtdx = t.shade.idtdx << tscale; + ti.dtdy = t.shade.idtdy << tscale; + } else { + ti.ydtdy= ty >> -tscale; + ti.dtdx = t.shade.idtdx >> -tscale; + ti.dtdy = t.shade.idtdy >> -tscale; + } + } + } + // mirror for generated code... + generated_tex_vars_t& gen = c->generated_vars.texture[i]; + gen.width = t.surface.width; + gen.height = t.surface.height; + gen.stride = t.surface.stride; + gen.data = int32_t(t.surface.data); + gen.dsdx = ti.dsdx; + gen.dtdx = ti.dtdx; + } + } + + // choose the y-stepper + c->step_y = step_y__nop; + if (enables & GGL_ENABLE_FOG) { + c->step_y = step_y__generic; + } else if (enables & GGL_ENABLE_TMUS) { + if (enables & GGL_ENABLE_SMOOTH) { + c->step_y = step_y__generic; + } else if (enables & GGL_ENABLE_W) { + c->step_y = step_y__w; + } else { + c->step_y = step_y__tmu; + } + } else { + if (enables & GGL_ENABLE_SMOOTH) { + c->step_y = step_y__smooth; + } + } + + // choose the rectangle blitter + c->rect = rect_generic; + if ((c->step_y == step_y__nop) && + (c->scanline == scanline_memcpy)) + { + c->rect = rect_memcpy; + } +} + +void init_y_packed(context_t* c, int32_t y0) +{ + uint8_t f = c->state.buffers.color.format; + c->packed = ggl_pack_color(c, f, + c->shade.r0, c->shade.g0, c->shade.b0, c->shade.a0); + c->iterators.y = y0; + c->step_y = step_y__nop; + // choose the rectangle blitter + c->rect = rect_generic; + if (c->scanline == scanline_memcpy) { + c->rect = rect_memcpy; + } +} + +void init_y_noop(context_t* c, int32_t y0) +{ + c->iterators.y = y0; + c->step_y = step_y__nop; + // choose the rectangle blitter + c->rect = rect_generic; + if (c->scanline == scanline_memcpy) { + c->rect = rect_memcpy; + } +} + +void init_y_error(context_t* c, int32_t y0) +{ + // woooops, shoud never happen, + // fail gracefully (don't display anything) + init_y_noop(c, y0); + LOGE("color-buffer has an invalid format!"); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void step_y__generic(context_t* c) +{ + const uint32_t enables = c->state.enables; + + // iterate... + iterators_t& ci = c->iterators; + ci.y += 1; + + if (enables & GGL_ENABLE_SMOOTH) { + ci.ydrdy += c->shade.drdy; + ci.ydgdy += c->shade.dgdy; + ci.ydbdy += c->shade.dbdy; + ci.ydady += c->shade.dady; + } + + const uint32_t mask = + GGL_ENABLE_DEPTH_TEST | + GGL_ENABLE_W | + GGL_ENABLE_FOG; + if (enables & mask) { + ci.ydzdy += c->shade.dzdy; + ci.ydwdy += c->shade.dwdy; + ci.ydfdy += c->shade.dfdy; + } + + if ((enables & GGL_ENABLE_TMUS) && (!(enables & GGL_ENABLE_W))) { + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + if (c->state.texture[i].enable) { + texture_iterators_t& ti = c->state.texture[i].iterators; + ti.ydsdy += ti.dsdy; + ti.ydtdy += ti.dtdy; + } + } + } +} + +void step_y__nop(context_t* c) +{ + c->iterators.y += 1; + c->iterators.ydzdy += c->shade.dzdy; +} + +void step_y__smooth(context_t* c) +{ + iterators_t& ci = c->iterators; + ci.y += 1; + ci.ydrdy += c->shade.drdy; + ci.ydgdy += c->shade.dgdy; + ci.ydbdy += c->shade.dbdy; + ci.ydady += c->shade.dady; + ci.ydzdy += c->shade.dzdy; +} + +void step_y__w(context_t* c) +{ + iterators_t& ci = c->iterators; + ci.y += 1; + ci.ydzdy += c->shade.dzdy; + ci.ydwdy += c->shade.dwdy; +} + +void step_y__tmu(context_t* c) +{ + iterators_t& ci = c->iterators; + ci.y += 1; + ci.ydzdy += c->shade.dzdy; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + if (c->state.texture[i].enable) { + texture_iterators_t& ti = c->state.texture[i].iterators; + ti.ydsdy += ti.dsdy; + ti.ydtdy += ti.dtdy; + } + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void scanline_perspective(context_t* c) +{ + struct { + union { + struct { + int32_t s, sq; + int32_t t, tq; + }; + struct { + int32_t v, q; + } st[2]; + }; + } tc[GGL_TEXTURE_UNIT_COUNT] __attribute__((aligned(16))); + + // XXX: we should have a special case when dwdx = 0 + + // 32 pixels spans works okay. 16 is a lot better, + // but hey, it's a software renderer... + const uint32_t SPAN_BITS = 5; + const uint32_t ys = c->iterators.y; + const uint32_t xs = c->iterators.xl; + const uint32_t x1 = c->iterators.xr; + const uint32_t xc = x1 - xs; + uint32_t remainder = xc & ((1<<SPAN_BITS)-1); + uint32_t numSpans = xc >> SPAN_BITS; + + const iterators_t& ci = c->iterators; + int32_t w0 = (xs * c->shade.dwdx) + ci.ydwdy; + int32_t q0 = gglRecipQ(w0, 30); + const int iwscale = 32 - gglClz(q0); + + const int32_t dwdx = c->shade.dwdx << SPAN_BITS; + int32_t xl = c->iterators.xl; + + // We process s & t with a loop to reduce the code size + // (and i-cache pressure). + + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + const texture_t& tmu = c->state.texture[i]; + if (!tmu.enable) continue; + int32_t s = tmu.shade.is0 + + (tmu.shade.idsdy * ys) + (tmu.shade.idsdx * xs) + + ((tmu.shade.idsdx + tmu.shade.idsdy)>>1); + int32_t t = tmu.shade.it0 + + (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) + + ((tmu.shade.idtdx + tmu.shade.idtdy)>>1); + tc[i].s = s; + tc[i].t = t; + tc[i].sq = gglMulx(s, q0, iwscale); + tc[i].tq = gglMulx(t, q0, iwscale); + } + + int32_t span = 0; + do { + int32_t w1; + if (ggl_likely(numSpans)) { + w1 = w0 + dwdx; + } else { + if (remainder) { + // finish off the scanline... + span = remainder; + w1 = (c->shade.dwdx * span) + w0; + } else { + break; + } + } + int32_t q1 = gglRecipQ(w1, 30); + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) { + texture_t& tmu = c->state.texture[i]; + if (!tmu.enable) continue; + texture_iterators_t& ti = tmu.iterators; + + for (int j=0 ; j<2 ; j++) { + int32_t v = tc[i].st[j].v; + if (span) v += (tmu.shade.st[j].dx)*span; + else v += (tmu.shade.st[j].dx)<<SPAN_BITS; + const int32_t v0 = tc[i].st[j].q; + const int32_t v1 = gglMulx(v, q1, iwscale); + int32_t dvdx = v1 - v0; + if (span) dvdx /= span; + else dvdx >>= SPAN_BITS; + tc[i].st[j].v = v; + tc[i].st[j].q = v1; + + const int scale = ti.st[j].scale + (iwscale - 30); + if (scale >= 0) { + ti.st[j].ydvdy = v0 << scale; + ti.st[j].dvdx = dvdx << scale; + } else { + ti.st[j].ydvdy = v0 >> -scale; + ti.st[j].dvdx = dvdx >> -scale; + } + } + generated_tex_vars_t& gen = c->generated_vars.texture[i]; + gen.dsdx = ti.st[0].dvdx; + gen.dtdx = ti.st[1].dvdx; + } + c->iterators.xl = xl; + c->iterators.xr = xl = xl + (span ? span : (1<<SPAN_BITS)); + w0 = w1; + q0 = q1; + c->span(c); + } while(numSpans--); +} + +void scanline_perspective_single(context_t* c) +{ + // 32 pixels spans works okay. 16 is a lot better, + // but hey, it's a software renderer... + const uint32_t SPAN_BITS = 5; + const uint32_t ys = c->iterators.y; + const uint32_t xs = c->iterators.xl; + const uint32_t x1 = c->iterators.xr; + const uint32_t xc = x1 - xs; + + const iterators_t& ci = c->iterators; + int32_t w = (xs * c->shade.dwdx) + ci.ydwdy; + int32_t iw = gglRecipQ(w, 30); + const int iwscale = 32 - gglClz(iw); + + const int i = 31 - gglClz(c->state.enabled_tmu); + generated_tex_vars_t& gen = c->generated_vars.texture[i]; + texture_t& tmu = c->state.texture[i]; + texture_iterators_t& ti = tmu.iterators; + const int sscale = ti.sscale + (iwscale - 30); + const int tscale = ti.tscale + (iwscale - 30); + int32_t s = tmu.shade.is0 + + (tmu.shade.idsdy * ys) + (tmu.shade.idsdx * xs) + + ((tmu.shade.idsdx + tmu.shade.idsdy)>>1); + int32_t t = tmu.shade.it0 + + (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) + + ((tmu.shade.idtdx + tmu.shade.idtdy)>>1); + int32_t s0 = gglMulx(s, iw, iwscale); + int32_t t0 = gglMulx(t, iw, iwscale); + int32_t xl = c->iterators.xl; + + int32_t sq, tq, dsdx, dtdx; + int32_t premainder = xc & ((1<<SPAN_BITS)-1); + uint32_t numSpans = xc >> SPAN_BITS; + if (c->shade.dwdx == 0) { + // XXX: we could choose to do this if the error is small enough + numSpans = 0; + premainder = xc; + goto no_perspective; + } + + if (premainder) { + w += c->shade.dwdx * premainder; + iw = gglRecipQ(w, 30); +no_perspective: + s += tmu.shade.idsdx * premainder; + t += tmu.shade.idtdx * premainder; + sq = gglMulx(s, iw, iwscale); + tq = gglMulx(t, iw, iwscale); + dsdx = (sq - s0) / premainder; + dtdx = (tq - t0) / premainder; + c->iterators.xl = xl; + c->iterators.xr = xl = xl + premainder; + goto finish; + } + + while (numSpans--) { + w += c->shade.dwdx << SPAN_BITS; + s += tmu.shade.idsdx << SPAN_BITS; + t += tmu.shade.idtdx << SPAN_BITS; + iw = gglRecipQ(w, 30); + sq = gglMulx(s, iw, iwscale); + tq = gglMulx(t, iw, iwscale); + dsdx = (sq - s0) >> SPAN_BITS; + dtdx = (tq - t0) >> SPAN_BITS; + c->iterators.xl = xl; + c->iterators.xr = xl = xl + (1<<SPAN_BITS); +finish: + if (sscale >= 0) { + ti.ydsdy = s0 << sscale; + ti.dsdx = dsdx << sscale; + } else { + ti.ydsdy = s0 >>-sscale; + ti.dsdx = dsdx >>-sscale; + } + if (tscale >= 0) { + ti.ydtdy = t0 << tscale; + ti.dtdx = dtdx << tscale; + } else { + ti.ydtdy = t0 >>-tscale; + ti.dtdx = dtdx >>-tscale; + } + s0 = sq; + t0 = tq; + gen.dsdx = ti.dsdx; + gen.dtdx = ti.dtdx; + c->span(c); + } +} + +// ---------------------------------------------------------------------------- + +void scanline_t32cb16(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + union { + uint16_t* dst; + uint32_t* dst32; + }; + dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y)); + + surface_t* tex = &(c->state.texture[0].surface); + const int32_t u = (c->state.texture[0].shade.is0>>16) + x; + const int32_t v = (c->state.texture[0].shade.it0>>16) + y; + uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v)); + int sR, sG, sB; + uint32_t s, d; + + if (ct==1 || uint32_t(dst)&2) { +last_one: + s = GGL_RGBA_TO_HOST( *src++ ); + sR = (s >> ( 3))&0x1F; + sG = (s >> ( 8+2))&0x3F; + sB = (s >> (16+3))&0x1F; + *dst++ = uint16_t((sR<<11)|(sG<<5)|sB); + ct--; + } + + while (ct >= 2) { + s = GGL_RGBA_TO_HOST( *src++ ); + sR = (s >> ( 3))&0x1F; + sG = (s >> ( 8+2))&0x3F; + sB = (s >> (16+3))&0x1F; + d = (sR<<11)|(sG<<5)|sB; + + s = GGL_RGBA_TO_HOST( *src++ ); + sR = (s >> ( 3))&0x1F; + sG = (s >> ( 8+2))&0x3F; + sB = (s >> (16+3))&0x1F; + d |= ((sR<<11)|(sG<<5)|sB)<<16; + +#if BYTE_ORDER == BIG_ENDIAN + d = (d>>16) | (d<<16); +#endif + + *dst32++ = d; + ct -= 2; + } + + if (ct > 0) { + goto last_one; + } +} + +void scanline_t32cb16blend(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + uint16_t* dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y)); + + surface_t* tex = &(c->state.texture[0].surface); + const int32_t u = (c->state.texture[0].shade.is0>>16) + x; + const int32_t v = (c->state.texture[0].shade.it0>>16) + y; + uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v)); + +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__)) + scanline_t32cb16blend_arm(dst, src, ct); +#else + while (ct--) { + uint32_t s = *src++; + if (!s) { + dst++; + continue; + } + uint16_t d = *dst; + s = GGL_RGBA_TO_HOST(s); + int sR = (s >> ( 3))&0x1F; + int sG = (s >> ( 8+2))&0x3F; + int sB = (s >> (16+3))&0x1F; + int sA = (s>>24); + int f = 0x100 - (sA + (sA>>7)); + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR += (f*dR)>>8; + sG += (f*dG)>>8; + sB += (f*dB)>>8; + *dst++ = uint16_t((sR<<11)|(sG<<5)|sB); + } +#endif +} + +void scanline_memcpy(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + const GGLFormat* fp = &(c->formats[cb->format]); + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + + (x + (cb->stride * y)) * fp->size; + + surface_t* tex = &(c->state.texture[0].surface); + const int32_t u = (c->state.texture[0].shade.is0>>16) + x; + const int32_t v = (c->state.texture[0].shade.it0>>16) + y; + uint8_t *src = reinterpret_cast<uint8_t*>(tex->data) + + (u + (tex->stride * v)) * fp->size; + + const size_t size = ct * fp->size; + memcpy(dst, src, size); +} + +void scanline_memset8(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + (x+(cb->stride*y)); + uint32_t packed = c->packed; + memset(dst, packed, ct); +} + +void scanline_memset16(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + uint16_t* dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y)); + uint32_t packed = c->packed; + android_memset16(dst, packed, ct*2); +} + +void scanline_memset32(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + uint32_t* dst = reinterpret_cast<uint32_t*>(cb->data) + (x+(cb->stride*y)); + uint32_t packed = GGL_HOST_TO_RGBA(c->packed); + android_memset32(dst, packed, ct*4); +} + +void scanline_clear(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + const GGLFormat* fp = &(c->formats[cb->format]); + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + + (x + (cb->stride * y)) * fp->size; + const size_t size = ct * fp->size; + memset(dst, 0, size); +} + +void scanline_set(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + const GGLFormat* fp = &(c->formats[cb->format]); + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + + (x + (cb->stride * y)) * fp->size; + const size_t size = ct * fp->size; + memset(dst, 0xFF, size); +} + +void scanline_noop(context_t* c) +{ +} + +void rect_generic(context_t* c, size_t yc) +{ + do { + c->scanline(c); + c->step_y(c); + } while (--yc); +} + +void rect_memcpy(context_t* c, size_t yc) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + const GGLFormat* fp = &(c->formats[cb->format]); + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + + (x + (cb->stride * y)) * fp->size; + + surface_t* tex = &(c->state.texture[0].surface); + const int32_t u = (c->state.texture[0].shade.is0>>16) + x; + const int32_t v = (c->state.texture[0].shade.it0>>16) + y; + uint8_t *src = reinterpret_cast<uint8_t*>(tex->data) + + (u + (tex->stride * v)) * fp->size; + + if (cb->stride == tex->stride && ct == size_t(cb->stride)) { + memcpy(dst, src, ct * fp->size * yc); + } else { + const size_t size = ct * fp->size; + const size_t dbpr = cb->stride * fp->size; + const size_t sbpr = tex->stride * fp->size; + do { + memcpy(dst, src, size); + dst += dbpr; + src += sbpr; + } while (--yc); + } +} +// ---------------------------------------------------------------------------- +}; // namespace android + +using namespace android; +extern "C" void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) +{ +#if ANDROID_ARM_CODEGEN + GGLContext* c; + gglInit(&c); + needs_t needs; + needs.n = n; + needs.p = p; + needs.t[0] = t0; + needs.t[1] = t1; + sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); + GGLAssembler assembler( new ARMAssembler(a) ); + int err = assembler.scanline(needs, (context_t*)c); + if (err != 0) { + printf("error %08x (%s)\n", err, strerror(-err)); + } + gglUninit(c); +#else + printf("This test runs only on ARM\n"); +#endif +} + diff --git a/libpixelflinger/scanline.h b/libpixelflinger/scanline.h new file mode 100644 index 0000000..b6f4d37 --- /dev/null +++ b/libpixelflinger/scanline.h @@ -0,0 +1,32 @@ +/* libs/pixelflinger/scanline.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_SCANLINE_H +#define ANDROID_SCANLINE_H + +#include <private/pixelflinger/ggl_context.h> + +namespace android { + +void ggl_init_scanline(context_t* c); +void ggl_uninit_scanline(context_t* c); +void ggl_pick_scanline(context_t* c); + +}; // namespace android + +#endif diff --git a/libpixelflinger/t32cb16blend.S b/libpixelflinger/t32cb16blend.S new file mode 100644 index 0000000..d4b2579 --- /dev/null +++ b/libpixelflinger/t32cb16blend.S @@ -0,0 +1,171 @@ +/* libs/pixelflinger/t32cb16blend.S +** +** 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. +*/ + + + .text + .align + + .global scanline_t32cb16blend_arm + +// uses r6, r7, lr + +.macro pixel, DREG, SRC, FB, OFFSET + + // SRC = AARRGGBB + mov r7, \SRC, lsr #24 // sA + add r7, r7, r7, lsr #7 // sA + (sA >> 7) + rsb r7, r7, #0x100 // sA = 0x100 - (sA+(sA>>7)) + +1: + +.if \OFFSET + + // red + mov lr, \DREG, lsr #(\OFFSET + 6 + 5) + smulbb lr, r7, lr + mov r6, \SRC, lsr #3 + and r6, r6, #0x1F + add lr, r6, lr, lsr #8 + orr \FB, lr, lsl #(\OFFSET + 11) + + // green + and r6, \DREG, #(0x3F<<(\OFFSET + 5)) + smulbt r6, r7, r6 + mov lr, \SRC, lsr #(8+2) + and lr, lr, #0x3F + add r6, lr, r6, lsr #(5+8) + orr \FB, \FB, r6, lsl #(\OFFSET + 5) + + // blue + and lr, \DREG, #(0x1F << \OFFSET) + smulbt lr, r7, lr + mov r6, \SRC, lsr #(8+8+3) + and r6, r6, #0x1F + add lr, r6, lr, lsr #8 + orr \FB, \FB, lr, lsl #\OFFSET + +.else + + // red + mov lr, \DREG, lsr #(6+5) + and lr, lr, #0x1F + smulbb lr, r7, lr + mov r6, \SRC, lsr #3 + and r6, r6, #0x1F + add lr, r6, lr, lsr #8 + mov \FB, lr, lsl #11 + + // green + and r6, \DREG, #(0x3F<<5) + smulbb r6, r7, r6 + mov lr, \SRC, lsr #(8+2) + and lr, lr, #0x3F + add r6, lr, r6, lsr #(5+8) + orr \FB, \FB, r6, lsl #5 + + // blue + and lr, \DREG, #0x1F + smulbb lr, r7, lr + mov r6, \SRC, lsr #(8+8+3) + and r6, r6, #0x1F + add lr, r6, lr, lsr #8 + orr \FB, \FB, lr + +.endif + + .endm + + +// r0: dst ptr +// r1: src ptr +// r2: count +// r3: d +// r4: s0 +// r5: s1 +// r6: pixel +// r7: pixel +// r8: free +// r9: free +// r10: free +// r11: free +// r12: scratch +// r14: pixel + +scanline_t32cb16blend_arm: + stmfd sp!, {r4-r7, lr} + + pld [r0] + pld [r1] + + // align DST to 32 bits + tst r0, #0x3 + beq aligned + subs r2, r2, #1 + ldmlofd sp!, {r4-r7, lr} // return + bxlo lr + +last: + ldr r4, [r1], #4 + ldrh r3, [r0] + pixel r3, r4, r12, 0 + strh r12, [r0], #2 + +aligned: + subs r2, r2, #2 + blo 9f + + // The main loop is unrolled twice and process 4 pixels +8: ldmia r1!, {r4, r5} + // stream the source + pld [r1, #32] + add r0, r0, #4 + // it's all zero, skip this pixel + orrs r3, r4, r5 + beq 7f + + // load the destination + ldr r3, [r0, #-4] + // stream the destination + pld [r0, #32] + pixel r3, r4, r12, 0 + pixel r3, r5, r12, 16 + // effectively, we're getting write-combining by virtue of the + // cpu's write-back cache. + str r12, [r0, #-4] + + // 2nd iterration of the loop, don't stream anything + subs r2, r2, #2 + movlt r4, r5 + blt 9f + ldmia r1!, {r4, r5} + add r0, r0, #4 + orrs r3, r4, r5 + beq 7f + ldr r3, [r0, #-4] + pixel r3, r4, r12, 0 + pixel r3, r5, r12, 16 + str r12, [r0, #-4] + + +7: subs r2, r2, #2 + bhs 8b + mov r4, r5 + +9: adds r2, r2, #1 + ldmlofd sp!, {r4-r7, lr} // return + bxlo lr + b last diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk new file mode 100644 index 0000000..6571161 --- /dev/null +++ b/libpixelflinger/tests/Android.mk @@ -0,0 +1 @@ +include $(all-subdir-makefiles) diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk new file mode 100644 index 0000000..1bc4214 --- /dev/null +++ b/libpixelflinger/tests/codegen/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + codegen.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libpixelflinger + +LOCAL_MODULE:= test-opengl-codegen + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp new file mode 100644 index 0000000..1865888 --- /dev/null +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <stdint.h> + +extern "C" void ggl_test_codegen( + uint32_t n, uint32_t p, uint32_t t0, uint32_t t1); + + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("usage: %s 00000117:03454504_00001501_00000000\n", argv[0]); + return 0; + } + uint32_t n; + uint32_t p; + uint32_t t0; + uint32_t t1; + sscanf(argv[1], "%08x:%08x_%08x_%08x", &p, &n, &t0, &t1); + ggl_test_codegen(n, p, t0, t1); + return 0; +} diff --git a/libpixelflinger/tinyutils/KeyedVector.h b/libpixelflinger/tinyutils/KeyedVector.h new file mode 100644 index 0000000..1be2094 --- /dev/null +++ b/libpixelflinger/tinyutils/KeyedVector.h @@ -0,0 +1,193 @@ +/* + * keyed_vector.h + * Android + * + * Created on 11/18/05. + * Copyright 2005 The Android Open Source Project + * + */ + +#ifndef ANDROID_KEYED_VECTOR_H +#define ANDROID_KEYED_VECTOR_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +#include "tinyutils/SortedVector.h" +#include "tinyutils/TypeHelpers.h" + +// --------------------------------------------------------------------------- + +namespace android { + +template <typename KEY, typename VALUE> +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + + /*! + * modifing the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t<KEY, VALUE> > mVector; +}; + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template <typename KEY, typename VALUE> +class DefaultKeyedVector : public KeyedVector<KEY, VALUE> +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template<typename KEY, typename VALUE> inline +KeyedVector<KEY,VALUE>::KeyedVector() +{ +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) ); +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.itemAt(i).value; +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template<typename KEY, typename VALUE> inline +const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template<typename KEY, typename VALUE> inline +VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.editItemAt(i).value; +} + +template<typename KEY, typename VALUE> inline +VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) ); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t<KEY,VALUE> pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) { + if (index<size()) { + mVector.editValueAt(index).value = item; + return index; + } + return BAD_INDEX; +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t<KEY,VALUE>(key)); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<typename KEY, typename VALUE> inline +DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template<typename KEY, typename VALUE> inline +const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_KEYED_VECTOR_H diff --git a/libpixelflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/tinyutils/SharedBuffer.cpp new file mode 100644 index 0000000..ef781a7 --- /dev/null +++ b/libpixelflinger/tinyutils/SharedBuffer.cpp @@ -0,0 +1,106 @@ +/* + * SharedBuffer.cpp + * Android + * + * Copyright 2005 The Android Open Source Project + * + */ + +#include <stdlib.h> +#include <string.h> + +#include <cutils/atomic.h> + +#include "tinyutils/SharedBuffer.h" + +// --------------------------------------------------------------------------- + +namespace android { + +SharedBuffer* SharedBuffer::alloc(size_t size) +{ + SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size)); + if (sb) { + sb->mRefs = 1; + sb->mSize = size; + } + return sb; +} + + +ssize_t SharedBuffer::dealloc(const SharedBuffer* released) +{ + if (released->mRefs != 0) return -1; // XXX: invalid operation + free(const_cast<SharedBuffer*>(released)); + return 0; +} + +SharedBuffer* SharedBuffer::edit() const +{ + if (onlyOwner()) { + return const_cast<SharedBuffer*>(this); + } + SharedBuffer* sb = alloc(mSize); + if (sb) { + memcpy(sb->data(), data(), size()); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::editResize(size_t newSize) const +{ + if (onlyOwner()) { + SharedBuffer* buf = const_cast<SharedBuffer*>(this); + if (buf->mSize == newSize) return buf; + buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); + if (buf != NULL) { + buf->mSize = newSize; + return buf; + } + } + SharedBuffer* sb = alloc(newSize); + if (sb) { + const size_t mySize = mSize; + memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::attemptEdit() const +{ + if (onlyOwner()) { + return const_cast<SharedBuffer*>(this); + } + return 0; +} + +SharedBuffer* SharedBuffer::reset(size_t new_size) const +{ + // cheap-o-reset. + SharedBuffer* sb = alloc(new_size); + if (sb) { + release(); + } + return sb; +} + +void SharedBuffer::acquire() const { + android_atomic_inc(&mRefs); +} + +int32_t SharedBuffer::release(uint32_t flags) const +{ + int32_t prev = 1; + if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { + mRefs = 0; + if ((flags & eKeepStorage) == 0) { + free(const_cast<SharedBuffer*>(this)); + } + } + return prev; +} + + +}; // namespace android diff --git a/libpixelflinger/tinyutils/SharedBuffer.h b/libpixelflinger/tinyutils/SharedBuffer.h new file mode 100644 index 0000000..9f63121 --- /dev/null +++ b/libpixelflinger/tinyutils/SharedBuffer.h @@ -0,0 +1,138 @@ +/* + * SharedBuffer.h + * Android + * + * Copyright 2005 The Android Open Source Project + * + */ + +#ifndef ANDROID_SHARED_BUFFER_H +#define ANDROID_SHARED_BUFFER_H + +#include <stdint.h> +#include <sys/types.h> + +// --------------------------------------------------------------------------- + +namespace android { + +class SharedBuffer +{ +public: + + /* flags to use with release() */ + enum { + eKeepStorage = 0x00000001 + }; + + /*! allocate a buffer of size 'size' and acquire() it. + * call release() to free it. + */ + static SharedBuffer* alloc(size_t size); + + /*! free the memory associated with the SharedBuffer. + * Fails if there are any users associated with this SharedBuffer. + * In other words, the buffer must have been release by all its + * users. + */ + static ssize_t dealloc(const SharedBuffer* released); + + //! get the SharedBuffer from the data pointer + static inline const SharedBuffer* sharedBuffer(const void* data); + + //! access the data for read + inline const void* data() const; + + //! access the data for read/write + inline void* data(); + + //! get size of the buffer + inline size_t size() const; + + //! get back a SharedBuffer object from its data + static inline SharedBuffer* bufferFromData(void* data); + + //! get back a SharedBuffer object from its data + static inline const SharedBuffer* bufferFromData(const void* data); + + //! get the size of a SharedBuffer object from its data + static inline size_t sizeFromData(const void* data); + + //! edit the buffer (get a writtable, or non-const, version of it) + SharedBuffer* edit() const; + + //! edit the buffer, resizing if needed + SharedBuffer* editResize(size_t size) const; + + //! like edit() but fails if a copy is required + SharedBuffer* attemptEdit() const; + + //! resize and edit the buffer, loose it's content. + SharedBuffer* reset(size_t size) const; + + //! acquire/release a reference on this buffer + void acquire() const; + + /*! release a reference on this buffer, with the option of not + * freeing the memory associated with it if it was the last reference + * returns the previous reference count + */ + int32_t release(uint32_t flags = 0) const; + + //! returns wether or not we're the only owner + inline bool onlyOwner() const; + + +private: + inline SharedBuffer() { } + inline ~SharedBuffer() { } + inline SharedBuffer(const SharedBuffer&); + + // 16 bytes. must be sized to preserve correct alingment. + mutable int32_t mRefs; + size_t mSize; + uint32_t mReserved[2]; +}; + +// --------------------------------------------------------------------------- + +const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { + return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0; +} + +const void* SharedBuffer::data() const { + return this + 1; +} + +void* SharedBuffer::data() { + return this + 1; +} + +size_t SharedBuffer::size() const { + return mSize; +} + +SharedBuffer* SharedBuffer::bufferFromData(void* data) +{ + return ((SharedBuffer*)data)-1; +} + +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) +{ + return ((const SharedBuffer*)data)-1; +} + +size_t SharedBuffer::sizeFromData(const void* data) +{ + return (((const SharedBuffer*)data)-1)->mSize; +} + +bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/libpixelflinger/tinyutils/TypeHelpers.h b/libpixelflinger/tinyutils/TypeHelpers.h new file mode 100644 index 0000000..9500c90 --- /dev/null +++ b/libpixelflinger/tinyutils/TypeHelpers.h @@ -0,0 +1,245 @@ +/* + * TypeHelpers.h + * + * Copyright 2005 The Android Open Source Project + * + */ + +#ifndef ANDROID_TYPE_HELPERS_H +#define ANDROID_TYPE_HELPERS_H + +#include <new> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +// --------------------------------------------------------------------------- + +namespace android { + +/* + * Types traits + */ + +template <typename T> struct trait_trivial_ctor { enum { value = false }; }; +template <typename T> struct trait_trivial_dtor { enum { value = false }; }; +template <typename T> struct trait_trivial_copy { enum { value = false }; }; +template <typename T> struct trait_trivial_assign{ enum { value = false }; }; + +template <typename T> struct trait_pointer { enum { value = false }; }; +template <typename T> struct trait_pointer<T*> { enum { value = true }; }; + +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = true }; }; + +#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ + template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; + +template <typename TYPE> +struct traits { + enum { + is_pointer = trait_pointer<TYPE>::value, + has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value, + has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value, + has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value, + has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value + }; +}; + +template <typename T, typename U> +struct aggregate_traits { + enum { + is_pointer = false, + has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor, + has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor, + has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy, + has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign + }; +}; + +// --------------------------------------------------------------------------- + +/* + * basic types traits + */ + +ANDROID_BASIC_TYPES_TRAITS( void ); +ANDROID_BASIC_TYPES_TRAITS( bool ); +ANDROID_BASIC_TYPES_TRAITS( char ); +ANDROID_BASIC_TYPES_TRAITS( unsigned char ); +ANDROID_BASIC_TYPES_TRAITS( short ); +ANDROID_BASIC_TYPES_TRAITS( unsigned short ); +ANDROID_BASIC_TYPES_TRAITS( int ); +ANDROID_BASIC_TYPES_TRAITS( unsigned int ); +ANDROID_BASIC_TYPES_TRAITS( long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long ); +ANDROID_BASIC_TYPES_TRAITS( long long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); +ANDROID_BASIC_TYPES_TRAITS( float ); +ANDROID_BASIC_TYPES_TRAITS( double ); + +// --------------------------------------------------------------------------- + + +/* + * compare and order types + */ + +template<typename TYPE> inline +int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { + return (lhs < rhs) ? 1 : 0; +} + +template<typename TYPE> inline +int compare_type(const TYPE& lhs, const TYPE& rhs) { + return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); +} + +/* + * create, destroy, copy and assign types... + */ + +template<typename TYPE> inline +void construct_type(TYPE* p, size_t n) { + if (!traits<TYPE>::has_trivial_ctor) { + while (n--) { + new(p++) TYPE; + } + } +} + +template<typename TYPE> inline +void destroy_type(TYPE* p, size_t n) { + if (!traits<TYPE>::has_trivial_dtor) { + while (n--) { + p->~TYPE(); + p++; + } + } +} + +template<typename TYPE> inline +void copy_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits<TYPE>::has_trivial_copy) { + while (n--) { + new(d) TYPE(*s); + d++, s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template<typename TYPE> inline +void assign_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits<TYPE>::has_trivial_assign) { + while (n--) { + *d++ = *s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template<typename TYPE> inline +void splat_type(TYPE* where, const TYPE* what, size_t n) { + if (!traits<TYPE>::has_trivial_copy) { + while (n--) { + new(where) TYPE(*what); + where++; + } + } else { + while (n--) { + *where++ = *what; + } + } +} + +template<typename TYPE> inline +void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) { + d += n; + s += n; + while (n--) { + --d, --s; + if (!traits<TYPE>::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits<TYPE>::has_trivial_dtor) { + s->~TYPE(); + } + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} + +template<typename TYPE> inline +void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) { + while (n--) { + if (!traits<TYPE>::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits<TYPE>::has_trivial_dtor) { + s->~TYPE(); + } + d++, s++; + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} +// --------------------------------------------------------------------------- + +/* + * a key/value pair + */ + +template <typename KEY, typename VALUE> +struct key_value_pair_t { + KEY key; + VALUE value; + key_value_pair_t() { } + key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } + key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } + key_value_pair_t(const KEY& k) : key(k) { } + inline bool operator < (const key_value_pair_t& o) const { + return strictly_order_type(key, o.key); + } +}; + +template<> +template <typename K, typename V> +struct trait_trivial_ctor< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; }; +template<> +template <typename K, typename V> +struct trait_trivial_dtor< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; }; +template<> +template <typename K, typename V> +struct trait_trivial_copy< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; }; +template<> +template <typename K, typename V> +struct trait_trivial_assign< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_assign};}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_TYPE_HELPERS_H diff --git a/libpixelflinger/tinyutils/Vector.h b/libpixelflinger/tinyutils/Vector.h new file mode 100644 index 0000000..182bc7b --- /dev/null +++ b/libpixelflinger/tinyutils/Vector.h @@ -0,0 +1,352 @@ +/* + * vector.h + * Android + * + * Copyright 2005 The Android Open Source Project + * + */ + +#ifndef ANDROID_VECTOR_H +#define ANDROID_VECTOR_H + +#include <new> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include "tinyutils/VectorImpl.h" +#include "tinyutils/TypeHelpers.h" + +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template <class TYPE> +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector<TYPE>& rhs); + virtual ~Vector(); + + /*! copy operator */ + const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; + Vector<TYPE>& operator = (const Vector<TYPE>& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right acces to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector<TYPE>& vector); + + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert on onr several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp); + inline status_t sort(compar_r_t cmp, void* state); + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template<class TYPE> inline +Vector<TYPE>::Vector() + : VectorImpl(sizeof(TYPE), + ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template<class TYPE> inline +Vector<TYPE>::Vector(const Vector<TYPE>& rhs) + : VectorImpl(rhs) { +} + +template<class TYPE> inline +Vector<TYPE>::~Vector() { + finish_vector(); +} + +template<class TYPE> inline +Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { + VectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const TYPE* Vector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); +} + +template<class TYPE> inline +TYPE* Vector<TYPE>::editArray() { + return static_cast<TYPE *>(editArrayImpl()); +} + + +template<class TYPE> inline +const TYPE& Vector<TYPE>::operator[](size_t index) const { + LOG_FATAL_IF( index>=size(), + "itemAt: index %d is past size %d", (int)index, (int)size() ); + return *(array() + index); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const { + LOG_FATAL_IF( (index>0 ? index : -index)>=size(), + "mirrorItemAt: index %d is past size %d", + (int)index, (int)size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> inline +TYPE& Vector<TYPE>::editItemAt(size_t index) { + return *( static_cast<TYPE *>(editItemLocation(index)) ); +} + +template<class TYPE> inline +TYPE& Vector<TYPE>::editTop() { + return *( static_cast<TYPE *>(editItemLocation(size()-1)) ); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) { + return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template<class TYPE> inline +void Vector<TYPE>::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template<class TYPE> inline +void Vector<TYPE>::pop() { + VectorImpl::pop(); +} + +template<class TYPE> inline +void Vector<TYPE>::push() { + VectorImpl::push(); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::add() { + return VectorImpl::add(); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template<class TYPE> inline +status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) { + return VectorImpl::sort((VectorImpl::compar_t)cmp); +} + +template<class TYPE> inline +status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) { + return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); +} + +// --------------------------------------------------------------------------- + +template<class TYPE> +void Vector<TYPE>::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/libpixelflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/tinyutils/VectorImpl.cpp new file mode 100644 index 0000000..a049706 --- /dev/null +++ b/libpixelflinger/tinyutils/VectorImpl.cpp @@ -0,0 +1,552 @@ +/* + * vector_impl.cpp + * Android + * + * Copyright 2005 The Android Open Source Project + * + */ + +#define LOG_TAG "Vector" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include <cutils/log.h> + +#include "tinyutils/SharedBuffer.h" +#include "tinyutils/VectorImpl.h" + +/*****************************************************************************/ + + +namespace android { + +enum { + NO_ERROR = 0, // No errors. + NO_MEMORY = -ENOMEM, + BAD_VALUE = -EINVAL, + BAD_INDEX = -EOVERFLOW, + NAME_NOT_FOUND = -ENOENT, +}; + +// ---------------------------------------------------------------------------- + +const size_t kMinVectorCapacity = 4; + +static inline size_t max(size_t a, size_t b) { + return a>b ? a : b; +} + +// ---------------------------------------------------------------------------- + +VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) + : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) +{ +} + +VectorImpl::VectorImpl(const VectorImpl& rhs) + : mStorage(rhs.mStorage), mCount(rhs.mCount), + mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) +{ + if (mStorage) { + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } +} + +VectorImpl::~VectorImpl() +{ + LOG_ASSERT(!mCount, + "[%p] " + "subclasses of VectorImpl must call finish_vector()" + " in their destructor. Leaking %d bytes.", + this, (int)(mCount*mItemSize)); + // We can't call _do_destroy() here because the vtable is already gone. +} + +VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) +{ + LOG_ASSERT(mItemSize == rhs.mItemSize, + "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); + if (this != &rhs) { + release_storage(); + if (rhs.mCount) { + mStorage = rhs.mStorage; + mCount = rhs.mCount; + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } else { + mStorage = 0; + mCount = 0; + } + } + return *this; +} + +void* VectorImpl::editArrayImpl() +{ + if (mStorage) { + SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + if (sb == 0) { + sb = SharedBuffer::alloc(capacity() * mItemSize); + if (sb) { + _do_copy(sb->data(), mStorage, mCount); + release_storage(); + mStorage = sb->data(); + } + } + } + return mStorage; +} + +size_t VectorImpl::capacity() const +{ + if (mStorage) { + return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + } + return 0; +} + +ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, vector.size()); + if (where) { + _do_copy(where, vector.arrayImpl(), vector.size()); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertAt(size_t index, size_t numItems) +{ + return insertAt(0, index, numItems); +} + +ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, numItems); + if (where) { + if (item) { + _do_splat(where, item, numItems); + } else { + _do_construct(where, numItems); + } + } + return where ? index : (ssize_t)NO_MEMORY; +} + +void VectorImpl::pop() +{ + if (size()) + removeItemsAt(size()-1, 1); +} + +void VectorImpl::push() +{ + push(0); +} + +void VectorImpl::push(const void* item) +{ + insertAt(item, size()); +} + +ssize_t VectorImpl::add() +{ + return add(0); +} + +ssize_t VectorImpl::add(const void* item) +{ + return insertAt(item, size()); +} + +ssize_t VectorImpl::replaceAt(size_t index) +{ + return replaceAt(0, index); +} + +ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) +{ + LOG_ASSERT(index<size(), + "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); + + void* item = editItemLocation(index); + if (item == 0) + return NO_MEMORY; + _do_destroy(item, 1); + if (prototype == 0) { + _do_construct(item, 1); + } else { + _do_copy(item, prototype, 1); + } + return ssize_t(index); +} + +ssize_t VectorImpl::removeItemsAt(size_t index, size_t count) +{ + LOG_ASSERT((index+count)<=size(), + "[%p] remove: index=%d, count=%d, size=%d", + this, (int)index, (int)count, (int)size()); + + if ((index+count) > size()) + return BAD_VALUE; + _shrink(index, count); + return index; +} + +void VectorImpl::finish_vector() +{ + release_storage(); + mStorage = 0; + mCount = 0; +} + +void VectorImpl::clear() +{ + _shrink(0, mCount); +} + +void* VectorImpl::editItemLocation(size_t index) +{ + LOG_ASSERT(index<capacity(), + "[%p] itemLocation: index=%d, capacity=%d, count=%d", + this, (int)index, (int)capacity(), (int)mCount); + + void* buffer = editArrayImpl(); + if (buffer) + return reinterpret_cast<char*>(buffer) + index*mItemSize; + return 0; +} + +const void* VectorImpl::itemLocation(size_t index) const +{ + LOG_ASSERT(index<capacity(), + "[%p] editItemLocation: index=%d, capacity=%d, count=%d", + this, (int)index, (int)capacity(), (int)mCount); + + const void* buffer = arrayImpl(); + if (buffer) + return reinterpret_cast<const char*>(buffer) + index*mItemSize; + return 0; +} + +ssize_t VectorImpl::setCapacity(size_t new_capacity) +{ + size_t current_capacity = capacity(); + ssize_t amount = new_capacity - size(); + if (amount <= 0) { + // we can't reduce the capacity + return current_capacity; + } + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + _do_copy(array, mStorage, size()); + release_storage(); + mStorage = const_cast<void*>(array); + } else { + return NO_MEMORY; + } + return new_capacity; +} + +void VectorImpl::release_storage() +{ + if (mStorage) { + const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + _do_destroy(mStorage, mCount); + SharedBuffer::dealloc(sb); + } + } +} + +void* VectorImpl::_grow(size_t where, size_t amount) +{ +// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where > mCount) + where = mCount; + + const size_t new_size = mCount + amount; + if (capacity() < new_size) { + const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); +// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); + if ((mStorage) && + (mCount==where) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount>where) { + const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize; + void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + _do_copy(dest, from, mCount-where); + } + release_storage(); + mStorage = const_cast<void*>(array); + } + } + } else { + ssize_t s = mCount-where; + if (s>0) { + void* array = editArrayImpl(); + void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize; + _do_move_forward(to, from, s); + } + } + mCount += amount; + void* free_space = const_cast<void*>(itemLocation(where)); + return free_space; +} + +void VectorImpl::_shrink(size_t where, size_t amount) +{ + if (!mStorage) + return; + +// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where >= mCount) + where = mCount - amount; + + const size_t new_size = mCount - amount; + if (new_size*3 < capacity()) { + const size_t new_capacity = max(kMinVectorCapacity, new_size*2); +// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + if ((where == mCount-amount) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount > where+amount) { + const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize; + void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize; + _do_copy(dest, from, mCount-(where+amount)); + } + release_storage(); + mStorage = const_cast<void*>(array); + } + } + } else { + void* array = editArrayImpl(); + void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize; + _do_destroy(to, amount); + ssize_t s = mCount-(where+amount); + if (s>0) { + const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + _do_move_backward(to, from, s); + } + } + + // adjust the number of items... + mCount -= amount; +} + +size_t VectorImpl::itemSize() const { + return mItemSize; +} + +void VectorImpl::_do_construct(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_CTOR)) { + do_construct(storage, num); + } +} + +void VectorImpl::_do_destroy(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_DTOR)) { + do_destroy(storage, num); + } +} + +void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_COPY)) { + do_copy(dest, from, num); + } else { + memcpy(dest, from, num*itemSize()); + } +} + +void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { + do_splat(dest, item, num); +} + +void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { + do_move_forward(dest, from, num); +} + +void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { + do_move_backward(dest, from, num); +} + +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + +/*****************************************************************************/ + +SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) + : VectorImpl(itemSize, flags) +{ +} + +SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) +: VectorImpl(rhs) +{ +} + +SortedVectorImpl::~SortedVectorImpl() +{ +} + +SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) +{ + return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) ); +} + +ssize_t SortedVectorImpl::indexOf(const void* item) const +{ + return _indexOrderOf(item); +} + +size_t SortedVectorImpl::orderOf(const void* item) const +{ + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size()-1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l)/2; + const void* const curr = reinterpret_cast<const char *>(a) + (mid*s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t SortedVectorImpl::add(const void* item) +{ + size_t order; + ssize_t index = _indexOrderOf(item, &order); + if (index < 0) { + index = VectorImpl::insertAt(item, order, 1); + } else { + index = VectorImpl::replaceAt(item, index); + } + return index; +} + +ssize_t SortedVectorImpl::merge(const VectorImpl& vector) +{ + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i=0 ; i<s ; i++) { + ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is ); + if (err<0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) +{ + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { + err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector)); + } else { + // this could be made a little better + err = merge(static_cast<const VectorImpl&>(vector)); + } + } + return err; +} + +ssize_t SortedVectorImpl::remove(const void* item) +{ + ssize_t i = indexOf(item); + if (i>=0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libpixelflinger/tinyutils/VectorImpl.h b/libpixelflinger/tinyutils/VectorImpl.h new file mode 100644 index 0000000..e868eca --- /dev/null +++ b/libpixelflinger/tinyutils/VectorImpl.h @@ -0,0 +1,185 @@ +/* + * vector_impl.h + * Android + * + * Copyright 2005 The Android Open Source Project + * + */ + +#ifndef ANDROID_VECTOR_IMPL_H +#define ANDROID_VECTOR_IMPL_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +// --------------------------------------------------------------------------- +// No user serviceable parts in here... +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * Implementation of the guts of the vector<> class + * this ensures backward binary compatibility and + * reduces code size. + * For performance reasons, we expose mStorage and mCount + * so these fields are set in stone. + * + */ + +class VectorImpl +{ +public: + enum { // flags passed to the ctor + HAS_TRIVIAL_CTOR = 0x00000001, + HAS_TRIVIAL_DTOR = 0x00000002, + HAS_TRIVIAL_COPY = 0x00000004, + HAS_TRIVIAL_ASSIGN = 0x00000008 + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + + /*! append/insert another vector */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + +protected: + size_t itemSize() const; + void release_storage(); + + virtual void do_construct(void* storage, size_t num) const = 0; + virtual void do_destroy(void* storage, size_t num) const = 0; + virtual void do_copy(void* dest, const void* from, size_t num) const = 0; + virtual void do_splat(void* dest, const void* item, size_t num) const = 0; + virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; + virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_IMPL_H diff --git a/libpixelflinger/tinyutils/smartpointer.h b/libpixelflinger/tinyutils/smartpointer.h new file mode 100644 index 0000000..88032d7 --- /dev/null +++ b/libpixelflinger/tinyutils/smartpointer.h @@ -0,0 +1,170 @@ +/* + * smartpointer.h + * Android + * + * Copyright 2005 The Android Open Source Project + * + */ + +#ifndef ANDROID_SMART_POINTER_H +#define ANDROID_SMART_POINTER_H + +#include <stdint.h> +#include <sys/types.h> +#include <stdlib.h> + +// --------------------------------------------------------------------------- +namespace android { + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp<T>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template<typename U> \ +inline bool operator _op_ (const sp<U>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template<typename U> \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +// --------------------------------------------------------------------------- + +template <typename T> +class sp +{ +public: + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp<T>& other); + template<typename U> sp(U* other); + template<typename U> sp(const sp<U>& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp<T>& other); + + template<typename U> sp& operator = (const sp<U>& other); + template<typename U> sp& operator = (U* other); + + // Reset + void clear(); + + // Accessors + + inline T& operator* () const { return *m_ptr; } + inline T* operator-> () const { return m_ptr; } + inline T* get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template<typename Y> friend class sp; + + T* m_ptr; +}; + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template<typename T> +sp<T>::sp(T* other) + : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template<typename T> +sp<T>::sp(const sp<T>& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template<typename T> template<typename U> +sp<T>::sp(U* other) : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template<typename T> template<typename U> +sp<T>::sp(const sp<U>& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template<typename T> +sp<T>::~sp() +{ + if (m_ptr) m_ptr->decStrong(this); +} + +template<typename T> +sp<T>& sp<T>::operator = (const sp<T>& other) { + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template<typename T> +sp<T>& sp<T>::operator = (T* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template<typename T> template<typename U> +sp<T>& sp<T>::operator = (const sp<U>& other) +{ + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template<typename T> template<typename U> +sp<T>& sp<T>::operator = (U* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template<typename T> +void sp<T>::clear() +{ + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_SMART_POINTER_H diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp new file mode 100644 index 0000000..30b633f --- /dev/null +++ b/libpixelflinger/trap.cpp @@ -0,0 +1,1173 @@ +/* libs/pixelflinger/trap.cpp +** +** 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "trap.h" +#include "picker.h" + +#include <cutils/log.h> +#include <cutils/memory.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +// enable to see triangles edges +#define DEBUG_TRANGLES 0 + +// ---------------------------------------------------------------------------- + +static void pointx_validate(void *con, const GGLcoord* c, GGLcoord r); +static void pointx(void *con, const GGLcoord* c, GGLcoord r); +static void aa_pointx(void *con, const GGLcoord* c, GGLcoord r); +static void aa_nice_pointx(void *con, const GGLcoord* c, GGLcoord r); + +static void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w); +static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w); +static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w); + +static void recti_validate(void* c, GGLint l, GGLint t, GGLint r, GGLint b); +static void recti(void* c, GGLint l, GGLint t, GGLint r, GGLint b); + +static void trianglex_validate(void*, + const GGLcoord*, const GGLcoord*, const GGLcoord*); +static void trianglex_small(void*, + const GGLcoord*, const GGLcoord*, const GGLcoord*); +static void trianglex_big(void*, + const GGLcoord*, const GGLcoord*, const GGLcoord*); +static void aa_trianglex(void*, + const GGLcoord*, const GGLcoord*, const GGLcoord*); +static void trianglex_debug(void* con, + const GGLcoord*, const GGLcoord*, const GGLcoord*); + +static void aapolyx(void* con, + const GGLcoord* pts, int count); + +static inline int min(int a, int b) CONST; +static inline int max(int a, int b) CONST; +static inline int min(int a, int b, int c) CONST; +static inline int max(int a, int b, int c) CONST; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Tools +#endif + +inline int min(int a, int b) { + return a<b ? a : b; +} +inline int max(int a, int b) { + return a<b ? b : a; +} +inline int min(int a, int b, int c) { + return min(a,min(b,c)); +} +inline int max(int a, int b, int c) { + return max(a,max(b,c)); +} + +template <typename T> +static inline void swap(T& a, T& b) { + T t(a); + a = b; + b = t; +} + +static void +triangle_dump_points( const GGLcoord* v0, + const GGLcoord* v1, + const GGLcoord* v2 ) +{ + float tri = 1.0f / TRI_ONE; + LOGD( " P0=(%.3f, %.3f) [%08x, %08x]\n" + " P1=(%.3f, %.3f) [%08x, %08x]\n" + " P2=(%.3f, %.3f) [%08x, %08x]\n", + v0[0]*tri, v0[1]*tri, v0[0], v0[1], + v1[0]*tri, v1[1]*tri, v1[0], v1[1], + v2[0]*tri, v2[1]*tri, v2[0], v2[1] ); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Misc +#endif + +void ggl_init_trap(context_t* c) +{ + ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE|GGL_TMU_STATE|GGL_CB_STATE); +} + +void ggl_state_changed(context_t* c, int flags) +{ + if (ggl_likely(!c->dirty)) { + c->procs.pointx = pointx_validate; + c->procs.linex = linex_validate; + c->procs.recti = recti_validate; + c->procs.trianglex = trianglex_validate; + } + c->dirty |= uint32_t(flags); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Point +#endif + +void pointx_validate(void *con, const GGLcoord* v, GGLcoord rad) +{ + GGL_CONTEXT(c, con); + ggl_pick(c); + if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { + if (c->state.enables & GGL_ENABLE_POINT_AA_NICE) { + c->procs.pointx = aa_nice_pointx; + } else { + c->procs.pointx = aa_pointx; + } + } else { + c->procs.pointx = pointx; + } + c->procs.pointx(con, v, rad); +} + +void pointx(void *con, const GGLcoord* v, GGLcoord rad) +{ + GGL_CONTEXT(c, con); + GGLcoord halfSize = TRI_ROUND(rad) >> 1; + if (halfSize == 0) + halfSize = TRI_HALF; + GGLcoord xc = v[0]; + GGLcoord yc = v[1]; + if (halfSize & TRI_HALF) { // size odd + xc = TRI_FLOOR(xc) + TRI_HALF; + yc = TRI_FLOOR(yc) + TRI_HALF; + } else { // size even + xc = TRI_ROUND(xc); + yc = TRI_ROUND(yc); + } + GGLint l = (xc - halfSize) >> TRI_FRACTION_BITS; + GGLint t = (yc - halfSize) >> TRI_FRACTION_BITS; + GGLint r = (xc + halfSize) >> TRI_FRACTION_BITS; + GGLint b = (yc + halfSize) >> TRI_FRACTION_BITS; + recti(c, l, t, r, b); +} + +// This way of computing the coverage factor, is more accurate and gives +// better results for small circles, but it is also a lot slower. +// Here we use super-sampling. +static int32_t coverageNice(GGLcoord x, GGLcoord y, + GGLcoord rmin, GGLcoord rmax, GGLcoord rr) +{ + const GGLcoord d2 = x*x + y*y; + if (d2 >= rmax) return 0; + if (d2 < rmin) return 0x7FFF; + + const int kSamples = 4; + const int kInc = 4; // 1/4 = 0.25 + const int kCoverageUnit = 1; // 1/(4^2) = 0.0625 + const GGLcoord kCoordOffset = -6; // -0.375 + + int hits = 0; + int x_sample = x + kCoordOffset; + for (int i=0 ; i<kSamples ; i++, x_sample += kInc) { + const int xval = rr - (x_sample * x_sample); + int y_sample = y + kCoordOffset; + for (int j=0 ; j<kSamples ; j++, y_sample += kInc) { + if (xval - (y_sample * y_sample) > 0) + hits += kCoverageUnit; + } + } + return min(0x7FFF, hits << (15 - kSamples)); +} + + +void aa_nice_pointx(void *con, const GGLcoord* v, GGLcoord size) +{ + GGL_CONTEXT(c, con); + + GGLcoord rad = ((size + 1)>>1); + GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS; + GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS; + GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; + GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; + GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF; + GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF; + + // scissor... + if (l < GGLint(c->state.scissor.left)) { + xstart += TRI_FROM_INT(c->state.scissor.left-l); + l = GGLint(c->state.scissor.left); + } + if (t < GGLint(c->state.scissor.top)) { + ystart += TRI_FROM_INT(c->state.scissor.top-t); + t = GGLint(c->state.scissor.top); + } + if (r > GGLint(c->state.scissor.right)) { + r = GGLint(c->state.scissor.right); + } + if (b > GGLint(c->state.scissor.bottom)) { + b = GGLint(c->state.scissor.bottom); + } + + int xc = r - l; + int yc = b - t; + if (xc>0 && yc>0) { + int16_t* covPtr = c->state.buffers.coverage; + const int32_t sqr2Over2 = 0xC; // rounded up + GGLcoord rr = rad*rad; + GGLcoord rmin = (rad - sqr2Over2)*(rad - sqr2Over2); + GGLcoord rmax = (rad + sqr2Over2)*(rad + sqr2Over2); + GGLcoord y = ystart; + c->iterators.xl = l; + c->iterators.xr = r; + c->init_y(c, t); + do { + // compute coverage factors for each pixel + GGLcoord x = xstart; + for (int i=l ; i<r ; i++) { + covPtr[i] = coverageNice(x, y, rmin, rmax, rr); + x += TRI_ONE; + } + y += TRI_ONE; + c->scanline(c); + c->step_y(c); + } while (--yc); + } +} + +// This is a cheap way of computing the coverage factor for a circle. +// We just lerp between the circles of radii r-sqrt(2)/2 and r+sqrt(2)/2 +static inline int32_t coverageFast(GGLcoord x, GGLcoord y, + GGLcoord rmin, GGLcoord rmax, GGLcoord scale) +{ + const GGLcoord d2 = x*x + y*y; + if (d2 >= rmax) return 0; + if (d2 < rmin) return 0x7FFF; + return 0x7FFF - (d2-rmin)*scale; +} + +void aa_pointx(void *con, const GGLcoord* v, GGLcoord size) +{ + GGL_CONTEXT(c, con); + + GGLcoord rad = ((size + 1)>>1); + GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS; + GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS; + GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; + GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; + GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF; + GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF; + + // scissor... + if (l < GGLint(c->state.scissor.left)) { + xstart += TRI_FROM_INT(c->state.scissor.left-l); + l = GGLint(c->state.scissor.left); + } + if (t < GGLint(c->state.scissor.top)) { + ystart += TRI_FROM_INT(c->state.scissor.top-t); + t = GGLint(c->state.scissor.top); + } + if (r > GGLint(c->state.scissor.right)) { + r = GGLint(c->state.scissor.right); + } + if (b > GGLint(c->state.scissor.bottom)) { + b = GGLint(c->state.scissor.bottom); + } + + int xc = r - l; + int yc = b - t; + if (xc>0 && yc>0) { + int16_t* covPtr = c->state.buffers.coverage; + rad <<= 4; + const int32_t sqr2Over2 = 0xB5; // fixed-point 24.8 + GGLcoord rmin = rad - sqr2Over2; + GGLcoord rmax = rad + sqr2Over2; + GGLcoord scale; + rmin *= rmin; + rmax *= rmax; + scale = 0x800000 / (rmax - rmin); + rmin >>= 8; + rmax >>= 8; + + GGLcoord y = ystart; + c->iterators.xl = l; + c->iterators.xr = r; + c->init_y(c, t); + + do { + // compute coverage factors for each pixel + GGLcoord x = xstart; + for (int i=l ; i<r ; i++) { + covPtr[i] = coverageFast(x, y, rmin, rmax, scale); + x += TRI_ONE; + } + y += TRI_ONE; + c->scanline(c); + c->step_y(c); + } while (--yc); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Line +#endif + +void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w) +{ + GGL_CONTEXT(c, con); + ggl_pick(c); + if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { + c->procs.linex = aa_linex; + } else { + c->procs.linex = linex; + } + c->procs.linex(con, v0, v1, w); +} + +static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width) +{ + GGL_CONTEXT(c, con); + GGLcoord v[4][2]; + v[0][0] = v0[0]; v[0][1] = v0[1]; + v[1][0] = v1[0]; v[1][1] = v1[1]; + v0 = v[0]; + v1 = v[1]; + const GGLcoord dx = abs(v0[0] - v1[0]); + const GGLcoord dy = abs(v0[1] - v1[1]); + GGLcoord nx, ny; + nx = ny = 0; + + GGLcoord halfWidth = TRI_ROUND(width) >> 1; + if (halfWidth == 0) + halfWidth = TRI_HALF; + + ((dx > dy) ? ny : nx) = halfWidth; + v[2][0] = v1[0]; v[2][1] = v1[1]; + v[3][0] = v0[0]; v[3][1] = v0[1]; + v[0][0] += nx; v[0][1] += ny; + v[1][0] += nx; v[1][1] += ny; + v[2][0] -= nx; v[2][1] -= ny; + v[3][0] -= nx; v[3][1] -= ny; + trianglex_big(con, v[0], v[1], v[2]); + trianglex_big(con, v[0], v[2], v[3]); +} + +static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width) +{ + GGL_CONTEXT(c, con); + GGLcoord v[4][2]; + v[0][0] = v0[0]; v[0][1] = v0[1]; + v[1][0] = v1[0]; v[1][1] = v1[1]; + v0 = v[0]; + v1 = v[1]; + + const GGLcoord dx = v0[0] - v1[0]; + const GGLcoord dy = v0[1] - v1[1]; + GGLcoord nx = -dy; + GGLcoord ny = dx; + + // generally, this will be well below 1.0 + const GGLfixed norm = gglMulx(width, gglSqrtRecipx(nx*nx+ny*ny), 4); + nx = gglMulx(nx, norm, 21); + ny = gglMulx(ny, norm, 21); + + v[2][0] = v1[0]; v[2][1] = v1[1]; + v[3][0] = v0[0]; v[3][1] = v0[1]; + v[0][0] += nx; v[0][1] += ny; + v[1][0] += nx; v[1][1] += ny; + v[2][0] -= nx; v[2][1] -= ny; + v[3][0] -= nx; v[3][1] -= ny; + aapolyx(con, v[0], 4); +} + + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Rect +#endif + +void recti_validate(void *con, GGLint l, GGLint t, GGLint r, GGLint b) +{ + GGL_CONTEXT(c, con); + ggl_pick(c); + c->procs.recti = recti; + c->procs.recti(con, l, t, r, b); +} + +void recti(void* con, GGLint l, GGLint t, GGLint r, GGLint b) +{ + GGL_CONTEXT(c, con); + + // scissor... + if (l < GGLint(c->state.scissor.left)) + l = GGLint(c->state.scissor.left); + if (t < GGLint(c->state.scissor.top)) + t = GGLint(c->state.scissor.top); + if (r > GGLint(c->state.scissor.right)) + r = GGLint(c->state.scissor.right); + if (b > GGLint(c->state.scissor.bottom)) + b = GGLint(c->state.scissor.bottom); + + int xc = r - l; + int yc = b - t; + if (xc>0 && yc>0) { + c->iterators.xl = l; + c->iterators.xr = r; + c->init_y(c, t); + c->rect(c, yc); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Triangle / Debugging +#endif + +static void scanline_set(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + const GGLFormat* fp = &(c->formats[cb->format]); + uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + + (x + (cb->stride * y)) * fp->size; + const size_t size = ct * fp->size; + memset(dst, 0xFF, size); +} + +static void trianglex_debug(void* con, + const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) +{ + GGL_CONTEXT(c, con); + if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { + aa_trianglex(con,v0,v1,v2); + } else { + trianglex_big(con,v0,v1,v2); + } + void (*save_scanline)(context_t*) = c->scanline; + c->scanline = scanline_set; + linex(con, v0, v1, TRI_ONE); + linex(con, v1, v2, TRI_ONE); + linex(con, v2, v0, TRI_ONE); + c->scanline = save_scanline; +} + +static void trianglex_xor(void* con, + const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) +{ + trianglex_big(con,v0,v1,v2); + trianglex_small(con,v0,v1,v2); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Triangle +#endif + +void trianglex_validate(void *con, + const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) +{ + GGL_CONTEXT(c, con); + ggl_pick(c); + if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { + c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : aa_trianglex; + } else { + c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : trianglex_big; + } + c->procs.trianglex(con, v0, v1, v2); +} + +// ---------------------------------------------------------------------------- + +void trianglex_small(void* con, + const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) +{ + GGL_CONTEXT(c, con); + + // vertices are in 28.4 fixed point, which allows + // us to use 32 bits multiplies below. + int32_t x0 = v0[0]; + int32_t y0 = v0[1]; + int32_t x1 = v1[0]; + int32_t y1 = v1[1]; + int32_t x2 = v2[0]; + int32_t y2 = v2[1]; + + int32_t dx01 = x0 - x1; + int32_t dy20 = y2 - y0; + int32_t dy01 = y0 - y1; + int32_t dx20 = x2 - x0; + + // The code below works only with CCW triangles + // so if we get a CW triangle, we need to swap two of its vertices + if (dx01*dy20 < dy01*dx20) { + swap(x0, x1); + swap(y0, y1); + dx01 = x0 - x1; + dy01 = y0 - y1; + dx20 = x2 - x0; + dy20 = y2 - y0; + } + int32_t dx12 = x1 - x2; + int32_t dy12 = y1 - y2; + + // bounding box & scissor + const int32_t bminx = TRI_FLOOR(min(x0, x1, x2)) >> TRI_FRACTION_BITS; + const int32_t bminy = TRI_FLOOR(min(y0, y1, y2)) >> TRI_FRACTION_BITS; + const int32_t bmaxx = TRI_CEIL( max(x0, x1, x2)) >> TRI_FRACTION_BITS; + const int32_t bmaxy = TRI_CEIL( max(y0, y1, y2)) >> TRI_FRACTION_BITS; + const int32_t minx = max(bminx, c->state.scissor.left); + const int32_t miny = max(bminy, c->state.scissor.top); + const int32_t maxx = min(bmaxx, c->state.scissor.right); + const int32_t maxy = min(bmaxy, c->state.scissor.bottom); + if ((minx >= maxx) || (miny >= maxy)) + return; // too small or clipped out... + + // step equations to the bounding box and snap to pixel center + const int32_t my = (miny << TRI_FRACTION_BITS) + TRI_HALF; + const int32_t mx = (minx << TRI_FRACTION_BITS) + TRI_HALF; + int32_t ey0 = dy01 * (x0 - mx) - dx01 * (y0 - my); + int32_t ey1 = dy12 * (x1 - mx) - dx12 * (y1 - my); + int32_t ey2 = dy20 * (x2 - mx) - dx20 * (y2 - my); + + // right-exclusive fill rule, to avoid rare cases + // of over drawing + if (dy01<0 || (dy01 == 0 && dx01>0)) ey0++; + if (dy12<0 || (dy12 == 0 && dx12>0)) ey1++; + if (dy20<0 || (dy20 == 0 && dx20>0)) ey2++; + + c->init_y(c, miny); + for (int32_t y = miny; y < maxy; y++) { + register int32_t ex0 = ey0; + register int32_t ex1 = ey1; + register int32_t ex2 = ey2; + register int32_t xl, xr; + for (xl=minx ; xl<maxx ; xl++) { + if (ex0>0 && ex1>0 && ex2>0) + break; // all strictly positive + ex0 -= dy01 << TRI_FRACTION_BITS; + ex1 -= dy12 << TRI_FRACTION_BITS; + ex2 -= dy20 << TRI_FRACTION_BITS; + } + xr = xl; + for ( ; xr<maxx ; xr++) { + if (!(ex0>0 && ex1>0 && ex2>0)) + break; // not all strictly positive + ex0 -= dy01 << TRI_FRACTION_BITS; + ex1 -= dy12 << TRI_FRACTION_BITS; + ex2 -= dy20 << TRI_FRACTION_BITS; + } + + if (xl < xr) { + c->iterators.xl = xl; + c->iterators.xr = xr; + c->scanline(c); + } + c->step_y(c); + + ey0 += dx01 << TRI_FRACTION_BITS; + ey1 += dx12 << TRI_FRACTION_BITS; + ey2 += dx20 << TRI_FRACTION_BITS; + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +// the following routine fills a triangle via edge stepping, which +// unfortunately requires divisions in the setup phase to get right, +// it should probably only be used for relatively large trianges + + +// x = y*DX/DY (ou DX and DY are constants, DY > 0, et y >= 0) +// +// for an equation of the type: +// x' = y*K/2^p (with K and p constants "carefully chosen") +// +// We can now do a DDA without precision loss. We define 'e' by: +// x' - x = y*(DX/DY - K/2^p) = y*e +// +// If we choose K = round(DX*2^p/DY) then, +// abs(e) <= 1/2^(p+1) by construction +// +// therefore abs(x'-x) = y*abs(e) <= y/2^(p+1) <= DY/2^(p+1) <= DMAX/2^(p+1) +// +// which means that if DMAX <= 2^p, therefore abs(x-x') <= 1/2, including +// at the last line. In fact, it's even a strict inequality except in one +// extrem case (DY == DMAX et e = +/- 1/2) +// +// Applying that to our coordinates, we need 2^p >= 4096*16 = 65536 +// so p = 16 is enough, we're so lucky! + +const int TRI_ITERATORS_BITS = 16; + +struct Edge +{ + int32_t x; // edge position in 16.16 coordinates + int32_t x_incr; // on each step, increment x by that amount + int32_t y_top; // starting scanline, 16.4 format + int32_t y_bot; +}; + +static void +edge_dump( Edge* edge ) +{ + LOGI( " top=%d (%.3f) bot=%d (%.3f) x=%d (%.3f) ix=%d (%.3f)", + edge->y_top, edge->y_top/float(TRI_ONE), + edge->y_bot, edge->y_bot/float(TRI_ONE), + edge->x, edge->x/float(FIXED_ONE), + edge->x_incr, edge->x_incr/float(FIXED_ONE) ); +} + +static void +triangle_dump_edges( Edge* edges, + int count ) +{ + LOGI( "%d edge%s:\n", count, count == 1 ? "" : "s" ); + for ( ; count > 0; count--, edges++ ) + edge_dump( edges ); +} + +// the following function sets up an edge, it assumes +// that ymin and ymax are in already in the 'reduced' +// format +static __attribute__((noinline)) +void edge_setup( + Edge* edges, + int* pcount, + const GGLcoord* p1, + const GGLcoord* p2, + int32_t ymin, + int32_t ymax ) +{ + const GGLfixed* top = p1; + const GGLfixed* bot = p2; + Edge* edge = edges + *pcount; + + if (top[1] > bot[1]) { + swap(top, bot); + } + + int y1 = top[1] | 1; + int y2 = bot[1] | 1; + int dy = y2 - y1; + + if ( dy == 0 || y1 > ymax || y2 < ymin ) + return; + + if ( y1 > ymin ) + ymin = TRI_SNAP_NEXT_HALF(y1); + + if ( y2 < ymax ) + ymax = TRI_SNAP_PREV_HALF(y2); + + if ( ymin > ymax ) // when the edge doesn't cross any scanline + return; + + const int x1 = top[0]; + const int dx = bot[0] - x1; + const int shift = TRI_ITERATORS_BITS - TRI_FRACTION_BITS; + + // setup edge fields + // We add 0.5 to edge->x here because it simplifies the rounding + // in triangle_sweep_edges() -- this doesn't change the ordering of 'x' + edge->x = (x1 << shift) + (1LU << (TRI_ITERATORS_BITS-1)); + edge->x_incr = 0; + edge->y_top = ymin; + edge->y_bot = ymax; + + if (ggl_likely(ymin <= ymax && dx)) { + edge->x_incr = gglDivQ16(dx, dy); + } + if (ggl_likely(y1 < ymin)) { + int32_t xadjust = (edge->x_incr * (ymin-y1)) >> TRI_FRACTION_BITS; + edge->x += xadjust; + } + + ++*pcount; +} + + +static void +triangle_sweep_edges( Edge* left, + Edge* right, + int ytop, + int ybot, + context_t* c ) +{ + int count = ((ybot - ytop)>>TRI_FRACTION_BITS) + 1; + if (count<=0) return; + + // sort the edges horizontally + if ((left->x > right->x) || + ((left->x == right->x) && (left->x_incr > right->x_incr))) { + swap(left, right); + } + + int left_x = left->x; + int right_x = right->x; + const int left_xi = left->x_incr; + const int right_xi = right->x_incr; + left->x += left_xi * count; + right->x += right_xi * count; + + const int xmin = c->state.scissor.left; + const int xmax = c->state.scissor.right; + do { + // horizontal scissoring + const int32_t xl = max(left_x >> TRI_ITERATORS_BITS, xmin); + const int32_t xr = min(right_x >> TRI_ITERATORS_BITS, xmax); + left_x += left_xi; + right_x += right_xi; + // invoke the scanline rasterizer + if (ggl_likely(xl < xr)) { + c->iterators.xl = xl; + c->iterators.xr = xr; + c->scanline(c); + } + c->step_y(c); + } while (--count); +} + + +void trianglex_big(void* con, + const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) +{ + GGL_CONTEXT(c, con); + + Edge edges[3]; + int num_edges = 0; + int32_t ymin = TRI_FROM_INT(c->state.scissor.top) + TRI_HALF; + int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom) - TRI_HALF; + + edge_setup( edges, &num_edges, v0, v1, ymin, ymax ); + edge_setup( edges, &num_edges, v0, v2, ymin, ymax ); + edge_setup( edges, &num_edges, v1, v2, ymin, ymax ); + + if (ggl_unlikely(num_edges<2)) // for really tiny triangles that don't + return; // cross any scanline centers + + Edge* left = &edges[0]; + Edge* right = &edges[1]; + Edge* other = &edges[2]; + int32_t y_top = min(left->y_top, right->y_top); + int32_t y_bot = max(left->y_bot, right->y_bot); + + if (ggl_likely(num_edges==3)) { + y_top = min(y_top, edges[2].y_top); + y_bot = max(y_bot, edges[2].y_bot); + if (edges[0].y_top > y_top) { + other = &edges[0]; + left = &edges[2]; + } else if (edges[1].y_top > y_top) { + other = &edges[1]; + right = &edges[2]; + } + } + + c->init_y(c, y_top >> TRI_FRACTION_BITS); + + int32_t y_mid = min(left->y_bot, right->y_bot); + triangle_sweep_edges( left, right, y_top, y_mid, c ); + + // second scanline sweep loop, if necessary + y_mid += TRI_ONE; + if (y_mid <= y_bot) { + ((left->y_bot == y_bot) ? right : left) = other; + if (other->y_top < y_mid) { + other->x += other->x_incr; + } + triangle_sweep_edges( left, right, y_mid, y_bot, c ); + } +} + +void aa_trianglex(void* con, + const GGLcoord* a, const GGLcoord* b, const GGLcoord* c) +{ + GGLcoord pts[6] = { a[0], a[1], b[0], b[1], c[0], c[1] }; + aapolyx(con, pts, 3); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +struct AAEdge +{ + GGLfixed x; // edge position in 12.16 coordinates + GGLfixed x_incr; // on each y step, increment x by that amount + GGLfixed y_incr; // on each x step, increment y by that amount + int16_t y_top; // starting scanline, 12.4 format + int16_t y_bot; // starting scanline, 12.4 format + void dump(); +}; + +void AAEdge::dump() +{ + float tri = 1.0f / TRI_ONE; + float iter = 1.0f / (1<<TRI_ITERATORS_BITS); + float fix = 1.0f / FIXED_ONE; + LOGD( "x=%08x (%.3f), " + "x_incr=%08x (%.3f), y_incr=%08x (%.3f), " + "y_top=%08x (%.3f), y_bot=%08x (%.3f) ", + x, x*fix, + x_incr, x_incr*iter, + y_incr, y_incr*iter, + y_top, y_top*tri, + y_bot, y_bot*tri ); +} + +// the following function sets up an edge, it assumes +// that ymin and ymax are in already in the 'reduced' +// format +static __attribute__((noinline)) +void aa_edge_setup( + AAEdge* edges, + int* pcount, + const GGLcoord* p1, + const GGLcoord* p2, + int32_t ymin, + int32_t ymax ) +{ + const GGLfixed* top = p1; + const GGLfixed* bot = p2; + AAEdge* edge = edges + *pcount; + + if (top[1] > bot[1]) + swap(top, bot); + + int y1 = top[1]; + int y2 = bot[1]; + int dy = y2 - y1; + + if (dy==0 || y1>ymax || y2<ymin) + return; + + if (y1 > ymin) + ymin = y1; + + if (y2 < ymax) + ymax = y2; + + const int x1 = top[0]; + const int dx = bot[0] - x1; + const int shift = FIXED_BITS - TRI_FRACTION_BITS; + + // setup edge fields + edge->x = x1 << shift; + edge->x_incr = 0; + edge->y_top = ymin; + edge->y_bot = ymax; + edge->y_incr = 0x7FFFFFFF; + + if (ggl_likely(ymin <= ymax && dx)) { + edge->x_incr = gglDivQ16(dx, dy); + if (dx != 0) { + edge->y_incr = abs(gglDivQ16(dy, dx)); + } + } + if (ggl_likely(y1 < ymin)) { + int32_t xadjust = (edge->x_incr * (ymin-y1)) + >> (TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS); + edge->x += xadjust; + } + + ++*pcount; +} + + +typedef int (*compar_t)(const void*, const void*); +static int compare_edges(const AAEdge *e0, const AAEdge *e1) { + if (e0->y_top > e1->y_top) return 1; + if (e0->y_top < e1->y_top) return -1; + if (e0->x > e1->x) return 1; + if (e0->x < e1->x) return -1; + if (e0->x_incr > e1->x_incr) return 1; + if (e0->x_incr < e1->x_incr) return -1; + return 0; // same edges, should never happen +} + +static inline +void SET_COVERAGE(int16_t*& p, int32_t value, ssize_t n) +{ + android_memset16((uint16_t*)p, value, n*2); + p += n; +} + +static inline +void ADD_COVERAGE(int16_t*& p, int32_t value) +{ + value = *p + value; + if (value >= 0x8000) + value = 0x7FFF; + *p++ = value; +} + +static inline +void SUB_COVERAGE(int16_t*& p, int32_t value) +{ + value = *p - value; + value &= ~(value>>31); + *p++ = value; +} + +void aapolyx(void* con, + const GGLcoord* pts, int count) +{ + /* + * NOTE: This routine assumes that the polygon has been clipped to the + * viewport already, that is, no vertex lies outside of the framebuffer. + * If this happens, the code below won't corrupt memory but the + * coverage values may not be correct. + */ + + GGL_CONTEXT(c, con); + + // we do only quads for now (it's used for thick lines) + if ((count>4) || (count<2)) return; + + // take scissor into account + const int xmin = c->state.scissor.left; + const int xmax = c->state.scissor.right; + if (xmin >= xmax) return; + + // generate edges from the vertices + int32_t ymin = TRI_FROM_INT(c->state.scissor.top); + int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom); + if (ymin >= ymax) return; + + AAEdge edges[4]; + int num_edges = 0; + GGLcoord const * p = pts; + for (int i=0 ; i<count-1 ; i++, p+=2) { + aa_edge_setup(edges, &num_edges, p, p+2, ymin, ymax); + } + aa_edge_setup(edges, &num_edges, p, pts, ymin, ymax ); + if (ggl_unlikely(num_edges<2)) + return; + + // sort the edge list top to bottom, left to right. + qsort(edges, num_edges, sizeof(AAEdge), (compar_t)compare_edges); + + int16_t* const covPtr = c->state.buffers.coverage; + memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr)); + + // now, sweep all edges in order + // start with the 2 first edges. We know that they share their top + // vertex, by construction. + int i = 2; + AAEdge* left = &edges[0]; + AAEdge* right = &edges[1]; + int32_t yt = left->y_top; + GGLfixed l = left->x; + GGLfixed r = right->x; + int retire = 0; + int16_t* coverage; + + // at this point we can initialize the rasterizer + c->init_y(c, yt>>TRI_FRACTION_BITS); + c->iterators.xl = xmax; + c->iterators.xr = xmin; + + do { + int32_t y = min(min(left->y_bot, right->y_bot), TRI_FLOOR(yt + TRI_ONE)); + const int32_t shift = TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS; + const int cf_shift = (1 + TRI_FRACTION_BITS*2 + TRI_ITERATORS_BITS - 15); + + // compute xmin and xmax for the left edge + GGLfixed l_min = gglMulAddx(left->x_incr, y - left->y_top, left->x, shift); + GGLfixed l_max = l; + l = l_min; + if (l_min > l_max) + swap(l_min, l_max); + + // compute xmin and xmax for the right edge + GGLfixed r_min = gglMulAddx(right->x_incr, y - right->y_top, right->x, shift); + GGLfixed r_max = r; + r = r_min; + if (r_min > r_max) + swap(r_min, r_max); + + // make sure we're not touching coverage values outside of the + // framebuffer + l_min &= ~(l_min>>31); + r_min &= ~(r_min>>31); + l_max &= ~(l_max>>31); + r_max &= ~(r_max>>31); + if (gglFixedToIntFloor(l_min) >= xmax) l_min = gglIntToFixed(xmax)-1; + if (gglFixedToIntFloor(r_min) >= xmax) r_min = gglIntToFixed(xmax)-1; + if (gglFixedToIntCeil(l_max) >= xmax) l_max = gglIntToFixed(xmax)-1; + if (gglFixedToIntCeil(r_max) >= xmax) r_max = gglIntToFixed(xmax)-1; + + // compute the integer versions of the above + const GGLfixed l_min_i = gglFloorx(l_min); + const GGLfixed l_max_i = gglCeilx (l_max); + const GGLfixed r_min_i = gglFloorx(r_min); + const GGLfixed r_max_i = gglCeilx (r_max); + + // clip horizontally using the scissor + const int xml = max(xmin, gglFixedToIntFloor(l_min_i)); + const int xmr = min(xmax, gglFixedToIntFloor(r_max_i)); + + // if we just stepped to a new scanline, render the previous one. + // and clear the coverage buffer + if (retire) { + if (c->iterators.xl < c->iterators.xr) + c->scanline(c); + c->step_y(c); + memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr)); + c->iterators.xl = xml; + c->iterators.xr = xmr; + } else { + // update the horizontal range of this scanline + c->iterators.xl = min(c->iterators.xl, xml); + c->iterators.xr = max(c->iterators.xr, xmr); + } + + coverage = covPtr + gglFixedToIntFloor(l_min_i); + if (l_min_i == gglFloorx(l_max)) { + + /* + * fully traverse this pixel vertically + * l_max + * +-----/--+ yt + * | / | + * | / | + * | / | + * +-/------+ y + * l_min (l_min_i + TRI_ONE) + */ + + GGLfixed dx = l_max - l_min; + int32_t dy = y - yt; + int cf = gglMulx((dx >> 1) + (l_min_i + FIXED_ONE - l_max), dy, + FIXED_BITS + TRI_FRACTION_BITS - 15); + ADD_COVERAGE(coverage, cf); + // all pixels on the right have cf = 1.0 + } else { + /* + * spans several pixels in one scanline + * l_max + * +--------+--/-----+ yt + * | |/ | + * | /| | + * | / | | + * +---/----+--------+ y + * l_min (l_min_i + TRI_ONE) + */ + + // handle the first pixel separately... + const int32_t y_incr = left->y_incr; + int32_t dx = TRI_FROM_FIXED(l_min_i - l_min) + TRI_ONE; + int32_t cf = (dx * dx * y_incr) >> cf_shift; + ADD_COVERAGE(coverage, cf); + + // following pixels get covered by y_incr, but we need + // to fix-up the cf to account for previous partial pixel + dx = TRI_FROM_FIXED(l_min - l_min_i); + cf -= (dx * dx * y_incr) >> cf_shift; + for (int x = l_min_i+FIXED_ONE ; x < l_max_i-FIXED_ONE ; x += FIXED_ONE) { + cf += y_incr >> (TRI_ITERATORS_BITS-15); + ADD_COVERAGE(coverage, cf); + } + + // and the last pixel + dx = TRI_FROM_FIXED(l_max - l_max_i) - TRI_ONE; + cf += (dx * dx * y_incr) >> cf_shift; + ADD_COVERAGE(coverage, cf); + } + + // now, fill up all fully covered pixels + coverage = covPtr + gglFixedToIntFloor(l_max_i); + int cf = ((y - yt) << (15 - TRI_FRACTION_BITS)); + if (ggl_likely(cf >= 0x8000)) { + SET_COVERAGE(coverage, 0x7FFF, ((r_max - l_max_i)>>FIXED_BITS)+1); + } else { + for (int x=l_max_i ; x<r_max ; x+=FIXED_ONE) { + ADD_COVERAGE(coverage, cf); + } + } + + // subtract the coverage of the right edge + coverage = covPtr + gglFixedToIntFloor(r_min_i); + if (r_min_i == gglFloorx(r_max)) { + GGLfixed dx = r_max - r_min; + int32_t dy = y - yt; + int cf = gglMulx((dx >> 1) + (r_min_i + FIXED_ONE - r_max), dy, + FIXED_BITS + TRI_FRACTION_BITS - 15); + SUB_COVERAGE(coverage, cf); + // all pixels on the right have cf = 1.0 + } else { + // handle the first pixel separately... + const int32_t y_incr = right->y_incr; + int32_t dx = TRI_FROM_FIXED(r_min_i - r_min) + TRI_ONE; + int32_t cf = (dx * dx * y_incr) >> cf_shift; + SUB_COVERAGE(coverage, cf); + + // following pixels get covered by y_incr, but we need + // to fix-up the cf to account for previous partial pixel + dx = TRI_FROM_FIXED(r_min - r_min_i); + cf -= (dx * dx * y_incr) >> cf_shift; + for (int x = r_min_i+FIXED_ONE ; x < r_max_i-FIXED_ONE ; x += FIXED_ONE) { + cf += y_incr >> (TRI_ITERATORS_BITS-15); + SUB_COVERAGE(coverage, cf); + } + + // and the last pixel + dx = TRI_FROM_FIXED(r_max - r_max_i) - TRI_ONE; + cf += (dx * dx * y_incr) >> cf_shift; + SUB_COVERAGE(coverage, cf); + } + + // did we reach the end of an edge? if so, get a new one. + if (y == left->y_bot || y == right->y_bot) { + // bail out if we're done + if (i>=num_edges) + break; + if (y == left->y_bot) + left = &edges[i++]; + if (y == right->y_bot) + right = &edges[i++]; + } + + // next scanline + yt = y; + + // did we just finish a scanline? + retire = (y << (32-TRI_FRACTION_BITS)) == 0; + } while (true); + + // render the last scanline + if (c->iterators.xl < c->iterators.xr) + c->scanline(c); +} + +}; // namespace android diff --git a/libpixelflinger/trap.h b/libpixelflinger/trap.h new file mode 100644 index 0000000..7cce7b3 --- /dev/null +++ b/libpixelflinger/trap.h @@ -0,0 +1,31 @@ +/* libs/pixelflinger/trap.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef ANDROID_TRAP_H +#define ANDROID_TRAP_H + +#include <private/pixelflinger/ggl_context.h> + +namespace android { + +void ggl_init_trap(context_t* c); +void ggl_state_changed(context_t* c, int flags); + +}; // namespace android + +#endif diff --git a/libzipfile/Android.mk b/libzipfile/Android.mk new file mode 100644 index 0000000..d2d758c --- /dev/null +++ b/libzipfile/Android.mk @@ -0,0 +1,48 @@ +LOCAL_PATH:= $(call my-dir) + +# build host static library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + centraldir.c \ + zipfile.c + +LOCAL_STATIC_LIBRARIES := \ + libunz + +LOCAL_MODULE:= libzipfile + +LOCAL_C_INCLUDES += external/zlib + +include $(BUILD_HOST_STATIC_LIBRARY) + +# build device static library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + centraldir.c \ + zipfile.c + +LOCAL_STATIC_LIBRARIES := \ + libunz + +LOCAL_MODULE:= libzipfile + +LOCAL_C_INCLUDES += external/zlib + +include $(BUILD_STATIC_LIBRARY) + + +# build test_zipfile +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + test_zipfile.c + +LOCAL_STATIC_LIBRARIES := libzipfile libunz + +LOCAL_MODULE := test_zipfile + +LOCAL_C_INCLUDES += external/zlib + +include $(BUILD_HOST_EXECUTABLE) diff --git a/libzipfile/MODULE_LICENSE_APACHE2 b/libzipfile/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libzipfile/MODULE_LICENSE_APACHE2 diff --git a/libzipfile/NOTICE b/libzipfile/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/libzipfile/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/libzipfile/centraldir.c b/libzipfile/centraldir.c new file mode 100644 index 0000000..4387ceb --- /dev/null +++ b/libzipfile/centraldir.c @@ -0,0 +1,256 @@ +#include "private.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum {
+ // finding the directory
+ CD_SIGNATURE = 0x06054b50,
+ EOCD_LEN = 22, // EndOfCentralDir len, excl. comment
+ MAX_COMMENT_LEN = 65535,
+ MAX_EOCD_SEARCH = MAX_COMMENT_LEN + EOCD_LEN,
+
+ // central directory entries
+ ENTRY_SIGNATURE = 0x02014b50,
+ ENTRY_LEN = 46, // CentralDirEnt len, excl. var fields
+
+ // local file header
+ LFH_SIZE = 30,
+};
+
+unsigned int
+read_le_int(const unsigned char* buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+unsigned int
+read_le_short(const unsigned char* buf)
+{
+ return buf[0] | (buf[1] << 8);
+}
+
+static int
+read_central_dir_values(Zipfile* file, const unsigned char* buf, int len)
+{
+ if (len < EOCD_LEN) {
+ // looks like ZIP file got truncated
+ fprintf(stderr, " Zip EOCD: expected >= %d bytes, found %d\n",
+ EOCD_LEN, len);
+ return -1;
+ }
+
+ file->disknum = read_le_short(&buf[0x04]);
+ file->diskWithCentralDir = read_le_short(&buf[0x06]);
+ file->entryCount = read_le_short(&buf[0x08]);
+ file->totalEntryCount = read_le_short(&buf[0x0a]);
+ file->centralDirSize = read_le_int(&buf[0x0c]);
+ file->centralDirOffest = read_le_int(&buf[0x10]);
+ file->commentLen = read_le_short(&buf[0x14]);
+
+ if (file->commentLen > 0) {
+ if (EOCD_LEN + file->commentLen > len) {
+ fprintf(stderr, "EOCD(%d) + comment(%d) exceeds len (%d)\n",
+ EOCD_LEN, file->commentLen, len);
+ return -1;
+ }
+ file->comment = buf + EOCD_LEN;
+ }
+
+ return 0;
+}
+
+static int
+read_central_directory_entry(Zipfile* file, Zipentry* entry,
+ const unsigned char** buf, ssize_t* len)
+{
+ const unsigned char* p;
+
+ unsigned short versionMadeBy;
+ unsigned short versionToExtract;
+ unsigned short gpBitFlag;
+ unsigned short compressionMethod;
+ unsigned short lastModFileTime;
+ unsigned short lastModFileDate;
+ unsigned long crc32;
+ unsigned long compressedSize;
+ unsigned long uncompressedSize;
+ unsigned short extraFieldLength;
+ unsigned short fileCommentLength;
+ unsigned short diskNumberStart;
+ unsigned short internalAttrs;
+ unsigned long externalAttrs;
+ unsigned long localHeaderRelOffset;
+ const unsigned char* extraField;
+ const unsigned char* fileComment;
+ unsigned int dataOffset;
+ unsigned short lfhExtraFieldSize;
+
+
+ p = *buf;
+
+ if (*len < ENTRY_LEN) {
+ fprintf(stderr, "cde entry not large enough\n");
+ return -1;
+ }
+
+ if (read_le_int(&p[0x00]) != ENTRY_SIGNATURE) {
+ fprintf(stderr, "Whoops: didn't find expected signature\n");
+ return -1;
+ }
+
+ versionMadeBy = read_le_short(&p[0x04]);
+ versionToExtract = read_le_short(&p[0x06]);
+ gpBitFlag = read_le_short(&p[0x08]);
+ entry->compressionMethod = read_le_short(&p[0x0a]);
+ lastModFileTime = read_le_short(&p[0x0c]);
+ lastModFileDate = read_le_short(&p[0x0e]);
+ crc32 = read_le_int(&p[0x10]);
+ compressedSize = read_le_int(&p[0x14]);
+ entry->uncompressedSize = read_le_int(&p[0x18]);
+ entry->fileNameLength = read_le_short(&p[0x1c]);
+ extraFieldLength = read_le_short(&p[0x1e]);
+ fileCommentLength = read_le_short(&p[0x20]);
+ diskNumberStart = read_le_short(&p[0x22]);
+ internalAttrs = read_le_short(&p[0x24]);
+ externalAttrs = read_le_int(&p[0x26]);
+ localHeaderRelOffset = read_le_int(&p[0x2a]);
+
+ p += ENTRY_LEN;
+
+ // filename
+ if (entry->fileNameLength != 0) {
+ entry->fileName = p;
+ } else {
+ entry->fileName = NULL;
+ }
+ p += entry->fileNameLength;
+
+ // extra field
+ if (extraFieldLength != 0) {
+ extraField = p;
+ } else {
+ extraField = NULL;
+ }
+ p += extraFieldLength;
+
+ // comment, if any
+ if (fileCommentLength != 0) {
+ fileComment = p;
+ } else {
+ fileComment = NULL;
+ }
+ p += fileCommentLength;
+
+ *buf = p;
+
+ // the size of the extraField in the central dir is how much data there is,
+ // but the one in the local file header also contains some padding.
+ p = file->buf + localHeaderRelOffset;
+ extraFieldLength = read_le_short(&p[0x1c]);
+
+ dataOffset = localHeaderRelOffset + LFH_SIZE
+ + entry->fileNameLength + extraFieldLength;
+ entry->data = file->buf + dataOffset;
+#if 0
+ printf("file->buf=%p entry->data=%p dataOffset=%x localHeaderRelOffset=%d "
+ "entry->fileNameLength=%d extraFieldLength=%d\n",
+ file->buf, entry->data, dataOffset, localHeaderRelOffset,
+ entry->fileNameLength, extraFieldLength);
+#endif
+ return 0;
+}
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end. In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards. The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly. If the wrong value ends up in the EOCD
+ * area, we're hosed. This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+int
+read_central_dir(Zipfile *file)
+{
+ int err;
+
+ const unsigned char* buf = file->buf;
+ ssize_t bufsize = file->bufsize;
+ const unsigned char* eocd;
+ const unsigned char* p;
+ const unsigned char* start;
+ ssize_t len;
+ int i;
+
+ // too small to be a ZIP archive?
+ if (bufsize < EOCD_LEN) {
+ fprintf(stderr, "Length is %d -- too small\n", bufsize);
+ goto bail;
+ }
+
+ // find the end-of-central-dir magic
+ if (bufsize > MAX_EOCD_SEARCH) {
+ start = buf + bufsize - MAX_EOCD_SEARCH;
+ } else {
+ start = buf;
+ }
+ p = buf + bufsize - 4;
+ while (p >= start) {
+ if (*p == 0x50 && read_le_int(p) == CD_SIGNATURE) {
+ eocd = p;
+ break;
+ }
+ p--;
+ }
+ if (p < start) {
+ fprintf(stderr, "EOCD not found, not Zip\n");
+ goto bail;
+ }
+
+ // extract eocd values
+ err = read_central_dir_values(file, eocd, (buf+bufsize)-eocd);
+ if (err != 0) {
+ goto bail;
+ }
+
+ if (file->disknum != 0
+ || file->diskWithCentralDir != 0
+ || file->entryCount != file->totalEntryCount) {
+ fprintf(stderr, "Archive spanning not supported\n");
+ goto bail;
+ }
+
+ // Loop through and read the central dir entries.
+ p = buf + file->centralDirOffest;
+ len = (buf+bufsize)-p;
+ for (i=0; i < file->totalEntryCount; i++) {
+ Zipentry* entry = malloc(sizeof(Zipentry));
+ memset(entry, sizeof(Zipentry), 0);
+
+ err = read_central_directory_entry(file, entry, &p, &len);
+ if (err != 0) {
+ fprintf(stderr, "read_central_directory_entry failed\n");
+ free(entry);
+ goto bail;
+ }
+
+ // add it to our list
+ entry->next = file->entries;
+ file->entries = entry;
+ }
+
+ return 0;
+bail:
+ return -1;
+}
+
diff --git a/libzipfile/private.h b/libzipfile/private.h new file mode 100644 index 0000000..06f788d --- /dev/null +++ b/libzipfile/private.h @@ -0,0 +1,45 @@ +#ifndef PRIVATE_H
+#define PRIVATE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct Zipentry {
+ unsigned long fileNameLength;
+ const unsigned char* fileName;
+ unsigned short compressionMethod;
+ unsigned int uncompressedSize;
+ unsigned int compressedSize;
+ const unsigned char* data;
+
+ struct Zipentry* next;
+} Zipentry;
+
+typedef struct Zipfile
+{
+ const unsigned char *buf;
+ ssize_t bufsize;
+
+ // Central directory
+ unsigned short disknum; //mDiskNumber;
+ unsigned short diskWithCentralDir; //mDiskWithCentralDir;
+ unsigned short entryCount; //mNumEntries;
+ unsigned short totalEntryCount; //mTotalNumEntries;
+ unsigned int centralDirSize; //mCentralDirSize;
+ unsigned int centralDirOffest; // offset from first disk //mCentralDirOffset;
+ unsigned short commentLen; //mCommentLen;
+ const unsigned char* comment; //mComment;
+
+ Zipentry* entries;
+} Zipfile;
+
+int read_central_dir(Zipfile* file);
+
+unsigned int read_le_int(const unsigned char* buf);
+unsigned int read_le_short(const unsigned char* buf);
+
+#endif // PRIVATE_H
+
diff --git a/libzipfile/test_zipfile.c b/libzipfile/test_zipfile.c new file mode 100644 index 0000000..40840ec --- /dev/null +++ b/libzipfile/test_zipfile.c @@ -0,0 +1,92 @@ +#include <zipfile/zipfile.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void dump_zipfile(FILE* to, zipfile_t file);
+
+int
+main(int argc, char** argv)
+{
+ FILE* f;
+ size_t size, unsize;
+ void* buf;
+ void* scratch;
+ zipfile_t zip;
+ zipentry_t entry;
+ int err;
+ enum { HUH, LIST, UNZIP } what = HUH;
+
+ if (strcmp(argv[2], "-l") == 0 && argc == 3) {
+ what = LIST;
+ }
+ else if (strcmp(argv[2], "-u") == 0 && argc == 5) {
+ what = UNZIP;
+ }
+ else {
+ fprintf(stderr, "usage: test_zipfile ZIPFILE -l\n"
+ " lists the files in the zipfile\n"
+ " test_zipfile ZIPFILE -u FILENAME SAVETO\n"
+ " saves FILENAME from the zip file into SAVETO\n");
+ return 1;
+ }
+
+ f = fopen(argv[1], "r");
+ if (f == NULL) {
+ fprintf(stderr, "couldn't open %s\n", argv[1]);
+ return 1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ rewind(f);
+
+ buf = malloc(size);
+ fread(buf, 1, size, f);
+
+ zip = init_zipfile(buf, size);
+ if (zip == NULL) {
+ fprintf(stderr, "inti_zipfile failed\n");
+ return 1;
+ }
+
+ fclose(f);
+
+
+ switch (what)
+ {
+ case LIST:
+ dump_zipfile(stdout, zip);
+ break;
+ case UNZIP:
+ entry = lookup_zipentry(zip, argv[3]);
+ if (entry == NULL) {
+ fprintf(stderr, "zip file '%s' does not contain file '%s'\n",
+ argv[1], argv[1]);
+ return 1;
+ }
+ f = fopen(argv[4], "w");
+ if (f == NULL) {
+ fprintf(stderr, "can't open file for writing '%s'\n", argv[4]);
+ return 1;
+ }
+ unsize = get_zipentry_size(entry);
+ size = unsize * 1.001;
+ scratch = malloc(size);
+ printf("scratch=%p\n", scratch);
+ err = decompress_zipentry(entry, scratch, size);
+ if (err != 0) {
+ fprintf(stderr, "error decompressing file\n");
+ return 1;
+ }
+ fwrite(scratch, unsize, 1, f);
+ free(scratch);
+ fclose(f);
+ break;
+ }
+
+ free(buf);
+
+ return 0;
+}
+
diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c new file mode 100644 index 0000000..b52d02d --- /dev/null +++ b/libzipfile/zipfile.c @@ -0,0 +1,160 @@ +#include <zipfile/zipfile.h> + +#include "private.h" +#include <stdlib.h> +#include <string.h> +#include <zlib.h> +#define DEF_MEM_LEVEL 8 // normally in zutil.h? + +zipfile_t +init_zipfile(const void* data, size_t size) +{ + int err; + + Zipfile *file = malloc(sizeof(Zipfile)); + if (file == NULL) return NULL; + memset(file, 0, sizeof(Zipfile)); + file->buf = data; + file->bufsize = size; + + err = read_central_dir(file); + if (err != 0) goto fail; + + return file; +fail: + free(file); + return NULL; +} + +void +release_zipfile(zipfile_t f) +{ + Zipfile* file = (Zipfile*)f; + Zipentry* entry = file->entries; + while (entry) { + Zipentry* next = entry->next; + free(entry); + entry = next; + } + free(file); +} + +zipentry_t +lookup_zipentry(zipfile_t f, const char* entryName) +{ + Zipfile* file = (Zipfile*)f; + Zipentry* entry = file->entries; + while (entry) { + if (0 == memcmp(entryName, entry->fileName, entry->fileNameLength)) { + return entry; + } + entry = entry->next; + } + return NULL; +} + +size_t +get_zipentry_size(zipentry_t entry) +{ + return ((Zipentry*)entry)->uncompressedSize; +} + +char* +get_zipentry_name(zipentry_t entry) +{ + Zipentry* e = (Zipentry*)entry; + int l = e->fileNameLength; + char* s = malloc(l+1); + memcpy(s, e->fileName, l); + s[l] = '\0'; + return s; +} + +enum { + STORED = 0, + DEFLATED = 8 +}; + +static int +uninflate(unsigned char* out, int unlen, const unsigned char* in, int clen) +{ + z_stream zstream; + unsigned long crc; + int err = 0; + int zerr; + + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (void*)in; + zstream.avail_in = unlen; + zstream.next_out = (Bytef*) out; + zstream.avail_out = unlen; + zstream.data_type = Z_UNKNOWN; + + // Use the undocumented "negative window bits" feature to tell zlib + // that there's no zlib header waiting for it. + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + return -1; + } + + // uncompress the data + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + fprintf(stderr, "zerr=%d Z_STREAM_END=%d total_out=%lu\n", zerr, Z_STREAM_END, + zstream.total_out); + err = -1; + } + + inflateEnd(&zstream); + return err; +} + +int +decompress_zipentry(zipentry_t e, void* buf, int bufsize) +{ + Zipentry* entry = (Zipentry*)e; + switch (entry->compressionMethod) + { + case STORED: + memcpy(buf, entry->data, entry->uncompressedSize); + return 0; + case DEFLATED: + return uninflate(buf, bufsize, entry->data, entry->compressedSize); + default: + return -1; + } +} + +void +dump_zipfile(FILE* to, zipfile_t file) +{ + Zipfile* zip = (Zipfile*)file; + Zipentry* entry = zip->entries; + int i; + + fprintf(to, "entryCount=%d\n", zip->entryCount); + for (i=0; i<zip->entryCount; i++) { + fprintf(to, " file \""); + fwrite(entry->fileName, entry->fileNameLength, 1, to); + fprintf(to, "\"\n"); + entry = entry->next; + } +} + +zipentry_t +iterate_zipfile(zipfile_t file, void** cookie) +{ + Zipentry* entry = (Zipentry*)*cookie; + if (entry == NULL) { + Zipfile* zip = (Zipfile*)file; + *cookie = zip->entries; + return *cookie; + } else { + entry = entry->next; + *cookie = entry; + return entry; + } +} diff --git a/logcat/Android.mk b/logcat/Android.mk new file mode 100644 index 0000000..3ee051d --- /dev/null +++ b/logcat/Android.mk @@ -0,0 +1,27 @@ +# Copyright 2006 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= logcat.cpp + +LOCAL_SHARED_LIBRARIES := liblog + +LOCAL_MODULE:= logcat + +include $(BUILD_EXECUTABLE) + +######################## +include $(CLEAR_VARS) + +LOCAL_MODULE := event-log-tags + +#LOCAL_MODULE_TAGS := user development + +# This will install the file in /system/etc +# +LOCAL_MODULE_CLASS := ETC + +LOCAL_SRC_FILES := $(LOCAL_MODULE) + +include $(BUILD_PREBUILT) diff --git a/logcat/MODULE_LICENSE_APACHE2 b/logcat/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/logcat/MODULE_LICENSE_APACHE2 diff --git a/logcat/NOTICE b/logcat/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/logcat/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/logcat/event-log-tags b/logcat/event-log-tags new file mode 100644 index 0000000..28cad0a --- /dev/null +++ b/logcat/event-log-tags @@ -0,0 +1,346 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (<name>|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# TODO: generate ".java" and ".h" files with integer constants from this file. + +# These are used for testing, do not modify without updating +# tests/framework-tests/src/android/util/EventLogFunctionalTest.java. +42 answer (to life the universe etc|3) +314 pi +2718 e + +2719 configuration_changed (config mask|1|5) +2720 sync (id|3),(event|1|5),(source|1|5) +2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6) +2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1) +2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3) +# This is logged when the device is being forced to sleep (typically by +# the user pressing the power button). +2724 power_sleep_requested (wakeLocksCleared|1|1) +# This is logged when the screen on broadcast has completed +2725 power_screen_broadcast_send (wakelockCount|1|1) +# This is logged when the screen broadcast has completed +2726 power_screen_broadcast_done (on|1|5),(broadcastDuration|2|3),(wakelockCount|1|1) +# This is logged when the screen on broadcast has completed +2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1) +# This is logged when the screen is turned on or off. +2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1) +# This is logged when the partial wake lock (keeping the device awake +# regardless of whether the screen is off) is acquired or released. +2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3) +# This is logged when battery goes from discharging to charging. +# It lets us count the total amount of time between charges and the discharge level +2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6) +# +# Leave IDs through 2739 for more power logs +# + +# This event is logged when the location service uploads location data. +2740 location_controller +# This event is logged when someone is deciding to force a garbage collection +2741 force_gc (reason|3) +# This event is logged on each tickle +2742 tickle (authority|3) +# What happens in a sync operation (bytes sent and received, and +# operation details) +2743 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3) + +# The disk space free on the /data partition, in bytes +2744 free_storage_changed (data|2|2) +# Device low memory notification and disk space free on the /data partition, in bytes at that time +2745 low_storage (data|2|2) +# disk space free on the /data partition in bytes +2746 free_storage_left (data|2|2) + +# when a NotificationManager.notify is called +2750 notification_enqueue (pkg|3),(id|1|5),(notification|3) +# when someone tries to cancel a notification, the notification manager sometimes +# calls this with flags too +2751 notification_cancel (pkg|3),(id|1|5),(required_flags|1) +# when someone tries to cancel all of the notifications for a particular package +2752 notification_cancel_all (pkg|3),(required_flags|1) + +# This event is logged when GTalkService encounters important events +2800 gtalkservice (eventType|1) +# This event is logged for GTalk connection state changes. The status field is an int, but +# it really contains 4 separate values, each taking up a byte +# (eventType, connection state, connection error, network state) +2801 gtalk_connection (status|1) + +2802 watchdog (Service|3) +2803 watchdog_proc_pss (Process|3),(Pid|1|5),(Pss|1|2) +2804 watchdog_soft_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2),(Skip|3) +2805 watchdog_hard_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2) +2806 watchdog_pss_stats (EmptyPss|1|2),(EmptyCount|1|1),(BackgroundPss|1|2),(BackgroundCount|1|1),(ServicePss|1|2),(ServiceCount|1|1),(VisiblePss|1|2),(VisibleCount|1|1),(ForegroundPss|1|2),(ForegroundCount|1|1),(NoPssCount|1|1) +2807 watchdog_proc_stats (DeathsInOne|1|1),(DeathsInTwo|1|1),(DeathsInThree|1|1),(DeathsInFour|1|1),(DeathsInFive|1|1) +2808 watchdog_scheduled_reboot (Now|2|1),(Interval|1|3),(StartTime|1|3),(Window|1|3),(Skip|3) +2809 watchdog_meminfo (MemFree|1|2),(Buffers|1|2),(Cached|1|2),(Active|1|2),(Inactive|1|2),(AnonPages|1|2),(Mapped|1|2),(Slab|1|2),(SReclaimable|1|2),(SUnreclaim|1|2),(PageTables|1|2) +2810 watchdog_vmstat (runtime|2|3),(pgfree|1|1),(pgactivate|1|1),(pgdeactivate|1|1),(pgfault|1|1),(pgmajfault|1|1) +2811 watchdog_requested_reboot (NoWait|1|1),(ScheduleInterval|1|3),(RecheckInterval|1|3),(StartTime|1|3),(Window|1|3),(MinScreenOff|1|3),(MinNextAlarm|1|3) + +# Device boot timings. We include monotonic clock values because the +# intrinsic event log times are wall-clock. +# +# Runtime starts: +3000 boot_progress_start (time|2|3) +# SystemServer.run() starts: +3010 boot_progress_system_run (time|2|3) +# ZygoteInit class preloading starts: +3020 boot_progress_preload_start (time|2|3) +# ZygoteInit class preloading ends: +3030 boot_progress_preload_end (time|2|3) +# ActivityManagerService.systemReady() starts: +3040 boot_progress_ams_ready (time|2|3) +# ActivityManagerService calls enableScreenAfterBoot(): +3050 boot_progress_enable_screen (time|2|3) +# Package Manager starts: +3060 boot_progress_pms_start (time|2|3) +# Package Manager .apk scan starts: +3070 boot_progress_pms_system_scan_start (time|2|3) +# Package Manager .apk scan starts: +3080 boot_progress_pms_data_scan_start (time|2|3) +# Package Manager .apk scan ends: +3090 boot_progress_pms_scan_end (time|2|3) +# Package Manager ready: +3100 boot_progress_pms_ready (time|2|3) +# + check activity_launch_time for Home app + +# Do not change these names without updating the checkin_events setting in +# google3/googledata/wireless/android/provisioning/gservices.config !! +# +# An activity is being finished: +30001 am_finish_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) +# A task is being brought to the front of the screen: +30002 am_task_to_front (Task|1|5) +# An existing activity is being given a new intent: +30003 am_new_intent (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) +# A new task is being created: +30004 am_create_task (Task ID|1|5) +# A new activity is being created in an existing task: +30005 am_create_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) +# An activity has been resumed into the foreground but was not already running: +30006 am_restart_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# An activity has been resumed and is now in the foreground: +30007 am_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# Application Not Responding +30008 anr (pid|1|5),(Package Name|3),(reason|3) +# Activity launch time +30009 activity_launch_time (Token|1|5),(Component Name|3),(time|2|3) +# Application process bound to work +30010 am_proc_bound (PID|1|5),(Process Name|3) +# Application process died +30011 am_proc_died (PID|1|5),(Process Name|3) +# The Activity Manager failed to pause the given activity. +30012 am_failed_to_pause (Token|1|5),(Wanting to pause|3),(Currently pausing|3) +# Attempting to pause the current activity +30013 am_pause_activity (Token|1|5),(Component Name|3) +# Application process has been started +30014 am_proc_start (PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3) +# An application process has been marked as bad +30015 am_proc_bad (UID|1|5),(Process Name|3) +# An application process that was bad is now marked as good +30016 am_proc_good (UID|1|5),(Process Name|3) +# Reporting to applications that memory is low +30017 am_low_memory (Num Processes|1|1) +# An activity is being destroyed: +30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# An activity has been relaunched, resumed, and is now in the foreground: +30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# An activity has been relaunched: +30020 am_relaunch_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# The activity's onPause has been called. +30021 am_on_paused_called (Component Name|3) +# The activity's onResume has been called. +30022 am_on_resume_called (Component Name|3) +# Kill a process to reclaim memory. +30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5) +# Discard an undelivered serialized broadcast (timeout/ANR/crash) +30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5) +30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3) +# A service is being created +30030 am_create_service (Service Record|1|5),(Name|3),(Intent|3),(PID|1|5) +# A service is being destroyed +30031 am_destroy_service (Service Record|1|5),(Name|3),(PID|1|5) +# A process has crashed too many times, it is being cleared +30032 am_process_crashed_too_much (Name|3),(PID|1|5) +# An unknown process is trying to attach to the activity manager +30033 am_drop_process (PID|1|5) +# A service has crashed too many times, it is being stopped +30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5) +# A service is going to be restarted after its process went away +30035 am_schedule_service_restart (Component Name|3),(Time|2|3) +# A client was waiting for a content provider, but its process was lost +30036 am_provider_lost_process (Package Name|3),(UID|1|5),(Name|3) + +# Out of memory for surfaces. +31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3) + +# Re-connecting to input method service because we haven't received its interface +32000 imf_force_reconnect_ime (IME|4),(Time Since Connect|2|3),(Showing|1|1) + +# dvm_gc_info: LIST (LONG, LONG, LONG) +# +# First LONG: +# +# [63] 1 +# [62-24] ASCII process identifier +# [23-12] GC time in ms +# [11- 0] Bytes freed +# +# Second LONG (aggregated heap info): +# +# [63-62] 10 +# [61-60] Reserved; must be zero +# [59-48] Objects freed +# [47-36] Actual size (current footprint) +# [35-24] Allowed size (current hard max) +# [23-12] Objects allocated +# [11- 0] Bytes allocated +# +# Third LONG (zygote heap info): +# +# [63-62] 11 +# [61-60] Reserved; must be zero +# [59-48] Soft limit +# [47-36] Actual size (current footprint) +# [35-24] Allowed size (current hard max) +# [23-12] Objects allocated +# [11- 0] Bytes allocated +# +# Fourth LONG: +# +# [63-48] Reserved; must be zero +# [47-36] dlmallocFootprint +# [35-24] mallinfo: total allocated space +# [23-12] External byte limit +# [11- 0] External bytes allocated +# +# See HeapDebug.c +# +20001 dvm_gc_info (custom|2),(custom|2),(custom|2),(custom|2) +20002 dvm_gc_madvise_info (total|1|2),(zygote|1|2) + +75000 sqlite_mem_alarm_current (current|1|2) +75001 sqlite_mem_alarm_max (max|1|2) +75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4) +75003 sqlite_mem_released (Memory released|1|2) + +40000 checkin (Check in time|2|3) + +50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3) +50001 menu_opened (Menu type where 0 is options and 1 is context|1|5) +# Connectivity state changed: +# [31-13] Reserved for future use +# [12- 9] Network subtype (for mobile network, as defined by TelephonyManager) +# [ 8- 3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) +# [ 2- 0] Network type (as defined by ConnectivityManager) +50020 connectivity_state_changed (custom|1|5) + +# Wi-Fi network state changed: +# [31- 6] Reserved for future use +# [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState) +50021 wifi_network_state_changed (network_state|1|5) + +# Wi-Fi supplicant state changed: +# [31- 6] Reserved for future use +# [ 5- 0] Supplicant state ordinal (as defined by SupplicantState) +50022 wifi_supplicant_state_changed (supplicant_state|1|5) + +# Wi-Fi driver state changed: +# [31- 1] Reserved for future use +# [ 0- 0] Driver start (1) or stopped (0) +50023 wifi_driver_state_changed (driver_state|1|5) + +# Wi-Fi interface configuration state changed: +# [31- 1] Reserved for future use +# [ 0- 0] Interface configuration succeeded (1) or failed (0) +50024 wifi_interface_configuration_state_changed (IP_configuration|1|5) + +# Wi-Fi supplicant connection state changed: +# [31- 2] Reserved for future use +# [ 1- 0] Connected to supplicant (1) or disconnected from supplicant (0), +# or supplicant died (2) +50025 wifi_supplicant_connection_state_changed (connected|1|5) + +# PDP Context has a bad DNS address +50100 pdp_bad_dns_address (dns_address|3) + +# For data connection on PDP context, reached the data-out-without-data-in +# packet count that triggers a countdown to radio restart +50101 pdp_radio_reset_countdown_triggered (out_packet_count|1|1) + +# Radio restart - timed out with no incoming packets. +50102 pdp_radio_reset (out_packet_count|1|1) + +# PDP context reset - timed out with no incoming packets. +50103 pdp_context_reset (out_packet_count|1|1) + +# Reregister to data network - timed out with no incoming packets. +50104 pdp_reregister_network (out_packet_count|1|1) + +# PDP Setup failures +50105 pdp_setup_fail (cause|1|5), (cid|1|5), (network_type|1|5) + +# Call drops +50106 call_drop (cause|1|5), (cid|1|5), (network_type|1|5) + +# Data network registration failed after successful voice registration +50107 data_network_registration_fail (op_numeric|1|5), (cid|1|5) + +# Suspicious status of data connection while radio poweroff +50108 data_network_status_on_radio_off (dc_state|3), (enable|1|5) + +# PDP drop caused by network +50109 pdp_network_drop (cid|1|5), (network_type|1|5) + +# Do not change these names without updating tag in: +#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c +51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5) + +# db stats. 0 is query, 1 is write (may become more fine grained in the +# future) +52000 db_operation (name|3),(op_type|1|5),(time|2|3) + +# http request/response stats +52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2) +60000 viewroot_draw (Draw time|1|3) +60001 viewroot_layout (Layout time|1|3) +60002 view_build_drawing_cache (View created drawing cache|1|5) +60003 view_use_drawing_cache (View drawn using bitmap cache|1|5) + +# 0 for screen off, 1 for screen on, 2 for key-guard done +70000 screen_toggled (screen_state|1|5) + +# browser stats for diary study +70101 browser_zoom_level_change (start level|1|5),(end level|1|5),(time|2|3) +70102 browser_double_tap_duration (duration|1|3),(time|2|3) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp new file mode 100644 index 0000000..3130a1c --- /dev/null +++ b/logcat/logcat.cpp @@ -0,0 +1,568 @@ +// Copyright 2006 The Android Open Source Project + +#include <cutils/logger.h> +#include <cutils/logd.h> +#include <cutils/sockets.h> +#include <cutils/logprint.h> +#include <cutils/event_tag_map.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <arpa/inet.h> + +#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 +#define DEFAULT_MAX_ROTATED_LOGS 4 + +static AndroidLogFormat * g_logformat; + +/* logd prefixes records with a length field */ +#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) + +#define LOG_FILE_DIR "/dev/log/" + + +namespace android { + +/* Global Variables */ + +static const char * g_outputFileName = NULL; +static int g_logRotateSizeKBytes = 0; // 0 means "no log rotation" +static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded" +static int g_outFD = -1; +static off_t g_outByteCount = 0; +static int g_isBinary = 0; +static int g_printBinary = 0; + +static EventTagMap* g_eventTagMap = NULL; + +static int openLogFile (const char *pathname) +{ + return open(g_outputFileName, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); +} + +static void rotateLogs() +{ + int err; + + // Can't rotate logs if we're not outputting to a file + if (g_outputFileName == NULL) { + return; + } + + close(g_outFD); + + for (int i = g_maxRotatedLogs ; i > 0 ; i--) { + char *file0, *file1; + + asprintf(&file1, "%s.%d", g_outputFileName, i); + + if (i - 1 == 0) { + asprintf(&file0, "%s", g_outputFileName); + } else { + asprintf(&file0, "%s.%d", g_outputFileName, i - 1); + } + + err = rename (file0, file1); + + if (err < 0 && errno != ENOENT) { + perror("while rotating log files"); + } + + free(file1); + free(file0); + } + + g_outFD = openLogFile (g_outputFileName); + + if (g_outFD < 0) { + perror ("couldn't open output file"); + exit(-1); + } + + g_outByteCount = 0; + +} + +void printBinary(struct logger_entry *buf) +{ + size_t size = sizeof(logger_entry) + buf->len; + int ret; + + do { + ret = write(g_outFD, buf, size); + } while (ret < 0 && errno == EINTR); +} + +static void processBuffer(struct logger_entry *buf) +{ + int bytesWritten; + int err; + AndroidLogEntry entry; + char binaryMsgBuf[1024]; + + if (g_isBinary) { + err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap, + binaryMsgBuf, sizeof(binaryMsgBuf)); + //printf(">>> pri=%d len=%d msg='%s'\n", + // entry.priority, entry.messageLen, entry.message); + } else { + err = android_log_processLogBuffer(buf, &entry); + } + if (err < 0) + goto error; + + bytesWritten = android_log_filterAndPrintLogLine( + g_logformat, g_outFD, &entry); + + if (bytesWritten < 0) { + perror("output error"); + exit(-1); + } + + g_outByteCount += bytesWritten; + + if (g_logRotateSizeKBytes > 0 + && (g_outByteCount / 1024) >= g_logRotateSizeKBytes + ) { + rotateLogs(); + } + +error: + //fprintf (stderr, "Error processing record\n"); + return; +} + +static void readLogLines(int logfd) +{ + while (1) { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); + struct logger_entry *entry = (struct logger_entry *) buf; + int ret; + + ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN); + if (ret < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + break; + perror("logcat read"); + exit(EXIT_FAILURE); + } + else if (!ret) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + + /* NOTE: driver guarantees we read exactly one full entry */ + + entry->msg[entry->len] = '\0'; + + if (g_printBinary) { + printBinary(entry); + } else { + (void) processBuffer(entry); + } + } +} + +static int clearLog(int logfd) +{ + return ioctl(logfd, LOGGER_FLUSH_LOG); +} + +/* returns the total size of the log's ring buffer */ +static int getLogSize(int logfd) +{ + return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE); +} + +/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */ +static int getLogReadableSize(int logfd) +{ + return ioctl(logfd, LOGGER_GET_LOG_LEN); +} + +static void setupOutput() +{ + + if (g_outputFileName == NULL) { + g_outFD = STDOUT_FILENO; + + } else { + struct stat statbuf; + + g_outFD = openLogFile (g_outputFileName); + + if (g_outFD < 0) { + perror ("couldn't open output file"); + exit(-1); + } + + fstat(g_outFD, &statbuf); + + g_outByteCount = statbuf.st_size; + } +} + +static void show_help(const char *cmd) +{ + fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd); + + fprintf(stderr, "options include:\n" + " -s Set default filter to silent.\n" + " Like specifying filterspec '*:s'\n" + " -f <filename> Log to file. Default to stdout\n" + " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n" + " -n <count> Sets max number of rotated logs to <count>, default 4\n" + " -v <format> Sets the log print format, where <format> is one of:\n\n" + " brief process tag thread raw time threadtime long\n\n" + " -c clear (flush) the entire log and exit\n" + " -d dump the log and then exit (don't block)\n" + " -g get the size of the log's ring buffer and exit\n" + " -b <buffer> request alternate ring buffer\n" + " ('main' (default), 'radio', 'events')\n" + " -B output the log in binary"); + + + fprintf(stderr,"\nfilterspecs are a series of \n" + " <tag>[:priority]\n\n" + "where <tag> is a log component tag (or * for all) and priority is:\n" + " V Verbose\n" + " D Debug\n" + " I Info\n" + " W Warn\n" + " E Error\n" + " F Fatal\n" + " S Silent (supress all output)\n" + "\n'*' means '*:d' and <tag> by itself means <tag>:v\n" + "\nIf not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS.\n" + "If no filterspec is found, filter defaults to '*:I'\n" + "\nIf not specified with -v, format is set from ANDROID_PRINTF_LOG\n" + "or defaults to \"brief\"\n\n"); + + + +} + + +} /* namespace android */ + +static int setLogFormat(const char * formatString) +{ + static AndroidLogPrintFormat format; + + format = android_log_formatFromString(formatString); + + if (format == FORMAT_OFF) { + // FORMAT_OFF means invalid string + return -1; + } + + android_log_setPrintFormat(g_logformat, format); + + return 0; +} + +extern "C" void logprint_run_tests(void); + +int main (int argc, char **argv) +{ + int logfd; + int err; + int hasSetLogFormat = 0; + int clearLog = 0; + int getLogSize = 0; + int mode = O_RDONLY; + char *log_device = strdup("/dev/"LOGGER_LOG_MAIN); + const char *forceFilters = NULL; + + g_logformat = android_log_format_new(); + + if (argc == 2 && 0 == strcmp(argv[1], "--test")) { + logprint_run_tests(); + exit(0); + } + + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { + android::show_help(argv[0]); + exit(0); + } + + for (;;) { + int ret; + + ret = getopt(argc, argv, "cdgsQf:r::n:v:b:B"); + + if (ret < 0) { + break; + } + + switch(ret) { + case 's': + // default to all silent + android_log_addFilterRule(g_logformat, "*:s"); + break; + + case 'c': + clearLog = 1; + mode = O_WRONLY; + break; + + case 'd': + mode |= O_NONBLOCK; + break; + + case 'g': + getLogSize = 1; + break; + + case 'b': + free(log_device); + log_device = + (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); + strcpy(log_device, LOG_FILE_DIR); + strcat(log_device, optarg); + + android::g_isBinary = (strcmp(optarg, "events") == 0); + break; + + case 'B': + android::g_printBinary = 1; + break; + + case 'f': + // redirect output to a file + + android::g_outputFileName = optarg; + + break; + + case 'r': + if (optarg == NULL) { + android::g_logRotateSizeKBytes + = DEFAULT_LOG_ROTATE_SIZE_KBYTES; + } else { + long logRotateSize; + char *lastDigit; + + if (!isdigit(optarg[0])) { + fprintf(stderr,"Invalid parameter to -r\n"); + android::show_help(argv[0]); + exit(-1); + } + android::g_logRotateSizeKBytes = atoi(optarg); + } + break; + + case 'n': + if (!isdigit(optarg[0])) { + fprintf(stderr,"Invalid parameter to -r\n"); + android::show_help(argv[0]); + exit(-1); + } + + android::g_maxRotatedLogs = atoi(optarg); + break; + + case 'v': + err = setLogFormat (optarg); + if (err < 0) { + fprintf(stderr,"Invalid parameter to -v\n"); + android::show_help(argv[0]); + exit(-1); + } + + hasSetLogFormat = 1; + break; + + case 'Q': + /* this is a *hidden* option used to start a version of logcat */ + /* in an emulated device only. it basically looks for androidboot.logcat= */ + /* on the kernel command line. If something is found, it extracts a log filter */ + /* and uses it to run the program. If nothing is found, the program should */ + /* quit immediately */ +#define KERNEL_OPTION "androidboot.logcat=" +#define CONSOLE_OPTION "androidboot.console=" + { + int fd; + char* logcat; + char* console; + int force_exit = 1; + static char cmdline[1024]; + + fd = open("/proc/cmdline", O_RDONLY); + if (fd >= 0) { + int n = read(fd, cmdline, sizeof(cmdline)-1 ); + if (n < 0) n = 0; + cmdline[n] = 0; + close(fd); + } else { + cmdline[0] = 0; + } + + logcat = strstr( cmdline, KERNEL_OPTION ); + console = strstr( cmdline, CONSOLE_OPTION ); + if (logcat != NULL) { + char* p = logcat + sizeof(KERNEL_OPTION)-1;; + char* q = strpbrk( p, " \t\n\r" );; + + if (q != NULL) + *q = 0; + + forceFilters = p; + force_exit = 0; + } + /* if nothing found or invalid filters, exit quietly */ + if (force_exit) + exit(0); + + /* redirect our output to the emulator console */ + if (console) { + char* p = console + sizeof(CONSOLE_OPTION)-1; + char* q = strpbrk( p, " \t\n\r" ); + char devname[64]; + int len; + + if (q != NULL) { + len = q - p; + } else + len = strlen(p); + + len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p ); + fprintf(stderr, "logcat using %s (%d)\n", devname, len); + if (len < (int)sizeof(devname)) { + fd = open( devname, O_WRONLY ); + if (fd >= 0) { + dup2(fd, 1); + dup2(fd, 2); + close(fd); + } + } + } + } + break; + + default: + fprintf(stderr,"Unrecognized Option\n"); + android::show_help(argv[0]); + exit(-1); + break; + } + } + + if (android::g_logRotateSizeKBytes != 0 + && android::g_outputFileName == NULL + ) { + fprintf(stderr,"-r requires -f as well\n"); + android::show_help(argv[0]); + exit(-1); + } + + android::setupOutput(); + + if (hasSetLogFormat == 0) { + const char* logFormat = getenv("ANDROID_PRINTF_LOG"); + + if (logFormat != NULL) { + err = setLogFormat(logFormat); + + if (err < 0) { + fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", + logFormat); + } + } + } + + if (forceFilters) { + err = android_log_addFilterString(g_logformat, forceFilters); + if (err < 0) { + fprintf (stderr, "Invalid filter expression in -logcat option\n"); + exit(0); + } + } else if (argc == optind) { + // Add from environment variable + char *env_tags_orig = getenv("ANDROID_LOG_TAGS"); + + if (env_tags_orig != NULL) { + err = android_log_addFilterString(g_logformat, env_tags_orig); + + if (err < 0) { + fprintf(stderr, "Invalid filter expression in" + " ANDROID_LOG_TAGS\n"); + android::show_help(argv[0]); + exit(-1); + } + } + } else { + // Add from commandline + for (int i = optind ; i < argc ; i++) { + err = android_log_addFilterString(g_logformat, argv[i]); + + if (err < 0) { + fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]); + android::show_help(argv[0]); + exit(-1); + } + } + } + + logfd = open(log_device, mode); + if (logfd < 0) { + fprintf(stderr, "Unable to open log device '%s': %s\n", + log_device, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (clearLog) { + int ret; + ret = android::clearLog(logfd); + if (ret) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + return 0; + } + + if (getLogSize) { + int size, readable; + + size = android::getLogSize(logfd); + if (size < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + readable = android::getLogReadableSize(logfd); + if (readable < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + printf("ring buffer is %dKb (%dKb consumed), " + "max entry is %db, max payload is %db\n", + size / 1024, readable / 1024, + (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); + return 0; + } + + //LOG_EVENT_INT(10, 12345); + //LOG_EVENT_LONG(11, 0x1122334455667788LL); + //LOG_EVENT_STRING(0, "whassup, doc?"); + + if (android::g_isBinary) + android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); + + android::readLogLines(logfd); + + return 0; +} diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk new file mode 100644 index 0000000..5fd6356 --- /dev/null +++ b/logwrapper/Android.mk @@ -0,0 +1,7 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= logwrapper.c +LOCAL_MODULE := logwrapper +LOCAL_STATIC_LIBRARIES := liblog +include $(BUILD_EXECUTABLE) diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c new file mode 100644 index 0000000..f00bfbf --- /dev/null +++ b/logwrapper/logwrapper.c @@ -0,0 +1,181 @@ +/* + * 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 <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include "private/android_filesystem_config.h" +#include "cutils/log.h" + +void fatal(const char *msg) { + fprintf(stderr, msg); + LOG(LOG_ERROR, "logwrapper", msg); + exit(-1); +} + +void usage() { + fatal( + "Usage: logwrapper [-x] BINARY [ARGS ...]\n" + "\n" + "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n" + "the Android logging system. Tag is set to BINARY, priority is\n" + "always LOG_INFO.\n" + "\n" + "-x: Causes logwrapper to SIGSEGV when BINARY terminates\n" + " fault address is set to the status of wait()\n"); +} + +void parent(const char *tag, int seg_fault_on_exit, int parent_read) { + int status; + char buffer[4096]; + + int a = 0; // start index of unprocessed data + int b = 0; // end index of unprocessed data + int sz; + while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { + + sz += b; + // Log one line at a time + for (b = 0; b < sz; b++) { + if (buffer[b] == '\r') { + buffer[b] = '\0'; + } else if (buffer[b] == '\n') { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + a = b + 1; + } + } + + if (a == 0 && b == sizeof(buffer) - 1) { + // buffer is full, flush + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + b = 0; + } else if (a != b) { + // Keep left-overs + b -= a; + memmove(buffer, &buffer[a], b); + a = 0; + } else { + a = 0; + b = 0; + } + + } + // Flush remaining data + if (a != b) { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + } + status = 0xAAAA; + if (wait(&status) != -1) { // Wait for child + if (WIFEXITED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag, + WTERMSIG(status)); + else if (WIFSTOPPED(status)) + LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag, + WSTOPSIG(status)); + } else + LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag, + strerror(errno), errno); + if (seg_fault_on_exit) + *(int *)status = 0; // causes SIGSEGV with fault_address = status +} + +void child(int argc, char* argv[]) { + // create null terminated argv_child array + char* argv_child[argc + 1]; + memcpy(argv_child, argv, argc * sizeof(char *)); + argv_child[argc] = NULL; + + if (execvp(argv_child[0], argv_child)) { + LOG(LOG_ERROR, "logwrapper", + "executing %s failed: %s\n", argv_child[0], strerror(errno)); + exit(-1); + } +} + +int main(int argc, char* argv[]) { + pid_t pid; + int seg_fault_on_exit = 0; + + int parent_ptty; + int child_ptty; + char *child_devname = NULL; + + if (argc < 2) { + usage(); + } + + if (strncmp(argv[1], "-d", 2) == 0) { + seg_fault_on_exit = 1; + argc--; + argv++; + } + + if (argc < 2) { + usage(); + } + + /* Use ptty instead of socketpair so that STDOUT is not buffered */ + parent_ptty = open("/dev/ptmx", O_RDWR); + if (parent_ptty < 0) { + fatal("Cannot create parent ptty\n"); + } + + if (grantpt(parent_ptty) || unlockpt(parent_ptty) || + ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { + fatal("Problem with /dev/ptmx\n"); + } + + pid = fork(); + if (pid < 0) { + fatal("Failed to fork\n"); + } else if (pid == 0) { + child_ptty = open(child_devname, O_RDWR); + if (child_ptty < 0) { + fatal("Problem with child ptty\n"); + } + + // redirect stdout and stderr + close(parent_ptty); + dup2(child_ptty, 1); + dup2(child_ptty, 2); + close(child_ptty); + + child(argc - 1, &argv[1]); + + } else { + // switch user and group to "log" + // this may fail if we are not root, + // but in that case switching user/group is unnecessary + setgid(AID_LOG); + setuid(AID_LOG); + + parent(argv[1], seg_fault_on_exit, parent_ptty); + } + + return 0; +} diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk new file mode 100644 index 0000000..18f0ff3 --- /dev/null +++ b/mkbootimg/Android.mk @@ -0,0 +1,12 @@ + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := mkbootimg.c +LOCAL_STATIC_LIBRARIES := libmincrypt + +LOCAL_MODULE := mkbootimg + +include $(BUILD_HOST_EXECUTABLE) + +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h new file mode 100644 index 0000000..242ab35 --- /dev/null +++ b/mkbootimg/bootimg.h @@ -0,0 +1,97 @@ +/* tools/mkbootimg/bootimg.h +** +** 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. +*/ + +#ifndef _BOOT_IMAGE_H_ +#define _BOOT_IMAGE_H_ + +typedef struct boot_img_hdr boot_img_hdr; + +#define BOOT_MAGIC "ANDROID!" +#define BOOT_MAGIC_SIZE 8 +#define BOOT_NAME_SIZE 16 +#define BOOT_ARGS_SIZE 512 + +struct boot_img_hdr +{ + unsigned char magic[BOOT_MAGIC_SIZE]; + + unsigned kernel_size; /* size in bytes */ + unsigned kernel_addr; /* physical load addr */ + + unsigned ramdisk_size; /* size in bytes */ + unsigned ramdisk_addr; /* physical load addr */ + + unsigned second_size; /* size in bytes */ + unsigned second_addr; /* physical load addr */ + + unsigned tags_addr; /* physical addr for kernel tags */ + unsigned page_size; /* flash page size we assume */ + unsigned unused[2]; /* future expansion: should be 0 */ + + unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ + + unsigned char cmdline[BOOT_ARGS_SIZE]; + + unsigned id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + +#if 0 +typedef struct ptentry ptentry; + +struct ptentry { + char name[16]; /* asciiz partition name */ + unsigned start; /* starting block number */ + unsigned length; /* length in blocks */ + unsigned flags; /* set to zero */ +}; + +/* MSM Partition Table ATAG +** +** length: 2 + 7 * n +** atag: 0x4d534d70 +** <ptentry> x n +*/ +#endif + +#endif diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c new file mode 100644 index 0000000..d803cf6 --- /dev/null +++ b/mkbootimg/mkbootimg.c @@ -0,0 +1,251 @@ +/* tools/mkbootimg/mkbootimg.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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include "mincrypt/sha.h" +#include "bootimg.h" + +static 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); + if(data == 0) goto oops; + + if(read(fd, data, sz) != sz) goto oops; + close(fd); + + if(_sz) *_sz = sz; + return data; + +oops: + close(fd); + if(data != 0) free(data); + return 0; +} + +int usage(void) +{ + fprintf(stderr,"usage: mkbootimg\n" + " --kernel <filename>\n" + " --ramdisk <filename>\n" + " [ --second <2ndbootloader-filename> ]\n" + " [ --cmdline <kernel-commandline> ]\n" + " [ --board <boardname> ]\n" + " -o|--output <filename>\n" + ); + return 1; +} + + + +static unsigned char padding[2048] = { 0, }; + +int write_padding(int fd, unsigned pagesize, unsigned itemsize) +{ + unsigned pagemask = pagesize - 1; + unsigned count; + + if((itemsize & pagemask) == 0) { + return 0; + } + + count = pagesize - (itemsize & pagemask); + + if(write(fd, padding, count) != count) { + return -1; + } else { + return 0; + } +} + +int main(int argc, char **argv) +{ + boot_img_hdr hdr; + + char *kernel_fn = 0; + void *kernel_data = 0; + char *ramdisk_fn = 0; + void *ramdisk_data = 0; + char *second_fn = 0; + void *second_data = 0; + char *cmdline = ""; + char *bootimg = 0; + char *board = ""; + unsigned pagesize = 2048; + unsigned saddr = 0; + int fd; + SHA_CTX ctx; + uint8_t* sha; + + argc--; + argv++; + + memset(&hdr, 0, sizeof(hdr)); + + while(argc > 0){ + char *arg = argv[0]; + char *val = argv[1]; + if(argc < 2) { + return usage(); + } + argc -= 2; + argv += 2; + if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) { + bootimg = val; + } else if(!strcmp(arg, "--kernel")) { + kernel_fn = val; + } else if(!strcmp(arg, "--ramdisk")) { + ramdisk_fn = val; + } else if(!strcmp(arg, "--second")) { + second_fn = val; + } else if(!strcmp(arg, "--cmdline")) { + cmdline = val; + } else if(!strcmp(arg, "--saddr")) { + saddr = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--board")) { + board = val; + } else { + return usage(); + } + } + + if(bootimg == 0) { + fprintf(stderr,"error: no output filename specified\n"); + return usage(); + } + + if(kernel_fn == 0) { + fprintf(stderr,"error: no kernel image specified\n"); + return usage(); + } + + if(ramdisk_fn == 0) { + fprintf(stderr,"error: no ramdisk image specified\n"); + return usage(); + } + + if(strlen(board) >= BOOT_NAME_SIZE) { + fprintf(stderr,"error: board name too large\n"); + return usage(); + } + + strcpy(hdr.name, board); + + hdr.kernel_addr = 0x10008000; + hdr.ramdisk_addr = 0x11000000; + if(saddr) { + hdr.second_addr = 0x00300000; + } else { + hdr.second_addr = 0x10F00000; + } + hdr.tags_addr = 0x10000100; + hdr.page_size = pagesize; + + memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); + + if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) { + fprintf(stderr,"error: kernel commandline too large\n"); + return 1; + } + strcpy((char*)hdr.cmdline, cmdline); + + kernel_data = load_file(kernel_fn, &hdr.kernel_size); + if(kernel_data == 0) { + fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn); + return 1; + } + + if(!strcmp(ramdisk_fn,"NONE")) { + ramdisk_data = 0; + hdr.ramdisk_size = 0; + } else { + ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size); + if(ramdisk_data == 0) { + fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn); + return 1; + } + } + + if(second_fn) { + second_data = load_file(second_fn, &hdr.second_size); + if(second_data == 0) { + fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn); + return 1; + } + } + + /* put a hash of the contents in the header so boot images can be + * differentiated based on their first 2k. + */ + SHA_init(&ctx); + SHA_update(&ctx, kernel_data, hdr.kernel_size); + SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size)); + SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size); + SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size)); + SHA_update(&ctx, second_data, hdr.second_size); + SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size)); + sha = SHA_final(&ctx); + memcpy(hdr.id, sha, + SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE); + + fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644); + if(fd < 0) { + fprintf(stderr,"error: could not create '%s'\n", bootimg); + return 1; + } + + if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail; + if(write_padding(fd, pagesize, sizeof(hdr))) goto fail; + + if(write(fd, kernel_data, hdr.kernel_size) != hdr.kernel_size) goto fail; + if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail; + + if(write(fd, ramdisk_data, hdr.ramdisk_size) != hdr.ramdisk_size) goto fail; + if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; + + if(second_data) { + if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail; + if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; + } + + return 0; + +fail: + unlink(bootimg); + close(fd); + fprintf(stderr,"error: failed writing '%s': %s\n", bootimg, + strerror(errno)); + return 1; +} diff --git a/mountd/ASEC.c b/mountd/ASEC.c new file mode 100644 index 0000000..a6aab9c --- /dev/null +++ b/mountd/ASEC.c @@ -0,0 +1,774 @@ +/* + * 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. + */ + +/* +** Android Secure External Cache +*/ + +#include "mountd.h" + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <poll.h> +#include <errno.h> + +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <linux/dm-ioctl.h> +#include <linux/loop.h> + +#include <cutils/properties.h> +#include <cutils/misc.h> + +#include "ASEC.h" + +//#define MODULE_FAILURE_IS_FATAL + +extern int init_module(void *, unsigned long, const char *); +extern int delete_module(const char *, unsigned int); + +struct asec_context +{ + char *name; // Device mapper volume name + char *srcPath; // Path to the source (original) mount + char *backingFile; // Name of the image file + unsigned int sectors; // Number of sectors + char *dstPath; // Destination mount point + char *crypt; // Crypt options + + boolean needs_format; + boolean started; + int cacheFd; + int lo_num; + int dm_num; + unsigned char key[16]; +}; + +static const char *MODULES[] = { "dm_mod", "crypto", "crypto_algapi", "crypto_blkcipher", + "cryptomgr", "dm_crypt", "jbd", + "twofish_common", "twofish", "cbc", + "mbcache", "ext3", + NULL }; +static const char KEY_PATH[] = "/data/system/asec.key"; +static const char MODULE_PATH[] = "/system/lib/modules"; +static const char MKE2FS_PATH[] = "/system/bin/mke2fs"; +static const char E2FSCK_PATH[] = "/system/bin/e2fsck"; + +boolean AsecIsStarted(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + return ctx->started; +} + +const char *AsecMountPoint(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + return ctx->dstPath; +} + +static boolean AsecIsEnabled() +{ + char value[PROPERTY_VALUE_MAX]; + int enabled; + + property_get(ASEC_ENABLED, value, "0"); + + if (atoi(value) == 1) + return true; + return false; +} + +void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile, + const char *Size, const char *DstPath, const char *Crypt) +{ + struct asec_context *ctx; + + if (!AsecIsEnabled()) + return NULL; + + LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n", + Name, SrcPath, BackingFile, Size, DstPath, Crypt); + + if (!Name || !SrcPath || !BackingFile || !Size || !DstPath || !Crypt) { + LOG_ERROR("AsecInit(): Invalid arguments\n"); + return NULL; + } + + if (!(ctx = malloc(sizeof(struct asec_context)))) { + LOG_ERROR("AsecInit(): Out of memory\n"); + return NULL; + } + + memset(ctx, 0, sizeof(struct asec_context)); + ctx->name = strdup(Name); + ctx->srcPath = strdup(SrcPath); + ctx->backingFile = strdup(BackingFile); + ctx->sectors = atoi(Size); + ctx->dstPath = strdup(DstPath); + ctx->crypt = strdup(Crypt); + return ctx; +} + +void AsecDeinit(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + free(ctx->name); + free(ctx->srcPath); + free(ctx->backingFile); + free(ctx->dstPath); + free(ctx->crypt); + + free(ctx); +} + +static int AsecLoadModules() +{ + int i; + + for (i = 0; MODULES[i] != NULL; i++) { + const char *moduleName = MODULES[i]; + char moduleFile[255]; + int rc = 0; + void *module; + unsigned int size; + + sprintf(moduleFile, "%s/%s.ko", MODULE_PATH, moduleName); + module = load_file(moduleFile, &size); + if (!module) { + LOG_ERROR("Failed to load module %s (%d)\n", moduleFile, errno); + return -1; + } + + rc = init_module(module, size, ""); + free(module); + if (rc && errno != EEXIST) { + LOG_ERROR("Failed to init module %s (%d)\n", moduleFile, errno); + return -errno; + } + } + return 0; +} + +static int AsecUnloadModules() +{ + int i, j, rc; + + for (i = 0; MODULES[i] != NULL; i++); + + for (j = (i - 1); j >= 0; j--) { + const char *moduleName = MODULES[j]; + int maxretry = 10; + while(maxretry-- > 0) { + rc = delete_module(moduleName, O_NONBLOCK | O_EXCL); + if (rc < 0 && errno == EAGAIN) + usleep(500000); + else + break; + } + if (rc != 0) { + LOG_ERROR("Failed to unload module %s\n", moduleName); + return -errno; + } + } + return 0; +} + +static int AsecGenerateKey(struct asec_context *ctx) +{ + LOG_ASEC("AsecGenerateKey():\n"); + + memset((void *) ctx->key, 0x69, sizeof(ctx->key)); + return 0; +} + +static int AsecLoadGenerateKey(struct asec_context *ctx) +{ + int fd; + int rc = 0; + + if ((fd = open(KEY_PATH, O_RDWR | O_CREAT, 0600)) < 0) { + LOG_ERROR("Error opening / creating keyfile (%d)\n", errno); + return -errno; + } + + if (read(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) { + LOG_ASEC("Generating key\n"); + if ((rc = AsecGenerateKey(ctx)) < 0) { + LOG_ERROR("Error generating key (%d)\n", rc); + goto out; + } + if (write(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) { + LOG_ERROR("Error writing keyfile (%d)\n", errno); + rc = -1; + goto out; + } + } + + out: + close (fd); + return rc; +} + +static int AsecFormatFilesystem(struct asec_context *ctx) +{ + char cmdline[255]; + int rc; + + sprintf(cmdline, + "%s -b 4096 -m 1 -j -L \"%s\" /dev/block/dm-%d", + MKE2FS_PATH, ctx->name, ctx->dm_num); + + LOG_ASEC("Formatting filesystem (%s)\n", cmdline); + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing format command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (!rc) { + LOG_ASEC("Format completed\n"); + } else { + LOG_ASEC("Format failed (%d)\n", rc); + } + + return rc; +} + +static int AsecCheckFilesystem(struct asec_context *ctx) +{ + char cmdline[255]; + int rc; + + sprintf(cmdline, "%s -p /dev/block/dm-%d", E2FSCK_PATH, ctx->dm_num); + + LOG_ASEC("Checking filesystem (%s)\n", cmdline); + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing check command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (rc == 0) { + LOG_ASEC("ASEC volume '%s' had no errors\n", ctx->name); + } else if (rc == 1) { + LOG_ASEC("ASEC volume '%s' had corrected errors\n", ctx->name); + rc = 0; + } else if (rc == 2) { + LOG_ERROR("ASEC volume '%s' had corrected errors (system should be rebooted)\n", ctx->name); + } else if (rc == 4) { + LOG_ERROR("ASEC volume '%s' had uncorrectable errors\n", ctx->name); + } else if (rc == 8) { + LOG_ERROR("Operational error while checking volume '%s'\n", ctx->name); + } else { + LOG_ERROR("Unknown e2fsck exit code (%d)\n", rc); + } + return rc; +} + +static int AsecOpenCreateCache(struct asec_context *ctx) +{ + char filepath[255]; + + sprintf(filepath, "%s/%s", ctx->srcPath, ctx->backingFile); + + if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) { + if (errno == ENOENT) { + int rc = 0; + + LOG_ASEC("Creating cache file (%u sectors)\n", ctx->sectors); + if ((ctx->cacheFd = creat(filepath, 0600)) < 0) { + LOG_ERROR("Error creating cache (%d)\n", errno); + return -errno; + } + if (ftruncate(ctx->cacheFd, ctx->sectors * 512) < 0) { + LOG_ERROR("Error truncating cache (%d)\n", errno); + close(ctx->cacheFd); + unlink(filepath); + return -errno; + } + LOG_ASEC("Cache created (%u sectors) \n", ctx->sectors); + close(ctx->cacheFd); // creat() is WRONLY + + if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) { + LOG_ERROR("Error opening cache file (%d)\n", errno); + close(ctx->cacheFd); + unlink(filepath); + return -errno; + } + + ctx->needs_format = 1; + } else + return -errno; + } else { + struct stat stat_buf; + + if (fstat(ctx->cacheFd, &stat_buf) < 0) { + LOG_ERROR("Failed to fstat cache (%d)\n", errno); + close(ctx->cacheFd); + return -errno; + } + if (stat_buf.st_size != ctx->sectors * 512) { + LOG_ERROR("Cache size %lld != configured size %u\n", + stat_buf.st_size, ctx->sectors * 512); + } + + // XXX: Verify volume label matches ctx->name + } + + return 0; +} + +static void AsecCloseCache(struct asec_context *ctx) +{ + close(ctx->cacheFd); +} + +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +static struct dm_ioctl *_dm_ioctl_setup(struct asec_context *ctx, int flags) +{ + void *buffer; + void *p; + const size_t min_size = 16 * 1024; + size_t len = sizeof(struct dm_ioctl); + struct dm_ioctl *io; + struct dm_target_spec *tgt; + int i; + char params[1024]; + char key[80]; + + key[0] = '\0'; + + for (i = 0; i < (int) sizeof(ctx->key); i++) { + char tmp[8]; + + sprintf(tmp, "%02x", ctx->key[i]); + strcat(key, tmp); + } + + // XXX: Handle ctx->crypt + sprintf(params, "twofish %s 0 /dev/block/loop%d 0", key, ctx->lo_num); + + if (len < min_size) + len = min_size; + + if (!(buffer = malloc(len))) { + LOG_ERROR("Unable to allocate memory\n"); + return NULL; + } + + memset(buffer, 0, len); + io = buffer; + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + io->data_size = len; + io->data_start = sizeof(struct dm_ioctl); + + io->flags = flags; + io->dev = 0; + + io->target_count = 1; + io->event_nr = 1; + strncpy(io->name, ctx->name, sizeof(io->name)); + + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = ctx->sectors; + strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type)); + + p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + strcpy((char *) p, params); + p+= strlen(params) + 1; + + p = _align(p, 8); + tgt->next = p - buffer; + + return io; +} + +static int FindNextAvailableDm() +{ + int i; + + for (i = 0; i < 8; i++) { + char path[255]; + sprintf(path, "/dev/block/dm-%d", i); + if ((access(path, F_OK) < 0) && (errno == ENOENT)) + return i; + } + + LOG_ERROR("Out of device mapper numbers\n"); + return -1; +} + +static int AsecCreateDeviceMapping(struct asec_context *ctx) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + ctx->dm_num = FindNextAvailableDm(); + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOG_ERROR("Error opening device mapper (%d)\n", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(ctx, 0))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + close(dmFd); + return -ENOMEM; + } + + if ((rc = ioctl(dmFd, DM_DEV_CREATE, io)) < 0) { + LOG_ERROR("device-mapper create ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(ctx, DM_STATUS_TABLE_FLAG))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_TABLE_LOAD, io)) < 0) { + LOG_ERROR("device-mapper load ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(ctx, 0))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_SUSPEND, io)) < 0) { + LOG_ERROR("device-mapper resume ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int AsecDestroyDeviceMapping(struct asec_context *ctx) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOG_ERROR("Error opening device mapper (%d)\n", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(ctx, DM_PERSISTENT_DEV_FLAG))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) { + LOG_ERROR("device-mapper remove ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int AsecMountCache(struct asec_context *ctx) +{ + int flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME; + char devname[255]; + + if (access(ctx->dstPath, R_OK)) { + LOG_ERROR("Destination mount point '%s' unavailable (%d)\n", ctx->dstPath, errno); + return -errno; + } + + sprintf(devname, "/dev/block/dm-%d", ctx->dm_num); + + if (mount(devname, ctx->dstPath, "ext3", flags, NULL)) { + LOG_ERROR("ASEC mount failed (%d)\n", errno); + return -errno; + } + + return 0; +} + +static int AsecUnmountCache(struct asec_context *ctx) +{ + if (umount(ctx->dstPath)) { + if (errno == EBUSY) { + LOG_ASEC("ASEC volume '%s' still busy\n", ctx->name); + } else { + LOG_ERROR("ASEC umount failed (%d)\n", errno); + } + return -errno; + } + LOG_ASEC("ASEC volume '%s' unmounted\n", ctx->name); + return 0; +} + +static int FindNextAvailableLoop() +{ + int i; + + for (i = 0; i < MAX_LOOP; i++) { + struct loop_info info; + char devname[255]; + int fd; + + sprintf(devname, "/dev/block/loop%d", i); + + if ((fd = open(devname, O_RDONLY)) < 0) { + LOG_ERROR("Unable to open %s (%d)\n", devname, errno); + return -errno; + } + + if (ioctl(fd, LOOP_GET_STATUS, &info) < 0) { + close(fd); + + if (errno == ENXIO) + return i; + + LOG_ERROR("Unable to get loop status for %s (%d)\n", devname, errno); + return -errno; + } + close(fd); + } + return -ENXIO; +} + +static int AsecCreateLoop(struct asec_context *ctx) +{ + char devname[255]; + int device_fd; + int rc = 0; + + ctx->lo_num = FindNextAvailableLoop(); + if (ctx->lo_num < 0) { + LOG_ERROR("No loop devices available\n"); + return -ENXIO; + } + + sprintf(devname, "/dev/block/loop%d", ctx->lo_num); + device_fd = open(devname, O_RDWR); + if (device_fd < 0) { + LOG_ERROR("failed to open loop device (%d)\n", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_SET_FD, ctx->cacheFd) < 0) { + LOG_ERROR("loop_set_fd ioctl failed (%d)\n", errno); + rc = -errno; + } + close(device_fd); + return rc; +} + +static int AsecDestroyLoop(struct asec_context *ctx) +{ + char devname[255]; + int device_fd; + int rc = 0; + + sprintf(devname, "/dev/block/loop%d", ctx->lo_num); + device_fd = open(devname, O_RDONLY); + if (device_fd < 0) { + LOG_ERROR("Failed to open loop (%d)\n", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("Failed to destroy loop (%d)\n", errno); + rc = -errno; + } + + close(device_fd); + return rc; +} + +int AsecStart(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + char value[PROPERTY_VALUE_MAX]; + int rc = 0; + + if (!ctx) + return -EINVAL; + + if (ctx->started) + return -EBUSY; + + LOG_ASEC("AsecStart(%s):\n", ctx->name); + + NotifyAsecState(ASEC_BUSY, ctx->dstPath); + + if ((rc = AsecLoadModules()) < 0) { + LOG_ERROR("AsecStart: Failed to load kernel modules\n"); +#ifdef MODULE_FAILURE_IS_FATAL + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; +#endif + } + + if ((rc = AsecLoadGenerateKey(ctx))) { + LOG_ERROR("AsecStart: Failed to load / generate key\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecOpenCreateCache(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to open / create cache\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecCreateLoop(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to create loop\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_closecache; + } + + if ((rc = AsecCreateDeviceMapping(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to create devmapping (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroyloop; + } + + if (ctx->needs_format) { + if ((rc = AsecFormatFilesystem(ctx))) { + LOG_ERROR("AsecStart: Failed to format cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + ctx->needs_format = 0; + } else { + if ((rc = AsecCheckFilesystem(ctx))) { + LOG_ERROR("AsecStart: Failed to check filesystem (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + } + + if ((rc = AsecMountCache(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to mount cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + + NotifyAsecState(ASEC_AVAILABLE, ctx->dstPath); + ctx->started = true; + + return rc; + + fail_destroydm: + AsecDestroyDeviceMapping(ctx); + fail_destroyloop: + AsecDestroyLoop(ctx); + fail_closecache: + AsecCloseCache(ctx); + return rc; +} + +int AsecStop(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + int rc = 0; + + if (!ctx->started) + return -EINVAL; + + LOG_ASEC("AsecStop(%s):\n", ctx->name); + + NotifyAsecState(ASEC_BUSY, ctx->dstPath); + + if ((rc = AsecUnmountCache(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to unmount cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecDestroyDeviceMapping(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to destroy devmapping (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecDestroyLoop(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to destroy loop device (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + AsecCloseCache(ctx); + + if ((rc = AsecUnloadModules()) < 0) { + if (rc == -EAGAIN) { + LOG_ASEC("AsecStop: Kernel modules still in use\n"); + } else { + LOG_ERROR("AsecStop: Failed to unload kernel modules (%d)\n", rc); +#ifdef MODULE_FAILURE_IS_FATAL + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; +#endif + } + } + + ctx->started = false; + NotifyAsecState(ASEC_DISABLED, ctx->dstPath); + return rc; +} diff --git a/mountd/ASEC.h b/mountd/ASEC.h new file mode 100644 index 0000000..c87b288 --- /dev/null +++ b/mountd/ASEC.h @@ -0,0 +1,66 @@ +#ifndef _ASEC_H +#define _ASEC_H + +#define ASEC_STORES_MAX 4 +#define MAX_LOOP 8 + +typedef enum AsecState { + // Feature disabled + ASEC_DISABLED, + + // Feature enabled and operational + ASEC_AVAILABLE, + + // Busy + ASEC_BUSY, + + // Internal Error + ASEC_FAILED_INTERR, + + // No media available + ASEC_FAILED_NOMEDIA, + + // Media is corrupt + ASEC_FAILED_BADMEDIA, + + // Key mismatch + ASEC_FAILED_BADKEY, +} AsecState; + +/* + * ASEC commands + */ +#define ASEC_CMD_SEND_STATUS "asec_send_status" +#define ASEC_CMD_ENABLE "asec_enable" +#define ASEC_CMD_DISABLE "asec_disable" + +/* + * ASEC events + */ + +// These events correspond to the states in the AsecState enum. +// A path to the ASEC mount point follows the colon +#define ASEC_EVENT_DISABLED "asec_disabled:" +#define ASEC_EVENT_AVAILABLE "asec_available:" +#define ASEC_EVENT_BUSY "asec_busy:" +#define ASEC_EVENT_FAILED_INTERR "asec_failed_interror:" +#define ASEC_EVENT_FAILED_NOMEDIA "asec_failed_nomedia" +#define ASEC_EVENT_FAILED_BADMEDIA "asec_failed_badmedia:" +#define ASEC_EVENT_FAILED_BADKEY "asec_failed_badkey:" + +/* + * System Properties + */ + +#define ASEC_ENABLED "asec.enabled" + +#define ASEC_STATUS "ro.asec.status" +#define ASEC_STATUS_DISABLED "disabled" +#define ASEC_STATUS_AVAILABLE "available" +#define ASEC_STATUS_BUSY "busy" +#define ASEC_STATUS_FAILED_INTERR "internal_error" +#define ASEC_STATUS_FAILED_NOMEDIA "no_media" +#define ASEC_STATUS_FAILED_BADMEDIA "bad_media" +#define ASEC_STATUS_FAILED_BADKEY "bad_key" + +#endif diff --git a/mountd/Android.mk b/mountd/Android.mk new file mode 100644 index 0000000..16532fa --- /dev/null +++ b/mountd/Android.mk @@ -0,0 +1,22 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AutoMount.c \ + ProcessKiller.c \ + Server.c \ + mountd.c \ + ASEC.c \ + logwrapper.c + +LOCAL_MODULE:= mountd + +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) + +LOCAL_CFLAGS := -DCREATE_MOUNT_POINTS=0 + +LOCAL_SHARED_LIBRARIES := libcutils + +# disabled - we are using vold now instead +# include $(BUILD_EXECUTABLE) diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c new file mode 100644 index 0000000..12ad572 --- /dev/null +++ b/mountd/AutoMount.c @@ -0,0 +1,1062 @@ +/* + * 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. + */ + +/* +** mountd automount support +*/ + +#include "mountd.h" + +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <poll.h> + +#include <sys/mount.h> +#include <sys/stat.h> +#include <linux/loop.h> +#include <sys/inotify.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <linux/netlink.h> + +#define DEVPATH "/dev/block/" +#define DEVPATHLENGTH 11 // strlen(DEVPATH) + +// FIXME - only one loop mount is supported at a time +#define LOOP_DEVICE "/dev/block/loop0" + +// timeout value for poll() when retries are pending +#define POLL_TIMEOUT 1000 + +#define MAX_MOUNT_RETRIES 3 +#define MAX_UNMOUNT_RETRIES 5 + +typedef enum { + // device is unmounted + kUnmounted, + + // attempting to mount device + kMounting, + + // device is unmounted + kMounted, + + // attempting to unmount device + // so the media can be removed + kUnmountingForEject, + + // attempting to mount device + // so it can be shared via USB mass storage + kUnmountingForUms, +} MountState; + +typedef struct MountPoint { + // block device to mount + const char* device; + + // mount point for device + const char* mountPoint; + + // path to the UMS driver file for specifying the block device path + const char* driverStorePath; + + // true if device can be shared via + // USB mass storage + boolean enableUms; + + // Array of ASEC handles + void *asecHandles[ASEC_STORES_MAX]; + + // true if the device is being shared via USB mass storage + boolean umsActive; + + // current state of the mount point + MountState state; + + // number of mount or unmount retries so far, + // when attempting to mount or unmount the device + int retryCount; + + // next in sMountPointList linked list + struct MountPoint* next; +} MountPoint; + +// list of our mount points (does not change after initialization) +static MountPoint* sMountPointList = NULL; +boolean gMassStorageEnabled = false; +boolean gMassStorageConnected = false; + +static pthread_t sAutoMountThread = 0; +static pid_t gExcludedPids[2] = {-1, -1}; + +static const char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck"; + +// number of mount points that have timeouts pending +static int sRetriesPending = 0; + +// for synchronization between sAutoMountThread and the server thread +static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; + +// requests the USB mass_storage driver to begin or end sharing a block device +// via USB mass storage. +static void SetBackingStore(MountPoint* mp, boolean enable) +{ + int fd; + + if (!mp->driverStorePath) { + LOG_ERROR("no driver_store_path specified in config file for %s", mp->device); + return; + } + + LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false")); + fd = open(mp->driverStorePath, O_WRONLY); + if (fd < 0) + { + LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath); + } + else + { + if (enable) + { + write(fd, mp->device, strlen(mp->device)); + mp->umsActive = true; + } + else + { + char ch = 0; + write(fd, &ch, 1); + mp->umsActive = false; + } + close(fd); + } +} + +static boolean ReadMassStorageState() +{ + FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r"); + if (file) + { + char buffer[20]; + fgets(buffer, sizeof(buffer), file); + fclose(file); + return (strncmp(buffer, "online", strlen("online")) == 0); + } + else + { + LOG_ERROR("could not read initial mass storage state\n"); + return false; + } +} + +static boolean IsLoopMounted(const char* path) +{ + FILE* f; + int count; + char device[256]; + char mount_path[256]; + char rest[256]; + int result = 0; + int path_length = strlen(path); + + f = fopen("/proc/mounts", "r"); + if (!f) { + LOG_ERROR("could not open /proc/mounts\n"); + return -1; + } + + do { + count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest); + if (count == 3) { + if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0) + { + result = 1; + break; + } + } + } while (count == 3); + + fclose(f); + LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result); + return result; +} + +static int CheckFilesystem(const char *device) +{ + char cmdline[255]; + int rc; + + // XXX: SAN: Check for FAT signature + + int result = access(FSCK_MSDOS_PATH, X_OK); + if (result != 0) { + LOG_MOUNT("CheckFilesystem(%s): %s not found (skipping checks)\n", FSCK_MSDOS_PATH, device); + return 0; + } + + char *args[7]; + args[0] = FSCK_MSDOS_PATH; + args[1] = "-v"; + args[2] = "-V"; + args[3] = "-w"; + args[4] = "-p"; + args[5] = device; + args[6] = NULL; + + LOG_MOUNT("Checking filesystem on %s\n", device); + rc = logwrap(6, args); + + // XXX: We need to be able to distinguish between a FS with an error + // and a block device which does not have a FAT fs at all on it + if (rc == 0) { + LOG_MOUNT("Filesystem check completed OK\n"); + return 0; + } else if (rc == 1) { + LOG_MOUNT("Filesystem check failed (general failure)\n"); + return -EINVAL; + } else if (rc == 2) { + LOG_MOUNT("Filesystem check failed (invalid usage)\n"); + return -EIO; + } else { + LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc); + return -EIO; + } +} + +static int DoMountDevice(const char* device, const char* mountPoint) +{ + LOG_MOUNT("Attempting mount of %s on %s\n", device, mountPoint); + +#if CREATE_MOUNT_POINTS + // make sure mount point exists + mkdir(mountPoint, 0000); +#endif + + int flags = 0; + + if (device && strncmp(device, "/dev/", 5)) + { + // mount with the loop driver if device does not start with "/dev/" + int file_fd, device_fd; + + // FIXME - only one loop mount supported at a time + file_fd = open(device, O_RDWR); + if (file_fd < -1) { + LOG_ERROR("open backing file %s failed\n", device); + return 1; + } + device_fd = open(LOOP_DEVICE, O_RDWR); + if (device_fd < -1) { + LOG_ERROR("open %s failed", LOOP_DEVICE); + close(file_fd); + return 1; + } + if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) + { + LOG_ERROR("ioctl LOOP_SET_FD failed\n"); + close(file_fd); + close(device_fd); + return 1; + } + + close(file_fd); + close(device_fd); + device = "/dev/block/loop0"; + } + + int result = access(device, R_OK); + if (result) { + LOG_ERROR("Unable to access '%s' (%d)\n", device, errno); + return -errno; + } + +#if 0 + if ((result = CheckFilesystem(device))) { + LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result); + // XXX: Notify framework - need a new SDCARD state for the following: + // - SD cards which are not present + // - SD cards with no partition table + // - SD cards with no filesystem + // - SD cards with bad filesystem + return result; + } +#endif + + // Extra safety measures: + flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC; + // Also, set fmask = 711 so that files cannot be marked executable, + // and cannot by opened by uid 1000 (system). Similar, dmask = 700 + // so that directories cannot be accessed by uid 1000. + result = mount(device, mountPoint, "vfat", flags, + "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); + if (result && errno == EROFS) { + LOG_ERROR("mount failed EROFS, try again read-only\n"); + flags |= MS_RDONLY; + result = mount(device, mountPoint, "vfat", flags, + "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); + } + + if (result == 0) { + LOG_MOUNT("Partition %s mounted on %s\n", device, mountPoint); + NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0); + + MountPoint* mp = sMountPointList; + while (mp) { + if (!strcmp(mountPoint, mp->mountPoint)) { + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] != NULL) { + int a_result; + if ((a_result = AsecStart(mp->asecHandles[i])) < 0) { + LOG_ERROR("ASEC start failure (%d)\n", a_result); + } + } + } + break; + } + mp = mp -> next; + } + } else if (errno == EBUSY) { + LOG_MOUNT("Mount failed (already mounted)\n"); + result = 0; + } else { +#if CREATE_MOUNT_POINTS + rmdir(mountPoint); +#endif + LOG_MOUNT("Unable to mount %s on %s\n", device, mountPoint); + } + + return result; +} + +static int DoUnmountDevice(MountPoint *mp) +{ + boolean loop = IsLoopMounted(mp->mountPoint); + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) + AsecStop(mp->asecHandles[i]); + } + + int result = umount(mp->mountPoint); + LOG_MOUNT("umount returned %d errno: %d\n", result, errno); + + if (result == 0) + { +#if CREATE_MOUNT_POINTS + rmdir(mountPoint); +#endif + NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false); + } + + if (loop) + { + // free the loop device + int loop_fd = open(LOOP_DEVICE, O_RDONLY); + if (loop_fd < -1) { + LOG_ERROR("open loop device failed\n"); + } + if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("ioctl LOOP_CLR_FD failed\n"); + } + + close(loop_fd); + } + + // ignore EINVAL and ENOENT, since it usually means the device is already unmounted + if (result && (errno == EINVAL || errno == ENOENT)) + result = 0; + + return result; +} + +static int MountPartition(const char* device, const char* mountPoint) +{ + char buf[100]; + int i; + + // attempt to mount subpartitions of the device + for (i = 1; i < 10; i++) + { + int rc; + snprintf(buf, sizeof(buf), "%sp%d", device, i); + rc = DoMountDevice(buf, mountPoint); + LOG_MOUNT("DoMountDevice(%s, %s) = %d\n", buf, mountPoint, rc); + if (rc == 0) + return 0; + } + + return -1; +} + +/***************************************************** + * + * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION + * + *****************************************************/ + +static void SetState(MountPoint* mp, MountState state) +{ + mp->state = state; +} + +// Enter a state that requires retries and timeouts. +static void SetRetries(MountPoint* mp, MountState state) +{ + SetState(mp, state); + mp->retryCount = 0; + + sRetriesPending++; + // wake up the automounter thread if we are being called + // from somewhere else with no retries pending + if (sRetriesPending == 1 && sAutoMountThread != 0 && + pthread_self() != sAutoMountThread) + pthread_kill(sAutoMountThread, SIGUSR1); +} + +// Exit a state that requires retries and timeouts. +static void ClearRetries(MountPoint* mp, MountState state) +{ + SetState(mp, state); + sRetriesPending--; +} + +// attempt to mount the specified mount point. +// set up retry/timeout if it does not succeed at first. +static void RequestMount(MountPoint* mp) +{ + LOG_MOUNT("RequestMount %s\n", mp->mountPoint); + + if (mp->state != kMounted && mp->state != kMounting && + access(mp->device, R_OK) == 0) { + // try raw device first + if (DoMountDevice(mp->device, mp->mountPoint) == 0 || + MountPartition(mp->device, mp->mountPoint) == 0) + { + SetState(mp, kMounted); + } + else + { + SetState(mp, kMounting); + mp->retryCount = 0; + SetRetries(mp, kMounting); + } + } +} + +// Force the kernel to drop all caches. +static void DropSystemCaches(void) +{ + int fd; + + LOG_MOUNT("Dropping system caches\n"); + fd = open("/proc/sys/vm/drop_caches", O_WRONLY); + + if (fd > 0) { + char ch = 3; + int rc; + + rc = write(fd, &ch, 1); + if (rc <= 0) + LOG_MOUNT("Error dropping caches (%d)\n", rc); + close(fd); + } +} + +// attempt to unmount the specified mount point. +// set up retry/timeout if it does not succeed at first. +static void RequestUnmount(MountPoint* mp, MountState retryState) +{ + int result; + + LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState); + + if (mp->state == kMounted) + { + SendUnmountRequest(mp->mountPoint); + + // do this in case the user pulls the SD card before we can successfully unmount + sync(); + DropSystemCaches(); + + if (DoUnmountDevice(mp) == 0) + { + SetState(mp, kUnmounted); + if (retryState == kUnmountingForUms) + { + SetBackingStore(mp, true); + NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false); + } + } + else + { + LOG_MOUNT("unmount failed, set retry\n"); + SetRetries(mp, retryState); + } + } + else if (mp->state == kMounting) + { + SetState(mp, kUnmounted); + } +} + +// returns true if the mount point should be shared via USB mass storage +static boolean MassStorageEnabledForMountPoint(const MountPoint* mp) +{ + return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms); +} + +// handles changes in gMassStorageEnabled and gMassStorageConnected +static void MassStorageStateChanged() +{ + MountPoint* mp = sMountPointList; + + boolean enable = (gMassStorageEnabled && gMassStorageConnected); + LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false")); + + while (mp) + { + if (mp->enableUms) + { + if (enable) + { + if (mp->state == kMounting) + SetState(mp, kUnmounted); + if (mp->state == kUnmounted) + { + SetBackingStore(mp, true); + NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false); + } + else + { + LOG_MOUNT("MassStorageStateChanged requesting unmount\n"); + // need to successfully unmount first + RequestUnmount(mp, kUnmountingForUms); + } + } else if (mp->umsActive) { + SetBackingStore(mp, false); + if (mp->state == kUnmountingForUms) + { + ClearRetries(mp, kMounted); + NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false); + } + else if (mp->state == kUnmounted) + { + NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false); + RequestMount(mp); + } + } + } + + mp = mp->next; + } +} + +// called when USB mass storage connected state changes +static void HandleMassStorageOnline(boolean connected) +{ + if (connected != gMassStorageConnected) + { + gMassStorageConnected = connected; + SendMassStorageConnected(connected); + + // we automatically reset to mass storage off after USB is connected + if (!connected) + gMassStorageEnabled = false; + + MassStorageStateChanged(); + } +} + +// called when a new block device has been created +static void HandleMediaInserted(const char* device) +{ + MountPoint* mp = sMountPointList; + + LOG_MOUNT("HandleMediaInserted(%s):\n", device); + + while (mp) + { + // see if the device matches mount point's block device + if (mp->state == kUnmounted && + strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0) + { + if (MassStorageEnabledForMountPoint(mp)) + { + SetBackingStore(mp, true); + NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false); + } + else + RequestMount(mp); + } + mp = mp->next; + } +} + +// called when a new block device has been deleted +static void HandleMediaRemoved(const char* device) +{ + MountPoint* mp = sMountPointList; + while (mp) + { + if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0) + { + if (mp->enableUms) + SetBackingStore(mp, false); + + if (mp->state == kMounted) + { + RequestUnmount(mp, kUnmountingForEject); + NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false); + } + + NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false); + break; + } + mp = mp->next; + } +} + +// Handle retrying to mount or unmount devices, +// and handle timeout condition if we have tried too many times +static void HandleRetries() +{ + MountPoint* mp = sMountPointList; + + while (mp) + { + if (mp->state == kMounting) + { + if (MountPartition(mp->device, mp->mountPoint) == 0) + { + // mount succeeded - clear the retry for this mount point + ClearRetries(mp, kMounted); + } + else + { + mp->retryCount++; + if (mp->retryCount == MAX_MOUNT_RETRIES) + { + // we failed to mount the device too many times + ClearRetries(mp, kUnmounted); + // notify that we failed to mount + NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false); + } + } + } + else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms) + { + if (DoUnmountDevice(mp) == 0) + { + // unmounting succeeded + // start mass storage, if state is kUnmountingForUms + if (mp->state == kUnmountingForUms) + { + SetBackingStore(mp, true); + NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false); + } + // clear the retry for this mount point + ClearRetries(mp, kUnmounted); + } + else + { + mp->retryCount++; + if (mp->retryCount >= MAX_UNMOUNT_RETRIES) + { + // kill any processes that are preventing the device from unmounting + // send SIGKILL instead of SIGTERM if the first attempt did not succeed + boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES); + + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) { + LOG_MOUNT("Killing processes for ASEC path '%s'\n", + AsecMountPoint(mp->asecHandles[i])); + KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]), + sigkill, + gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t)); + + // Now that we've killed the processes, try to stop the volume again + AsecStop(mp->asecHandles[i]); + } + } + + // unmounting the device is failing, so start killing processes + KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids, + sizeof(gExcludedPids) / sizeof(pid_t)); + + } + } + } + + mp = mp->next; + } +} + +/***************************************************** + * + * AUTO-MOUNTER THREAD + * + *****************************************************/ + +static void sigusr1_handler(int signo) +{ + // don't need to do anything here +} + +// create a socket for listening to inotify events +int CreateINotifySocket() +{ + // initialize inotify + int fd = inotify_init(); + + if (fd < 0) { + LOG_ERROR("inotify_init failed, %s\n", strerror(errno)); + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL)); + + return fd; +} + + +// create a socket for listening to uevents +int CreateUEventSocket() +{ + struct sockaddr_nl addr; + int sz = 64*1024; + int fd; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0xffffffff; + + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(fd < 0) + { + LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n"); + return -1; + } + + setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); + + if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n"); + close(fd); + return -1; + } + + return fd; +} + +/* + * Automounter main event thread. + * This thread listens for block devices being created and deleted via inotify, + * and listens for changes in the USB mass storage connected/disconnected via uevents from the + * power supply driver. + * This thread also handles retries and timeouts for requests to mount or unmount a device. + */ +static void* AutoMountThread(void* arg) +{ + int inotify_fd; + int uevent_fd; + int id; + struct sigaction actions; + + gExcludedPids[1] = getpid(); + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = sigusr1_handler; + sigaction(SIGUSR1, &actions, NULL); + + // initialize inotify + inotify_fd = CreateINotifySocket(); + // watch for files created and deleted in "/dev" + inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE); + + // initialize uevent watcher + uevent_fd = CreateUEventSocket(); + if (uevent_fd < 0) + { + LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno)); + return NULL; + } + + while (1) + { + struct pollfd fds[2]; + int timeout, result; + +#define INOTIFY_IDX 0 +#define UEVENT_IDX 1 + + fds[INOTIFY_IDX].fd = inotify_fd; + fds[INOTIFY_IDX].events = POLLIN; + fds[INOTIFY_IDX].revents = 0; + fds[UEVENT_IDX].fd = uevent_fd; + fds[UEVENT_IDX].events = POLLIN; + fds[UEVENT_IDX].revents = 0; + + // wait for an event or a timeout to occur. + // poll() can also return in response to a SIGUSR1 signal + timeout = (sRetriesPending ? POLL_TIMEOUT : -1); + result = poll(fds, 2, timeout); + + // lock the mutex while we are handling events + pthread_mutex_lock(&sMutex); + + // handle inotify notifications for block device creation and deletion + if (fds[INOTIFY_IDX].revents == POLLIN) + { + struct inotify_event event; + char buffer[512]; + int length = read(inotify_fd, buffer, sizeof(buffer)); + int offset = 0; + + while (length >= (int)sizeof(struct inotify_event)) + { + struct inotify_event* event = (struct inotify_event *)&buffer[offset]; + + if (event->mask == IN_CREATE) + { + LOG_MOUNT("/dev/block/%s created\n", event->name); + HandleMediaInserted(event->name); + } + else if (event->mask == IN_DELETE) + { + LOG_MOUNT("/dev/block/%s deleted\n", event->name); + HandleMediaRemoved(event->name); + } + + int size = sizeof(struct inotify_event) + event->len; + length -= size; + offset += size; + } + } + + // handle uevent notifications for USB state changes + if (fds[UEVENT_IDX].revents == POLLIN) + { + char buffer[64*1024]; + int count; + + count = recv(uevent_fd, buffer, sizeof(buffer), 0); + if (count > 0) { + char* s = buffer; + char* end = s + count; + char* type = NULL; + char* online = NULL; + char* switchName = NULL; + char* switchState = NULL; + + while (s < end) { + if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE="))) + type = s + strlen("POWER_SUPPLY_TYPE="); + else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE="))) + online = s + strlen("POWER_SUPPLY_ONLINE="); + else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME="))) + switchName = s + strlen("SWITCH_NAME="); + else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE="))) + switchState = s + strlen("SWITCH_STATE="); + s += (strlen(s) + 1); + } + + // we use the usb_mass_storage switch state to tell us when USB is online + if (switchName && switchState && + !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online")) + { + LOG_MOUNT("USB online\n"); + HandleMassStorageOnline(true); + } + + // and we use the power supply state to tell us when USB is offline + // we can't rely on the switch for offline detection because we get false positives + // when USB is reenumerated by the host. + if (type && online && !strcmp(type, "USB") && !strcmp(online, "0")) + { + LOG_MOUNT("USB offline\n"); + HandleMassStorageOnline(false); + } + } + } + + // handle retries + if (sRetriesPending) + HandleRetries(); + + // done handling events, so unlock the mutex + pthread_mutex_unlock(&sMutex); + } + + inotify_rm_watch(inotify_fd, id); + close(inotify_fd); + close(uevent_fd); + + return NULL; +} + +/***************************************************** + * + * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD + * + *****************************************************/ + +// Called to enable or disable USB mass storage support +void EnableMassStorage(boolean enable) +{ + pthread_mutex_lock(&sMutex); + + LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false")); + gMassStorageEnabled = enable; + MassStorageStateChanged(); + pthread_mutex_unlock(&sMutex); + } + +// Called to request that the specified mount point be mounted +void MountMedia(const char* mountPoint) +{ + MountPoint* mp = sMountPointList; + + LOG_MOUNT("MountMedia(%s)\n", mountPoint); + + pthread_mutex_lock(&sMutex); + while (mp) + { + if (strcmp(mp->mountPoint, mountPoint) == 0) + { + if (mp->state == kUnmountingForEject) + { + // handle the case where we try to remount before we actually unmounted + ClearRetries(mp, kMounted); + } + + // don't attempt to mount if mass storage is active + if (!MassStorageEnabledForMountPoint(mp)) + RequestMount(mp); + } + + mp = mp->next; + } + pthread_mutex_unlock(&sMutex); + } + +// Called to request that the specified mount point be unmounted +void UnmountMedia(const char* mountPoint) +{ + MountPoint* mp = sMountPointList; + + pthread_mutex_lock(&sMutex); + while (mp) + { + if (strcmp(mp->mountPoint, mountPoint) == 0) + RequestUnmount(mp, kUnmountingForEject); + + mp = mp->next; + } + pthread_mutex_unlock(&sMutex); +} + +boolean IsMassStorageEnabled() +{ + return gMassStorageEnabled; +} + +boolean IsMassStorageConnected() +{ + return gMassStorageConnected; +} + +/*********************************************** + * + * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP + * + ***********************************************/ + +void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms) +{ + MountPoint* newMountPoint; + + LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath); + // add a new MountPoint to the head of our linked list + newMountPoint = (MountPoint *)malloc(sizeof(MountPoint)); + newMountPoint->device = device; + newMountPoint->mountPoint = mountPoint; + newMountPoint->driverStorePath = driverStorePath; + newMountPoint->enableUms = enableUms; + newMountPoint->umsActive = false; + newMountPoint->state = kUnmounted; + newMountPoint->retryCount = 0; + + // add to linked list + newMountPoint->next = sMountPointList; + sMountPointList = newMountPoint; + return newMountPoint; +} + +int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size, + const char *mount_point, const char *crypt) +{ + MountPoint *mp = (MountPoint *) Mp; + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (!mp->asecHandles[i]) + break; + } + + if (i == ASEC_STORES_MAX) { + LOG_ERROR("Maximum # of ASEC stores exceeded\n"); + return -EINVAL; + } + + if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt))) + return -1; + + return 0; +} +static void MountDevices() +{ + MountPoint* mp = sMountPointList; + while (mp) + { + RequestMount(mp); + mp = mp->next; + } +} + +void StartAutoMounter() +{ + gExcludedPids[0] = getpid(); + + gMassStorageConnected = ReadMassStorageState(); + LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n"); + + MountDevices(); + pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL); +} diff --git a/mountd/MODULE_LICENSE_APACHE2 b/mountd/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mountd/MODULE_LICENSE_APACHE2 diff --git a/mountd/NOTICE b/mountd/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/mountd/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/mountd/ProcessKiller.c b/mountd/ProcessKiller.c new file mode 100644 index 0000000..e377774 --- /dev/null +++ b/mountd/ProcessKiller.c @@ -0,0 +1,222 @@ +/* + * 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. + */ + +/* +** mountd process killer +*/ + +#include "mountd.h" + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <poll.h> +#include <sys/stat.h> + + +static boolean ReadSymLink(const char* path, char* link) +{ + struct stat s; + int length; + + if (lstat(path, &s) < 0) + return false; + if ((s.st_mode & S_IFMT) != S_IFLNK) + return false; + + // we have a symlink + length = readlink(path, link, PATH_MAX - 1); + if (length <= 0) + return false; + link[length] = 0; + return true; +} + +static boolean PathMatchesMountPoint(const char* path, const char* mountPoint) +{ + int length = strlen(mountPoint); + if (length > 1 && strncmp(path, mountPoint, length) == 0) + { + // we need to do extra checking if mountPoint does not end in a '/' + if (mountPoint[length - 1] == '/') + return true; + // if mountPoint does not have a trailing slash, we need to make sure + // there is one in the path to avoid partial matches. + return (path[length] == 0 || path[length] == '/'); + } + + return false; +} + +static void GetProcessName(int pid, char buffer[PATH_MAX]) +{ + int fd; + sprintf(buffer, "/proc/%d/cmdline", pid); + fd = open(buffer, O_RDONLY); + if (fd < 0) { + strcpy(buffer, "???"); + } else { + int length = read(fd, buffer, PATH_MAX - 1); + buffer[length] = 0; + close(fd); + } +} + +static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint) +{ + DIR* dir; + struct dirent* de; + boolean fileOpen = false; + char path[PATH_MAX]; + char link[PATH_MAX]; + int parent_length; + + // compute path to process's directory of open files + sprintf(path, "/proc/%d/fd", pid); + dir = opendir(path); + if (!dir) + return false; + + // remember length of the path + parent_length = strlen(path); + // append a trailing '/' + path[parent_length++] = '/'; + + while ((de = readdir(dir)) != 0 && !fileOpen) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + // append the file name, after truncating to parent directory + path[parent_length] = 0; + strcat(path, de->d_name); + + if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has open file %s\n", name, pid, link); + fileOpen = true; + } + } + + closedir(dir); + return fileOpen; +} + +static boolean CheckFileMaps(int pid, const char* mountPoint) +{ + FILE* file; + char buffer[PATH_MAX + 100]; + boolean mapOpen = false; + + sprintf(buffer, "/proc/%d/maps", pid); + file = fopen(buffer, "r"); + if (!file) + return false; + + while (!mapOpen && fgets(buffer, sizeof(buffer), file)) + { + // skip to the path + const char* path = strchr(buffer, '/'); + if (path && PathMatchesMountPoint(path, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has open file map for %s\n", name, pid, path); + mapOpen = true; + } + } + + fclose(file); + return mapOpen; +} + +static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message) +{ + char path[PATH_MAX]; + char link[PATH_MAX]; + + sprintf(path, "/proc/%d/%s", pid, name); + if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has %s in %s\n", name, pid, message, mountPoint); + return true; + } + else + return false; +} + +static int get_pid(const char* s) +{ + int result = 0; + while (*s) { + if (!isdigit(*s)) return -1; + result = 10 * result + (*s++ - '0'); + } + return result; +} + +// hunt down and kill processes that have files open on the given mount point +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded) +{ + DIR* dir; + struct dirent* de; + + LOG_ERROR("KillProcessesWithOpenFiles %s\n", mountPoint); + dir = opendir("/proc"); + if (!dir) return; + + while ((de = readdir(dir)) != 0) + { + boolean killed = false; + // does the name look like a process ID? + int pid = get_pid(de->d_name); + if (pid == -1) continue; + + if (CheckFileDescriptorSymLinks(pid, mountPoint) // check for open files + || CheckFileMaps(pid, mountPoint) // check for mmap() + || CheckSymLink(pid, mountPoint, "cwd", "working directory") // check working directory + || CheckSymLink(pid, mountPoint, "root", "chroot") // check for chroot() + || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path + ) + { + int i; + boolean hit = false; + + for (i = 0; i < num_excluded; i++) { + if (pid == excluded[i]) { + LOG_ERROR("I just need a little more TIME captain!\n"); + hit = true; + break; + } + } + + if (!hit) { + LOG_ERROR("Killing process %d\n", pid); + kill(pid, (sigkill ? SIGKILL : SIGTERM)); + } + } + } + + closedir(dir); +} diff --git a/mountd/Server.c b/mountd/Server.c new file mode 100644 index 0000000..64459bd --- /dev/null +++ b/mountd/Server.c @@ -0,0 +1,313 @@ +/* + * 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. + */ + +/* +** mountd server support +*/ + +#include "mountd.h" +#include "ASEC.h" + +#include <cutils/properties.h> +#include <cutils/sockets.h> + +#include <pthread.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> + +#include <private/android_filesystem_config.h> + + +// current client file descriptor +static int sFD = -1; + +// to synchronize writing to client +static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER; + +// path for media that failed to mount before the runtime is connected +static char* sDeferredUnmountableMediaPath = NULL; + +// last asec msg before the runtime was connected +static char* sAsecDeferredMessage = NULL; +static char* sAsecDeferredArgument = NULL; + +static int Write(const char* message) +{ + int result = -1; + + pthread_mutex_lock(&sWriteMutex); + + LOG_SERVER("Write: %s\n", message); + if (sFD >= 0) + result = write(sFD, message, strlen(message) + 1); + + pthread_mutex_unlock(&sWriteMutex); + + return result; +} + +static int Write2(const char* message, const char* data) +{ + int result = -1; + + char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1); + if (!buffer) + { + LOG_ERROR("alloca failed in Write2\n"); + return -1; + } + + strcpy(buffer, message); + strcat(buffer, data); + return Write(buffer); +} + +static void SendStatus() +{ + Write(IsMassStorageConnected() ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED); + Write(IsMassStorageEnabled() ? MOUNTD_UMS_ENABLED : MOUNTD_UMS_DISABLED); +} + +static void DoCommand(const char* command) +{ + LOG_SERVER("DoCommand %s\n", command); + + if (strcmp(command, MOUNTD_ENABLE_UMS) == 0) + { + EnableMassStorage(true); + Write(MOUNTD_UMS_ENABLED); + } + else if (strcmp(command, MOUNTD_DISABLE_UMS) == 0) + { + EnableMassStorage(false); + Write(MOUNTD_UMS_DISABLED); + } + else if (strcmp(command, MOUNTD_SEND_STATUS) == 0) + { + SendStatus(); + } + else if (strncmp(command, MOUNTD_MOUNT_MEDIA, strlen(MOUNTD_MOUNT_MEDIA)) == 0) + { + const char* path = command + strlen(MOUNTD_MOUNT_MEDIA); + MountMedia(path); + } + else if (strncmp(command, MOUNTD_EJECT_MEDIA, strlen(MOUNTD_EJECT_MEDIA)) == 0) + { + const char* path = command + strlen(MOUNTD_EJECT_MEDIA); + UnmountMedia(path); + } + else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) { + LOG_ASEC("Got ASEC_CMD_ENABLE\n"); + // XXX: SAN: Impliment + } + else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) { + LOG_ASEC("Got ASEC_CMD_DISABLE\n"); + // XXX: SAN: Impliment + } + else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) { + LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n"); + // XXX: SAN: Impliment + } + else + LOGE("unknown command %s\n", command); +} + +int RunServer() +{ + int socket = android_get_control_socket(MOUNTD_SOCKET); + if (socket < 0) { + LOGE("Obtaining file descriptor for socket '%s' failed: %s", + MOUNTD_SOCKET, strerror(errno)); + return -1; + } + + if (listen(socket, 4) < 0) { + LOGE("Unable to listen on file descriptor '%d' for socket '%s': %s", + socket, MOUNTD_SOCKET, strerror(errno)); + return -1; + } + + while (1) + { + struct sockaddr addr; + socklen_t alen; + struct ucred cred; + socklen_t size; + + alen = sizeof(addr); + sFD = accept(socket, &addr, &alen); + if (sFD < 0) + continue; + + if (sDeferredUnmountableMediaPath) { + NotifyMediaState(sDeferredUnmountableMediaPath, MEDIA_UNMOUNTABLE, false); + free(sDeferredUnmountableMediaPath); + sDeferredUnmountableMediaPath = NULL; + } + + if (sAsecDeferredMessage) { + + if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0) + LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n"); + free(sAsecDeferredMessage); + free(sAsecDeferredArgument); + sAsecDeferredMessage = sAsecDeferredArgument = NULL; + } + + while (1) + { + char buffer[101]; + int result = read(sFD, buffer, sizeof(buffer) - 1); + if (result > 0) + { + int start = 0; + int i; + // command should be zero terminated, but just in case + buffer[result] = 0; + for (i = 0; i < result; i++) + { + if (buffer[i] == 0) + { + DoCommand(buffer + start); + start = i + 1; + } + } + } + else + { + close(sFD); + sFD = -1; + break; + } + } + } + + // should never get here + return 0; +} + +void SendMassStorageConnected(boolean connected) +{ + Write(connected ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED); +} + +void SendUnmountRequest(const char* path) +{ + Write2(MOUNTD_REQUEST_EJECT, path); +} + +void NotifyAsecState(AsecState state, const char *argument) +{ + const char *event = NULL; + const char *status = NULL; + boolean deferr = true;; + + switch (state) { + case ASEC_DISABLED: + event = ASEC_EVENT_DISABLED; + status = ASEC_STATUS_DISABLED; + break; + case ASEC_AVAILABLE: + event = ASEC_EVENT_AVAILABLE; + status = ASEC_STATUS_AVAILABLE; + break; + case ASEC_BUSY: + event = ASEC_EVENT_BUSY; + status = ASEC_STATUS_BUSY; + deferr = false; + break; + case ASEC_FAILED_INTERR: + event = ASEC_EVENT_FAILED_INTERR; + status = ASEC_STATUS_FAILED_INTERR; + break; + case ASEC_FAILED_NOMEDIA: + event = ASEC_EVENT_FAILED_NOMEDIA; + status = ASEC_STATUS_FAILED_NOMEDIA; + break; + case ASEC_FAILED_BADMEDIA: + event = ASEC_EVENT_FAILED_BADMEDIA; + status = ASEC_STATUS_FAILED_BADMEDIA; + break; + case ASEC_FAILED_BADKEY: + event = ASEC_EVENT_FAILED_BADKEY; + status = ASEC_STATUS_FAILED_BADKEY; + break; + default: + LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state); + return; + } + + property_set(ASEC_STATUS, status); + + int result = Write2(event, argument); + if ((result < 0) && deferr) { + if (sAsecDeferredMessage) + free(sAsecDeferredMessage); + sAsecDeferredMessage = strdup(event); + if (sAsecDeferredArgument) + free(sAsecDeferredArgument); + sAsecDeferredArgument = strdup(argument); + LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument); + } +} + +void NotifyMediaState(const char* path, MediaState state, boolean readOnly) +{ + const char* event = NULL; + const char* propertyValue = NULL; + + switch (state) { + case MEDIA_REMOVED: + event = MOUNTD_MEDIA_REMOVED; + propertyValue = EXTERNAL_STORAGE_REMOVED; + break; + case MEDIA_UNMOUNTED: + event = MOUNTD_MEDIA_UNMOUNTED; + propertyValue = EXTERNAL_STORAGE_UNMOUNTED; + break; + case MEDIA_MOUNTED: + event = (readOnly ? MOUNTD_MEDIA_MOUNTED_READ_ONLY : MOUNTD_MEDIA_MOUNTED); + propertyValue = (readOnly ? EXTERNAL_STORAGE_MOUNTED_READ_ONLY : EXTERNAL_STORAGE_MOUNTED); + break; + case MEDIA_SHARED: + event = MOUNTD_MEDIA_SHARED; + propertyValue = EXTERNAL_STORAGE_SHARED; + break; + case MEDIA_BAD_REMOVAL: + event = MOUNTD_MEDIA_BAD_REMOVAL; + propertyValue = EXTERNAL_STORAGE_BAD_REMOVAL; + break; + case MEDIA_UNMOUNTABLE: + event = MOUNTD_MEDIA_UNMOUNTABLE; + propertyValue = EXTERNAL_STORAGE_UNMOUNTABLE; + break; + default: + LOG_ERROR("unknown MediaState %d in NotifyMediaState\n", state); + return; + } + + property_set(EXTERNAL_STORAGE_STATE, propertyValue); + int result = Write2(event, path); + if (result < 0 && state == MEDIA_UNMOUNTABLE) { + + // if we cannot communicate with the runtime, defer this message until the runtime is available + sDeferredUnmountableMediaPath = strdup(path); + } +} diff --git a/mountd/logwrapper.c b/mountd/logwrapper.c new file mode 100644 index 0000000..69606ab --- /dev/null +++ b/mountd/logwrapper.c @@ -0,0 +1,154 @@ +/* + * 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 <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include "private/android_filesystem_config.h" +#include "cutils/log.h" + +int parent(const char *tag, int parent_read) { + int status; + char buffer[4096]; + + int a = 0; // start index of unprocessed data + int b = 0; // end index of unprocessed data + int sz; + while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { + + sz += b; + // Log one line at a time + for (b = 0; b < sz; b++) { + if (buffer[b] == '\r') { + buffer[b] = '\0'; + } else if (buffer[b] == '\n') { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + a = b + 1; + } + } + + if (a == 0 && b == sizeof(buffer) - 1) { + // buffer is full, flush + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + b = 0; + } else if (a != b) { + // Keep left-overs + b -= a; + memmove(buffer, &buffer[a], b); + a = 0; + } else { + a = 0; + b = 0; + } + + } + // Flush remaining data + if (a != b) { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + } + status = 0xAAAA; + if (wait(&status) != -1) { // Wait for child + if (WIFEXITED(status)) { + LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, + WEXITSTATUS(status)); + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag, + WTERMSIG(status)); + else if (WIFSTOPPED(status)) + LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag, + WSTOPSIG(status)); + } else + LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag, + strerror(errno), errno); + return -EAGAIN; +} + +void child(int argc, char* argv[]) { + // create null terminated argv_child array + char* argv_child[argc + 1]; + memcpy(argv_child, argv, argc * sizeof(char *)); + argv_child[argc] = NULL; + + // XXX: PROTECT FROM VIKING KILLER + if (execvp(argv_child[0], argv_child)) { + LOG(LOG_ERROR, "logwrapper", + "executing %s failed: %s\n", argv_child[0], strerror(errno)); + exit(-1); + } +} + +int logwrap(int argc, char* argv[]) +{ + pid_t pid; + + int parent_ptty; + int child_ptty; + char *child_devname = NULL; + + /* Use ptty instead of socketpair so that STDOUT is not buffered */ + parent_ptty = open("/dev/ptmx", O_RDWR); + if (parent_ptty < 0) { + LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty\n"); + return -errno; + } + + if (grantpt(parent_ptty) || unlockpt(parent_ptty) || + ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { + LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx\n"); + return -1; + } + + pid = fork(); + if (pid < 0) { + LOG(LOG_ERROR, "logwrapper", "Failed to fork\n"); + return -errno; + } else if (pid == 0) { + child_ptty = open(child_devname, O_RDWR); + if (child_ptty < 0) { + LOG(LOG_ERROR, "logwrapper", "Problem with child ptty\n"); + return -errno; + } + + // redirect stdout and stderr + close(parent_ptty); + dup2(child_ptty, 1); + dup2(child_ptty, 2); + close(child_ptty); + + child(argc, argv); + } else { + // switch user and group to "log" + // this may fail if we are not root, + // but in that case switching user/group is unnecessary + + // setgid(AID_LOG); + // setuid(AID_LOG); + + return parent(argv[0], parent_ptty); + } + + return 0; +} diff --git a/mountd/mountd.c b/mountd/mountd.c new file mode 100644 index 0000000..27ec8de --- /dev/null +++ b/mountd/mountd.c @@ -0,0 +1,174 @@ +/* + * 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. + */ + +/* +** mountd main program +*/ + +#include "mountd.h" + +#include <cutils/config_utils.h> +#include <cutils/cpu_info.h> +#include <cutils/properties.h> + +#include <sys/mount.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/capability.h> +#include <linux/prctl.h> + +#include <private/android_filesystem_config.h> + +#ifdef MOUNTD_LOG +FILE* logFile; +#endif + +struct asec_cfg { + const char *name; + const char *backing_file; + const char *size; + const char *mount_point; + const char *crypt; +}; + +static int ProcessAsecData(cnode *node, struct asec_cfg *stores, int idx) +{ + cnode *child = node->first_child; + const char *name = NULL; + const char *file = NULL; + const char *size = NULL; + const char *mp = NULL; + const char *crypt = NULL; + + LOG_ASEC("ProcessAsecData(%s, %p, %d)\n", node->name, stores, idx); + + while (child) { + if (!strcmp(child->name, "name")) + name = child->value; + else if (!strcmp(child->name, "backing_file")) + file = child->value; + else if (!strcmp(child->name, "size")) + size = child->value; + else if (!strcmp(child->name, "mount_point")) + mp = child->value; + else if (!strcmp(child->name, "crypt")) + crypt = child->value; + child = child->next; + } + + if (!name || !file || !size || !mp || !crypt) { + LOG_ERROR("Missing required token from config. Skipping ASEC volume\n"); + return -1; + } else if (idx == ASEC_STORES_MAX) { + LOG_ERROR("Maximum # of ASEC stores already defined\n"); + return -1; + } + + stores[idx].name = name; + stores[idx].backing_file = file; + stores[idx].size = size; + stores[idx].mount_point = mp; + stores[idx].crypt = crypt; + return ++idx; +} + +static void ReadConfigFile(const char* path) +{ + cnode* root = config_node("", ""); + cnode* node; + + config_load_file(root, path); + node = root->first_child; + + while (node) + { + if (strcmp(node->name, "mount") == 0) + { + const char* block_device = NULL; + const char* mount_point = NULL; + const char* driver_store_path = NULL; + boolean enable_ums = false; + cnode* child = node->first_child; + struct asec_cfg asec_stores[ASEC_STORES_MAX]; + int asec_idx = 0; + + memset(asec_stores, 0, sizeof(asec_stores)); + + while (child) + { + const char* name = child->name; + const char* value = child->value; + + if (!strncmp(name, "asec_", 5)) { + int rc = ProcessAsecData(child, asec_stores, asec_idx); + if (rc < 0) { + LOG_ERROR("Error processing ASEC cfg data\n"); + } else + asec_idx = rc; + } else if (strcmp(name, "block_device") == 0) + block_device = value; + else if (strcmp(name, "mount_point") == 0) + mount_point = value; + else if (strcmp(name, "driver_store_path") == 0) + driver_store_path = value; + else if (strcmp(name, "enable_ums") == 0 && + strcmp(value, "true") == 0) + enable_ums = true; + + child = child->next; + } + + // mount point and removable fields are optional + if (block_device && mount_point) + { + void *mp = AddMountPoint(block_device, mount_point, driver_store_path, enable_ums); + int i; + + for (i = 0; i < asec_idx; i++) { + AddAsecToMountPoint(mp, asec_stores[i].name, asec_stores[i].backing_file, + asec_stores[i].size, asec_stores[i].mount_point, + asec_stores[i].crypt); + } + } + } + + node = node->next; + } +} + +int main(int argc, char* argv[]) +{ + const char* configPath = "/system/etc/mountd.conf"; + int i; + + for (i = 1; i < argc; i++) + { + const char* arg = argv[i]; + + if (strcmp(arg, "-f") == 0) + { + if (i < argc - 1) + configPath = argv[++i]; + } + } + + ReadConfigFile(configPath); + StartAutoMounter(); + return RunServer(); +} diff --git a/mountd/mountd.h b/mountd/mountd.h new file mode 100644 index 0000000..c4bc91d --- /dev/null +++ b/mountd/mountd.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOUNTD_H__ +#define MOUNTD_H__ + +#define LOG_TAG "mountd" +#include "cutils/log.h" + +#include "ASEC.h" + +typedef int boolean; +enum { + false = 0, + true = 1 +}; + +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) + +// Set this for logging error messages +#define ENABLE_LOG_ERROR + +// set this to log automounter events +#define ENABLE_LOG_MOUNT + +// set this to log server events +//#define ENABLE_LOG_SERVER + +// set this to log ASEC events +#define ENABLE_LOG_ASEC + +#ifdef ENABLE_LOG_ERROR +#define LOG_ERROR(fmt, args...) \ + { LOGE(fmt , ## args); } +#else +#define LOG_ERROR(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ERROR */ + +#ifdef ENABLE_LOG_MOUNT +#define LOG_MOUNT(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_MOUNT(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_MOUNT */ + +#ifdef ENABLE_LOG_SERVER +#define LOG_SERVER(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_SERVER(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_SERVER */ + +#ifdef ENABLE_LOG_ASEC +#define LOG_ASEC(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_ASEC(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ASEC */ + + +typedef enum MediaState { + // no media in SD card slot + MEDIA_REMOVED, + + // media in SD card slot, but not mounted + MEDIA_UNMOUNTED, + + // media in SD card slot and mounted at its mount point + MEDIA_MOUNTED, + + // media in SD card slot, unmounted, and shared as a mass storage device + MEDIA_SHARED, + + // media was removed from SD card slot, but mount point was not unmounted + // this state is cleared after the mount point is unmounted + MEDIA_BAD_REMOVAL, + + // media in SD card slot could not be mounted (corrupt file system?) + MEDIA_UNMOUNTABLE, +} MediaState; + +// socket name for connecting to mountd +#define MOUNTD_SOCKET "mountd" + +// mountd commands +// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java +#define MOUNTD_ENABLE_UMS "enable_ums" +#define MOUNTD_DISABLE_UMS "disable_ums" +#define MOUNTD_SEND_STATUS "send_status" + +// these commands should contain a mount point following the colon +#define MOUNTD_MOUNT_MEDIA "mount_media:" +#define MOUNTD_EJECT_MEDIA "eject_media:" + +// mountd events +// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java +#define MOUNTD_UMS_ENABLED "ums_enabled" +#define MOUNTD_UMS_DISABLED "ums_disabled" +#define MOUNTD_UMS_CONNECTED "ums_connected" +#define MOUNTD_UMS_DISCONNECTED "ums_disconnected" + +// these events correspond to the states in the MediaState enum. +// a path to the mount point follows the colon. +#define MOUNTD_MEDIA_REMOVED "media_removed:" +#define MOUNTD_MEDIA_UNMOUNTED "media_unmounted:" +#define MOUNTD_MEDIA_MOUNTED "media_mounted:" +#define MOUNTD_MEDIA_MOUNTED_READ_ONLY "media_mounted_ro:" +#define MOUNTD_MEDIA_SHARED "media_shared:" +#define MOUNTD_MEDIA_BAD_REMOVAL "media_bad_removal:" +#define MOUNTD_MEDIA_UNMOUNTABLE "media_unmountable:" + +// this event sent to request unmount for media mount point +#define MOUNTD_REQUEST_EJECT "request_eject:" + +// system properties +// these must match the corresponding strings in //device/java/android/android/os/Environment.java +#define EXTERNAL_STORAGE_STATE "EXTERNAL_STORAGE_STATE" +#define EXTERNAL_STORAGE_REMOVED "removed" +#define EXTERNAL_STORAGE_UNMOUNTED "unmounted" +#define EXTERNAL_STORAGE_MOUNTED "mounted" +#define EXTERNAL_STORAGE_MOUNTED_READ_ONLY "mounted_ro" +#define EXTERNAL_STORAGE_SHARED "shared" +#define EXTERNAL_STORAGE_BAD_REMOVAL "bad_removal" +#define EXTERNAL_STORAGE_UNMOUNTABLE "unmountable" + +// AutoMount.c + +boolean IsMassStorageEnabled(); +boolean IsMassStorageConnected(); + +void MountMedia(const char* mountPoint); +void UnmountMedia(const char* mountPoint); +void EnableMassStorage(boolean enable); + +// call this before StartAutoMounter() to add a mount point to monitor +void *AddMountPoint(const char* device, const char* mountPoint, const char* driverStorePath, + boolean enableUms); + +int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, + const char *size, const char *mount_point, const char *crypt); + +// start automounter thread +void StartAutoMounter(); + +// check /proc/mounts for mounted file systems, and notify mount or unmount for any that are in our automount list +void NotifyExistingMounts(); + + +// ASEC.c + +void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile, + const char *Size, const char *DstPath, const char *Crypt); +int AsecStart(void *Handle); +int AsecStop(void *Handle); +void AsecDeinit(void *Handle); +boolean AsecIsStarted(void *Handle); +const char *AsecMountPoint(void *Handle); + +// ProcessKiller.c + +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, pid_t *excluded, int num_excluded); + +// logwrapper.c +int logwrap(int argc, char* argv[]); + +// Server.c + +int RunServer(); +void SendMassStorageConnected(boolean connected); +void SendUnmountRequest(const char* path); +void NotifyMediaState(const char* path, MediaState state, boolean readOnly); +void NotifyAsecState(AsecState state, const char *argument); +#endif // MOUNTD_H__ diff --git a/netcfg/Android.mk b/netcfg/Android.mk new file mode 100644 index 0000000..949f417 --- /dev/null +++ b/netcfg/Android.mk @@ -0,0 +1,16 @@ +ifneq ($(BUILD_TINY_ANDROID),true) +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= netcfg.c +LOCAL_MODULE:= netcfg + +#LOCAL_FORCE_STATIC_EXECUTABLE := true +#LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +#LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) +#LOCAL_STATIC_LIBRARIES := libcutils libc + +LOCAL_SHARED_LIBRARIES := libc libnetutils + +include $(BUILD_EXECUTABLE) +endif diff --git a/netcfg/MODULE_LICENSE_APACHE2 b/netcfg/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/netcfg/MODULE_LICENSE_APACHE2 diff --git a/netcfg/NOTICE b/netcfg/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/netcfg/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/netcfg/netcfg.c b/netcfg/netcfg.c new file mode 100644 index 0000000..fc9cf48 --- /dev/null +++ b/netcfg/netcfg.c @@ -0,0 +1,175 @@ +/* system/bin/netcfg/netcfg.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 <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <dirent.h> + +static int verbose = 0; + +int ifc_init(); +void ifc_close(); +int ifc_up(char *iname); +int ifc_down(char *iname); +int ifc_remove_host_routes(char *iname); +int ifc_remove_default_route(char *iname); +int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags); +int do_dhcp(char *iname); + +void die(const char *reason) +{ + perror(reason); + exit(1); +} + +const char *ipaddr(unsigned addr) +{ + static char buf[32]; + + sprintf(buf,"%d.%d.%d.%d", + addr & 255, + ((addr >> 8) & 255), + ((addr >> 16) & 255), + (addr >> 24)); + return buf; +} + +void usage(void) +{ + fprintf(stderr,"usage: netcfg [<interface> {dhcp|up|down}]\n"); + exit(1); +} + +int dump_interface(const char *name) +{ + unsigned addr, mask, flags; + + if(ifc_get_info(name, &addr, &mask, &flags)) { + return 0; + } + + printf("%-8s %s ", name, flags & 1 ? "UP " : "DOWN"); + printf("%-16s", ipaddr(addr)); + printf("%-16s", ipaddr(mask)); + printf("0x%08x\n", flags); + return 0; +} + +int dump_interfaces(void) +{ + DIR *d; + struct dirent *de; + + d = opendir("/sys/class/net"); + if(d == 0) return -1; + + while((de = readdir(d))) { + if(de->d_name[0] == '.') continue; + dump_interface(de->d_name); + } + closedir(d); + return 0; +} + +struct +{ + const char *name; + int nargs; + void *func; +} CMDS[] = { + { "dhcp", 1, do_dhcp }, + { "up", 1, ifc_up }, + { "down", 1, ifc_down }, + { "flhosts", 1, ifc_remove_host_routes }, + { "deldefault", 1, ifc_remove_default_route }, + { 0, 0, 0 }, +}; + +static int call_func(void *_func, unsigned nargs, char **args) +{ + switch(nargs){ + case 1: { + int (*func)(char *a0) = _func; + return func(args[0]); + } + case 2: { + int (*func)(char *a0, char *a1) = _func; + return func(args[0], args[1]); + } + case 3: { + int (*func)(char *a0, char *a1, char *a2) = _func; + return func(args[0], args[1], args[2]); + } + default: + return -1; + } +} + +int main(int argc, char **argv) +{ + char *iname; + int n; + + if(ifc_init()) { + die("Cannot perform requested operation"); + } + + if(argc == 1) { + int result = dump_interfaces(); + ifc_close(); + return result; + } + + if(argc < 3) usage(); + + iname = argv[1]; + if(strlen(iname) > 16) usage(); + + argc -= 2; + argv += 2; + while(argc > 0) { + for(n = 0; CMDS[n].name; n++){ + if(!strcmp(argv[0], CMDS[n].name)) { + char *cmdname = argv[0]; + int nargs = CMDS[n].nargs; + + argv[0] = iname; + if(argc < nargs) { + fprintf(stderr, "not enough arguments for '%s'\n", cmdname); + ifc_close(); + exit(1); + } + if(call_func(CMDS[n].func, nargs, argv)) { + fprintf(stderr, "action '%s' failed (%s)\n", cmdname, strerror(errno)); + ifc_close(); + exit(1); + } + argc -= nargs; + argv += nargs; + goto done; + } + } + fprintf(stderr,"no such action '%s'\n", argv[0]); + usage(); + done: + ; + } + ifc_close(); + + return 0; +} diff --git a/rootdir/Android.mk b/rootdir/Android.mk new file mode 100644 index 0000000..b2fe8cf --- /dev/null +++ b/rootdir/Android.mk @@ -0,0 +1,58 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# files that live under /system/etc/... + +copy_from := \ + etc/dbus.conf \ + etc/init.goldfish.sh \ + etc/hosts + +dont_copy := \ + etc/init.gprs-pppd \ + etc/ppp/chap-secrets \ + etc/ppp/ip-down \ + etc/ppp/ip-up + +copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from)) +copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from)) + +$(copy_to) : PRIVATE_MODULE := system_etcdir +$(copy_to) : $(TARGET_OUT)/% : $(LOCAL_PATH)/% | $(ACP) + $(transform-prebuilt-to-target) + +ALL_PREBUILT += $(copy_to) + + +# files that live under /... + +# Only copy init.rc if the target doesn't have its own. +ifneq ($(TARGET_PROVIDES_INIT_RC),true) +file := $(TARGET_ROOT_OUT)/init.rc +$(file) : $(LOCAL_PATH)/init.rc | $(ACP) + $(transform-prebuilt-to-target) +ALL_PREBUILT += $(file) +endif + +file := $(TARGET_ROOT_OUT)/init.goldfish.rc +$(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP) + $(transform-prebuilt-to-target) +ALL_PREBUILT += $(file) + + +# create some directories (some are mount points) +DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \ + sbin \ + dev \ + proc \ + sys \ + system \ + data \ + ) \ + $(TARGET_OUT_DATA) + +$(DIRS): + @echo Directory: $@ + @mkdir -p $@ + +ALL_PREBUILT += $(DIRS) diff --git a/rootdir/etc/dbus.conf b/rootdir/etc/dbus.conf new file mode 100644 index 0000000..75586b9 --- /dev/null +++ b/rootdir/etc/dbus.conf @@ -0,0 +1,27 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- Our well-known bus type, do not change this --> + <type>system</type> + + <!-- Only allow socket-credentials-based authentication --> + <auth>EXTERNAL</auth> + + <!-- Only listen on a local socket. (abstract=/path/to/socket + means use abstract namespace, don't really create filesystem + file; only Linux supports this. Use path=/whatever on other + systems.) --> + <listen>unix:path=/dev/socket/dbus</listen> + + <!-- Allow everything, D-Bus socket is protected by unix filesystem + permissions --> + <policy context="default"> + <allow send_interface="*"/> + <allow receive_interface="*"/> + <allow own="*"/> + <allow user="*"/> + <allow send_requested_reply="true"/> + <allow receive_requested_reply="true"/> + </policy> +</busconfig> diff --git a/rootdir/etc/hosts b/rootdir/etc/hosts new file mode 100644 index 0000000..99848f6 --- /dev/null +++ b/rootdir/etc/hosts @@ -0,0 +1 @@ +127.0.0.1 localhost diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc new file mode 100644 index 0000000..96480f3 --- /dev/null +++ b/rootdir/etc/init.goldfish.rc @@ -0,0 +1,54 @@ +on boot + setprop ARGH ARGH + setprop net.eth0.dns1 10.0.2.3 + setprop net.gprs.local-ip 10.0.2.15 + setprop ro.radio.use-ppp no + setprop ro.build.product generic + setprop ro.product.device generic + +# fake some battery state + setprop status.battery.state Slow + setprop status.battery.level 5 + setprop status.battery.level_raw 50 + setprop status.battery.level_scale 9 + +# disable some daemons the emulator doesn't want + stop dund + stop akmd + + setprop ro.setupwizard.mode EMULATOR + +# enable Google-specific location features, +# like NetworkLocationProvider and LocationCollector + setprop ro.com.google.locationfeatures 1 + +# For the emulator, which bypasses Setup Wizard, you can specify +# account info for the device via these two properties. Google +# Login Service will insert these accounts into the database when +# it is created (ie, after a data wipe). +# +# setprop ro.config.hosted_account username@hosteddomain.org:password +# setprop ro.config.google_account username@gmail.com:password +# +# You MUST have a Google account on the device, and you MAY +# additionally have a hosted account. No other configuration is +# supported, and arbitrary breakage may result if you specify +# something else. + +service goldfish-setup /system/etc/init.goldfish.sh + oneshot + +service qemud /system/bin/qemud + socket qemud_gsm stream 666 + socket qemud_gps stream 666 + socket qemud_control stream 666 + oneshot + +# -Q is a special logcat option that forces the +# program to check wether it runs on the emulator +# if it does, it redirects its output to the device +# named by the androidboot.console kernel option +# if not, is simply exit immediately + +service goldfish-logcat /system/bin/logcat -Q + oneshot diff --git a/rootdir/etc/init.goldfish.sh b/rootdir/etc/init.goldfish.sh new file mode 100755 index 0000000..0eb0154 --- /dev/null +++ b/rootdir/etc/init.goldfish.sh @@ -0,0 +1,39 @@ +#!/system/bin/sh + +ifconfig eth0 10.0.2.15 netmask 255.255.255.0 up +route add default gw 10.0.2.2 dev eth0 + +qemud=`getprop.ro.kernel.android.qemud` +if test -z "$qemud"; then + radio_ril=`getprop ro.kernel.android.ril` + if test -z "$radio_ril"; then + # no need for the radio interface daemon + # telephony is entirely emulated in Java + setprop ro.radio.noril yes + stop ril-daemon + fi +fi + +num_dns=`getprop ro.kernel.android.ndns` +case "$num_dns" in + 2) setprop net.eth0.dns2 10.0.2.4 + ;; + 3) setprop net.eth0.dns2 10.0.2.4 + setprop net.eth0.dns3 10.0.2.5 + ;; + 4) setprop net.eth0.dns2 10.0.2.4 + setprop net.eth0.dns3 10.0.2.5 + setprop net.eth0.dns4 10.0.2.6 + ;; +esac + +# disable boot animation for a faster boot sequence when needed +boot_anim=`getprop ro.kernel.android.bootanim` +case "$boot_anim" in + 0) setprop debug.sf.nobootanimation 1 + ;; +esac + +# this line doesn't really do anything useful. however without it the +# previous setprop doesn't seem to apply for some really odd reason +setprop ro.qemu.init.completed 1 diff --git a/rootdir/etc/init.gprs-pppd b/rootdir/etc/init.gprs-pppd new file mode 100755 index 0000000..521eec9 --- /dev/null +++ b/rootdir/etc/init.gprs-pppd @@ -0,0 +1,23 @@ +#!/system/bin/sh +# An unforunate wrapper script +# so that the exit code of pppd may be retrieved + + +# this is a workaround for issue #651747 +#trap "/system/bin/sleep 1;exit 0" TERM + + +PPPD_PID= + +/system/bin/setprop "net.gprs.ppp-exit" "" + +/system/bin/log -t pppd "Starting pppd" + +/system/bin/pppd $* + +PPPD_EXIT=$? +PPPD_PID=$! + +/system/bin/log -t pppd "pppd exited with $PPPD_EXIT" + +/system/bin/setprop "net.gprs.ppp-exit" "$PPPD_EXIT" diff --git a/rootdir/etc/init.testmenu b/rootdir/etc/init.testmenu new file mode 100755 index 0000000..7ae16d5 --- /dev/null +++ b/rootdir/etc/init.testmenu @@ -0,0 +1,322 @@ +#!/system/bin/sh + +atdev=/dev/omap_csmi_tty0 +pppdev=/dev/omap_csmi_tty1 + +n1=`cat /data/phoneentry1 2>/dev/null` +n2=`cat /data/phoneentry2 2>/dev/null` +n3=`cat /data/phoneentry3 2>/dev/null` +n1=${n1:-"*#06#"} +n2=${n2:-"*#06#"} +n3=${n3:-"*#06#"} +phoneoutputpid= +eventoutputpid= +notifypid= +notifytoggle=false +pppdpid= +powerdidletime=120 + +# map phone specific keys +setkey -k 0xe4 -v 0x23 # map # +setkey -k 0xe3 -v 0x2a # map * +setkey -k 231 -v 513 # map send to newline +#setkey -k 0x67 -v 0x20b # map up to scroll back +#setkey -k 0x6c -v 0x20a # map down to scroll forward +setkey -k 0x73 -v 0x20b # map volume up to scroll back +setkey -k 0x72 -v 0x20a # map volume down to scroll forward +setkey -k 0x60 -v 0x211 # map PoC to next console + +# tuttle keys +setkey -k 0x38 -v 0x703 # map leftalt to alt +setkey -k 0x9b -v 0x703 # map mail to alt +setkey -t 8 -k 0x9b -v 0x703 # map alt-mail to alt +setkey -t 8 -k 0x10 -v 0x21 # map alt-q to ! +setkey -t 8 -k 0x11 -v 0x31 # map alt-w to 1 +setkey -t 8 -k 0x12 -v 0x32 # map alt-e to 2 +setkey -t 8 -k 0x13 -v 0x33 # map alt-r to 3 +setkey -t 8 -k 0x14 -v 0x2b # map alt-t to + +setkey -t 8 -k 0x15 -v 0x28 # map alt-y to ( +setkey -t 8 -k 0x16 -v 0x29 # map alt-u to ) +setkey -t 8 -k 0x17 -v 0x2d # map alt-i to - +setkey -t 8 -k 0x18 -v 0x5f # map alt-o to _ +setkey -t 8 -k 0x19 -v 0x22 # map alt-p to " +setkey -t 8 -k 0x1e -v 0x23 # map alt-a to # +setkey -t 8 -k 0x1f -v 0x34 # map alt-s to 4 +setkey -t 8 -k 0x20 -v 0x35 # map alt-d to 5 +setkey -t 8 -k 0x21 -v 0x36 # map alt-f to 6 +setkey -t 8 -k 0x22 -v 0x2f # map alt-g to / +setkey -t 8 -k 0x23 -v 0x3f # map alt-h to ? +setkey -t 8 -k 0x24 -v 0xa3 # map alt-j to pound +setkey -t 8 -k 0x25 -v 0x24 # map alt-k to $ +setkey -t 8 -k 0x2c -v 0x2a # map alt-z to * +setkey -t 8 -k 0x2d -v 0x37 # map alt-x to 7 +setkey -t 8 -k 0x2e -v 0x38 # map alt-c to 8 +setkey -t 8 -k 0x2f -v 0x39 # map alt-v to 9 +setkey -t 8 -k 0x30 -v 0x7c # map alt-b to | +setkey -t 8 -k 0x31 -v 0x40 # map alt-n to @ +setkey -t 8 -k 0x32 -v 0x3d # map alt-m to = +setkey -t 8 -k 0x33 -v 0x3b # map alt-, to ; +setkey -t 8 -k 0x34 -v 0x3a # map alt-. to : +setkey -t 8 -k 0x0f -v 0x30 # map alt-tab to 0 +setkey -t 8 -k 0x67 -v 0x20b # map alt-up to scroll back +setkey -t 8 -k 0x6c -v 0x20a # map alt-down to scroll forward + +while true +do + echo + echo "------------------------------" + echo " 1: init commands" + echo " 2: call commands" + echo " 3: misc phone" + echo " 4: phone debug output" + echo " 5: test data connection" + echo " 6: start runtime" + echo " 7: start runtime w/output" + echo " 8: stop runtime" + echo " 9: misc" + echo -n ": " + while true + do + c=`readtty -t 50 -f -a 1234567890#` + case "$c" in + "" ) ;; + * ) break; + esac + done + echo Got key -$c- + case $c in + "1" ) + while true; do + echo + echo "------------------------------" + echo " 1: Print phone output" + echo " 2: ATQ0V1E1+CMEE=2;+CREG=0" + echo " 3: AT+CFUN=1" + echo " 4: AT+COPS=0" + echo " 5: AT+CREG?" + echo " 6: Stop phone output" + echo " 0: back" + echo -n ": " + c=`readtty -f -a 1234560#` + echo Got key -$c- + case "$c" in + "1" ) kill $phoneoutputpid; cat $atdev & phoneoutputpid=$! ;; + "2" ) echo -e "ATQ0V1E1+CMEE=2;+CREG=0\r" >$atdev;; + "3" ) echo -e "AT+CFUN=1\r" >$atdev;; + "4" ) echo -e "AT+COPS=0\r" >$atdev;; + "5" ) echo -e "AT+CREG?\r" >$atdev;; + "6" ) kill $phoneoutputpid; phoneoutputpid= ;; + "0" ) break;; + esac + done + ;; + "2" ) + while true; do + echo + echo "------------------------------" + echo " 1: Dial: ATD $n1;" + echo " 2: Dial: ATD $n2;" + echo " 3: Dial: ATD $n3;" + echo " 4: Set number for 1" + echo " 5: Set number for 2" + echo " 6: Set number for 3" + echo " 7: Dial: ATD ...;" + echo " 8: Hang up: ATH" + echo " 9: Answer: ATA" + echo " 0: back" + echo -n ": " + c=`readtty -f -a 1234567890#` + echo Got key -$c- + case "$c" in + "1" ) echo "Dialing $n1"; echo -e "ATD $n1;\r" >$atdev;; + "2" ) echo "Dialing $n2"; echo -e "ATD $n2;\r" >$atdev;; + "3" ) echo "Dialing $n3"; echo -e "ATD $n3;\r" >$atdev;; + "4" ) echo -n "Number: "; read n1; echo $n1 >/data/phoneentry1;; + "5" ) echo -n "Number: "; read n2; echo $n2 >/data/phoneentry2;; + "6" ) echo -n "Number: "; read n3; echo $n3 >/data/phoneentry3;; + "7" ) echo -n "Number: "; read n; echo "Dialing $n"; echo -e "ATD $n;\r" >$atdev;; + "8" ) echo -e "ATH\r" >$atdev;; + "9" ) echo -e "ATA\r" >$atdev;; + "0" ) break;; + esac + done + ;; + "3" ) + while true; do + echo + echo "------------------------------" + echo " 1: Save FFS data" + echo " 2: Load user FFS data" + echo " 3: Load system FFS data" + echo " 4: Reset FFS data" + echo " 5: Set uplink gain" + echo " 6: Set echo" + echo " 7: cat /dev/omap_csmi_battery_t" + echo " 8: cat /dev/omap_csmi_htc" + echo " 0: back" + echo -n ": " + c=`readtty -f -a 123456780#` + echo Got key -$c- + case "$c" in + "1" ) cat /dev/omap_csmi_ffs >/data/ffsdata;; + "2" ) cat /data/ffsdata >/dev/omap_csmi_ffs;; + "3" ) cat /system/ffsdata >/dev/omap_csmi_ffs;; + "4" ) echo - >/dev/omap_csmi_ffs;; + "5" ) + echo -n "Gain: "; read g; + echo gu$g >/tmp/gain; + cat /tmp/gain 2>/dev/null >/dev/omap_csmi_audio_tes + ;; + "6" ) + echo -n "Echo param (hex): "; read e; + echo "e0x$e" >/tmp/echo; + cat /tmp/echo 2>/dev/null >/dev/omap_csmi_audio_tes + ;; + "7" ) cat /dev/omap_csmi_battery_t;; + "8" ) cat /dev/omap_csmi_htc;; + "0" ) break;; + esac + done + ;; + "4" ) + while true; do + echo + echo "------------------------------" + echo " 1: Toggle debug I/O" + echo " 2: Toggle debug Flow" + echo " 3: Toggle debug Interrupt" + echo " 4: Toggle debug Info" + echo " 5: Toggle GSM run state" + echo " 6: Clear GSM data area" + echo " 0: back" + echo -n ": " + c=`readtty -f -a 1234560#` + echo Got key -$c- + case "$c" in + "1" ) echo -n "i" >/sys/devices/system/omap_csmi/debug;; + "2" ) echo -n "f" >/sys/devices/system/omap_csmi/debug;; + "3" ) echo -n "I" >/sys/devices/system/omap_csmi/debug;; + "4" ) echo -n "F" >/sys/devices/system/omap_csmi/debug;; + "5" ) echo -n "s" >/sys/devices/system/omap_csmi/debug;; + "6" ) echo -n "c" >/sys/devices/system/omap_csmi/debug;; + "0" ) break;; + esac + done + ;; + "5" ) + while true; do + echo + echo "------------------------------" + echo " 1: Start pppd - userspace" + echo " 2: Start pppd - kernel" + echo " 3: Start pppd - kernel <at1" + echo " 4: Configure ppp data to at2" + echo " 5: Test with HTTP GET" + echo " 6: Kill pppd" + echo " 0: back" + echo -n ": " + c=`readtty -f -a 1234560#` + echo Got key -$c- + case "$c" in + "1" ) kill $pppdpid; pppd notty < $pppdev > $pppdev & pppdpid=$!;; + "2" ) kill $pppdpid; pppd nodetach $pppdev & pppdpid=$!;; + "3" ) kill &pppdpid; pppd nodetach $pppdev connect "sh -c \"chat -v -f /etc/ppp/connect-data <$atdev >$atdev\"" & pppdpid=$!;; + "4" ) echo -e 'AT%DATA=2,"UART",1,,"SER","UART",0\r' >$atdev;; + "5" ) test-data-connection;; + "6" ) kill $pppdpid; pppdpid=;; + "0" ) break;; + esac + done + ;; + "6" ) + echo + echo ------------------------ + echo Starting android runtime + echo ------------------------ + start + ;; + "7" ) + echo + echo ------------------------ + echo Starting android runtime + echo ------------------------ + if exists /data/singleproc + then + single_process="-s" + else + single_process="" + fi + start runtime $single_process + ;; + "8" ) + stop + ;; + "9" ) + while true; do + echo + echo "------------------------------" + echo " 1: Print events" + echo " 2: Stop event output" + if $notifytoggle + then + echo " 3: stop notify" + else + echo " 3: notify /sys/android_power" + fi + echo " 4: start powerd" + echo " 5: start powerd verbose" + echo " 6: stop powerd" + echo " 7: set powerd idletime ($powerdidletime)" + echo " 8: start multitap shell" + if exists /data/singleproc + then + echo " 9: enable multiprocess" + else + echo " 9: disable multiprocess" + fi + echo " c: start shell" + echo " 0: back" + echo -n ": " + c=`readtty -f -a 1234567890c#` + echo Got key -$c- + case "$c" in + "1" ) kill $eventoutputpid; getevent & eventoutputpid=$! ;; + "2" ) kill $eventoutputpid; eventoutputpid= ;; + "3" ) + if $notifytoggle + then + kill $notifypid + notifypid= + notifytoggle=false + else + kill $notifypid + notify -m 0x00000002 -c 0 -p -v 0 -w 30 /sys/android_power & + notifypid=$! + notifytoggle=true + fi + ;; + "4" ) start powerd -i $powerdidletime ;; + "5" ) start powerd -i $powerdidletime -v ;; + "6" ) stop powerd ;; + "7" ) echo -n "Idle time (seconds): "; read powerdidletime ;; + "8" ) + readtty -f -p -t 10 -e "[ ~" | sh -i + ;; + "9" ) + if exists /data/singleproc + then + echo "Enabling multiprocess environment." + rm /data/singleproc + else + echo "Disabling multiprocess environment." + echo >/data/singleproc "true" + fi + ;; + "c" ) sh -i <>/dev/tty0 1>&0 2>&1 ;; + "0" ) break;; + esac + done + ;; + esac +done + diff --git a/rootdir/etc/mountd.conf b/rootdir/etc/mountd.conf new file mode 100644 index 0000000..094a2c7 --- /dev/null +++ b/rootdir/etc/mountd.conf @@ -0,0 +1,19 @@ +## mountd configuration file + +## add a mount entry for each mount point to be managed by mountd +mount { + ## root block device with partition map or raw FAT file system + block_device /dev/block/mmcblk0 + + ## mount point for block device + mount_point /sdcard + + ## true if this mount point can be shared via USB mass storage + enable_ums true + + ## path to the UMS driver file for specifying the block device path + ## use this for the mass_storage function driver + driver_store_path /sys/devices/platform/usb_mass_storage/lun0/file + ## use this for android_usb composite gadget driver + ##driver_store_path /sys/devices/platform/msm_hsusb/gadget/lun0/file +} diff --git a/rootdir/etc/ppp/chap-secrets b/rootdir/etc/ppp/chap-secrets new file mode 100644 index 0000000..6546b0f --- /dev/null +++ b/rootdir/etc/ppp/chap-secrets @@ -0,0 +1,2 @@ +* * bogus + diff --git a/rootdir/etc/ppp/ip-down b/rootdir/etc/ppp/ip-down new file mode 100755 index 0000000..672fa1e --- /dev/null +++ b/rootdir/etc/ppp/ip-down @@ -0,0 +1,14 @@ +#!/system/bin/sh +case $1 in + ppp1) + echo 0 > /proc/sys/net/ipv4/ip_forward; + ;; +esac + +# Use interface name if linkname is not available +NAME=${LINKNAME:-"$1"} + +/system/bin/setprop "net.$NAME.dns1" "$DNS1" +/system/bin/setprop "net.$NAME.dns2" "$DNS2" +/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" +/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" diff --git a/rootdir/etc/ppp/ip-up b/rootdir/etc/ppp/ip-up new file mode 100755 index 0000000..cb2d577 --- /dev/null +++ b/rootdir/etc/ppp/ip-up @@ -0,0 +1,24 @@ +#!/system/bin/sh +case $1 in + ppp1) + /android/bin/iptables --flush; + /android/bin/iptables --table nat --flush; + /android/bin/iptables --delete-chain; + /android/bin/iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE; + /android/bin/iptables --append FORWARD --in-interface ppp1 -j ACCEPT; + echo 0 > /proc/sys/net/ipv4/ip_forward; + echo 1 > /proc/sys/net/ipv4/ip_forward; + ;; + ppp0) + /system/bin/setprop "net.interfaces.defaultroute" "gprs" + ;; +esac + +# Use interface name if linkname is not available +NAME=${LINKNAME:-"$1"} + +/system/bin/setprop "net.$NAME.dns1" "$DNS1" +/system/bin/setprop "net.$NAME.dns2" "$DNS2" +/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" +/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" + diff --git a/rootdir/init.rc b/rootdir/init.rc new file mode 100644 index 0000000..3f8c6a0 --- /dev/null +++ b/rootdir/init.rc @@ -0,0 +1,245 @@ + +on init + +sysclktz 0 + +loglevel 3 + +# setup the global environment + export PATH /sbin:/system/sbin:/system/bin:/system/xbin + export LD_LIBRARY_PATH /system/lib + export ANDROID_BOOTLOGO 1 + export ANDROID_ROOT /system + export ANDROID_ASSETS /system/app + export ANDROID_DATA /data + export EXTERNAL_STORAGE /sdcard + export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar + +# Backward compatibility + symlink /system/etc /etc + +# create mountpoints and mount tmpfs on sqlite_stmt_journals + mkdir /sdcard 0000 system system + mkdir /system + mkdir /data 0771 system system + mkdir /cache 0770 system cache + mkdir /sqlite_stmt_journals 01777 root root + mount tmpfs tmpfs /sqlite_stmt_journals size=4m + + mount rootfs rootfs / ro remount + + write /proc/sys/kernel/panic_on_oops 1 + write /proc/sys/kernel/hung_task_timeout_secs 0 + write /proc/cpu/alignment 4 + write /proc/sys/kernel/sched_latency_ns 10000000 + write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000 + +# mount mtd partitions + # Mount /system rw first to give the filesystem a chance to save a checkpoint + mount yaffs2 mtd@system /system + mount yaffs2 mtd@system /system ro remount + + # We chown/chmod /data again so because mount is run as root + defaults + mount yaffs2 mtd@userdata /data nosuid nodev + chown system system /data + chmod 0771 /data + + # Same reason as /data above + mount yaffs2 mtd@cache /cache nosuid nodev + chown system cache /cache + chmod 0770 /cache + + # This may have been created by the recovery system with odd permissions + chown system system /cache/recovery + chmod 0770 /cache/recovery + +# create basic filesystem structure + mkdir /data/misc 01771 system misc + mkdir /data/misc/hcid 0770 bluetooth bluetooth + mkdir /data/local 0771 shell shell + mkdir /data/local/tmp 0771 shell shell + mkdir /data/data 0771 system system + mkdir /data/app-private 0771 system system + mkdir /data/app 0771 system system + mkdir /data/property 0700 root root + + # create dalvik-cache and double-check the perms + mkdir /data/dalvik-cache 0771 system system + chown system system /data/dalvik-cache + chmod 0771 /data/dalvik-cache + + # create the lost+found directories, so as to enforce our permissions + mkdir /data/lost+found 0770 + mkdir /cache/lost+found 0770 + + # double check the perms, in case lost+found already exists, and set owner + chown root root /data/lost+found + chmod 0770 /data/lost+found + chown root root /cache/lost+found + chmod 0770 /cache/lost+found + +on boot +# basic network init + ifup lo + hostname localhost + domainname localdomain + +# set RLIMIT_NICE to allow priorities from 19 to -20 + setrlimit 13 40 40 + +# Define the oom_adj values for the classes of processes that can be +# killed by the kernel. These are used in ActivityManagerService. + setprop ro.FOREGROUND_APP_ADJ 0 + setprop ro.VISIBLE_APP_ADJ 1 + setprop ro.SECONDARY_SERVER_ADJ 2 + setprop ro.HIDDEN_APP_MIN_ADJ 7 + setprop ro.CONTENT_PROVIDER_ADJ 14 + setprop ro.EMPTY_APP_ADJ 15 + +# Define the memory thresholds at which the above process classes will +# be killed. These numbers are in pages (4k). + setprop ro.FOREGROUND_APP_MEM 1536 + setprop ro.VISIBLE_APP_MEM 2048 + setprop ro.SECONDARY_SERVER_MEM 4096 + setprop ro.HIDDEN_APP_MEM 5120 + setprop ro.CONTENT_PROVIDER_MEM 5632 + setprop ro.EMPTY_APP_MEM 6144 + +# Write value must be consistent with the above properties. + write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15 + + write /proc/sys/vm/overcommit_memory 1 + write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144 + + # Set init its forked children's oom_adj. + write /proc/1/oom_adj -16 + + # Permissions for System Server and daemons. + chown radio system /sys/android_power/state + chown radio system /sys/android_power/request_state + chown radio system /sys/android_power/acquire_full_wake_lock + chown radio system /sys/android_power/acquire_partial_wake_lock + chown radio system /sys/android_power/release_wake_lock + chown radio system /sys/power/state + chown radio system /sys/power/wake_lock + chown radio system /sys/power/wake_unlock + chmod 0660 /sys/power/state + chmod 0660 /sys/power/wake_lock + chmod 0660 /sys/power/wake_unlock + chown system system /sys/class/timed_output/vibrator/enable + chown system system /sys/class/leds/keyboard-backlight/brightness + chown system system /sys/class/leds/lcd-backlight/brightness + chown system system /sys/class/leds/button-backlight/brightness + chown system system /sys/class/leds/red/brightness + chown system system /sys/class/leds/green/brightness + chown system system /sys/class/leds/blue/brightness + chown system system /sys/class/leds/red/device/grpfreq + chown system system /sys/class/leds/red/device/grppwm + chown system system /sys/class/leds/red/device/blink + chown system system /sys/class/leds/red/brightness + chown system system /sys/class/leds/green/brightness + chown system system /sys/class/leds/blue/brightness + chown system system /sys/class/leds/red/device/grpfreq + chown system system /sys/class/leds/red/device/grppwm + chown system system /sys/class/leds/red/device/blink + chown system system /sys/class/timed_output/vibrator/enable + chown system system /sys/module/sco/parameters/disable_esco + chown system system /sys/kernel/ipv4/tcp_wmem_min + chown system system /sys/kernel/ipv4/tcp_wmem_def + chown system system /sys/kernel/ipv4/tcp_wmem_max + chown system system /sys/kernel/ipv4/tcp_rmem_min + chown system system /sys/kernel/ipv4/tcp_rmem_def + chown system system /sys/kernel/ipv4/tcp_rmem_max + chown root radio /proc/cmdline + +# Define TCP buffer sizes for various networks +# ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, + setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 + setprop net.tcp.buffersize.wifi 4095,87380,110208,4096,16384,110208 + setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 + setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 + setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 + + class_start default + +## Daemon processes to be run by init. +## +service console /system/bin/sh + console + +# adbd is controlled by the persist.service.adb.enable system property +service adbd /sbin/adbd + disabled + +# adbd on at boot in emulator +on property:ro.kernel.qemu=1 + start adbd + +on property:persist.service.adb.enable=1 + start adbd + +on property:persist.service.adb.enable=0 + stop adbd + +service servicemanager /system/bin/servicemanager + user system + critical + onrestart restart zygote + onrestart restart media + +service mountd /system/bin/mountd + socket mountd stream 0660 root mount + +service debuggerd /system/bin/debuggerd + +service ril-daemon /system/bin/rild + socket rild stream 660 root radio + socket rild-debug stream 660 radio system + user root + group radio cache inet misc + +service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server + socket zygote stream 666 + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + +service media /system/bin/mediaserver + user media + group system audio camera graphics inet net_bt net_bt_admin + +service bootsound /system/bin/playmp3 + user media + group audio + oneshot + +service dbus /system/bin/dbus-daemon --system --nofork + socket dbus stream 660 bluetooth bluetooth + user bluetooth + group bluetooth net_bt_admin + +#STOPSHIP: disable the verbose logging +service hcid /system/bin/logwrapper /system/bin/hcid -d -s -n -f /etc/bluez/hcid.conf + socket bluetooth stream 660 bluetooth bluetooth + socket dbus_bluetooth stream 660 bluetooth bluetooth + # init.rc does not yet support applying capabilities, so run as root and + # let hcid drop uid to bluetooth with the right linux capabilities + group bluetooth net_bt_admin misc + disabled + +service hfag /system/bin/sdptool add --channel=10 HFAG + user bluetooth + group bluetooth net_bt_admin + disabled + oneshot + +service hsag /system/bin/sdptool add --channel=11 HSAG + user bluetooth + group bluetooth net_bt_admin + disabled + oneshot + +service installd /system/bin/installd + socket installd stream 600 system system + +service flash_recovery /system/bin/flash_image recovery /system/recovery.img + oneshot diff --git a/sh/Android.mk b/sh/Android.mk new file mode 100644 index 0000000..09bb6ac --- /dev/null +++ b/sh/Android.mk @@ -0,0 +1,49 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + alias.c \ + arith.c \ + arith_lex.c \ + builtins.c \ + cd.c \ + error.c \ + eval.c \ + exec.c \ + expand.c \ + input.c \ + jobs.c \ + main.c \ + memalloc.c \ + miscbltin.c \ + mystring.c \ + nodes.c \ + options.c \ + parser.c \ + redir.c \ + show.c \ + syntax.c \ + trap.c \ + output.c \ + var.c \ + bltin/echo.c \ + init.c + +LOCAL_MODULE:= sh + +LOCAL_CFLAGS += -DSHELL + +make_ash_files: PRIVATE_SRC_FILES := $(SRC_FILES) +make_ash_files: PRIVATE_CFLAGS := $(LOCAL_CFLAGS) +make_ash_files: + p4 edit arith.c arith_lex.c arith.h builtins.h builtins.c + p4 edit init.c nodes.c nodes.h token.h + sh ./mktokens + bison -o arith.c arith.y + flex -o arith_lex.c arith_lex.l + perl -ne 'print if ( /^\#\s*define\s+ARITH/ );' < arith.c > arith.h + sh ./mkbuiltins shell.h builtins.def . -Wall -O2 + sh ./mknodes.sh nodetypes nodes.c.pat . + sh ./mkinit.sh $(PRIVATE_SRC_FILES) + +include $(BUILD_EXECUTABLE) diff --git a/sh/MODULE_LICENSE_BSD b/sh/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/sh/MODULE_LICENSE_BSD diff --git a/sh/NOTICE b/sh/NOTICE new file mode 100644 index 0000000..49a66d2 --- /dev/null +++ b/sh/NOTICE @@ -0,0 +1,31 @@ +Copyright (c) 1991, 1993 + The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by +Kenneth Almquist. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + @@ -0,0 +1,357 @@ +# $NetBSD: TOUR,v 1.8 1996/10/16 14:24:56 christos Exp $ +# @(#)TOUR 8.1 (Berkeley) 5/31/93 + +NOTE -- This is the original TOUR paper distributed with ash and +does not represent the current state of the shell. It is provided anyway +since it provides helpful information for how the shell is structured, +but be warned that things have changed -- the current shell is +still under development. + +================================================================ + + A Tour through Ash + + Copyright 1989 by Kenneth Almquist. + + +DIRECTORIES: The subdirectory bltin contains commands which can +be compiled stand-alone. The rest of the source is in the main +ash directory. + +SOURCE CODE GENERATORS: Files whose names begin with "mk" are +programs that generate source code. A complete list of these +programs is: + + program intput files generates + ------- ------------ --------- + mkbuiltins builtins builtins.h builtins.c + mkinit *.c init.c + mknodes nodetypes nodes.h nodes.c + mksignames - signames.h signames.c + mksyntax - syntax.h syntax.c + mktokens - token.h + bltin/mkexpr unary_op binary_op operators.h operators.c + +There are undoubtedly too many of these. Mkinit searches all the +C source files for entries looking like: + + INIT { + x = 1; /* executed during initialization */ + } + + RESET { + x = 2; /* executed when the shell does a longjmp + back to the main command loop */ + } + + SHELLPROC { + x = 3; /* executed when the shell runs a shell procedure */ + } + +It pulls this code out into routines which are when particular +events occur. The intent is to improve modularity by isolating +the information about which modules need to be explicitly +initialized/reset within the modules themselves. + +Mkinit recognizes several constructs for placing declarations in +the init.c file. + INCLUDE "file.h" +includes a file. The storage class MKINIT makes a declaration +available in the init.c file, for example: + MKINIT int funcnest; /* depth of function calls */ +MKINIT alone on a line introduces a structure or union declara- +tion: + MKINIT + struct redirtab { + short renamed[10]; + }; +Preprocessor #define statements are copied to init.c without any +special action to request this. + +INDENTATION: The ash source is indented in multiples of six +spaces. The only study that I have heard of on the subject con- +cluded that the optimal amount to indent is in the range of four +to six spaces. I use six spaces since it is not too big a jump +from the widely used eight spaces. If you really hate six space +indentation, use the adjind (source included) program to change +it to something else. + +EXCEPTIONS: Code for dealing with exceptions appears in +exceptions.c. The C language doesn't include exception handling, +so I implement it using setjmp and longjmp. The global variable +exception contains the type of exception. EXERROR is raised by +calling error. EXINT is an interrupt. EXSHELLPROC is an excep- +tion which is raised when a shell procedure is invoked. The pur- +pose of EXSHELLPROC is to perform the cleanup actions associated +with other exceptions. After these cleanup actions, the shell +can interpret a shell procedure itself without exec'ing a new +copy of the shell. + +INTERRUPTS: In an interactive shell, an interrupt will cause an +EXINT exception to return to the main command loop. (Exception: +EXINT is not raised if the user traps interrupts using the trap +command.) The INTOFF and INTON macros (defined in exception.h) +provide uninterruptable critical sections. Between the execution +of INTOFF and the execution of INTON, interrupt signals will be +held for later delivery. INTOFF and INTON can be nested. + +MEMALLOC.C: Memalloc.c defines versions of malloc and realloc +which call error when there is no memory left. It also defines a +stack oriented memory allocation scheme. Allocating off a stack +is probably more efficient than allocation using malloc, but the +big advantage is that when an exception occurs all we have to do +to free up the memory in use at the time of the exception is to +restore the stack pointer. The stack is implemented using a +linked list of blocks. + +STPUTC: If the stack were contiguous, it would be easy to store +strings on the stack without knowing in advance how long the +string was going to be: + p = stackptr; + *p++ = c; /* repeated as many times as needed */ + stackptr = p; +The folloing three macros (defined in memalloc.h) perform these +operations, but grow the stack if you run off the end: + STARTSTACKSTR(p); + STPUTC(c, p); /* repeated as many times as needed */ + grabstackstr(p); + +We now start a top-down look at the code: + +MAIN.C: The main routine performs some initialization, executes +the user's profile if necessary, and calls cmdloop. Cmdloop is +repeatedly parses and executes commands. + +OPTIONS.C: This file contains the option processing code. It is +called from main to parse the shell arguments when the shell is +invoked, and it also contains the set builtin. The -i and -j op- +tions (the latter turns on job control) require changes in signal +handling. The routines setjobctl (in jobs.c) and setinteractive +(in trap.c) are called to handle changes to these options. + +PARSING: The parser code is all in parser.c. A recursive des- +cent parser is used. Syntax tables (generated by mksyntax) are +used to classify characters during lexical analysis. There are +three tables: one for normal use, one for use when inside single +quotes, and one for use when inside double quotes. The tables +are machine dependent because they are indexed by character vari- +ables and the range of a char varies from machine to machine. + +PARSE OUTPUT: The output of the parser consists of a tree of +nodes. The various types of nodes are defined in the file node- +types. + +Nodes of type NARG are used to represent both words and the con- +tents of here documents. An early version of ash kept the con- +tents of here documents in temporary files, but keeping here do- +cuments in memory typically results in significantly better per- +formance. It would have been nice to make it an option to use +temporary files for here documents, for the benefit of small +machines, but the code to keep track of when to delete the tem- +porary files was complex and I never fixed all the bugs in it. +(AT&T has been maintaining the Bourne shell for more than ten +years, and to the best of my knowledge they still haven't gotten +it to handle temporary files correctly in obscure cases.) + +The text field of a NARG structure points to the text of the +word. The text consists of ordinary characters and a number of +special codes defined in parser.h. The special codes are: + + CTLVAR Variable substitution + CTLENDVAR End of variable substitution + CTLBACKQ Command substitution + CTLBACKQ|CTLQUOTE Command substitution inside double quotes + CTLESC Escape next character + +A variable substitution contains the following elements: + + CTLVAR type name '=' [ alternative-text CTLENDVAR ] + +The type field is a single character specifying the type of sub- +stitution. The possible types are: + + VSNORMAL $var + VSMINUS ${var-text} + VSMINUS|VSNUL ${var:-text} + VSPLUS ${var+text} + VSPLUS|VSNUL ${var:+text} + VSQUESTION ${var?text} + VSQUESTION|VSNUL ${var:?text} + VSASSIGN ${var=text} + VSASSIGN|VSNUL ${var=text} + +In addition, the type field will have the VSQUOTE flag set if the +variable is enclosed in double quotes. The name of the variable +comes next, terminated by an equals sign. If the type is not +VSNORMAL, then the text field in the substitution follows, ter- +minated by a CTLENDVAR byte. + +Commands in back quotes are parsed and stored in a linked list. +The locations of these commands in the string are indicated by +CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether +the back quotes were enclosed in double quotes. + +The character CTLESC escapes the next character, so that in case +any of the CTL characters mentioned above appear in the input, +they can be passed through transparently. CTLESC is also used to +escape '*', '?', '[', and '!' characters which were quoted by the +user and thus should not be used for file name generation. + +CTLESC characters have proved to be particularly tricky to get +right. In the case of here documents which are not subject to +variable and command substitution, the parser doesn't insert any +CTLESC characters to begin with (so the contents of the text +field can be written without any processing). Other here docu- +ments, and words which are not subject to splitting and file name +generation, have the CTLESC characters removed during the vari- +able and command substitution phase. Words which are subject +splitting and file name generation have the CTLESC characters re- +moved as part of the file name phase. + +EXECUTION: Command execution is handled by the following files: + eval.c The top level routines. + redir.c Code to handle redirection of input and output. + jobs.c Code to handle forking, waiting, and job control. + exec.c Code to to path searches and the actual exec sys call. + expand.c Code to evaluate arguments. + var.c Maintains the variable symbol table. Called from expand.c. + +EVAL.C: Evaltree recursively executes a parse tree. The exit +status is returned in the global variable exitstatus. The alter- +native entry evalbackcmd is called to evaluate commands in back +quotes. It saves the result in memory if the command is a buil- +tin; otherwise it forks off a child to execute the command and +connects the standard output of the child to a pipe. + +JOBS.C: To create a process, you call makejob to return a job +structure, and then call forkshell (passing the job structure as +an argument) to create the process. Waitforjob waits for a job +to complete. These routines take care of process groups if job +control is defined. + +REDIR.C: Ash allows file descriptors to be redirected and then +restored without forking off a child process. This is accom- +plished by duplicating the original file descriptors. The redir- +tab structure records where the file descriptors have be dupli- +cated to. + +EXEC.C: The routine find_command locates a command, and enters +the command in the hash table if it is not already there. The +third argument specifies whether it is to print an error message +if the command is not found. (When a pipeline is set up, +find_command is called for all the commands in the pipeline be- +fore any forking is done, so to get the commands into the hash +table of the parent process. But to make command hashing as +transparent as possible, we silently ignore errors at that point +and only print error messages if the command cannot be found +later.) + +The routine shellexec is the interface to the exec system call. + +EXPAND.C: Arguments are processed in three passes. The first +(performed by the routine argstr) performs variable and command +substitution. The second (ifsbreakup) performs word splitting +and the third (expandmeta) performs file name generation. If the +"/u" directory is simulated, then when "/u/username" is replaced +by the user's home directory, the flag "didudir" is set. This +tells the cd command that it should print out the directory name, +just as it would if the "/u" directory were implemented using +symbolic links. + +VAR.C: Variables are stored in a hash table. Probably we should +switch to extensible hashing. The variable name is stored in the +same string as the value (using the format "name=value") so that +no string copying is needed to create the environment of a com- +mand. Variables which the shell references internally are preal- +located so that the shell can reference the values of these vari- +ables without doing a lookup. + +When a program is run, the code in eval.c sticks any environment +variables which precede the command (as in "PATH=xxx command") in +the variable table as the simplest way to strip duplicates, and +then calls "environment" to get the value of the environment. +There are two consequences of this. First, if an assignment to +PATH precedes the command, the value of PATH before the assign- +ment must be remembered and passed to shellexec. Second, if the +program turns out to be a shell procedure, the strings from the +environment variables which preceded the command must be pulled +out of the table and replaced with strings obtained from malloc, +since the former will automatically be freed when the stack (see +the entry on memalloc.c) is emptied. + +BUILTIN COMMANDS: The procedures for handling these are scat- +tered throughout the code, depending on which location appears +most appropriate. They can be recognized because their names al- +ways end in "cmd". The mapping from names to procedures is +specified in the file builtins, which is processed by the mkbuil- +tins command. + +A builtin command is invoked with argc and argv set up like a +normal program. A builtin command is allowed to overwrite its +arguments. Builtin routines can call nextopt to do option pars- +ing. This is kind of like getopt, but you don't pass argc and +argv to it. Builtin routines can also call error. This routine +normally terminates the shell (or returns to the main command +loop if the shell is interactive), but when called from a builtin +command it causes the builtin command to terminate with an exit +status of 2. + +The directory bltins contains commands which can be compiled in- +dependently but can also be built into the shell for efficiency +reasons. The makefile in this directory compiles these programs +in the normal fashion (so that they can be run regardless of +whether the invoker is ash), but also creates a library named +bltinlib.a which can be linked with ash. The header file bltin.h +takes care of most of the differences between the ash and the +stand-alone environment. The user should call the main routine +"main", and #define main to be the name of the routine to use +when the program is linked into ash. This #define should appear +before bltin.h is included; bltin.h will #undef main if the pro- +gram is to be compiled stand-alone. + +CD.C: This file defines the cd and pwd builtins. The pwd com- +mand runs /bin/pwd the first time it is invoked (unless the user +has already done a cd to an absolute pathname), but then +remembers the current directory and updates it when the cd com- +mand is run, so subsequent pwd commands run very fast. The main +complication in the cd command is in the docd command, which +resolves symbolic links into actual names and informs the user +where the user ended up if he crossed a symbolic link. + +SIGNALS: Trap.c implements the trap command. The routine set- +signal figures out what action should be taken when a signal is +received and invokes the signal system call to set the signal ac- +tion appropriately. When a signal that a user has set a trap for +is caught, the routine "onsig" sets a flag. The routine dotrap +is called at appropriate points to actually handle the signal. +When an interrupt is caught and no trap has been set for that +signal, the routine "onint" in error.c is called. + +OUTPUT: Ash uses it's own output routines. There are three out- +put structures allocated. "Output" represents the standard out- +put, "errout" the standard error, and "memout" contains output +which is to be stored in memory. This last is used when a buil- +tin command appears in backquotes, to allow its output to be col- +lected without doing any I/O through the UNIX operating system. +The variables out1 and out2 normally point to output and errout, +respectively, but they are set to point to memout when appropri- +ate inside backquotes. + +INPUT: The basic input routine is pgetc, which reads from the +current input file. There is a stack of input files; the current +input file is the top file on this stack. The code allows the +input to come from a string rather than a file. (This is for the +-c option and the "." and eval builtin commands.) The global +variable plinno is saved and restored when files are pushed and +popped from the stack. The parser routines store the number of +the current line in this variable. + +DEBUGGING: If DEBUG is defined in shell.h, then the shell will +write debugging information to the file $HOME/trace. Most of +this is done using the TRACE macro, which takes a set of printf +arguments inside two sets of parenthesis. Example: +"TRACE(("n=%d0, n))". The double parenthesis are necessary be- +cause the preprocessor can't handle functions with a variable +number of arguments. Defining DEBUG also causes the shell to +generate a core dump if it is sent a quit signal. The tracing +code is in show.c. diff --git a/sh/alias.c b/sh/alias.c new file mode 100644 index 0000000..59a3dc1 --- /dev/null +++ b/sh/alias.c @@ -0,0 +1,273 @@ +/* $NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> +#include "shell.h" +#include "input.h" +#include "output.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "options.h" /* XXX for argptr (should remove?) */ +#include "var.h" + +#define ATABSIZE 39 + +struct alias *atab[ATABSIZE]; + +STATIC void setalias(char *, char *); +STATIC int unalias(char *); +STATIC struct alias **hashalias(char *); + +STATIC +void +setalias(char *name, char *val) +{ + struct alias *ap, **app; + + app = hashalias(name); + for (ap = *app; ap; ap = ap->next) { + if (equal(name, ap->name)) { + INTOFF; + ckfree(ap->val); + ap->val = savestr(val); + INTON; + return; + } + } + /* not found */ + INTOFF; + ap = ckmalloc(sizeof (struct alias)); + ap->name = savestr(name); + /* + * XXX - HACK: in order that the parser will not finish reading the + * alias value off the input before processing the next alias, we + * dummy up an extra space at the end of the alias. This is a crock + * and should be re-thought. The idea (if you feel inclined to help) + * is to avoid alias recursions. The mechanism used is: when + * expanding an alias, the value of the alias is pushed back on the + * input as a string and a pointer to the alias is stored with the + * string. The alias is marked as being in use. When the input + * routine finishes reading the string, it markes the alias not + * in use. The problem is synchronization with the parser. Since + * it reads ahead, the alias is marked not in use before the + * resulting token(s) is next checked for further alias sub. The + * H A C K is that we add a little fluff after the alias value + * so that the string will not be exhausted. This is a good + * idea ------- ***NOT*** + */ +#ifdef notyet + ap->val = savestr(val); +#else /* hack */ + { + int len = strlen(val); + ap->val = ckmalloc(len + 2); + memcpy(ap->val, val, len); + ap->val[len] = ' '; /* fluff */ + ap->val[len+1] = '\0'; + } +#endif + ap->next = *app; + *app = ap; + INTON; +} + +STATIC int +unalias(char *name) +{ + struct alias *ap, **app; + + app = hashalias(name); + + for (ap = *app; ap; app = &(ap->next), ap = ap->next) { + if (equal(name, ap->name)) { + /* + * if the alias is currently in use (i.e. its + * buffer is being used by the input routine) we + * just null out the name instead of freeing it. + * We could clear it out later, but this situation + * is so rare that it hardly seems worth it. + */ + if (ap->flag & ALIASINUSE) + *ap->name = '\0'; + else { + INTOFF; + *app = ap->next; + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); + INTON; + } + return (0); + } + } + + return (1); +} + +#ifdef mkinit +MKINIT void rmaliases(void); + +SHELLPROC { + rmaliases(); +} +#endif + +void +rmaliases(void) +{ + struct alias *ap, *tmp; + int i; + + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + ap = atab[i]; + atab[i] = NULL; + while (ap) { + ckfree(ap->name); + ckfree(ap->val); + tmp = ap; + ap = ap->next; + ckfree(tmp); + } + } + INTON; +} + +struct alias * +lookupalias(char *name, int check) +{ + struct alias *ap = *hashalias(name); + + for (; ap; ap = ap->next) { + if (equal(name, ap->name)) { + if (check && (ap->flag & ALIASINUSE)) + return (NULL); + return (ap); + } + } + + return (NULL); +} + +char * +get_alias_text(char *name) +{ + struct alias *ap; + + ap = lookupalias(name, 0); + if (ap == NULL) + return NULL; + return ap->val; +} + +/* + * TODO - sort output + */ +int +aliascmd(int argc, char **argv) +{ + char *n, *v; + int ret = 0; + struct alias *ap; + + if (argc == 1) { + int i; + + for (i = 0; i < ATABSIZE; i++) + for (ap = atab[i]; ap; ap = ap->next) { + if (*ap->name != '\0') { + out1fmt("alias %s=", ap->name); + print_quoted(ap->val); + out1c('\n'); + } + } + return (0); + } + while ((n = *++argv) != NULL) { + if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ + if ((ap = lookupalias(n, 0)) == NULL) { + outfmt(out2, "alias: %s not found\n", n); + ret = 1; + } else { + out1fmt("alias %s=", n); + print_quoted(ap->val); + out1c('\n'); + } + } else { + *v++ = '\0'; + setalias(n, v); + } + } + + return (ret); +} + +int +unaliascmd(int argc, char **argv) +{ + int i; + + while ((i = nextopt("a")) != '\0') { + if (i == 'a') { + rmaliases(); + return (0); + } + } + for (i = 0; *argptr; argptr++) + i = unalias(*argptr); + + return (i); +} + +STATIC struct alias ** +hashalias(char *p) +{ + unsigned int hashval; + + hashval = *p << 4; + while (*p) + hashval+= *p++; + return &atab[hashval % ATABSIZE]; +} diff --git a/sh/alias.h b/sh/alias.h new file mode 100644 index 0000000..7ce25f4 --- /dev/null +++ b/sh/alias.h @@ -0,0 +1,50 @@ +/* $NetBSD: alias.h,v 1.6 2003/08/07 09:05:29 agc Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)alias.h 8.2 (Berkeley) 5/4/95 + */ + +#define ALIASINUSE 1 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +struct alias *lookupalias(char *, int); +char *get_alias_text(char *); +int aliascmd(int, char **); +int unaliascmd(int, char **); +void rmaliases(void); diff --git a/sh/arith.c b/sh/arith.c new file mode 100644 index 0000000..f8f92a9 --- /dev/null +++ b/sh/arith.c @@ -0,0 +1,1587 @@ +/* A Bison parser, made by GNU Bison 1.875d. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + ARITH_NUM = 258, + ARITH_LPAREN = 259, + ARITH_RPAREN = 260, + ARITH_OR = 261, + ARITH_AND = 262, + ARITH_BOR = 263, + ARITH_BXOR = 264, + ARITH_BAND = 265, + ARITH_NE = 266, + ARITH_EQ = 267, + ARITH_LE = 268, + ARITH_GE = 269, + ARITH_GT = 270, + ARITH_LT = 271, + ARITH_RSHIFT = 272, + ARITH_LSHIFT = 273, + ARITH_SUB = 274, + ARITH_ADD = 275, + ARITH_REM = 276, + ARITH_DIV = 277, + ARITH_MUL = 278, + ARITH_BNOT = 279, + ARITH_NOT = 280, + ARITH_UNARYPLUS = 281, + ARITH_UNARYMINUS = 282 + }; +#endif +#define ARITH_NUM 258 +#define ARITH_LPAREN 259 +#define ARITH_RPAREN 260 +#define ARITH_OR 261 +#define ARITH_AND 262 +#define ARITH_BOR 263 +#define ARITH_BXOR 264 +#define ARITH_BAND 265 +#define ARITH_NE 266 +#define ARITH_EQ 267 +#define ARITH_LE 268 +#define ARITH_GE 269 +#define ARITH_GT 270 +#define ARITH_LT 271 +#define ARITH_RSHIFT 272 +#define ARITH_LSHIFT 273 +#define ARITH_SUB 274 +#define ARITH_ADD 275 +#define ARITH_REM 276 +#define ARITH_DIV 277 +#define ARITH_MUL 278 +#define ARITH_BNOT 279 +#define ARITH_NOT 280 +#define ARITH_UNARYPLUS 281 +#define ARITH_UNARYMINUS 282 + + + + +/* Copy the first part of user declarations. */ +#line 1 "arith.y" + +/* $NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> +#include "expand.h" +#include "shell.h" +#include "error.h" +#include "output.h" +#include "memalloc.h" + +const char *arith_buf, *arith_startbuf; + +void yyerror(const char *); +#ifdef TESTARITH +int main(int , char *[]); +int error(char *); +#endif + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 214 of yacc.c. */ +#line 202 "arith.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +# ifndef YYFREE +# define YYFREE free +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# endif + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# endif +# else +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short int yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short int yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 14 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 170 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 28 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 3 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 26 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 52 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 282 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 5, 9, 13, 17, 21, 25, 29, + 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, + 73, 77, 81, 84, 87, 90, 93 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 29, 0, -1, 30, -1, 4, 30, 5, -1, 30, + 6, 30, -1, 30, 7, 30, -1, 30, 8, 30, + -1, 30, 9, 30, -1, 30, 10, 30, -1, 30, + 12, 30, -1, 30, 15, 30, -1, 30, 14, 30, + -1, 30, 16, 30, -1, 30, 13, 30, -1, 30, + 11, 30, -1, 30, 18, 30, -1, 30, 17, 30, + -1, 30, 20, 30, -1, 30, 19, 30, -1, 30, + 23, 30, -1, 30, 22, 30, -1, 30, 21, 30, + -1, 25, 30, -1, 24, 30, -1, 19, 30, -1, + 20, 30, -1, 3, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned char yyrline[] = +{ + 0, 76, 76, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 104, 109, 110, 111, 112, 113 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ARITH_NUM", "ARITH_LPAREN", + "ARITH_RPAREN", "ARITH_OR", "ARITH_AND", "ARITH_BOR", "ARITH_BXOR", + "ARITH_BAND", "ARITH_NE", "ARITH_EQ", "ARITH_LE", "ARITH_GE", "ARITH_GT", + "ARITH_LT", "ARITH_RSHIFT", "ARITH_LSHIFT", "ARITH_SUB", "ARITH_ADD", + "ARITH_REM", "ARITH_DIV", "ARITH_MUL", "ARITH_BNOT", "ARITH_NOT", + "ARITH_UNARYPLUS", "ARITH_UNARYMINUS", "$accept", "exp", "expr", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short int yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 28, 29, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 1, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 0, 26, 0, 0, 0, 0, 0, 0, 2, 0, + 24, 25, 23, 22, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 4, 5, 6, 7, 8, 14, + 9, 13, 11, 10, 12, 16, 15, 18, 17, 21, + 20, 19 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 7, 8 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -13 +static const short int yypact[] = +{ + 28, -13, 28, 28, 28, 28, 28, 12, 67, 49, + -13, -13, -13, -13, -13, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, -13, 84, 100, 115, 23, 128, 139, + 139, -12, -12, -12, -12, 144, 144, 147, 147, -13, + -13, -13 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yysigned_char yypgoto[] = +{ + -13, -13, -2 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const unsigned char yytable[] = +{ + 9, 10, 11, 12, 13, 26, 27, 28, 29, 30, + 31, 32, 14, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 1, 2, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 3, 4, 0, + 0, 0, 5, 6, 33, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 28, 29, 30, 31, 32, 30, 31, + 32 +}; + +static const yysigned_char yycheck[] = +{ + 2, 3, 4, 5, 6, 17, 18, 19, 20, 21, + 22, 23, 0, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 3, 4, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 19, 20, -1, + -1, -1, 24, 25, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 19, 20, 21, 22, 23, 21, 22, + 23 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 3, 4, 19, 20, 24, 25, 29, 30, 30, + 30, 30, 30, 30, 0, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 5, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + ((Current).first_line = (Rhs)[1].first_line, \ + (Current).first_column = (Rhs)[1].first_column, \ + (Current).last_line = (Rhs)[N].last_line, \ + (Current).last_column = (Rhs)[N].last_column) +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short int *bottom, short int *top) +#else +static void +yy_stack_print (bottom, top) + short int *bottom; + short int *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short int yyssa[YYINITDEPTH]; + short int *yyss = yyssa; + register short int *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short int *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short int *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 76 "arith.y" + { + return (yyvsp[0]); + ;} + break; + + case 3: +#line 82 "arith.y" + { yyval = yyvsp[-1]; ;} + break; + + case 4: +#line 83 "arith.y" + { yyval = yyvsp[-2] ? yyvsp[-2] : yyvsp[0] ? yyvsp[0] : 0; ;} + break; + + case 5: +#line 84 "arith.y" + { yyval = yyvsp[-2] ? ( yyvsp[0] ? yyvsp[0] : 0 ) : 0; ;} + break; + + case 6: +#line 85 "arith.y" + { yyval = yyvsp[-2] | yyvsp[0]; ;} + break; + + case 7: +#line 86 "arith.y" + { yyval = yyvsp[-2] ^ yyvsp[0]; ;} + break; + + case 8: +#line 87 "arith.y" + { yyval = yyvsp[-2] & yyvsp[0]; ;} + break; + + case 9: +#line 88 "arith.y" + { yyval = yyvsp[-2] == yyvsp[0]; ;} + break; + + case 10: +#line 89 "arith.y" + { yyval = yyvsp[-2] > yyvsp[0]; ;} + break; + + case 11: +#line 90 "arith.y" + { yyval = yyvsp[-2] >= yyvsp[0]; ;} + break; + + case 12: +#line 91 "arith.y" + { yyval = yyvsp[-2] < yyvsp[0]; ;} + break; + + case 13: +#line 92 "arith.y" + { yyval = yyvsp[-2] <= yyvsp[0]; ;} + break; + + case 14: +#line 93 "arith.y" + { yyval = yyvsp[-2] != yyvsp[0]; ;} + break; + + case 15: +#line 94 "arith.y" + { yyval = yyvsp[-2] << yyvsp[0]; ;} + break; + + case 16: +#line 95 "arith.y" + { yyval = yyvsp[-2] >> yyvsp[0]; ;} + break; + + case 17: +#line 96 "arith.y" + { yyval = yyvsp[-2] + yyvsp[0]; ;} + break; + + case 18: +#line 97 "arith.y" + { yyval = yyvsp[-2] - yyvsp[0]; ;} + break; + + case 19: +#line 98 "arith.y" + { yyval = yyvsp[-2] * yyvsp[0]; ;} + break; + + case 20: +#line 99 "arith.y" + { + if (yyvsp[0] == 0) + yyerror("division by zero"); + yyval = yyvsp[-2] / yyvsp[0]; + ;} + break; + + case 21: +#line 104 "arith.y" + { + if (yyvsp[0] == 0) + yyerror("division by zero"); + yyval = yyvsp[-2] % yyvsp[0]; + ;} + break; + + case 22: +#line 109 "arith.y" + { yyval = !(yyvsp[0]); ;} + break; + + case 23: +#line 110 "arith.y" + { yyval = ~(yyvsp[0]); ;} + break; + + case 24: +#line 111 "arith.y" + { yyval = -(yyvsp[0]); ;} + break; + + case 25: +#line 112 "arith.y" + { yyval = yyvsp[0]; ;} + break; + + + } + +/* Line 1010 of yacc.c. */ +#line 1276 "arith.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + const char* yyprefix; + char *yymsg; + int yyx; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 0; + + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); + yycount += 1; + if (yycount == 5) + { + yysize = 0; + break; + } + } + yysize += (sizeof ("syntax error, unexpected ") + + yystrlen (yytname[yytype])); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yyp = yystpcpy (yyp, yyprefix); + yyp = yystpcpy (yyp, yytname[yyx]); + yyprefix = " or "; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* If at end of input, pop the error token, + then the rest of the stack, then return failure. */ + if (yychar == YYEOF) + for (;;) + { + YYPOPSTACK; + if (yyssp == yyss) + YYABORT; + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + } + } + else + { + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + +#ifdef __GNUC__ + /* Pacify GCC when the user code never invokes YYERROR and the label + yyerrorlab therefore never appears in user code. */ + if (0) + goto yyerrorlab; +#endif + + yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 115 "arith.y" + +int +arith(s) + const char *s; +{ + long result; + + arith_buf = arith_startbuf = s; + + INTOFF; + result = yyparse(); + arith_lex_reset(); /* reprime lex */ + INTON; + + return (result); +} + + +/* + * The exp(1) builtin. + */ +int +expcmd(argc, argv) + int argc; + char **argv; +{ + const char *p; + char *concat; + char **ap; + long i; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + /* + * concatenate arguments + */ + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + } else + p = ""; + + i = arith(p); + + out1fmt("%ld\n", i); + return (! i); +} + +/*************************/ +#ifdef TEST_ARITH +#include <stdio.h> +main(argc, argv) + char *argv[]; +{ + printf("%d\n", exp(argv[1])); +} +error(s) + char *s; +{ + fprintf(stderr, "exp: %s\n", s); + exit(1); +} +#endif + +void +yyerror(s) + const char *s; +{ + +// yyerrok; + yyclearin; + arith_lex_reset(); /* reprime lex */ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} + + diff --git a/sh/arith.h b/sh/arith.h new file mode 100644 index 0000000..f70c093 --- /dev/null +++ b/sh/arith.h @@ -0,0 +1,25 @@ +#define ARITH_NUM 258 +#define ARITH_LPAREN 259 +#define ARITH_RPAREN 260 +#define ARITH_OR 261 +#define ARITH_AND 262 +#define ARITH_BOR 263 +#define ARITH_BXOR 264 +#define ARITH_BAND 265 +#define ARITH_NE 266 +#define ARITH_EQ 267 +#define ARITH_LE 268 +#define ARITH_GE 269 +#define ARITH_GT 270 +#define ARITH_LT 271 +#define ARITH_RSHIFT 272 +#define ARITH_LSHIFT 273 +#define ARITH_SUB 274 +#define ARITH_ADD 275 +#define ARITH_REM 276 +#define ARITH_DIV 277 +#define ARITH_MUL 278 +#define ARITH_BNOT 279 +#define ARITH_NOT 280 +#define ARITH_UNARYPLUS 281 +#define ARITH_UNARYMINUS 282 diff --git a/sh/arith.y b/sh/arith.y new file mode 100644 index 0000000..d51ed38 --- /dev/null +++ b/sh/arith.y @@ -0,0 +1,199 @@ +%{ +/* $NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> +#include "expand.h" +#include "shell.h" +#include "error.h" +#include "output.h" +#include "memalloc.h" + +const char *arith_buf, *arith_startbuf; + +void yyerror(const char *); +#ifdef TESTARITH +int main(int , char *[]); +int error(char *); +#endif + +%} +%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN + +%left ARITH_OR +%left ARITH_AND +%left ARITH_BOR +%left ARITH_BXOR +%left ARITH_BAND +%left ARITH_EQ ARITH_NE +%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE +%left ARITH_LSHIFT ARITH_RSHIFT +%left ARITH_ADD ARITH_SUB +%left ARITH_MUL ARITH_DIV ARITH_REM +%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT +%% + +exp: expr { + return ($1); + } + ; + + +expr: ARITH_LPAREN expr ARITH_RPAREN { $$ = $2; } + | expr ARITH_OR expr { $$ = $1 ? $1 : $3 ? $3 : 0; } + | expr ARITH_AND expr { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } + | expr ARITH_BOR expr { $$ = $1 | $3; } + | expr ARITH_BXOR expr { $$ = $1 ^ $3; } + | expr ARITH_BAND expr { $$ = $1 & $3; } + | expr ARITH_EQ expr { $$ = $1 == $3; } + | expr ARITH_GT expr { $$ = $1 > $3; } + | expr ARITH_GE expr { $$ = $1 >= $3; } + | expr ARITH_LT expr { $$ = $1 < $3; } + | expr ARITH_LE expr { $$ = $1 <= $3; } + | expr ARITH_NE expr { $$ = $1 != $3; } + | expr ARITH_LSHIFT expr { $$ = $1 << $3; } + | expr ARITH_RSHIFT expr { $$ = $1 >> $3; } + | expr ARITH_ADD expr { $$ = $1 + $3; } + | expr ARITH_SUB expr { $$ = $1 - $3; } + | expr ARITH_MUL expr { $$ = $1 * $3; } + | expr ARITH_DIV expr { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 / $3; + } + | expr ARITH_REM expr { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 % $3; + } + | ARITH_NOT expr { $$ = !($2); } + | ARITH_BNOT expr { $$ = ~($2); } + | ARITH_SUB expr %prec ARITH_UNARYMINUS { $$ = -($2); } + | ARITH_ADD expr %prec ARITH_UNARYPLUS { $$ = $2; } + | ARITH_NUM + ; +%% +int +arith(s) + const char *s; +{ + long result; + + arith_buf = arith_startbuf = s; + + INTOFF; + result = yyparse(); + arith_lex_reset(); /* reprime lex */ + INTON; + + return (result); +} + + +/* + * The exp(1) builtin. + */ +int +expcmd(argc, argv) + int argc; + char **argv; +{ + const char *p; + char *concat; + char **ap; + long i; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + /* + * concatenate arguments + */ + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + } else + p = ""; + + i = arith(p); + + out1fmt("%ld\n", i); + return (! i); +} + +/*************************/ +#ifdef TEST_ARITH +#include <stdio.h> +main(argc, argv) + char *argv[]; +{ + printf("%d\n", exp(argv[1])); +} +error(s) + char *s; +{ + fprintf(stderr, "exp: %s\n", s); + exit(1); +} +#endif + +void +yyerror(s) + const char *s; +{ + +// yyerrok; + yyclearin; + arith_lex_reset(); /* reprime lex */ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} diff --git a/sh/arith_lex.c b/sh/arith_lex.c new file mode 100644 index 0000000..9a132dd --- /dev/null +++ b/sh/arith_lex.c @@ -0,0 +1,1890 @@ +#line 2 "arith_lex.c" + +#line 4 "arith_lex.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 31 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 29 +#define YY_END_OF_BUFFER 30 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[39] = + { 0, + 0, 0, 30, 28, 1, 1, 27, 23, 12, 6, + 7, 21, 24, 25, 22, 3, 4, 17, 28, 15, + 5, 11, 10, 26, 14, 9, 3, 0, 4, 19, + 18, 13, 16, 20, 5, 8, 2, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 1, 1, 1, 5, 6, 1, 7, + 8, 9, 10, 1, 11, 1, 12, 13, 14, 14, + 14, 14, 14, 14, 14, 15, 15, 1, 1, 16, + 17, 18, 1, 1, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 1, 1, 1, 21, 20, 1, 19, 19, 19, 19, + + 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 22, + 20, 20, 1, 23, 1, 24, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[25] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 1, 1, 1, 2, 3, + 1, 3, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[41] = + { 0, + 0, 0, 47, 48, 48, 48, 29, 48, 39, 48, + 48, 48, 48, 48, 48, 12, 14, 14, 27, 15, + 0, 48, 20, 48, 48, 48, 22, 0, 24, 48, + 48, 48, 48, 48, 0, 48, 0, 48, 38, 40 + } ; + +static yyconst flex_int16_t yy_def[41] = + { 0, + 38, 1, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 39, 38, 38, 38, 38, 38, 38, 40, 38, 38, + 38, 38, 38, 38, 39, 38, 40, 0, 38, 38 + } ; + +static yyconst flex_int16_t yy_nxt[73] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 17, 18, 19, 20, 21, 21, + 22, 21, 23, 24, 27, 27, 29, 29, 29, 30, + 31, 33, 34, 28, 27, 27, 29, 29, 29, 35, + 35, 37, 36, 32, 26, 25, 38, 3, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38 + } ; + +static yyconst flex_int16_t yy_chk[73] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 16, 16, 17, 17, 17, 18, + 18, 20, 20, 16, 27, 27, 29, 29, 29, 39, + 39, 40, 23, 19, 9, 7, 3, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "arith_lex.l" +#line 2 "arith_lex.l" +/* $NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $"); +#endif +#endif /* not lint */ + +#include <unistd.h> +#include "arith.h" +#include "error.h" +#include "expand.h" +#include "var.h" + +extern int yylval; +extern char *arith_buf, *arith_startbuf; +#undef YY_INPUT +#define YY_INPUT(buf,result,max) \ + result = (*buf = *arith_buf++) ? 1 : YY_NULL; +#define YY_NO_UNPUT +#line 526 "arith_lex.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 60 "arith_lex.l" + +#line 679 "arith_lex.c" + + if ( (yy_init) ) + { + (yy_init) = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 39 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 48 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 61 "arith_lex.l" +{ ; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 62 "arith_lex.l" +{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 63 "arith_lex.l" +{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 64 "arith_lex.l" +{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 65 "arith_lex.l" +{ char *v = lookupvar(yytext); + if (v) { + yylval = strtol(v, &v, 0); + if (*v == 0) + return ARITH_NUM; + } + error("arith: syntax error: \"%s\"", arith_startbuf); + } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 73 "arith_lex.l" +{ return(ARITH_LPAREN); } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 74 "arith_lex.l" +{ return(ARITH_RPAREN); } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 75 "arith_lex.l" +{ return(ARITH_OR); } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 76 "arith_lex.l" +{ return(ARITH_AND); } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 77 "arith_lex.l" +{ return(ARITH_BOR); } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 78 "arith_lex.l" +{ return(ARITH_BXOR); } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 79 "arith_lex.l" +{ return(ARITH_BAND); } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 80 "arith_lex.l" +{ return(ARITH_EQ); } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 81 "arith_lex.l" +{ return(ARITH_NE); } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 82 "arith_lex.l" +{ return(ARITH_GT); } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 83 "arith_lex.l" +{ return(ARITH_GE); } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 84 "arith_lex.l" +{ return(ARITH_LT); } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 85 "arith_lex.l" +{ return(ARITH_LE); } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 86 "arith_lex.l" +{ return(ARITH_LSHIFT); } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 87 "arith_lex.l" +{ return(ARITH_RSHIFT); } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 88 "arith_lex.l" +{ return(ARITH_MUL); } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 89 "arith_lex.l" +{ return(ARITH_DIV); } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 90 "arith_lex.l" +{ return(ARITH_REM); } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 91 "arith_lex.l" +{ return(ARITH_ADD); } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 92 "arith_lex.l" +{ return(ARITH_SUB); } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 93 "arith_lex.l" +{ return(ARITH_BNOT); } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 94 "arith_lex.l" +{ return(ARITH_NOT); } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 95 "arith_lex.l" +{ error("arith: syntax error: \"%s\"", arith_startbuf); } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 96 "arith_lex.l" +ECHO; + YY_BREAK +#line 915 "arith_lex.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 39 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 39 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 38); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yy_str ) +{ + + return yy_scan_bytes(yy_str,strlen(yy_str) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * bytes, int len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef yytext_ptr +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif +#line 96 "arith_lex.l" + + + +void +arith_lex_reset() { +#ifdef YY_NEW_FILE + YY_NEW_FILE; +#endif +} + diff --git a/sh/arith_lex.l b/sh/arith_lex.l new file mode 100644 index 0000000..79116fc --- /dev/null +++ b/sh/arith_lex.l @@ -0,0 +1,103 @@ +%{ +/* $NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $"); +#endif +#endif /* not lint */ + +#include <unistd.h> +#include "arith.h" +#include "error.h" +#include "expand.h" +#include "var.h" + +extern int yylval; +extern char *arith_buf, *arith_startbuf; +#undef YY_INPUT +#define YY_INPUT(buf,result,max) \ + result = (*buf = *arith_buf++) ? 1 : YY_NULL; +#define YY_NO_UNPUT +%} +%option noyywrap + +%% +[ \t\n] { ; } +0x[0-9a-fA-F]+ { yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } +0[0-7]* { yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } +[1-9][0-9]* { yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } +[A-Za-z_][A-Za-z_0-9]* { char *v = lookupvar(yytext); + if (v) { + yylval = strtol(v, &v, 0); + if (*v == 0) + return ARITH_NUM; + } + error("arith: syntax error: \"%s\"", arith_startbuf); + } +"(" { return(ARITH_LPAREN); } +")" { return(ARITH_RPAREN); } +"||" { return(ARITH_OR); } +"&&" { return(ARITH_AND); } +"|" { return(ARITH_BOR); } +"^" { return(ARITH_BXOR); } +"&" { return(ARITH_BAND); } +"==" { return(ARITH_EQ); } +"!=" { return(ARITH_NE); } +">" { return(ARITH_GT); } +">=" { return(ARITH_GE); } +"<" { return(ARITH_LT); } +"<=" { return(ARITH_LE); } +"<<" { return(ARITH_LSHIFT); } +">>" { return(ARITH_RSHIFT); } +"*" { return(ARITH_MUL); } +"/" { return(ARITH_DIV); } +"%" { return(ARITH_REM); } +"+" { return(ARITH_ADD); } +"-" { return(ARITH_SUB); } +"~" { return(ARITH_BNOT); } +"!" { return(ARITH_NOT); } +. { error("arith: syntax error: \"%s\"", arith_startbuf); } +%% + +void +arith_lex_reset() { +#ifdef YY_NEW_FILE + YY_NEW_FILE; +#endif +} diff --git a/sh/bltin/bltin.h b/sh/bltin/bltin.h new file mode 100644 index 0000000..b8f9d75 --- /dev/null +++ b/sh/bltin/bltin.h @@ -0,0 +1,94 @@ +/* $NetBSD: bltin.h,v 1.11 2003/08/07 09:05:40 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bltin.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * This file is included by programs which are optionally built into the + * shell. If SHELL is defined, we try to map the standard UNIX library + * routines to ash routines using defines. + */ + +#include "../shell.h" +#include "../mystring.h" +#ifdef SHELL +#include "../output.h" +#include "../error.h" +#undef stdout +#undef stderr +#undef putc +#undef putchar +#undef fileno +#define stdout out1 +#define stderr out2 +#define printf out1fmt +#define putc(c, file) outc(c, file) +#define putchar(c) out1c(c) +#define FILE struct output +#define fprintf outfmt +#define fputs outstr +#define fflush flushout +#define fileno(f) ((f)->fd) +#define INITARGS(argv) +#define err sh_err +#define verr sh_verr +#define errx sh_errx +#define verrx sh_verrx +#define warn sh_warn +#define vwarn sh_vwarn +#define warnx sh_warnx +#define vwarnx sh_vwarnx +#define exit sh_exit +#define setprogname(s) +#define getprogname() commandname +#define setlocate(l,s) 0 + +#define getenv(p) bltinlookup((p),0) + +#else +#undef NULL +#include <stdio.h> +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +pointer stalloc(int); +void error(const char *, ...); +void sh_warnx(const char *, ...); +void sh_exit(int) __attribute__((__noreturn__)); + +int echocmd(int, char **); + + +extern const char *commandname; diff --git a/sh/bltin/echo.1 b/sh/bltin/echo.1 new file mode 100644 index 0000000..7e71fa3 --- /dev/null +++ b/sh/bltin/echo.1 @@ -0,0 +1,109 @@ +.\" $NetBSD: echo.1,v 1.13 2003/08/07 09:05:40 agc Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" Copyright 1989 by Kenneth Almquist +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)echo.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd produce message in a shell script +.Sh SYNOPSIS +.Nm +.Op Fl n | Fl e +.Ar args ... +.Sh DESCRIPTION +.Nm +prints its arguments on the standard output, separated by spaces. +Unless the +.Fl n +option is present, a newline is output following the arguments. +The +.Fl e +option causes +.Nm +to treat the escape sequences specially, as described in the following +paragraph. +The +.Fl e +option is the default, and is provided solely for compatibility with +other systems. +Only one of the options +.Fl n +and +.Fl e +may be given. +.Pp +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.Bl -tag -width indent +.It Li \eb +A backspace character is output. +.It Li \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.Nm +would otherwise output. +.It Li \ef +Output a form feed. +.It Li \en +Output a newline character. +.It Li \er +Output a carriage return. +.It Li \et +Output a (horizontal) tab character. +.It Li \ev +Output a vertical tab. +.It Li \e0 Ns Ar digits +Output the character whose value is given by zero to three digits. +If there are zero digits, a nul character is output. +.It Li \e\e +Output a backslash. +.El +.Sh HINTS +Remember that backslash is special to the shell and needs to be escaped. +To output a message to standard error, say +.Pp +.D1 echo message \*[Gt]\*[Am]2 +.Sh BUGS +The octal character escape mechanism +.Pq Li \e0 Ns Ar digits +differs from the +C language mechanism. +.Pp +There is no way to force +.Nm +to treat its arguments literally, rather than interpreting them as +options and escape sequences. diff --git a/sh/bltin/echo.c b/sh/bltin/echo.c new file mode 100644 index 0000000..bed7535 --- /dev/null +++ b/sh/bltin/echo.c @@ -0,0 +1,116 @@ +/* $NetBSD: echo.c,v 1.12 2005/02/06 04:43:43 perry Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)echo.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Echo command. + * + * echo is steeped in tradition - several of them! + * netbsd has supported 'echo [-n | -e] args' in spite of -e not being + * documented anywhere. + * Posix requires that -n be supported, output from strings containing + * \ is implementation defined + * The Single Unix Spec requires that \ escapes be treated as if -e + * were set, but that -n not be treated as an option. + * (ksh supports 'echo [-eEn] args', but not -- so that it is actually + * impossible to actually output '-n') + * + * It is suggested that 'printf "%b" "string"' be used to get \ sequences + * expanded. printf is now a builtin of netbsd's sh and csh. + */ + +//#define main echocmd + +#include "bltin.h" + +int +echocmd(int argc, char **argv) +{ + char **ap; + char *p; + char c; + int count; + int nflag = 0; + int eflag = 0; + + ap = argv; + if (argc) + ap++; + + if ((p = *ap) != NULL) { + if (equal(p, "-n")) { + nflag = 1; + ap++; + } else if (equal(p, "-e")) { + eflag = 1; + ap++; + } + } + + while ((p = *ap++) != NULL) { + while ((c = *p++) != '\0') { + if (c == '\\' && eflag) { + switch (*p++) { + case 'a': c = '\a'; break; /* bell */ + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'e': c = 033; break; /* escape */ + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': break; /* c = '\\' */ + case '0': + c = 0; + count = 3; + while (--count >= 0 && (unsigned)(*p - '0') < 8) + c = (c << 3) + (*p++ - '0'); + break; + default: + /* Output the '/' and char following */ + p--; + break; + } + } + putchar(c); + } + if (*ap) + putchar(' '); + } + if (! nflag) + putchar('\n'); + return 0; +} diff --git a/sh/builtins.c b/sh/builtins.c new file mode 100644 index 0000000..344dbd6 --- /dev/null +++ b/sh/builtins.c @@ -0,0 +1,61 @@ +/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +const struct builtincmd builtincmd[] = { + + { "command", bltincmd }, + { "bg", bgcmd }, + { "cd", cdcmd }, + { "chdir", cdcmd }, + { "echo", echocmd }, + { "exp", expcmd }, + { "let", expcmd }, + { "false", falsecmd }, +#if WITH_HISTORY + { "fc", histcmd }, + { "inputrc", inputrc }, +#endif + { "fg", fgcmd }, + { "getopts", getoptscmd }, + { "hash", hashcmd }, + { "jobid", jobidcmd }, + { "jobs", jobscmd }, + { "local", localcmd }, +#ifndef SMALL +#endif + { "pwd", pwdcmd }, + { "read", readcmd }, + { "setvar", setvarcmd }, + { "true", truecmd }, + { "type", typecmd }, + { "umask", umaskcmd }, + { "unalias", unaliascmd }, + { "wait", waitcmd }, + { "alias", aliascmd }, + { "ulimit", ulimitcmd }, + { "wordexp", wordexpcmd }, + { 0, 0 }, +}; + +const struct builtincmd splbltincmd[] = { + { "break", breakcmd }, + { "continue", breakcmd }, + { ".", dotcmd }, + { "eval", evalcmd }, + { "exec", execcmd }, + { "exit", exitcmd }, + { "export", exportcmd }, + { "readonly", exportcmd }, + { "return", returncmd }, + { "set", setcmd }, + { "shift", shiftcmd }, + { "times", timescmd }, + { "trap", trapcmd }, + { ":", truecmd }, + { "unset", unsetcmd }, + { 0, 0 }, +}; diff --git a/sh/builtins.def b/sh/builtins.def new file mode 100644 index 0000000..18e56c6 --- /dev/null +++ b/sh/builtins.def @@ -0,0 +1,94 @@ +#!/bin/sh - +# $NetBSD: builtins.def,v 1.21 2004/07/13 15:05:59 seb Exp $ +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)builtins.def 8.4 (Berkeley) 5/4/95 + +# +# This file lists all the builtin commands. The first column is the name +# of a C routine. +# The -j flag specifies that this command is to be excluded from systems +# without job control. +# The -h flag specifies that this command is to be excluded from systems +# based on the SMALL compile-time symbol. +# The -s flag specifies that this is a posix 'special builtin' command. +# The -u flag specifies that this is a posix 'standard utility'. +# The rest of the line specifies the command name or names used to run +# the command. + +bltincmd -u command +bgcmd -j -u bg +breakcmd -s break -s continue +cdcmd -u cd chdir +dotcmd -s . +echocmd echo +evalcmd -s eval +execcmd -s exec +exitcmd -s exit +expcmd exp let +exportcmd -s export -s readonly +falsecmd -u false +#if WITH_HISTORY +histcmd -h -u fc +inputrc inputrc +#endif +fgcmd -j -u fg +getoptscmd -u getopts +hashcmd hash +jobidcmd jobid +jobscmd -u jobs +localcmd local +#ifndef SMALL +##printfcmd printf +#endif +pwdcmd -u pwd +readcmd -u read +returncmd -s return +setcmd -s set +setvarcmd setvar +shiftcmd -s shift +timescmd -s times +trapcmd -s trap +truecmd -s : -u true +typecmd type +umaskcmd -u umask +unaliascmd -u unalias +unsetcmd -s unset +waitcmd -u wait +aliascmd -u alias +ulimitcmd ulimit +##testcmd test [ +##killcmd -u kill # mandated by posix for 'kill %job' +wordexpcmd wordexp +#newgrp -u newgrp # optional command in posix + +#exprcmd expr diff --git a/sh/builtins.h b/sh/builtins.h new file mode 100644 index 0000000..1f9e45a --- /dev/null +++ b/sh/builtins.h @@ -0,0 +1,56 @@ +/* + * This file was generated by the mkbuiltins program. + */ + +#include <sys/cdefs.h> + +struct builtincmd { + const char *name; + int (*builtin)(int, char **); +}; + +extern const struct builtincmd builtincmd[]; +extern const struct builtincmd splbltincmd[]; + + +int bltincmd(int, char **); +int bgcmd(int, char **); +int breakcmd(int, char **); +int cdcmd(int, char **); +int dotcmd(int, char **); +int echocmd(int, char **); +int evalcmd(int, char **); +int execcmd(int, char **); +int exitcmd(int, char **); +int expcmd(int, char **); +int exportcmd(int, char **); +int falsecmd(int, char **); +#if WITH_HISTORY +int histcmd(int, char **); +int inputrc(int, char **); +#endif +int fgcmd(int, char **); +int getoptscmd(int, char **); +int hashcmd(int, char **); +int jobidcmd(int, char **); +int jobscmd(int, char **); +int localcmd(int, char **); +#ifndef SMALL +#endif +int pwdcmd(int, char **); +int readcmd(int, char **); +int returncmd(int, char **); +int setcmd(int, char **); +int setvarcmd(int, char **); +int shiftcmd(int, char **); +int timescmd(int, char **); +int trapcmd(int, char **); +int truecmd(int, char **); +int typecmd(int, char **); +int umaskcmd(int, char **); +int unaliascmd(int, char **); +int unsetcmd(int, char **); +int waitcmd(int, char **); +int aliascmd(int, char **); +int ulimitcmd(int, char **); +int wordexpcmd(int, char **); @@ -0,0 +1,446 @@ +/* $NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +/* + * The cd and pwd commands. + */ + +#include "shell.h" +#include "var.h" +#include "nodes.h" /* for jobs.h */ +#include "jobs.h" +#include "options.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "exec.h" +#include "redir.h" +#include "mystring.h" +#include "show.h" +#include "cd.h" + +STATIC int docd(char *, int); +STATIC char *getcomponent(void); +STATIC void updatepwd(char *); +STATIC void find_curdir(int noerror); + +char *curdir = NULL; /* current working directory */ +char *prevdir; /* previous working directory */ +STATIC char *cdcomppath; + +int +cdcmd(int argc, char **argv) +{ + const char *dest; + const char *path; + char *p, *d; + struct stat statb; + int print = cdprint; /* set -cdprint to enable */ + + nextopt(nullstr); + + /* + * Try (quite hard) to have 'curdir' defined, nothing has set + * it on entry to the shell, but we want 'cd fred; cd -' to work. + */ + getpwd(1); + dest = *argptr; + if (dest == NULL) { + dest = bltinlookup("HOME", 1); + if (dest == NULL) + error("HOME not set"); + } else { + if (argptr[1]) { + /* Do 'ksh' style substitution */ + if (!curdir) + error("PWD not set"); + p = strstr(curdir, dest); + if (!p) + error("bad substitution"); + d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1); + memcpy(d, curdir, p - curdir); + strcpy(d + (p - curdir), argptr[1]); + strcat(d, p + strlen(dest)); + dest = d; + print = 1; + } + } + + if (dest[0] == '-' && dest[1] == '\0') { + dest = prevdir ? prevdir : curdir; + print = 1; + } + if (*dest == '\0') + dest = "."; + if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { + if (!print) { + /* + * XXX - rethink + */ + if (p[0] == '.' && p[1] == '/' && p[2] != '\0') + p += 2; + print = strcmp(p, dest); + } + if (docd(p, print) >= 0) + return 0; + + } + } + error("can't cd to %s", dest); + /* NOTREACHED */ +} + + +/* + * Actually do the chdir. In an interactive shell, print the + * directory name if "print" is nonzero. + */ + +STATIC int +docd(char *dest, int print) +{ + char *p; + char *q; + char *component; + struct stat statb; + int first; + int badstat; + + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + + /* + * Check each component of the path. If we find a symlink or + * something we can't stat, clear curdir to force a getcwd() + * next time we get the value of the current directory. + */ + badstat = 0; + cdcomppath = stalloc(strlen(dest) + 1); + scopy(dest, cdcomppath); + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + cdcomppath++; + } + first = 1; + while ((q = getcomponent()) != NULL) { + if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + while (*q) + STPUTC(*q++, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if ((lstat(stackblock(), &statb) < 0) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ + badstat = 1; + break; + } + } + + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(badstat ? NULL : dest); + INTON; + if (print && iflag && curdir) + out1fmt("%s\n", curdir); + return 0; +} + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ + +STATIC char * +getcomponent() +{ + char *p; + char *start; + + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; + } + return start; +} + + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ + +STATIC void +updatepwd(char *dir) +{ + char *new; + char *p; + + hashcd(); /* update command hash table */ + + /* + * If our argument is NULL, we don't know the current directory + * any more because we traversed a symbolic link or something + * we couldn't stat(). + */ + if (dir == NULL || curdir == NULL) { + if (prevdir) + ckfree(prevdir); + INTOFF; + prevdir = curdir; + curdir = NULL; + getpwd(1); + INTON; + if (curdir) + setvar("PWD", curdir, VEXPORT); + else + unsetvar("PWD", 0); + return; + } + cdcomppath = stalloc(strlen(dir) + 1); + scopy(dir, cdcomppath); + STARTSTACKSTR(new); + if (*dir != '/') { + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); + } + while ((p = getcomponent()) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + while (*p) + STPUTC(*p++, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + INTOFF; + if (prevdir) + ckfree(prevdir); + prevdir = curdir; + curdir = savestr(stackblock()); + setvar("PWD", curdir, VEXPORT); + INTON; +} + +/* + * Posix says the default should be 'pwd -L' (as below), however + * the 'cd' command (above) does something much nearer to the + * posix 'cd -P' (not the posix default of 'cd -L'). + * If 'cd' is changed to support -P/L then the default here + * needs to be revisited if the historic behaviour is to be kept. + */ + +int +pwdcmd(int argc, char **argv) +{ + int i; + char opt = 'L'; + + while ((i = nextopt("LP")) != '\0') + opt = i; + if (*argptr) + error("unexpected argument"); + + if (opt == 'L') + getpwd(0); + else + find_curdir(0); + + setvar("PWD", curdir, VEXPORT); + out1str(curdir); + out1c('\n'); + return 0; +} + + + + +#define MAXPWD 256 + +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +void +getpwd(int noerror) +{ + char *pwd; + struct stat stdot, stpwd; + static int first = 1; + + if (curdir) + return; + + if (first) { + first = 0; + pwd = getenv("PWD"); + if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && + stat(pwd, &stpwd) != -1 && + stdot.st_dev == stpwd.st_dev && + stdot.st_ino == stpwd.st_ino) { + curdir = savestr(pwd); + return; + } + } + + find_curdir(noerror); + + return; +} + +STATIC void +find_curdir(int noerror) +{ + int i; + char *pwd; + + /* + * Things are a bit complicated here; we could have just used + * getcwd, but traditionally getcwd is implemented using popen + * to /bin/pwd. This creates a problem for us, since we cannot + * keep track of the job if it is being ran behind our backs. + * So we re-implement getcwd(), and we suppress interrupts + * throughout the process. This is not completely safe, since + * the user can still break out of it by killing the pwd program. + * We still try to use getcwd for systems that we know have a + * c implementation of getcwd, that does not open a pipe to + * /bin/pwd. + */ +#if defined(__NetBSD__) || defined(__SVR4) || defined(__linux__) + for (i = MAXPWD;; i *= 2) { + pwd = stalloc(i); + if (getcwd(pwd, i) != NULL) { + curdir = savestr(pwd); + return; + } + stunalloc(pwd); + if (errno == ERANGE) + continue; + if (!noerror) + error("getcwd() failed: %s", strerror(errno)); + return; + } +#else + { + char *p; + int status; + struct job *jp; + int pip[2]; + + pwd = stalloc(MAXPWD); + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob((union node *)NULL, 1); + if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { + (void) close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + (void) execl("/bin/pwd", "pwd", (char *)0); + sh_warn("Cannot exec /bin/pwd"); + exit(1); + } + (void) close(pip[1]); + pip[1] = -1; + p = pwd; + while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0 + || (i == -1 && errno == EINTR)) { + if (i > 0) + p += i; + } + (void) close(pip[0]); + pip[0] = -1; + status = waitforjob(jp); + if (status != 0) + error((char *)0); + if (i < 0 || p == pwd || p[-1] != '\n') { + if (noerror) { + INTON; + return; + } + error("pwd command failed"); + } + p[-1] = '\0'; + INTON; + curdir = savestr(pwd); + return; + } +#endif +} @@ -0,0 +1,35 @@ +/* $NetBSD: cd.h,v 1.4 2003/08/07 09:05:30 agc Exp $ */ + +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +void getpwd(int); +int cdcmd(int, char **); +int pwdcmd(int, char **); diff --git a/sh/error.c b/sh/error.c new file mode 100644 index 0000000..8cbed19 --- /dev/null +++ b/sh/error.c @@ -0,0 +1,366 @@ +/* $NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $"); +#endif +#endif /* not lint */ + +/* + * Errors and exceptions. + */ + +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include "show.h" + +#define signal bsd_signal +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +int exception; +volatile int suppressint; +volatile int intpending; +char *commandname; + + +static void exverror(int, const char *, va_list) + __attribute__((__noreturn__)); + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +void +exraise(int e) +{ + if (handler == NULL) + abort(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +void +onint(void) +{ + sigset_t nsigset; + + if (suppressint) { + intpending = 1; + return; + } + intpending = 0; + sigemptyset(&nsigset); + sigprocmask(SIG_SETMASK, &nsigset, NULL); + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + /* NOTREACHED */ +} + +static void +exvwarning(int sv_errno, const char *msg, va_list ap) +{ + /* Partially emulate line buffered output so that: + * printf '%d\n' 1 a 2 + * and + * printf '%d %d %d\n' 1 a 2 + * both generate sensible text when stdout and stderr are merged. + */ + if (output.nextc != output.buf && output.nextc[-1] == '\n') + flushout(&output); + if (commandname) + outfmt(&errout, "%s: ", commandname); + if (msg != NULL) { + doformat(&errout, msg, ap); + if (sv_errno >= 0) + outfmt(&errout, ": "); + } + if (sv_errno >= 0) + outfmt(&errout, "%s", strerror(sv_errno)); + out2c('\n'); + flushout(&errout); +} + +/* + * Exverror is called to raise the error exception. If the second argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static void +exverror(int cond, const char *msg, va_list ap) +{ + CLEAR_PENDING_INT; + INTOFF; + +#ifdef DEBUG + if (msg) { + TRACE(("exverror(%d, \"", cond)); + TRACEV((msg, ap)); + TRACE(("\") pid=%d\n", getpid())); + } else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) + exvwarning(-1, msg, ap); + + flushall(); + exraise(cond); + /* NOTREACHED */ +} + + +void +error(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(EXERROR, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + + +void +exerror(int cond, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(cond, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + +/* + * error/warning routines for external builtins + */ + +void +sh_exit(int rval) +{ + exerrno = rval & 255; + exraise(EXEXEC); +} + +void +sh_err(int status, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(errno, fmt, ap); + va_end(ap); + sh_exit(status); +} + +void +sh_verr(int status, const char *fmt, va_list ap) +{ + exvwarning(errno, fmt, ap); + sh_exit(status); +} + +void +sh_errx(int status, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(-1, fmt, ap); + va_end(ap); + sh_exit(status); +} + +void +sh_verrx(int status, const char *fmt, va_list ap) +{ + exvwarning(-1, fmt, ap); + sh_exit(status); +} + +void +sh_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(errno, fmt, ap); + va_end(ap); +} + +void +sh_vwarn(const char *fmt, va_list ap) +{ + exvwarning(errno, fmt, ap); +} + +void +sh_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(-1, fmt, ap); + va_end(ap); +} + +void +sh_vwarnx(const char *fmt, va_list ap) +{ + exvwarning(-1, fmt, ap); +} + + +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + const char *msg; /* text describing the error */ +}; + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +STATIC const struct errname errormsg[] = { + { EINTR, ALL, "interrupted" }, + { EACCES, ALL, "permission denied" }, + { EIO, ALL, "I/O error" }, + { EEXIST, ALL, "file exists" }, + { ENOENT, E_OPEN, "no such file" }, + { ENOENT, E_CREAT,"directory nonexistent" }, + { ENOENT, E_EXEC, "not found" }, + { ENOTDIR, E_OPEN, "no such file" }, + { ENOTDIR, E_CREAT,"directory nonexistent" }, + { ENOTDIR, E_EXEC, "not found" }, + { EISDIR, ALL, "is a directory" }, +#ifdef EMFILE + { EMFILE, ALL, "too many open files" }, +#endif + { ENFILE, ALL, "file table overflow" }, + { ENOSPC, ALL, "file system full" }, +#ifdef EDQUOT + { EDQUOT, ALL, "disk quota exceeded" }, +#endif +#ifdef ENOSR + { ENOSR, ALL, "no streams resources" }, +#endif + { ENXIO, ALL, "no such device or address" }, + { EROFS, ALL, "read-only file system" }, + { ETXTBSY, ALL, "text busy" }, +#ifdef EAGAIN + { EAGAIN, E_EXEC, "not enough memory" }, +#endif + { ENOMEM, ALL, "not enough memory" }, +#ifdef ENOLINK + { ENOLINK, ALL, "remote access failed" }, +#endif +#ifdef EMULTIHOP + { EMULTIHOP, ALL, "remote access failed" }, +#endif +#ifdef ECOMM + { ECOMM, ALL, "remote access failed" }, +#endif +#ifdef ESTALE + { ESTALE, ALL, "remote access failed" }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, ALL, "remote access failed" }, +#endif +#ifdef ELOOP + { ELOOP, ALL, "symbolic link loop" }, +#endif + { E2BIG, E_EXEC, "argument list too long" }, +#ifdef ELIBACC + { ELIBACC, E_EXEC, "shared library missing" }, +#endif + { 0, 0, NULL }, +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +const char * +errmsg(int e, int action) +{ + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} diff --git a/sh/error.h b/sh/error.h new file mode 100644 index 0000000..8e70ca4 --- /dev/null +++ b/sh/error.h @@ -0,0 +1,117 @@ +/* $NetBSD: error.h,v 1.16 2003/08/07 09:05:30 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)error.h 8.2 (Berkeley) 5/4/95 + */ + +#include <stdarg.h> + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +#include <setjmp.h> + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern int exception; +extern int exerrno; /* error for EXEXEC */ + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ +#define EXEXEC 3 /* command execution failed */ + + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +extern volatile int suppressint; +extern volatile int intpending; + +#define INTOFF suppressint++ +#define INTON { if (--suppressint == 0 && intpending) onint(); } +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +void exraise(int) __attribute__((__noreturn__)); +void onint(void); +void error(const char *, ...) __attribute__((__noreturn__)); +void exerror(int, const char *, ...) __attribute__((__noreturn__)); +const char *errmsg(int, int); + +void sh_err(int, const char *, ...) __attribute__((__noreturn__)); +void sh_verr(int, const char *, va_list) __attribute__((__noreturn__)); +void sh_errx(int, const char *, ...) __attribute__((__noreturn__)); +void sh_verrx(int, const char *, va_list) __attribute__((__noreturn__)); +void sh_warn(const char *, ...); +void sh_vwarn(const char *, va_list); +void sh_warnx(const char *, ...); +void sh_vwarnx(const char *, va_list); + +void sh_exit(int) __attribute__((__noreturn__)); + + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#if defined(BSD) && !defined(__SVR4) && !defined(__linux__) +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif diff --git a/sh/eval.c b/sh/eval.c new file mode 100644 index 0000000..9acfd64 --- /dev/null +++ b/sh/eval.c @@ -0,0 +1,1257 @@ +/* $NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; +#else +__RCSID("$NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <unistd.h> +#ifdef __linux__ +#include <fcntl.h> +#else +#include <sys/fcntl.h> +#endif +#include <sys/times.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/wait.h> + +/* + * Evaluate a command. + */ + +#include "shell.h" +#include "nodes.h" +#include "syntax.h" +#include "expand.h" +#include "parser.h" +#include "jobs.h" +#include "eval.h" +#include "builtins.h" +#include "options.h" +#include "exec.h" +#include "redir.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "show.h" +#include "mystring.h" +#include "main.h" +#ifndef SMALL +#include "myhistedit.h" +#endif + + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + +int evalskip; /* set if we are skipping commands */ +STATIC int skipcount; /* number of levels to skip */ +MKINIT int loopnest; /* current loop nesting level */ +int funcnest; /* depth of function calls */ + + +char *commandname; +struct strlist *cmdenviron; +int exitstatus; /* exit status of last command */ +int back_exitstatus; /* exit status of backquoted command */ + + +STATIC void evalloop(union node *, int); +STATIC void evalfor(union node *, int); +STATIC void evalcase(union node *, int); +STATIC void evalsubshell(union node *, int); +STATIC void expredir(union node *); +STATIC void evalpipe(union node *); +STATIC void evalcommand(union node *, int, struct backcmd *); +STATIC void prehash(union node *); + + +/* + * Called to reset things after an exception. + */ + +#ifdef mkinit +INCLUDE "eval.h" + +RESET { + evalskip = 0; + loopnest = 0; + funcnest = 0; +} + +SHELLPROC { + exitstatus = 0; +} +#endif + +static int +sh_pipe(int fds[2]) +{ + int nfd; + + if (pipe(fds)) + return -1; + + if (fds[0] < 3) { + nfd = fcntl(fds[0], F_DUPFD, 3); + if (nfd != -1) { + close(fds[0]); + fds[0] = nfd; + } + } + + if (fds[1] < 3) { + nfd = fcntl(fds[1], F_DUPFD, 3); + if (nfd != -1) { + close(fds[1]); + fds[1] = nfd; + } + } + return 0; +} + + +/* + * The eval commmand. + */ + +int +evalcmd(int argc, char **argv) +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p, EV_TESTED); + } + return exitstatus; +} + + +/* + * Execute a command or commands contained in a string. + */ + +void +evalstring(char *s, int flag) +{ + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s, 1); + + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, flag); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} + + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +void +evaltree(union node *n, int flags) +{ + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + exitstatus = 0; + goto out; + } +#ifdef WITH_HISTORY + displayhist = 1; /* show history substitutions done with fc */ +#endif + TRACE(("pid %d, evaltree(%p: %d, %d) called\n", + getpid(), n, n->type, flags)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, flags & EV_TESTED); + if (evalskip) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NREDIR: + expredir(n->nredir.redirect); + redirect(n->nredir.redirect, REDIR_PUSH); + evaltree(n->nredir.n, flags); + popredir(); + break; + case NSUBSHELL: + evalsubshell(n, flags); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + evaltree(n->nif.ifpart, flags); + else if (n->nif.elsepart) + evaltree(n->nif.elsepart, flags); + else + exitstatus = 0; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n, flags); + break; + case NFOR: + evalfor(n, flags); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: + defun(n->narg.text, n->narg.next); + exitstatus = 0; + break; + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + exitstatus = !exitstatus; + break; + case NPIPE: + evalpipe(n); + break; + case NCMD: + evalcommand(n, flags, (struct backcmd *)NULL); + break; + default: + out1fmt("Node type = %d\n", n->type); + flushout(&output); + break; + } +out: + if (pendingsigs) + dotrap(); + if ((flags & EV_EXIT) != 0) + exitshell(exitstatus); +} + + +STATIC void +evalloop(union node *n, int flags) +{ + int status; + + loopnest++; + status = 0; + for (;;) { + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, flags & EV_TESTED); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + + + +STATIC void +evalfor(union node *n, int flags) +{ + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; + int status = 0; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + loopnest++; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, flags & EV_TESTED); + status = exitstatus; + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; + exitstatus = status; +out: + popstackmark(&smark); +} + + + +STATIC void +evalcase(union node *n, int flags) +{ + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + int status = 0; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + status = exitstatus; + } + goto out; + } + } + } +out: + exitstatus = status; + popstackmark(&smark); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +STATIC void +evalsubshell(union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); + + expredir(n->nredir.redirect); + INTOFF; + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + INTON; + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + /* never returns */ + evaltree(n->nredir.n, flags | EV_EXIT); + } + if (! backgnd) + exitstatus = waitforjob(jp); + INTON; +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +STATIC void +expredir(union node *n) +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + switch (redir->type) { + case NFROMTO: + case NFROM: + case NTO: + case NCLOBBER: + case NAPPEND: + expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); + redir->nfile.expfname = fn.list->text; + break; + case NFROMFD: + case NTOFD: + if (redir->ndup.vname) { + expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); + fixredir(redir, fn.list->text, 1); + } + break; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +STATIC void +evalpipe(union node *n) +{ + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(0x%lx) called\n", (long)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (sh_pipe(pip) < 0) { + close(prevfd); + error("Pipe call failed"); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + close(0); + copyfd(prevfd, 0); + close(prevfd); + } + if (pip[1] >= 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + if (n->npipe.backgnd == 0) { + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + } + INTON; +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(union node *n, struct backcmd *result) +{ + int pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + goto out; + } +#ifdef notyet + /* + * For now we disable executing builtins in the same + * context as the shell, because we are not keeping + * enough state to recover from changes that are + * supposed only to affect subshells. eg. echo "`cd /`" + */ + if (n->type == NCMD) { + exitstatus = oexitstatus; + evalcommand(n, EV_BACKCMD, result); + } else +#endif + { + INTOFF; + if (sh_pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + eflag = 0; + evaltree(n, EV_EXIT); + /* NOTREACHED */ + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + INTON; + } +out: + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + +static const char * +syspath(void) +{ + static char *sys_path = NULL; +#ifndef __linux__ + static int mib[] = {CTL_USER, USER_CS_PATH}; +#endif + static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; + + if (sys_path == NULL) { +#ifndef __linux__ + size_t len; + if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && + (sys_path = ckmalloc(len + 5)) != NULL && + sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { + memcpy(sys_path, "PATH=", 5); + } else +#endif + { + ckfree(sys_path); + /* something to keep things happy */ + sys_path = def_path; + } + } + return sys_path; +} + +static int +parse_command_args(int argc, char **argv, int *use_syspath) +{ + int sv_argc = argc; + char *cp, c; + + *use_syspath = 0; + + for (;;) { + argv++; + if (--argc == 0) + break; + cp = *argv; + if (*cp++ != '-') + break; + if (*cp == '-' && cp[1] == 0) { + argv++; + argc--; + break; + } + while ((c = *cp++)) { + switch (c) { + case 'p': + *use_syspath = 1; + break; + default: + /* run 'typecmd' for other options */ + return 0; + } + } + } + return sv_argc - argc; +} + +int vforked = 0; + +/* + * Execute a simple command. + */ + +STATIC void +evalcommand(union node *cmd, int flags, struct backcmd *backcmd) +{ + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + int varflag; + struct strlist *sp; + int mode; + int pip[2]; + struct cmdentry cmdentry; + struct job *jp; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + const char *path = pathval(); + volatile int temp_path; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &argv; + (void) &argc; + (void) &lastarg; + (void) &flags; +#endif + + vforked = 0; + /* First expand the arguments. */ + TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); + setstackmark(&smark); + back_exitstatus = 0; + + arglist.lastp = &arglist.list; + varflag = 1; + /* Expand arguments, ignoring the initial 'name=value' ones */ + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + char *p = argp->narg.text; + if (varflag && is_name(*p)) { + do { + p++; + } while (is_in_name(*p)); + if (*p == '=') + continue; + } + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + varflag = 0; + } + *arglist.lastp = NULL; + + expredir(cmd->ncmd.redirect); + + /* Now do the initial 'name=value' ones we skipped above */ + varlist.lastp = &varlist.list; + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + char *p = argp->narg.text; + if (!is_name(*p)) + break; + do + p++; + while (is_in_name(*p)); + if (*p != '=') + break; + expandarg(argp, &varlist, EXP_VARTILDE); + } + *varlist.lastp = NULL; + + argc = 0; + for (sp = arglist.list ; sp ; sp = sp->next) + argc++; + argv = stalloc(sizeof (char *) * (argc + 1)); + + for (sp = arglist.list ; sp ; sp = sp->next) { + TRACE(("evalcommand arg: %s\n", sp->text)); + *argv++ = sp->text; + } + *argv = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[-1]; + argv -= argc; + + /* Print the command if xflag is set. */ + if (xflag) { + char sep = 0; + out2str(ps4val()); + for (sp = varlist.list ; sp ; sp = sp->next) { + if (sep != 0) + outc(sep, &errout); + out2str(sp->text); + sep = ' '; + } + for (sp = arglist.list ; sp ; sp = sp->next) { + if (sep != 0) + outc(sep, &errout); + out2str(sp->text); + sep = ' '; + } + outc('\n', &errout); + flushout(&errout); + } + + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDSPLBLTIN; + cmdentry.u.bltin = bltincmd; + } else { + static const char PATH[] = "PATH="; + int cmd_flags = DO_ERR; + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + for (sp = varlist.list; sp; sp = sp->next) + if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) + path = sp->text + sizeof(PATH) - 1; + + do { + int argsused, use_syspath; + find_command(argv[0], &cmdentry, cmd_flags, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { + exitstatus = 127; + flushout(&errout); + goto out; + } + + /* implement the 'command' builtin here */ + if (cmdentry.cmdtype != CMDBUILTIN || + cmdentry.u.bltin != bltincmd) + break; + cmd_flags |= DO_NOFUNC; + argsused = parse_command_args(argc, argv, &use_syspath); + if (argsused == 0) { + /* use 'type' builting to display info */ + cmdentry.u.bltin = typecmd; + break; + } + argc -= argsused; + argv += argsused; + if (use_syspath) + path = syspath() + 5; + } while (argc != 0); + if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) + /* posix mandates that 'command <splbltin>' act as if + <splbltin> was a normal builtin */ + cmdentry.cmdtype = CMDBUILTIN; + } + + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) + || ((flags & EV_BACKCMD) != 0 + && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) + || cmdentry.u.bltin == dotcmd + || cmdentry.u.bltin == evalcmd))) { + INTOFF; + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (flags & EV_BACKCMD) { + mode = FORK_NOJOB; + if (sh_pipe(pip) < 0) + error("Pipe call failed"); + } +#ifdef DO_SHAREDVFORK + /* It is essential that if DO_SHAREDVFORK is defined that the + * child's address space is actually shared with the parent as + * we rely on this. + */ + if (cmdentry.cmdtype == CMDNORMAL) { + pid_t pid; + + savelocalvars = localvars; + localvars = NULL; + vforked = 1; + switch (pid = vfork()) { + case -1: + TRACE(("Vfork failed, errno=%d\n", errno)); + INTON; + error("Cannot vfork"); + break; + case 0: + /* Make sure that exceptions only unwind to + * after the vfork(2) + */ + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) { + /* We can't progress with the vfork, + * so, set vforked = 2 so the parent + * knows, and _exit(); + */ + vforked = 2; + _exit(0); + } else { + _exit(exerrno); + } + } + savehandler = handler; + handler = &jmploc; + listmklocal(varlist.list, VEXPORT | VNOFUNC); + forkchild(jp, cmd, mode, vforked); + break; + default: + handler = savehandler; /* restore from vfork(2) */ + poplocalvars(); + localvars = savelocalvars; + if (vforked == 2) { + vforked = 0; + + (void)waitpid(pid, NULL, 0); + /* We need to progress in a normal fork fashion */ + goto normal_fork; + } + vforked = 0; + forkparent(jp, cmd, mode, pid); + goto parent; + } + } else { +normal_fork: +#endif + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + FORCEINTON; +#ifdef DO_SHAREDVFORK + } +#endif + if (flags & EV_BACKCMD) { + if (!vforked) { + FORCEINTON; + } + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + switch (cmdentry.cmdtype) { + case CMDFUNCTION: +#ifdef DEBUG + trputs("Shell function: "); trargs(argv); +#endif + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.reset = 1; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + shellparam.optnext = NULL; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) { + freeparam((volatile struct shparam *) + &saveparam); + } else { + freeparam(&shellparam); + shellparam = saveparam; + } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + listmklocal(varlist.list, 0); + /* stop shell blowing its stack */ + if (++funcnest > 1000) + error("too many nested function calls"); + evaltree(cmdentry.u.func, flags & EV_TESTED); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + popredir(); + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + if (flags & EV_EXIT) + exitshell(exitstatus); + break; + + case CMDBUILTIN: + case CMDSPLBLTIN: +#ifdef DEBUG + trputs("builtin command: "); trargs(argv); +#endif + mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; + if (flags == EV_BACKCMD) { + memout.nleft = 0; + memout.nextc = memout.buf; + memout.bufsize = 64; + mode |= REDIR_BACKQ; + } + e = -1; + savehandler = handler; + savecmdname = commandname; + handler = &jmploc; + if (!setjmp(jmploc.loc)) { + /* We need to ensure the command hash table isn't + * corruped by temporary PATH assignments. + * However we must ensure the 'local' command works! + */ + if (path != pathval() && (cmdentry.u.bltin == hashcmd || + cmdentry.u.bltin == typecmd)) { + savelocalvars = localvars; + localvars = 0; + mklocal(path - 5 /* PATH= */, 0); + temp_path = 1; + } else + temp_path = 0; + redirect(cmd->ncmd.redirect, mode); + + /* exec is a special builtin, but needs this list... */ + cmdenviron = varlist.list; + /* we must check 'readonly' flag for all builtins */ + listsetvar(varlist.list, + cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); + commandname = argv[0]; + /* initialize nextopt */ + argptr = argv + 1; + optptr = NULL; + /* and getopt */ +#ifndef __linux__ + optreset = 1; +#endif + optind = 1; + exitstatus = cmdentry.u.bltin(argc, argv); + } else { + e = exception; + exitstatus = e == EXINT ? SIGINT + 128 : + e == EXEXEC ? exerrno : 2; + } + handler = savehandler; + flushall(); + out1 = &output; + out2 = &errout; + freestdout(); + if (temp_path) { + poplocalvars(); + localvars = savelocalvars; + } + cmdenviron = NULL; + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) + exitshell(exitstatus); + } + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.cmdtype == CMDSPLBLTIN) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.bltin != execcmd) + popredir(); + if (flags == EV_BACKCMD) { + backcmd->buf = memout.buf; + backcmd->nleft = memout.nextc - memout.buf; + memout.buf = NULL; + } + break; + + default: +#ifdef DEBUG + trputs("normal command: "); trargs(argv); +#endif + clearredir(vforked); + redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0); + if (!vforked) + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, path, cmdentry.u.index, vforked); + break; + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == FORK_FG) { /* argument to fork */ + exitstatus = waitforjob(jp); + } else if (mode == FORK_NOJOB) { + backcmd->fd = pip[0]; + close(pip[1]); + backcmd->jp = jp; + } + FORCEINTON; + +out: + if (lastarg) + /* dsl: I think this is intended to be used to support + * '_' in 'vi' command mode during line editing... + * However I implemented that within libedit itself. + */ + setvar("_", lastarg, 0); + popstackmark(&smark); + + if (eflag && exitstatus && !(flags & EV_TESTED)) + exitshell(exitstatus); +} + + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +STATIC void +prehash(union node *n) +{ + struct cmdentry entry; + + if (n->type == NCMD && n->ncmd.args) + if (goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0, + pathval()); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given. + */ + +int +bltincmd(int argc, char **argv) +{ + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + return back_exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +int +breakcmd(int argc, char **argv) +{ + int n = argc > 1 ? number(argv[1]) : 1; + + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +int +returncmd(int argc, char **argv) +{ + int ret = argc > 1 ? number(argv[1]) : exitstatus; + + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + return ret; + } + else { + /* Do what ksh does; skip the rest of the file */ + evalskip = SKIPFILE; + skipcount = 1; + return ret; + } +} + + +int +falsecmd(int argc, char **argv) +{ + return 1; +} + + +int +truecmd(int argc, char **argv) +{ + return 0; +} + + +int +execcmd(int argc, char **argv) +{ + if (argc > 1) { + struct strlist *sp; + + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + for (sp = cmdenviron; sp; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + shellexec(argv + 1, environment(), pathval(), 0, 0); + } + return 0; +} + +static int +conv_time(clock_t ticks, char *seconds, size_t l) +{ + static clock_t tpm = 0; + clock_t mins; + int i; + + mins = ticks / tpm; + snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); + + if (seconds[0] == '6' && seconds[1] == '0') { + /* 59.99995 got rounded up... */ + mins++; + strlcpy(seconds, "0.0", l); + return mins; + } + + /* suppress trailing zeros */ + i = strlen(seconds) - 1; + for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) + seconds[i] = 0; + return mins; +} + +int +timescmd(int argc, char **argv) +{ + struct tms tms; + int u, s, cu, cs; + char us[8], ss[8], cus[8], css[8]; + + nextopt(""); + + times(&tms); + + u = conv_time(tms.tms_utime, us, sizeof(us)); + s = conv_time(tms.tms_stime, ss, sizeof(ss)); + cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); + cs = conv_time(tms.tms_cstime, css, sizeof(css)); + + outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", + u, us, s, ss, cu, cus, cs, css); + + return 0; +} diff --git a/sh/eval.h b/sh/eval.h new file mode 100644 index 0000000..155bc44 --- /dev/null +++ b/sh/eval.h @@ -0,0 +1,64 @@ +/* $NetBSD: eval.h,v 1.14 2003/08/07 09:05:31 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)eval.h 8.2 (Berkeley) 5/4/95 + */ + +extern char *commandname; /* currently executing command */ +extern int exitstatus; /* exit status of last command */ +extern int back_exitstatus; /* exit status of backquoted command */ +extern struct strlist *cmdenviron; /* environment for builtin command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + +void evalstring(char *, int); +union node; /* BLETCH for ansi C */ +void evaltree(union node *, int); +void evalbackcmd(union node *, struct backcmd *); + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +extern int funcnest; +extern int evalskip; + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 +#define SKIPFILE 4 diff --git a/sh/exec.c b/sh/exec.c new file mode 100644 index 0000000..fe3613f --- /dev/null +++ b/sh/exec.c @@ -0,0 +1,1063 @@ +/* $NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; +#else +__RCSID("$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "parser.h" +#include "redir.h" +#include "eval.h" +#include "exec.h" +#include "builtins.h" +#include "var.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "syntax.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include "show.h" +#include "jobs.h" +#include "alias.h" + + +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + short cmdtype; /* index identifying command */ + char rehash; /* if set, cd done since entry created */ + char cmdname[ARB]; /* name of command */ +}; + + +STATIC struct tblentry *cmdtable[CMDTABLESIZE]; +STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ +int exerrno = 0; /* Last exec error */ + + +STATIC void tryexec(char *, char **, char **, int); +STATIC void execinterp(char **, char **); +STATIC void printentry(struct tblentry *, int); +STATIC void clearcmdentry(int); +STATIC struct tblentry *cmdlookup(const char *, int); +STATIC void delete_cmd_entry(void); + + +extern char *const parsekwd[]; + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +void +shellexec(char **argv, char **envp, const char *path, int idx, int vforked) +{ + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp, vforked); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--idx < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp, vforked); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + + /* Map to POSIX errors */ + switch (e) { + case EACCES: + exerrno = 126; + break; + case ENOENT: + exerrno = 127; + break; + default: + exerrno = 2; + break; + } + TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", + argv[0], e, vforked, suppressint )); + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ +} + + +STATIC void +tryexec(char *cmd, char **argv, char **envp, int vforked) +{ + int e; +#ifndef BSD + char *p; +#endif + +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else + execve(cmd, argv, envp); +#endif + e = errno; + if (e == ENOEXEC) { + if (vforked) { + /* We are currently vfork(2)ed, so raise an + * exception, and evalcommand will try again + * with a normal fork(2). + */ + exraise(EXSHELLPROC); + } + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); +#if !defined(BSD) && !defined(__linux__) + pgetc(); pungetc(); /* fill up input buffer */ + p = parsenextc; + if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { + argv[0] = cmd; + execinterp(argv, envp); + } +#endif + setparam(argv + 1); + exraise(EXSHELLPROC); + } + errno = e; +} + + +#if !defined(BSD) && !defined(__linux__) +/* + * Execute an interpreter introduced by "#!", for systems where this + * feature has not been built into the kernel. If the interpreter is + * the shell, return (effectively ignoring the "#!"). If the execution + * of the interpreter fails, exit. + * + * This code peeks inside the input buffer in order to avoid actually + * reading any input. It would benefit from a rewrite. + */ + +#define NEWARGS 5 + +STATIC void +execinterp(char **argv, char **envp) +{ + int n; + char *inp; + char *outp; + char c; + char *p; + char **ap; + char *newargs[NEWARGS]; + int i; + char **ap2; + char **new; + + n = parsenleft - 2; + inp = parsenextc + 2; + ap = newargs; + for (;;) { + while (--n >= 0 && (*inp == ' ' || *inp == '\t')) + inp++; + if (n < 0) + goto bad; + if ((c = *inp++) == '\n') + break; + if (ap == &newargs[NEWARGS]) +bad: error("Bad #! line"); + STARTSTACKSTR(outp); + do { + STPUTC(c, outp); + } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); + STPUTC('\0', outp); + n++, inp--; + *ap++ = grabstackstr(outp); + } + if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ + p = newargs[0]; + for (;;) { + if (equal(p, "sh") || equal(p, "ash")) { + return; + } + while (*p != '/') { + if (*p == '\0') + goto break2; + p++; + } + p++; + } +break2:; + } + i = (char *)ap - (char *)newargs; /* size in bytes */ + if (i == 0) + error("Bad #! line"); + for (ap2 = argv ; *ap2++ != NULL ; ); + new = ckmalloc(i + ((char *)ap2 - (char *)argv)); + ap = newargs, ap2 = new; + while ((i -= sizeof (char **)) >= 0) + *ap2++ = *ap++; + ap = argv; + while (*ap2++ = *ap++); + shellexec(new, envp, pathval(), 0); + /* NOTREACHED */ +} +#endif + + + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +const char *pathopt; + +char * +padvance(const char **path, const char *name) +{ + const char *p; + char *q; + const char *start; + int len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + memcpy(q, start, p - start); + q += p - start; + *q++ = '/'; + } + strcpy(q, name); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + + +/*** Command hashing code ***/ + + +int +hashcmd(int argc, char **argv) +{ + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; + + verbose = 0; + while ((c = nextopt("rv")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + } else if (c == 'v') { + verbose++; + } + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (verbose || cmdp->cmdtype == CMDNORMAL) + printentry(cmdp, verbose); + } + } + return 0; + } + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) + delete_cmd_entry(); + find_command(name, &entry, DO_ERR, pathval()); + if (verbose) { + if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ + cmdp = cmdlookup(name, 0); + printentry(cmdp, verbose); + } + flushall(); + } + argptr++; + } + return 0; +} + + +STATIC void +printentry(struct tblentry *cmdp, int verbose) +{ + int idx; + const char *path; + char *name; + + switch (cmdp->cmdtype) { + case CMDNORMAL: + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + out1str(name); + break; + case CMDSPLBLTIN: + out1fmt("special builtin %s", cmdp->cmdname); + break; + case CMDBUILTIN: + out1fmt("builtin %s", cmdp->cmdname); + break; + case CMDFUNCTION: + out1fmt("function %s", cmdp->cmdname); + if (verbose) { + struct procstat ps; + INTOFF; + commandtext(&ps, cmdp->param.func); + INTON; + out1str("() { "); + out1str(ps.cmd); + out1str("; }"); + } + break; + default: + error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype); + } + if (cmdp->rehash) + out1c('*'); + out1c('\n'); +} + + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +void +find_command(char *name, struct cmdentry *entry, int act, const char *path) +{ + struct tblentry *cmdp, loc_cmd; + int idx; + int prev; + char *fullname; + struct stat statb; + int e; + int (*bltin)(int,char **); + + /* If name contains a slash, don't use PATH or hash table */ + if (strchr(name, '/') != NULL) { + if (act & DO_ABS) { + while (stat(name, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + entry->cmdtype = CMDUNKNOWN; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } + + if (path != pathval()) + act |= DO_ALTPATH; + + if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) + act |= DO_ALTBLTIN; + + /* If name is in the table, check answer will be ok */ + if ((cmdp = cmdlookup(name, 0)) != NULL) { + do { + switch (cmdp->cmdtype) { + case CMDNORMAL: + if (act & DO_ALTPATH) { + cmdp = NULL; + continue; + } + break; + case CMDFUNCTION: + if (act & DO_NOFUNC) { + cmdp = NULL; + continue; + } + break; + case CMDBUILTIN: + if ((act & DO_ALTBLTIN) || builtinloc >= 0) { + cmdp = NULL; + continue; + } + break; + } + /* if not invalidated by cd, we're done */ + if (cmdp->rehash == 0) + goto success; + } while (0); + } + + /* If %builtin not in path, check for builtin next */ + if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && + (bltin = find_builtin(name)) != 0) + goto builtin_success; + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; + else + prev = cmdp->param.index; + } + + e = ENOENT; + idx = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + idx++; + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((bltin = find_builtin(name)) == 0) + goto loop; + goto builtin_success; + } else if (prefix("func", pathopt)) { + /* handled below */ + } else { + /* ignore unimplemented options */ + goto loop; + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && idx <= prev) { + if (idx < prev) + goto loop; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + goto loop; + } + e = EACCES; /* if we fail, this will be the error */ + if (!S_ISREG(statb.st_mode)) + goto loop; + if (pathopt) { /* this is a %func directory */ + if (act & DO_NOFUNC) + goto loop; + stalloc(strlen(fullname) + 1); + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || + cmdp->cmdtype != CMDFUNCTION) + error("%s not defined in %s", name, fullname); + stunalloc(fullname); + goto success; + } +#ifdef notdef + /* XXX this code stops root executing stuff, and is buggy + if you need a group from the group list. */ + if (statb.st_uid == geteuid()) { + if ((statb.st_mode & 0100) == 0) + goto loop; + } else if (statb.st_gid == getegid()) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else { + if ((statb.st_mode & 01) == 0) + goto loop; + } +#endif + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + INTOFF; + if (act & DO_ALTPATH) { + stalloc(strlen(fullname) + 1); + cmdp = &loc_cmd; + } else + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = idx; + INTON; + goto success; + } + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp) + delete_cmd_entry(); + if (act & DO_ERR) + outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +builtin_success: + INTOFF; + if (act & DO_ALTPATH) + cmdp = &loc_cmd; + else + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + /* DO_NOFUNC must have been set */ + cmdp = &loc_cmd; + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.bltin = bltin; + INTON; +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +/* + * Search the table of builtin commands. + */ + +int +(*find_builtin(name))(int, char **) + char *name; +{ + const struct builtincmd *bp; + + for (bp = builtincmd ; bp->name ; bp++) { + if (*bp->name == *name && equal(bp->name, name)) + return bp->builtin; + } + return 0; +} + +int +(*find_splbltin(name))(int, char **) + char *name; +{ + const struct builtincmd *bp; + + for (bp = splbltincmd ; bp->name ; bp++) { + if (*bp->name == *name && equal(bp->name, name)) + return bp->builtin; + } + return 0; +} + +/* + * At shell startup put special builtins into hash table. + * ensures they are executed first (see posix). + * We stop functions being added with the same name + * (as they are impossible to call) + */ + +void +hash_special_builtins(void) +{ + const struct builtincmd *bp; + struct tblentry *cmdp; + + for (bp = splbltincmd ; bp->name ; bp++) { + cmdp = cmdlookup(bp->name, 1); + cmdp->cmdtype = CMDSPLBLTIN; + cmdp->param.bltin = bp->builtin; + } +} + + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +void +hashcd(void) +{ + struct tblentry **pp; + struct tblentry *cmdp; + + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) + cmdp->rehash = 1; + } + } +} + + + +/* + * Fix command hash table when PATH changed. + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. + * Called with interrupts off. + */ + +void +changepath(const char *newval) +{ + const char *old, *new; + int idx; + int firstchange; + int bltin; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + idx = 0; + bltin = -1; + for (;;) { + if (*old != *new) { + firstchange = idx; + if ((*old == '\0' && *new == ':') + || (*old == ':' && *new == '\0')) + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) + bltin = idx; + if (*new == ':') { + idx++; + } + new++, old++; + } + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + if (builtinloc >= 0 && bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +STATIC void +clearcmdentry(int firstchange) +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) + || (cmdp->cmdtype == CMDBUILTIN && + builtinloc >= firstchange)) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + +/* + * Delete all functions. + */ + +#ifdef mkinit +MKINIT void deletefuncs(void); +MKINIT void hash_special_builtins(void); + +INIT { + hash_special_builtins(); +} + +SHELLPROC { + deletefuncs(); +} +#endif + +void +deletefuncs(void) +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION) { + *pp = cmdp->next; + freefunc(cmdp->param.func); + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +struct tblentry **lastcmdentry; + + +STATIC struct tblentry * +cmdlookup(const char *name, int add) +{ + int hashval; + const char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = *p << 4; + while (*p) + hashval += *p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + cmdp->rehash = 0; + strcpy(cmdp->cmdname, name); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +STATIC void +delete_cmd_entry(void) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + +#ifdef notdef +void +getcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp = cmdlookup(name, 0); + + if (cmdp) { + entry->u = cmdp->param; + entry->cmdtype = cmdp->cmdtype; + } else { + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + } +} +#endif + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name - except special builtins. + */ + +STATIC void +addcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype != CMDSPLBLTIN) { + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + } + INTON; +} + + +/* + * Define a shell function. + */ + +void +defun(char *name, union node *func) +{ + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + addcmdentry(name, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + */ + +int +unsetfunc(char *name) +{ + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && + cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); + return (0); + } + return (1); +} + +/* + * Locate and print what a word is... + * also used for 'command -[v|V]' + */ + +int +typecmd(int argc, char **argv) +{ + struct cmdentry entry; + struct tblentry *cmdp; + char * const *pp; + struct alias *ap; + int err = 0; + char *arg; + int c; + int V_flag = 0; + int v_flag = 0; + int p_flag = 0; + + while ((c = nextopt("vVp")) != 0) { + switch (c) { + case 'v': v_flag = 1; break; + case 'V': V_flag = 1; break; + case 'p': p_flag = 1; break; + } + } + + if (p_flag && (v_flag || V_flag)) + error("cannot specify -p with -v or -V"); + + while ((arg = *argptr++)) { + if (!v_flag) + out1str(arg); + /* First look at the keywords */ + for (pp = parsekwd; *pp; pp++) + if (**pp == *arg && equal(*pp, arg)) + break; + + if (*pp) { + if (v_flag) + err = 1; + else + out1str(" is a shell keyword\n"); + continue; + } + + /* Then look at the aliases */ + if ((ap = lookupalias(arg, 1)) != NULL) { + if (!v_flag) + out1fmt(" is an alias for \n"); + out1fmt("%s\n", ap->val); + continue; + } + + /* Then check if it is a tracked alias */ + if ((cmdp = cmdlookup(arg, 0)) != NULL) { + entry.cmdtype = cmdp->cmdtype; + entry.u = cmdp->param; + } else { + /* Finally use brute force */ + find_command(arg, &entry, DO_ABS, pathval()); + } + + switch (entry.cmdtype) { + case CMDNORMAL: { + if (strchr(arg, '/') == NULL) { + const char *path = pathval(); + char *name; + int j = entry.u.index; + do { + name = padvance(&path, arg); + stunalloc(name); + } while (--j >= 0); + if (!v_flag) + out1fmt(" is%s ", + cmdp ? " a tracked alias for" : ""); + out1fmt("%s\n", name); + } else { + if (access(arg, X_OK) == 0) { + if (!v_flag) + out1fmt(" is "); + out1fmt("%s\n", arg); + } else { + if (!v_flag) + out1fmt(": %s\n", + strerror(errno)); + else + err = 126; + } + } + break; + } + case CMDFUNCTION: + if (!v_flag) + out1str(" is a shell function\n"); + else + out1fmt("%s\n", arg); + break; + + case CMDBUILTIN: + if (!v_flag) + out1str(" is a shell builtin\n"); + else + out1fmt("%s\n", arg); + break; + + case CMDSPLBLTIN: + if (!v_flag) + out1str(" is a special shell builtin\n"); + else + out1fmt("%s\n", arg); + break; + + default: + if (!v_flag) + out1str(": not found\n"); + err = 127; + break; + } + } + return err; +} diff --git a/sh/exec.h b/sh/exec.h new file mode 100644 index 0000000..26fd09c --- /dev/null +++ b/sh/exec.h @@ -0,0 +1,79 @@ +/* $NetBSD: exec.h,v 1.21 2003/08/07 09:05:31 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exec.h 8.3 (Berkeley) 6/8/95 + */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDFUNCTION 1 /* command is a shell function */ +#define CMDBUILTIN 2 /* command is a shell builtin */ +#define CMDSPLBLTIN 3 /* command is a special shell builtin */ + + +struct cmdentry { + int cmdtype; + union param { + int index; + int (*bltin)(int, char**); + union node *func; + } u; +}; + + +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_ABS 0x02 /* checks absolute paths */ +#define DO_NOFUNC 0x04 /* don't return shell functions, for command */ +#define DO_ALTPATH 0x08 /* using alternate path */ +#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ + +extern const char *pathopt; /* set by padvance */ + +void shellexec(char **, char **, const char *, int, int) + __attribute__((__noreturn__)); +char *padvance(const char **, const char *); +int hashcmd(int, char **); +void find_command(char *, struct cmdentry *, int, const char *); +int (*find_builtin(char *))(int, char **); +int (*find_splbltin(char *))(int, char **); +void hashcd(void); +void changepath(const char *); +void deletefuncs(void); +void getcmdentry(char *, struct cmdentry *); +void addcmdentry(char *, struct cmdentry *); +void defun(char *, union node *); +int unsetfunc(char *); +int typecmd(int, char **); +void hash_special_builtins(void); diff --git a/sh/expand.c b/sh/expand.c new file mode 100644 index 0000000..d3462fc --- /dev/null +++ b/sh/expand.c @@ -0,0 +1,1559 @@ +/* $NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; +#else +__RCSID("$NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "show.h" + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int inquotes; /* search for nul bytes only */ +}; + + +char *expdest; /* output of current string */ +struct nodelist *argbackq; /* list of back quote expressions */ +struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +struct ifsregion *ifslastp; /* last struct in list */ +struct arglist exparg; /* holds expanded arg list */ + +STATIC void argstr(char *, int); +STATIC char *exptilde(char *, int); +STATIC void expbackq(union node *, int, int); +STATIC int subevalvar(char *, char *, int, int, int, int); +STATIC char *evalvar(char *, int); +STATIC int varisset(char *, int); +STATIC void varvalue(char *, int, int, int); +STATIC void recordregion(int, int, int); +STATIC void removerecordregions(int); +STATIC void ifsbreakup(char *, struct arglist *); +STATIC void ifsfree(void); +STATIC void expandmeta(struct strlist *, int); +STATIC void expmeta(char *, char *); +STATIC void addfname(char *); +STATIC struct strlist *expsort(struct strlist *); +STATIC struct strlist *msort(struct strlist *, int); +STATIC int pmatch(char *, char *, int); +STATIC char *cvtnum(int, char *); + +/* + * Expand shell variables and backquotes inside a here document. + */ + +void +expandhere(union node *arg, int fd) +{ + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - stackblock()); +} + + +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If EXP_FULL is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ + +void +expandarg(union node *arg, struct arglist *arglist, int flag) +{ + struct strlist *sp; + char *p; + + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + ifsfirst.next = NULL; + ifslastp = NULL; + argstr(arg->narg.text, flag); + if (arglist == NULL) { + return; /* here document expanded */ + } + STPUTC('\0', expdest); + p = grabstackstr(expdest); + exparg.lastp = &exparg.list; + /* + * TODO - EXP_REDIR + */ + if (flag & EXP_FULL) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list, flag); + } else { + if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ + rmescapes(p); + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + ifsfree(); + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + + +/* + * Perform variable and command substitution. + * If EXP_FULL is set, output CTLESC characters to allow for further processing. + * Otherwise treat $@ like $* since no splitting will be performed. + */ + +STATIC void +argstr(char *p, int flag) +{ + char c; + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int firsteq = 1; + const char *ifs = 0; + int ifs_split = EXP_IFS_SPLIT; + + if (flag & EXP_IFS_SPLIT) + ifs = ifsset() ? ifsval() : " \t\n"; + + if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) + p = exptilde(p, flag); + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ + return; + case CTLQUOTEMARK: + /* "$@" syntax adherence hack */ + if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') + break; + if ((flag & EXP_FULL) != 0) + STPUTC(c, expdest); + ifs_split = 0; + break; + case CTLQUOTEEND: + ifs_split = EXP_IFS_SPLIT; + break; + case CTLESC: + if (quotes) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, flag); + argbackq = argbackq->next; + break; + case CTLENDARI: + expari(flag); + break; + case ':': + case '=': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + STPUTC(c, expdest); + if (flag & EXP_VARTILDE && *p == '~') { + if (c == '=') { + if (firsteq) + firsteq = 0; + else + break; + } + p = exptilde(p, flag); + } + break; + default: + STPUTC(c, expdest); + if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) { + /* We need to get the output split here... */ + recordregion(expdest - stackblock() - 1, + expdest - stackblock(), 0); + } + break; + } + } +} + +STATIC char * +exptilde(char *p, int flag) +{ + char c, *startp = p; + const char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); + + while ((c = *p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + return (startp); + case ':': + if (flag & EXP_VARTILDE) + goto done; + break; + case '/': + goto done; + } + p++; + } +done: + *p = '\0'; + if (*(startp+1) == '\0') { + if ((home = lookupvar("HOME")) == NULL) + goto lose; + } else + goto lose; + if (*home == '\0') + goto lose; + *p = c; + while ((c = *home++) != '\0') { + if (quotes && SQSYNTAX[(int)c] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(c, expdest); + } + return (p); +lose: + *p = c; + return (startp); +} + + +STATIC void +removerecordregions(int endoff) +{ + if (ifslastp == NULL) + return; + + if (ifsfirst.endoff > endoff) { + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + if (ifsfirst.begoff > endoff) + ifslastp = NULL; + else { + ifslastp = &ifsfirst; + ifsfirst.endoff = endoff; + } + return; + } + + ifslastp = &ifsfirst; + while (ifslastp->next && ifslastp->next->begoff < endoff) + ifslastp=ifslastp->next; + while (ifslastp->next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifslastp->next->next; + ckfree(ifslastp->next); + ifslastp->next = ifsp; + INTON; + } + if (ifslastp->endoff > endoff) + ifslastp->endoff = endoff; +} + + +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +void +expari(int flag) +{ + char *p, *start; + int result; + int begoff; + int quotes = flag & (EXP_FULL | EXP_CASE); + int quoted; + + /* ifsfree(); */ + + /* + * This routine is slightly over-complicated for + * efficiency. First we make sure there is + * enough space for the result, which may be bigger + * than the expression if we add exponentation. Next we + * scan backwards looking for the start of arithmetic. If the + * next previous character is a CTLESC character, then we + * have to rescan starting from the beginning since CTLESC + * characters have to be processed left to right. + */ +#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 +#error "integers with more than 10 digits are not supported" +#endif + CHECKSTRSPACE(12 - 2, expdest); + USTPUTC('\0', expdest); + start = stackblock(); + p = expdest - 1; + while (*p != CTLARI && p >= start) + --p; + if (*p != CTLARI) + error("missing CTLARI (shouldn't happen)"); + if (p > start && *(p-1) == CTLESC) + for (p = start; *p != CTLARI; p++) + if (*p == CTLESC) + p++; + + if (p[1] == '"') + quoted=1; + else + quoted=0; + begoff = p - start; + removerecordregions(begoff); + if (quotes) + rmescapes(p+2); + result = arith(p+2); + fmtstr(p, 12, "%d", result); + + while (*p++) + ; + + if (quoted == 0) + recordregion(begoff, p - 1 - start, 0); + result = expdest - p + 1; + STADJUST(-result, expdest); +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(union node *cmd, int quoted, int flag) +{ + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + struct ifsregion saveifs, *savelastp; + struct nodelist *saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int saveherefd; + int quotes = flag & (EXP_FULL | EXP_CASE); + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (quotes && syntax[(int)lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + + /* Eat all trailing newlines */ + p = stackblock() + startloc; + while (dest > p && dest[-1] == '\n') + STUNPUTC(dest); + + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + back_exitstatus = waitforjob(in.jp); + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + + + +STATIC int +subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags) +{ + char *startp; + char *loc = NULL; + char *q; + int c = 0; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + int amount; + + herefd = -1; + argstr(p, 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + if (str == NULL) + str = stackblock() + strloc; + + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + varflags &= ~VSNUL; + if (c != 0) + *loc = c; + return 1; + + case VSQUESTION: + if (*p != CTLENDVAR) { + outfmt(&errout, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - str - 1, + str, (varflags & VSNUL) ? "null or " + : nullstr); + /* NOTREACHED */ + + case VSTRIMLEFT: + for (loc = startp; loc < str; loc++) { + c = *loc; + *loc = '\0'; + if (patmatch(str, startp, varflags & VSQUOTE)) + goto recordleft; + *loc = c; + if ((varflags & VSQUOTE) && *loc == CTLESC) + loc++; + } + return 0; + + case VSTRIMLEFTMAX: + for (loc = str - 1; loc >= startp;) { + c = *loc; + *loc = '\0'; + if (patmatch(str, startp, varflags & VSQUOTE)) + goto recordleft; + *loc = c; + loc--; + if ((varflags & VSQUOTE) && loc > startp && + *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch(str, loc, varflags & VSQUOTE)) + goto recordright; + loc--; + if ((varflags & VSQUOTE) && loc > startp && + *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch(str, loc, varflags & VSQUOTE)) + goto recordright; + if ((varflags & VSQUOTE) && *loc == CTLESC) + loc++; + } + return 0; + + default: + abort(); + } + +recordleft: + *loc = c; + amount = ((str - 1) - (loc - startp)) - expdest; + STADJUST(amount, expdest); + while (loc != str - 1) + *startp++ = *loc++; + return 1; + +recordright: + amount = loc - expdest; + STADJUST(amount, expdest); + STPUTC('\0', expdest); + STADJUST(-1, expdest); + return 1; +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +STATIC char * +evalvar(char *p, int flag) +{ + int subtype; + int varflags; + char *var; + char *val; + int patloc; + int c; + int set; + int special; + int startloc; + int varlen; + int apply_ifs; + int quotes = flag & (EXP_FULL | EXP_CASE); + + varflags = (unsigned char)*p++; + subtype = varflags & VSTYPE; + var = p; + special = !is_name(*p); + p = strchr(p, '=') + 1; + +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(var, varflags & VSNUL); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { + val = NULL; + set = 0; + } else + set = 1; + } + + varlen = 0; + startloc = expdest - stackblock(); + + if (!set && uflag) { + switch (subtype) { + case VSNORMAL: + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + case VSLENGTH: + error("%.*s: parameter not set", p - var - 1, var); + /* NOTREACHED */ + } + } + + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(var, varflags & VSQUOTE, subtype, flag); + if (subtype == VSLENGTH) { + varlen = expdest - stackblock() - startloc; + STADJUST(-varlen, expdest); + } + } else { + char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX + : BASESYNTAX; + + if (subtype == VSLENGTH) { + for (;*val; val++) + varlen++; + } else { + while (*val) { + if (quotes && syntax[(int)*val] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*val++, expdest); + } + + } + } + } + + + apply_ifs = ((varflags & VSQUOTE) == 0 || + (*var == '@' && shellparam.nparam != 1)); + + switch (subtype) { + case VSLENGTH: + expdest = cvtnum(varlen, expdest); + break; + + case VSNORMAL: + break; + + case VSPLUS: + set = !set; + /* FALLTHROUGH */ + case VSMINUS: + if (!set) { + argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); + /* + * ${x-a b c} doesn't get split, but removing the + * 'apply_ifs = 0' apparantly breaks ${1+"$@"}.. + * ${x-'a b' c} should generate 2 args. + */ + /* We should have marked stuff already */ + apply_ifs = 0; + } + break; + + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + if (!set) + break; + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags) == 0) { + int amount = (expdest - stackblock() - patloc) + 1; + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + apply_ifs = 1; + break; + + case VSASSIGN: + case VSQUESTION: + if (set) + break; + if (subevalvar(p, var, 0, subtype, startloc, varflags)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + apply_ifs = 0; + break; + + default: + abort(); + } + + if (apply_ifs) + recordregion(startloc, expdest - stackblock(), + varflags & VSQUOTE); + + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + + +/* + * Test whether a specialized variable is set. + */ + +STATIC int +varisset(char *name, int nulok) +{ + if (*name == '!') + return backgndpid != -1; + else if (*name == '@' || *name == '*') { + if (*shellparam.p == NULL) + return 0; + + if (nulok) { + char **av; + + for (av = shellparam.p; *av; av++) + if (**av != '\0') + return 1; + return 0; + } + } else if (is_digit(*name)) { + char *ap; + int num = atoi(name); + + if (num > shellparam.nparam) + return 0; + + if (num == 0) + ap = arg0; + else + ap = shellparam.p[num - 1]; + + if (nulok && (ap == NULL || *ap == '\0')) + return 0; + } + return 1; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC void +varvalue(char *name, int quoted, int subtype, int flag) +{ + int num; + char *p; + int i; + char sep; + char **ap; + char const *syntax; + +#define STRTODEST(p) \ + do {\ + if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ + syntax = quoted? DQSYNTAX : BASESYNTAX; \ + while (*p) { \ + if (syntax[(int)*p] == CCTL) \ + STPUTC(CTLESC, expdest); \ + STPUTC(*p++, expdest); \ + } \ + } else \ + while (*p) \ + STPUTC(*p++, expdest); \ + } while (0) + + + switch (*name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + expdest = cvtnum(num, expdest); + break; + case '-': + for (i = 0; optlist[i].name; i++) { + if (optlist[i].val) + STPUTC(optlist[i].letter, expdest); + } + break; + case '@': + if (flag & EXP_FULL && quoted) { + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + STRTODEST(p); + if (*ap) + STPUTC('\0', expdest); + } + break; + } + /* fall through */ + case '*': + if (ifsset() != 0) + sep = ifsval()[0]; + else + sep = ' '; + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + STRTODEST(p); + if (*ap && sep) + STPUTC(sep, expdest); + } + break; + case '0': + p = arg0; + STRTODEST(p); + break; + default: + if (is_digit(*name)) { + num = atoi(name); + if (num > 0 && num <= shellparam.nparam) { + p = shellparam.p[num - 1]; + STRTODEST(p); + } + } + break; + } +} + + + +/* + * Record the fact that we have to scan this region of the + * string for IFS characters. + */ + +STATIC void +recordregion(int start, int end, int inquotes) +{ + struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + if (ifslastp->endoff == start + && ifslastp->inquotes == inquotes) { + /* extend previous area */ + ifslastp->endoff = end; + return; + } + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifslastp->next = ifsp; + } + ifslastp = ifsp; + ifslastp->next = NULL; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->inquotes = inquotes; +} + + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ +STATIC void +ifsbreakup(char *string, struct arglist *arglist) +{ + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + char *p; + char *q; + const char *ifs; + const char *ifsspc; + int inquotes; + + start = string; + ifsspc = NULL; + inquotes = 0; + + if (ifslastp == NULL) { + /* Return entire argument, IFS doesn't apply to any of it */ + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + return; + } + + ifs = ifsset() ? ifsval() : " \t\n"; + + for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { + p = string + ifsp->begoff; + inquotes = ifsp->inquotes; + ifsspc = NULL; + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (inquotes) { + /* Only NULs (probably from "$@") end args */ + if (*p != 0) { + p++; + continue; + } + } else { + if (!strchr(ifs, *p)) { + p++; + continue; + } + ifsspc = strchr(" \t\n", *p); + + /* Ignore IFS whitespace at start */ + if (q == start && ifsspc != NULL) { + p++; + start = p; + continue; + } + } + + /* Save this argument... */ + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + p++; + + if (ifsspc != NULL) { + /* Ignore further trailing IFS whitespace */ + for (; p < string + ifsp->endoff; p++) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p) == NULL) { + p = q; + break; + } + if (strchr(" \t\n", *p) == NULL) { + p++; + break; + } + } + } + start = p; + } + } + + /* + * Save anything left as an argument. + * Traditionally we have treated 'IFS=':'; set -- x$IFS' as + * generating 2 arguments, the second of which is empty. + * Some recent clarification of the Posix spec say that it + * should only generate one.... + */ + if (*start /* || (!ifsspc && start > string) */) { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } +} + +STATIC void +ifsfree(void) +{ + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + ifslastp = NULL; + ifsfirst.next = NULL; +} + + + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +char *expdir; + + +STATIC void +expandmeta(struct strlist *str, int flag) +{ + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + /* TODO - EXP_REDIR */ + + while (str) { + if (fflag) + goto nometa; + p = str->text; + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) { + int i = strlen(str->text); + expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ + } + + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + INTON; + if (exparg.lastp == savelastp) { + /* + * no matches + */ +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +STATIC void +expmeta(char *enddir, char *name) +{ + char *p; + const char *cp; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + while (*q == CTLQUOTEMARK) + q++; + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLQUOTEMARK) + continue; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLQUOTEMARK) + continue; + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || lstat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + if (enddir == expdir) { + cp = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + cp = "/"; + } else { + cp = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(cp)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + p = start; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name, 0)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + for (p = enddir, cp = dp->d_name; + (*p++ = *cp++) != '\0';) + continue; + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; +} + + +/* + * Add a file name to the list. + */ + +STATIC void +addfname(char *name) +{ + char *p; + struct strlist *sp; + + p = stalloc(strlen(name) + 1); + scopy(name, p); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + + +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +STATIC struct strlist * +expsort(struct strlist *str) +{ + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} + + +STATIC struct strlist * +msort(struct strlist *list, int len) +{ + struct strlist *p, *q = NULL; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} + + + +/* + * Returns true if the pattern matches the string. + */ + +int +patmatch(char *pattern, char *string, int squoted) +{ +#ifdef notdef + if (pattern[0] == '!' && pattern[1] == '!') + return 1 - pmatch(pattern + 2, string); + else +#endif + return pmatch(pattern, string, squoted); +} + + +STATIC int +pmatch(char *pattern, char *string, int squoted) +{ + char *p, *q; + char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (squoted && *q == CTLESC) + q++; + if (*q++ != *p++) + return 0; + break; + case CTLQUOTEMARK: + continue; + case '?': + if (squoted && *q == CTLESC) + q++; + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == CTLQUOTEMARK || c == '*') + c = *++p; + if (c != CTLESC && c != CTLQUOTEMARK && + c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (squoted && *q == CTLESC && + q[1] == c) + break; + if (*q == '\0') + return 0; + if (squoted && *q == CTLESC) + q++; + q++; + } + } + do { + if (pmatch(p, q, squoted)) + return 1; + if (squoted && *q == CTLESC) + q++; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + while (*endp == CTLQUOTEMARK) + endp++; + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + if (squoted && chr == CTLESC) + chr = *q++; + if (chr == '\0') + return 0; + c = *p++; + do { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (squoted && *q == CTLESC) + q++; + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} + + + +/* + * Remove any CTLESC characters from a string. + */ + +void +rmescapes(char *str) +{ + char *p, *q; + + p = str; + while (*p != CTLESC && *p != CTLQUOTEMARK) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} + + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(union node *pattern, char *val) +{ + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, val, 0); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +STATIC char * +cvtnum(int num, char *buf) +{ + char temp[32]; + int neg = num < 0; + char *p = temp + 31; + + temp[31] = '\0'; + + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + + if (neg) + *--p = '-'; + + while (*p) + STPUTC(*p++, buf); + return buf; +} + +/* + * Do most of the work for wordexp(3). + */ + +int +wordexpcmd(int argc, char **argv) +{ + size_t len; + int i; + + out1fmt("%d", argc - 1); + out1c('\0'); + for (i = 1, len = 0; i < argc; i++) + len += strlen(argv[i]); + out1fmt("%zd", len); + out1c('\0'); + for (i = 1; i < argc; i++) { + out1str(argv[i]); + out1c('\0'); + } + return (0); +} diff --git a/sh/expand.h b/sh/expand.h new file mode 100644 index 0000000..1ea876d --- /dev/null +++ b/sh/expand.h @@ -0,0 +1,72 @@ +/* $NetBSD: expand.h,v 1.16 2004/07/13 15:05:59 seb Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)expand.h 8.2 (Berkeley) 5/4/95 + */ + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +/* + * expandarg() flags + */ +#define EXP_FULL 0x1 /* perform word splitting & file globbing */ +#define EXP_TILDE 0x2 /* do normal tilde expansion */ +#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ +#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ +#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ +#define EXP_IFS_SPLIT 0x20 /* need to record arguments for ifs breakup */ + + +union node; +void expandhere(union node *, int); +void expandarg(union node *, struct arglist *, int); +void expari(int); +int patmatch(char *, char *, int); +void rmescapes(char *); +int casematch(union node *, char *); +int wordexpcmd(int, char **); + +/* From arith.y */ +int arith(const char *); +int expcmd(int , char **); +void arith_lex_reset(void); +int yylex(void); diff --git a/sh/funcs/cmv b/sh/funcs/cmv new file mode 100644 index 0000000..667f846 --- /dev/null +++ b/sh/funcs/cmv @@ -0,0 +1,50 @@ +# $NetBSD: cmv,v 1.7 1995/05/11 21:31:05 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)cmv 8.2 (Berkeley) 5/4/95 + +# Conditional move--don't replace an existing file. + +cmv() { + if test $# != 2 + then echo "cmv: arg count" + return 2 + fi + if test -f "$2" -o -w "$2" + then echo "$2 exists" + return 2 + fi + /bin/mv "$1" "$2" +} diff --git a/sh/funcs/dirs b/sh/funcs/dirs new file mode 100644 index 0000000..68bb317 --- /dev/null +++ b/sh/funcs/dirs @@ -0,0 +1,74 @@ +# $NetBSD: dirs,v 1.7 1995/05/11 21:31:08 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)dirs 8.2 (Berkeley) 5/4/95 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/sh/funcs/kill b/sh/funcs/kill new file mode 100644 index 0000000..75b0180 --- /dev/null +++ b/sh/funcs/kill @@ -0,0 +1,50 @@ +# $NetBSD: kill,v 1.7 1995/05/11 21:31:10 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)kill 8.2 (Berkeley) 5/4/95 + +# Convert job names to process ids and then run /bin/kill. + +kill() { + local args x + args= + for x in "$@" + do case $x in + %*) x=`jobid "$x"` ;; + esac + args="$args $x" + done + /bin/kill $args +} diff --git a/sh/funcs/login b/sh/funcs/login new file mode 100644 index 0000000..7ae08b2 --- /dev/null +++ b/sh/funcs/login @@ -0,0 +1,39 @@ +# $NetBSD: login,v 1.7 1995/05/11 21:31:11 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)login 8.2 (Berkeley) 5/4/95 + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/sh/funcs/newgrp b/sh/funcs/newgrp new file mode 100644 index 0000000..796a4f1 --- /dev/null +++ b/sh/funcs/newgrp @@ -0,0 +1,38 @@ +# $NetBSD: newgrp,v 1.7 1995/05/11 21:31:12 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)newgrp 8.2 (Berkeley) 5/4/95 + +newgrp() exec newgrp "$@" diff --git a/sh/funcs/popd b/sh/funcs/popd new file mode 100644 index 0000000..b2b65d5 --- /dev/null +++ b/sh/funcs/popd @@ -0,0 +1,74 @@ +# $NetBSD: popd,v 1.7 1995/05/11 21:31:13 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)popd 8.2 (Berkeley) 5/4/95 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/sh/funcs/pushd b/sh/funcs/pushd new file mode 100644 index 0000000..b393038 --- /dev/null +++ b/sh/funcs/pushd @@ -0,0 +1,74 @@ +# $NetBSD: pushd,v 1.7 1995/05/11 21:31:15 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)pushd 8.2 (Berkeley) 5/4/95 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/sh/funcs/suspend b/sh/funcs/suspend new file mode 100644 index 0000000..8a4197d --- /dev/null +++ b/sh/funcs/suspend @@ -0,0 +1,42 @@ +# $NetBSD: suspend,v 1.7 1995/05/11 21:31:17 christos Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)suspend 8.2 (Berkeley) 5/4/95 + +suspend() { + local - + set +j + kill -TSTP 0 +} diff --git a/sh/histedit.c b/sh/histedit.c new file mode 100644 index 0000000..4bb2b34 --- /dev/null +++ b/sh/histedit.c @@ -0,0 +1,540 @@ +/* $NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +/* + * Editline and history functions (and glue). + */ +#include "shell.h" +#include "parser.h" +#include "var.h" +#include "options.h" +#include "main.h" +#include "output.h" +#include "mystring.h" +#include "myhistedit.h" +#include "error.h" +#ifndef SMALL +#include "eval.h" +#include "memalloc.h" + +#define MAXHISTLOOPS 4 /* max recursions through fc */ +#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ + +History *hist; /* history cookie */ +EditLine *el; /* editline cookie */ +int displayhist; +static FILE *el_in, *el_out; + +STATIC const char *fc_replace(const char *, char *, char *); + +#ifdef DEBUG +extern FILE *tracefile; +#endif + +/* + * Set history and editing status. Called whenever the status may + * have changed (figures out what to do). + */ +void +histedit(void) +{ + FILE *el_err; + +#define editing (Eflag || Vflag) + + if (iflag) { + if (!hist) { + /* + * turn history on + */ + INTOFF; + hist = history_init(); + INTON; + + if (hist != NULL) + sethistsize(histsizeval()); + else + out2str("sh: can't initialize history\n"); + } + if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ + /* + * turn editing on + */ + char *term, *shname; + + INTOFF; + if (el_in == NULL) + el_in = fdopen(0, "r"); + if (el_out == NULL) + el_out = fdopen(2, "w"); + if (el_in == NULL || el_out == NULL) + goto bad; + el_err = el_out; +#if DEBUG + if (tracefile) + el_err = tracefile; +#endif + term = lookupvar("TERM"); + if (term) + setenv("TERM", term, 1); + else + unsetenv("TERM"); + shname = arg0; + if (shname[0] == '-') + shname++; + el = el_init(shname, el_in, el_out, el_err); + if (el != NULL) { + if (hist) + el_set(el, EL_HIST, history, hist); + el_set(el, EL_PROMPT, getprompt); + el_set(el, EL_SIGNAL, 1); + } else { +bad: + out2str("sh: can't initialize editing\n"); + } + INTON; + } else if (!editing && el) { + INTOFF; + el_end(el); + el = NULL; + INTON; + } + if (el) { + if (Vflag) + el_set(el, EL_EDITOR, "vi"); + else if (Eflag) + el_set(el, EL_EDITOR, "emacs"); + el_source(el, NULL); + } + } else { + INTOFF; + if (el) { /* no editing if not interactive */ + el_end(el); + el = NULL; + } + if (hist) { + history_end(hist); + hist = NULL; + } + INTON; + } +} + + +void +sethistsize(const char *hs) +{ + int histsize; + HistEvent he; + + if (hist != NULL) { + if (hs == NULL || *hs == '\0' || + (histsize = atoi(hs)) < 0) + histsize = 100; + history(hist, &he, H_SETSIZE, histsize); + } +} + +void +setterm(const char *term) +{ + if (el != NULL && term != NULL) + if (el_set(el, EL_TERMINAL, term) != 0) { + outfmt(out2, "sh: Can't set terminal type %s\n", term); + outfmt(out2, "sh: Using dumb terminal settings.\n"); + } +} + +int +inputrc(argc, argv) + int argc; + char **argv; +{ + if (argc != 2) { + out2str("usage: inputrc file\n"); + return 1; + } + if (el != NULL) { + if (el_source(el, argv[1])) { + out2str("inputrc: failed\n"); + return 1; + } else + return 0; + } else { + out2str("sh: inputrc ignored, not editing\n"); + return 1; + } +} + +/* + * This command is provided since POSIX decided to standardize + * the Korn shell fc command. Oh well... + */ +int +histcmd(int argc, char **argv) +{ + int ch; + const char *editor = NULL; + HistEvent he; + int lflg = 0, nflg = 0, rflg = 0, sflg = 0; + int i, retval; + const char *firststr, *laststr; + int first, last, direction; + char *pat = NULL, *repl; /* ksh "fc old=new" crap */ + static int active = 0; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char editfile[MAXPATHLEN + 1]; + FILE *efp; +#ifdef __GNUC__ + /* Avoid longjmp clobbering */ + (void) &editor; + (void) &lflg; + (void) &nflg; + (void) &rflg; + (void) &sflg; + (void) &firststr; + (void) &laststr; + (void) &pat; + (void) &repl; + (void) &efp; + (void) &argc; + (void) &argv; +#endif + + if (hist == NULL) + error("history not active"); + + if (argc == 1) + error("missing history argument"); + + optreset = 1; optind = 1; /* initialize getopt */ + while (not_fcnumber(argv[optind]) && + (ch = getopt(argc, argv, ":e:lnrs")) != -1) + switch ((char)ch) { + case 'e': + editor = optionarg; + break; + case 'l': + lflg = 1; + break; + case 'n': + nflg = 1; + break; + case 'r': + rflg = 1; + break; + case 's': + sflg = 1; + break; + case ':': + error("option -%c expects argument", optopt); + /* NOTREACHED */ + case '?': + default: + error("unknown option: -%c", optopt); + /* NOTREACHED */ + } + argc -= optind, argv += optind; + + /* + * If executing... + */ + if (lflg == 0 || editor || sflg) { + lflg = 0; /* ignore */ + editfile[0] = '\0'; + /* + * Catch interrupts to reset active counter and + * cleanup temp files. + */ + if (setjmp(jmploc.loc)) { + active = 0; + if (*editfile) + unlink(editfile); + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + if (++active > MAXHISTLOOPS) { + active = 0; + displayhist = 0; + error("called recursively too many times"); + } + /* + * Set editor. + */ + if (sflg == 0) { + if (editor == NULL && + (editor = bltinlookup("FCEDIT", 1)) == NULL && + (editor = bltinlookup("EDITOR", 1)) == NULL) + editor = DEFEDITOR; + if (editor[0] == '-' && editor[1] == '\0') { + sflg = 1; /* no edit */ + editor = NULL; + } + } + } + + /* + * If executing, parse [old=new] now + */ + if (lflg == 0 && argc > 0 && + ((repl = strchr(argv[0], '=')) != NULL)) { + pat = argv[0]; + *repl++ = '\0'; + argc--, argv++; + } + /* + * determine [first] and [last] + */ + switch (argc) { + case 0: + firststr = lflg ? "-16" : "-1"; + laststr = "-1"; + break; + case 1: + firststr = argv[0]; + laststr = lflg ? "-1" : argv[0]; + break; + case 2: + firststr = argv[0]; + laststr = argv[1]; + break; + default: + error("too many args"); + /* NOTREACHED */ + } + /* + * Turn into event numbers. + */ + first = str_to_event(firststr, 0); + last = str_to_event(laststr, 1); + + if (rflg) { + i = last; + last = first; + first = i; + } + /* + * XXX - this should not depend on the event numbers + * always increasing. Add sequence numbers or offset + * to the history element in next (diskbased) release. + */ + direction = first < last ? H_PREV : H_NEXT; + + /* + * If editing, grab a temp file. + */ + if (editor) { + int fd; + INTOFF; /* easier */ + snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP); + if ((fd = mkstemp(editfile)) < 0) + error("can't create temporary file %s", editfile); + if ((efp = fdopen(fd, "w")) == NULL) { + close(fd); + error("can't allocate stdio buffer for temp"); + } + } + + /* + * Loop through selected history events. If listing or executing, + * do it now. Otherwise, put into temp file and call the editor + * after. + * + * The history interface needs rethinking, as the following + * convolutions will demonstrate. + */ + history(hist, &he, H_FIRST); + retval = history(hist, &he, H_NEXT_EVENT, first); + for (;retval != -1; retval = history(hist, &he, direction)) { + if (lflg) { + if (!nflg) + out1fmt("%5d ", he.num); + out1str(he.str); + } else { + const char *s = pat ? + fc_replace(he.str, pat, repl) : he.str; + + if (sflg) { + if (displayhist) { + out2str(s); + } + + evalstring(strcpy(stalloc(strlen(s) + 1), s), 0); + if (displayhist && hist) { + /* + * XXX what about recursive and + * relative histnums. + */ + history(hist, &he, H_ENTER, s); + } + } else + fputs(s, efp); + } + /* + * At end? (if we were to lose last, we'd sure be + * messed up). + */ + if (he.num == last) + break; + } + if (editor) { + char *editcmd; + + fclose(efp); + editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); + sprintf(editcmd, "%s %s", editor, editfile); + evalstring(editcmd, 0); /* XXX - should use no JC command */ + INTON; + readcmdfile(editfile); /* XXX - should read back - quick tst */ + unlink(editfile); + } + + if (lflg == 0 && active > 0) + --active; + if (displayhist) + displayhist = 0; + return 0; +} + +STATIC const char * +fc_replace(const char *s, char *p, char *r) +{ + char *dest; + int plen = strlen(p); + + STARTSTACKSTR(dest); + while (*s) { + if (*s == *p && strncmp(s, p, plen) == 0) { + while (*r) + STPUTC(*r++, dest); + s += plen; + *p = '\0'; /* so no more matches */ + } else + STPUTC(*s++, dest); + } + STACKSTRNUL(dest); + dest = grabstackstr(dest); + + return (dest); +} + +int +not_fcnumber(char *s) +{ + if (s == NULL) + return 0; + if (*s == '-') + s++; + return (!is_number(s)); +} + +int +str_to_event(const char *str, int last) +{ + HistEvent he; + const char *s = str; + int relative = 0; + int i, retval; + + retval = history(hist, &he, H_FIRST); + switch (*s) { + case '-': + relative = 1; + /*FALLTHROUGH*/ + case '+': + s++; + } + if (is_number(s)) { + i = atoi(s); + if (relative) { + while (retval != -1 && i--) { + retval = history(hist, &he, H_NEXT); + } + if (retval == -1) + retval = history(hist, &he, H_LAST); + } else { + retval = history(hist, &he, H_NEXT_EVENT, i); + if (retval == -1) { + /* + * the notion of first and last is + * backwards to that of the history package + */ + retval = history(hist, &he, + last ? H_FIRST : H_LAST); + } + } + if (retval == -1) + error("history number %s not found (internal error)", + str); + } else { + /* + * pattern + */ + retval = history(hist, &he, H_PREV_STR, str); + if (retval == -1) + error("history pattern not found: %s", str); + } + return (he.num); +} +#else +int +histcmd(int argc, char **argv) +{ + error("not compiled with history support"); + /* NOTREACHED */ +} +int +inputrc(int argc, char **argv) +{ + error("not compiled with history support"); + /* NOTREACHED */ +} +#endif diff --git a/sh/init.c b/sh/init.c new file mode 100644 index 0000000..55ad172 --- /dev/null +++ b/sh/init.c @@ -0,0 +1,1090 @@ +/* + * This file was generated by the mkinit program. + */ + +#include "shell.h" +#include "mystring.h" +#include "init.h" +#include "eval.h" +#include <stdio.h> +#include "input.h" +#include "error.h" +#include <stdlib.h> +#include "options.h" +#include "redir.h" +#include <signal.h> +#include "trap.h" +#include "output.h" +#include "memalloc.h" +#include "var.h" + + + +#undef ATABSIZE +#define ATABSIZE 39 +#undef YYBISON +#define YYBISON 1 +#undef YYSKELETON_NAME +#define YYSKELETON_NAME "yacc.c" +#undef YYPURE +#define YYPURE 0 +#undef YYLSP_NEEDED +#define YYLSP_NEEDED 0 +#undef ARITH_NUM +#define ARITH_NUM 258 +#undef ARITH_LPAREN +#define ARITH_LPAREN 259 +#undef ARITH_RPAREN +#define ARITH_RPAREN 260 +#undef ARITH_OR +#define ARITH_OR 261 +#undef ARITH_AND +#define ARITH_AND 262 +#undef ARITH_BOR +#define ARITH_BOR 263 +#undef ARITH_BXOR +#define ARITH_BXOR 264 +#undef ARITH_BAND +#define ARITH_BAND 265 +#undef ARITH_NE +#define ARITH_NE 266 +#undef ARITH_EQ +#define ARITH_EQ 267 +#undef ARITH_LE +#define ARITH_LE 268 +#undef ARITH_GE +#define ARITH_GE 269 +#undef ARITH_GT +#define ARITH_GT 270 +#undef ARITH_LT +#define ARITH_LT 271 +#undef ARITH_RSHIFT +#define ARITH_RSHIFT 272 +#undef ARITH_LSHIFT +#define ARITH_LSHIFT 273 +#undef ARITH_SUB +#define ARITH_SUB 274 +#undef ARITH_ADD +#define ARITH_ADD 275 +#undef ARITH_REM +#define ARITH_REM 276 +#undef ARITH_DIV +#define ARITH_DIV 277 +#undef ARITH_MUL +#define ARITH_MUL 278 +#undef ARITH_BNOT +#define ARITH_BNOT 279 +#undef ARITH_NOT +#define ARITH_NOT 280 +#undef ARITH_UNARYPLUS +#define ARITH_UNARYPLUS 281 +#undef ARITH_UNARYMINUS +#define ARITH_UNARYMINUS 282 +#undef YYFINAL +#define YYFINAL 14 +#undef YYLAST +#define YYLAST 170 +#undef YYNTOKENS +#define YYNTOKENS 28 +#undef YYNNTS +#define YYNNTS 3 +#undef YYNRULES +#define YYNRULES 26 +#undef YYNSTATES +#define YYNSTATES 52 +#undef YYUNDEFTOK +#define YYUNDEFTOK 2 +#undef YYMAXUTOK +#define YYMAXUTOK 282 +#undef YYPACT_NINF +#define YYPACT_NINF -13 +#undef YYTABLE_NINF +#define YYTABLE_NINF -1 +#undef yyerrok +#define yyerrok (yyerrstatus = 0) +#undef yyclearin +#define yyclearin (yychar = YYEMPTY) +#undef YYEMPTY +#define YYEMPTY (-2) +#undef YYEOF +#define YYEOF 0 +#undef YYACCEPT +#define YYACCEPT goto yyacceptlab +#undef YYABORT +#define YYABORT goto yyabortlab +#undef YYERROR +#define YYERROR goto yyerrorlab +#undef YYFAIL +#define YYFAIL goto yyerrlab +#undef YYTERROR +#define YYTERROR 1 +#undef YYERRCODE +#define YYERRCODE 256 +#undef YYPOPSTACK +#define YYPOPSTACK (yyvsp--, yyssp--) +#undef YY_INT_ALIGNED +#define YY_INT_ALIGNED short int +#undef FLEX_SCANNER +#define FLEX_SCANNER +#undef YY_FLEX_MAJOR_VERSION +#define YY_FLEX_MAJOR_VERSION 2 +#undef YY_FLEX_MINOR_VERSION +#define YY_FLEX_MINOR_VERSION 5 +#undef YY_FLEX_SUBMINOR_VERSION +#define YY_FLEX_SUBMINOR_VERSION 31 +#undef FLEX_BETA +#define FLEX_BETA +#undef FLEXINT_H +#define FLEXINT_H +#undef INT8_MIN +#define INT8_MIN (-128) +#undef INT16_MIN +#define INT16_MIN (-32767-1) +#undef INT32_MIN +#define INT32_MIN (-2147483647-1) +#undef INT8_MAX +#define INT8_MAX (127) +#undef INT16_MAX +#define INT16_MAX (32767) +#undef INT32_MAX +#define INT32_MAX (2147483647) +#undef UINT8_MAX +#define UINT8_MAX (255U) +#undef UINT16_MAX +#define UINT16_MAX (65535U) +#undef UINT32_MAX +#define UINT32_MAX (4294967295U) +#undef YY_USE_CONST +#define YY_USE_CONST +#undef YY_USE_CONST +#define YY_USE_CONST +#undef yyconst +#define yyconst const +#undef yyconst +#define yyconst +#undef YY_NULL +#define YY_NULL 0 +#undef BEGIN +#define BEGIN (yy_start) = 1 + 2 * +#undef YY_START +#define YY_START (((yy_start) - 1) / 2) +#undef YYSTATE +#define YYSTATE YY_START +#undef YY_NEW_FILE +#define YY_NEW_FILE yyrestart(yyin ) +#undef YY_END_OF_BUFFER_CHAR +#define YY_END_OF_BUFFER_CHAR 0 +#undef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#undef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +#undef EOB_ACT_CONTINUE_SCAN +#define EOB_ACT_CONTINUE_SCAN 0 +#undef EOB_ACT_END_OF_FILE +#define EOB_ACT_END_OF_FILE 1 +#undef EOB_ACT_LAST_MATCH +#define EOB_ACT_LAST_MATCH 2 +#undef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +#undef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +#undef YY_BUFFER_NEW +#define YY_BUFFER_NEW 0 +#undef YY_BUFFER_NORMAL +#define YY_BUFFER_NORMAL 1 +#undef YY_BUFFER_EOF_PENDING +#define YY_BUFFER_EOF_PENDING 2 +#undef YY_CURRENT_BUFFER +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ +#undef YY_CURRENT_BUFFER_LVALUE +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] +#undef YY_FLUSH_BUFFER +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) +#undef yy_new_buffer +#define yy_new_buffer yy_create_buffer +#undef YY_SKIP_YYWRAP +#define YY_SKIP_YYWRAP +#undef yytext_ptr +#define yytext_ptr yytext +#undef YY_DO_BEFORE_ACTION +#define YY_DO_BEFORE_ACTION \ +#undef YY_NUM_RULES +#define YY_NUM_RULES 29 +#undef YY_END_OF_BUFFER +#define YY_END_OF_BUFFER 30 +#undef REJECT +#define REJECT reject_used_but_not_detected +#undef YY_MORE_ADJ +#define YY_MORE_ADJ 0 +#undef YY_RESTORE_YY_MORE_OFFSET +#define YY_RESTORE_YY_MORE_OFFSET +#undef YY_NO_UNPUT +#define YY_NO_UNPUT +#undef INITIAL +#define INITIAL 0 +#undef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#undef ECHO +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#undef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#undef YY_DECL_IS_OURS +#define YY_DECL_IS_OURS 1 +#undef YY_DECL +#define YY_DECL int yylex (void) +#undef YY_USER_ACTION +#define YY_USER_ACTION +#undef YY_BREAK +#define YY_BREAK break; +#undef YY_RULE_SETUP +#define YY_RULE_SETUP \ +#undef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#undef YYTABLES_NAME +#define YYTABLES_NAME "yytables" +#undef MAXPWD +#define MAXPWD 256 +#undef signal +#define signal bsd_signal +#undef ALL +#define ALL (E_OPEN|E_CREAT|E_EXEC) +#undef EV_EXIT +#define EV_EXIT 01 /* exit after evaluating tree */ +#undef EV_TESTED +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#undef EV_BACKCMD +#define EV_BACKCMD 04 /* command executing within back quotes */ +#undef CMDTABLESIZE +#define CMDTABLESIZE 31 /* should be prime */ +#undef ARB +#define ARB 1 /* actual size determined at run time */ +#undef NEWARGS +#define NEWARGS 5 +#undef EOF_NLEFT +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ +#undef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#undef PROFILE +#define PROFILE 0 +#undef SIGSSIZE +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) +#undef MINSIZE +#define MINSIZE 504 /* minimum size of a block */ +#undef DEFINE_OPTIONS +#define DEFINE_OPTIONS +#undef EOFMARKLEN +#define EOFMARKLEN 79 +#undef OPENBRACE +#define OPENBRACE '{' +#undef CLOSEBRACE +#define CLOSEBRACE '}' +#undef EMPTY +#define EMPTY -2 /* marks an unused slot in redirtab */ +#undef signal +#define signal bsd_signal +#undef sys_signame +#define sys_signame sys_siglist +#undef S_DFL +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#undef S_CATCH +#define S_CATCH 2 /* signal is caught */ +#undef S_IGN +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#undef S_HARD_IGN +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#undef S_RESET +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ +#undef OUTBUFSIZ +#define OUTBUFSIZ BUFSIZ +#undef BLOCK_OUT +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#undef MEM_OUT +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#undef OUTPUT_ERR +#define OUTPUT_ERR 01 /* error occurred on output */ +#undef TEMPSIZE +#define TEMPSIZE 24 +#undef HAVE_VASPRINTF +#define HAVE_VASPRINTF 1 +#undef VTABSIZE +#define VTABSIZE 39 +#undef VTABSIZE +#define VTABSIZE 517 +#undef ATABSIZE +#define ATABSIZE 39 +#undef YYBISON +#define YYBISON 1 +#undef YYSKELETON_NAME +#define YYSKELETON_NAME "yacc.c" +#undef YYPURE +#define YYPURE 0 +#undef YYLSP_NEEDED +#define YYLSP_NEEDED 0 +#undef ARITH_NUM +#define ARITH_NUM 258 +#undef ARITH_LPAREN +#define ARITH_LPAREN 259 +#undef ARITH_RPAREN +#define ARITH_RPAREN 260 +#undef ARITH_OR +#define ARITH_OR 261 +#undef ARITH_AND +#define ARITH_AND 262 +#undef ARITH_BOR +#define ARITH_BOR 263 +#undef ARITH_BXOR +#define ARITH_BXOR 264 +#undef ARITH_BAND +#define ARITH_BAND 265 +#undef ARITH_NE +#define ARITH_NE 266 +#undef ARITH_EQ +#define ARITH_EQ 267 +#undef ARITH_LE +#define ARITH_LE 268 +#undef ARITH_GE +#define ARITH_GE 269 +#undef ARITH_GT +#define ARITH_GT 270 +#undef ARITH_LT +#define ARITH_LT 271 +#undef ARITH_RSHIFT +#define ARITH_RSHIFT 272 +#undef ARITH_LSHIFT +#define ARITH_LSHIFT 273 +#undef ARITH_SUB +#define ARITH_SUB 274 +#undef ARITH_ADD +#define ARITH_ADD 275 +#undef ARITH_REM +#define ARITH_REM 276 +#undef ARITH_DIV +#define ARITH_DIV 277 +#undef ARITH_MUL +#define ARITH_MUL 278 +#undef ARITH_BNOT +#define ARITH_BNOT 279 +#undef ARITH_NOT +#define ARITH_NOT 280 +#undef ARITH_UNARYPLUS +#define ARITH_UNARYPLUS 281 +#undef ARITH_UNARYMINUS +#define ARITH_UNARYMINUS 282 +#undef YYFINAL +#define YYFINAL 14 +#undef YYLAST +#define YYLAST 170 +#undef YYNTOKENS +#define YYNTOKENS 28 +#undef YYNNTS +#define YYNNTS 3 +#undef YYNRULES +#define YYNRULES 26 +#undef YYNSTATES +#define YYNSTATES 52 +#undef YYUNDEFTOK +#define YYUNDEFTOK 2 +#undef YYMAXUTOK +#define YYMAXUTOK 282 +#undef YYPACT_NINF +#define YYPACT_NINF -13 +#undef YYTABLE_NINF +#define YYTABLE_NINF -1 +#undef yyerrok +#define yyerrok (yyerrstatus = 0) +#undef yyclearin +#define yyclearin (yychar = YYEMPTY) +#undef YYEMPTY +#define YYEMPTY (-2) +#undef YYEOF +#define YYEOF 0 +#undef YYACCEPT +#define YYACCEPT goto yyacceptlab +#undef YYABORT +#define YYABORT goto yyabortlab +#undef YYERROR +#define YYERROR goto yyerrorlab +#undef YYFAIL +#define YYFAIL goto yyerrlab +#undef YYTERROR +#define YYTERROR 1 +#undef YYERRCODE +#define YYERRCODE 256 +#undef YYPOPSTACK +#define YYPOPSTACK (yyvsp--, yyssp--) +#undef YY_INT_ALIGNED +#define YY_INT_ALIGNED short int +#undef FLEX_SCANNER +#define FLEX_SCANNER +#undef YY_FLEX_MAJOR_VERSION +#define YY_FLEX_MAJOR_VERSION 2 +#undef YY_FLEX_MINOR_VERSION +#define YY_FLEX_MINOR_VERSION 5 +#undef YY_FLEX_SUBMINOR_VERSION +#define YY_FLEX_SUBMINOR_VERSION 31 +#undef FLEX_BETA +#define FLEX_BETA +#undef FLEXINT_H +#define FLEXINT_H +#undef INT8_MIN +#define INT8_MIN (-128) +#undef INT16_MIN +#define INT16_MIN (-32767-1) +#undef INT32_MIN +#define INT32_MIN (-2147483647-1) +#undef INT8_MAX +#define INT8_MAX (127) +#undef INT16_MAX +#define INT16_MAX (32767) +#undef INT32_MAX +#define INT32_MAX (2147483647) +#undef UINT8_MAX +#define UINT8_MAX (255U) +#undef UINT16_MAX +#define UINT16_MAX (65535U) +#undef UINT32_MAX +#define UINT32_MAX (4294967295U) +#undef YY_USE_CONST +#define YY_USE_CONST +#undef YY_USE_CONST +#define YY_USE_CONST +#undef yyconst +#define yyconst const +#undef yyconst +#define yyconst +#undef YY_NULL +#define YY_NULL 0 +#undef BEGIN +#define BEGIN (yy_start) = 1 + 2 * +#undef YY_START +#define YY_START (((yy_start) - 1) / 2) +#undef YYSTATE +#define YYSTATE YY_START +#undef YY_NEW_FILE +#define YY_NEW_FILE yyrestart(yyin ) +#undef YY_END_OF_BUFFER_CHAR +#define YY_END_OF_BUFFER_CHAR 0 +#undef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#undef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +#undef EOB_ACT_CONTINUE_SCAN +#define EOB_ACT_CONTINUE_SCAN 0 +#undef EOB_ACT_END_OF_FILE +#define EOB_ACT_END_OF_FILE 1 +#undef EOB_ACT_LAST_MATCH +#define EOB_ACT_LAST_MATCH 2 +#undef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +#undef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +#undef YY_BUFFER_NEW +#define YY_BUFFER_NEW 0 +#undef YY_BUFFER_NORMAL +#define YY_BUFFER_NORMAL 1 +#undef YY_BUFFER_EOF_PENDING +#define YY_BUFFER_EOF_PENDING 2 +#undef YY_CURRENT_BUFFER +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ +#undef YY_CURRENT_BUFFER_LVALUE +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] +#undef YY_FLUSH_BUFFER +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) +#undef yy_new_buffer +#define yy_new_buffer yy_create_buffer +#undef YY_SKIP_YYWRAP +#define YY_SKIP_YYWRAP +#undef yytext_ptr +#define yytext_ptr yytext +#undef YY_DO_BEFORE_ACTION +#define YY_DO_BEFORE_ACTION \ +#undef YY_NUM_RULES +#define YY_NUM_RULES 29 +#undef YY_END_OF_BUFFER +#define YY_END_OF_BUFFER 30 +#undef REJECT +#define REJECT reject_used_but_not_detected +#undef YY_MORE_ADJ +#define YY_MORE_ADJ 0 +#undef YY_RESTORE_YY_MORE_OFFSET +#define YY_RESTORE_YY_MORE_OFFSET +#undef YY_NO_UNPUT +#define YY_NO_UNPUT +#undef INITIAL +#define INITIAL 0 +#undef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#undef ECHO +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#undef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#undef YY_DECL_IS_OURS +#define YY_DECL_IS_OURS 1 +#undef YY_DECL +#define YY_DECL int yylex (void) +#undef YY_USER_ACTION +#define YY_USER_ACTION +#undef YY_BREAK +#define YY_BREAK break; +#undef YY_RULE_SETUP +#define YY_RULE_SETUP \ +#undef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#undef YYTABLES_NAME +#define YYTABLES_NAME "yytables" +#undef MAXPWD +#define MAXPWD 256 +#undef signal +#define signal bsd_signal +#undef ALL +#define ALL (E_OPEN|E_CREAT|E_EXEC) +#undef EV_EXIT +#define EV_EXIT 01 /* exit after evaluating tree */ +#undef EV_TESTED +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#undef EV_BACKCMD +#define EV_BACKCMD 04 /* command executing within back quotes */ +#undef CMDTABLESIZE +#define CMDTABLESIZE 31 /* should be prime */ +#undef ARB +#define ARB 1 /* actual size determined at run time */ +#undef NEWARGS +#define NEWARGS 5 +#undef EOF_NLEFT +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ +#undef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#undef PROFILE +#define PROFILE 0 +#undef SIGSSIZE +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) +#undef MINSIZE +#define MINSIZE 504 /* minimum size of a block */ +#undef DEFINE_OPTIONS +#define DEFINE_OPTIONS +#undef EOFMARKLEN +#define EOFMARKLEN 79 +#undef OPENBRACE +#define OPENBRACE '{' +#undef CLOSEBRACE +#define CLOSEBRACE '}' +#undef EMPTY +#define EMPTY -2 /* marks an unused slot in redirtab */ +#undef signal +#define signal bsd_signal +#undef sys_signame +#define sys_signame sys_siglist +#undef S_DFL +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#undef S_CATCH +#define S_CATCH 2 /* signal is caught */ +#undef S_IGN +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#undef S_HARD_IGN +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#undef S_RESET +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ +#undef OUTBUFSIZ +#define OUTBUFSIZ BUFSIZ +#undef BLOCK_OUT +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#undef MEM_OUT +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#undef OUTPUT_ERR +#define OUTPUT_ERR 01 /* error occurred on output */ +#undef TEMPSIZE +#define TEMPSIZE 24 +#undef HAVE_VASPRINTF +#define HAVE_VASPRINTF 1 +#undef VTABSIZE +#define VTABSIZE 39 +#undef VTABSIZE +#define VTABSIZE 517 +#undef main +#define main echocmd +#undef YYBISON +#define YYBISON 1 +#undef YYSKELETON_NAME +#define YYSKELETON_NAME "yacc.c" +#undef YYPURE +#define YYPURE 0 +#undef YYLSP_NEEDED +#define YYLSP_NEEDED 0 +#undef ARITH_NUM +#define ARITH_NUM 258 +#undef ARITH_LPAREN +#define ARITH_LPAREN 259 +#undef ARITH_RPAREN +#define ARITH_RPAREN 260 +#undef ARITH_OR +#define ARITH_OR 261 +#undef ARITH_AND +#define ARITH_AND 262 +#undef ARITH_BOR +#define ARITH_BOR 263 +#undef ARITH_BXOR +#define ARITH_BXOR 264 +#undef ARITH_BAND +#define ARITH_BAND 265 +#undef ARITH_NE +#define ARITH_NE 266 +#undef ARITH_EQ +#define ARITH_EQ 267 +#undef ARITH_LE +#define ARITH_LE 268 +#undef ARITH_GE +#define ARITH_GE 269 +#undef ARITH_GT +#define ARITH_GT 270 +#undef ARITH_LT +#define ARITH_LT 271 +#undef ARITH_RSHIFT +#define ARITH_RSHIFT 272 +#undef ARITH_LSHIFT +#define ARITH_LSHIFT 273 +#undef ARITH_SUB +#define ARITH_SUB 274 +#undef ARITH_ADD +#define ARITH_ADD 275 +#undef ARITH_REM +#define ARITH_REM 276 +#undef ARITH_DIV +#define ARITH_DIV 277 +#undef ARITH_MUL +#define ARITH_MUL 278 +#undef ARITH_BNOT +#define ARITH_BNOT 279 +#undef ARITH_NOT +#define ARITH_NOT 280 +#undef ARITH_UNARYPLUS +#define ARITH_UNARYPLUS 281 +#undef ARITH_UNARYMINUS +#define ARITH_UNARYMINUS 282 +#undef YYFINAL +#define YYFINAL 14 +#undef YYLAST +#define YYLAST 170 +#undef YYNTOKENS +#define YYNTOKENS 28 +#undef YYNNTS +#define YYNNTS 3 +#undef YYNRULES +#define YYNRULES 26 +#undef YYNSTATES +#define YYNSTATES 52 +#undef YYUNDEFTOK +#define YYUNDEFTOK 2 +#undef YYMAXUTOK +#define YYMAXUTOK 282 +#undef YYPACT_NINF +#define YYPACT_NINF -13 +#undef YYTABLE_NINF +#define YYTABLE_NINF -1 +#undef yyerrok +#define yyerrok (yyerrstatus = 0) +#undef yyclearin +#define yyclearin (yychar = YYEMPTY) +#undef YYEMPTY +#define YYEMPTY (-2) +#undef YYEOF +#define YYEOF 0 +#undef YYACCEPT +#define YYACCEPT goto yyacceptlab +#undef YYABORT +#define YYABORT goto yyabortlab +#undef YYERROR +#define YYERROR goto yyerrorlab +#undef YYFAIL +#define YYFAIL goto yyerrlab +#undef YYTERROR +#define YYTERROR 1 +#undef YYERRCODE +#define YYERRCODE 256 +#undef YYPOPSTACK +#define YYPOPSTACK (yyvsp--, yyssp--) +#undef YY_INT_ALIGNED +#define YY_INT_ALIGNED short int +#undef FLEX_SCANNER +#define FLEX_SCANNER +#undef YY_FLEX_MAJOR_VERSION +#define YY_FLEX_MAJOR_VERSION 2 +#undef YY_FLEX_MINOR_VERSION +#define YY_FLEX_MINOR_VERSION 5 +#undef YY_FLEX_SUBMINOR_VERSION +#define YY_FLEX_SUBMINOR_VERSION 31 +#undef FLEX_BETA +#define FLEX_BETA +#undef FLEXINT_H +#define FLEXINT_H +#undef INT8_MIN +#define INT8_MIN (-128) +#undef INT16_MIN +#define INT16_MIN (-32767-1) +#undef INT32_MIN +#define INT32_MIN (-2147483647-1) +#undef INT8_MAX +#define INT8_MAX (127) +#undef INT16_MAX +#define INT16_MAX (32767) +#undef INT32_MAX +#define INT32_MAX (2147483647) +#undef UINT8_MAX +#define UINT8_MAX (255U) +#undef UINT16_MAX +#define UINT16_MAX (65535U) +#undef UINT32_MAX +#define UINT32_MAX (4294967295U) +#undef YY_USE_CONST +#define YY_USE_CONST +#undef YY_USE_CONST +#define YY_USE_CONST +#undef yyconst +#define yyconst const +#undef yyconst +#define yyconst +#undef YY_NULL +#define YY_NULL 0 +#undef BEGIN +#define BEGIN (yy_start) = 1 + 2 * +#undef YY_START +#define YY_START (((yy_start) - 1) / 2) +#undef YYSTATE +#define YYSTATE YY_START +#undef YY_NEW_FILE +#define YY_NEW_FILE yyrestart(yyin ) +#undef YY_END_OF_BUFFER_CHAR +#define YY_END_OF_BUFFER_CHAR 0 +#undef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#undef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +#undef EOB_ACT_CONTINUE_SCAN +#define EOB_ACT_CONTINUE_SCAN 0 +#undef EOB_ACT_END_OF_FILE +#define EOB_ACT_END_OF_FILE 1 +#undef EOB_ACT_LAST_MATCH +#define EOB_ACT_LAST_MATCH 2 +#undef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +#undef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +#undef YY_BUFFER_NEW +#define YY_BUFFER_NEW 0 +#undef YY_BUFFER_NORMAL +#define YY_BUFFER_NORMAL 1 +#undef YY_BUFFER_EOF_PENDING +#define YY_BUFFER_EOF_PENDING 2 +#undef YY_CURRENT_BUFFER +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ +#undef YY_CURRENT_BUFFER_LVALUE +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] +#undef YY_FLUSH_BUFFER +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) +#undef yy_new_buffer +#define yy_new_buffer yy_create_buffer +#undef yytext_ptr +#define yytext_ptr yytext +#undef YY_DO_BEFORE_ACTION +#define YY_DO_BEFORE_ACTION \ +#undef YY_NUM_RULES +#define YY_NUM_RULES 29 +#undef YY_END_OF_BUFFER +#define YY_END_OF_BUFFER 30 +#undef REJECT +#define REJECT reject_used_but_not_detected +#undef YY_MORE_ADJ +#define YY_MORE_ADJ 0 +#undef YY_RESTORE_YY_MORE_OFFSET +#define YY_RESTORE_YY_MORE_OFFSET +#undef YY_NO_UNPUT +#define YY_NO_UNPUT +#undef INITIAL +#define INITIAL 0 +#undef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#undef ECHO +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#undef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#undef YY_DECL_IS_OURS +#define YY_DECL_IS_OURS 1 +#undef YY_DECL +#define YY_DECL int yylex (void) +#undef YY_USER_ACTION +#define YY_USER_ACTION +#undef YY_BREAK +#define YY_BREAK break; +#undef YY_RULE_SETUP +#define YY_RULE_SETUP \ +#undef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#undef YYTABLES_NAME +#define YYTABLES_NAME "yytables" +#undef MAXPWD +#define MAXPWD 256 +#undef ALL +#define ALL (E_OPEN|E_CREAT|E_EXEC) +#undef EV_EXIT +#define EV_EXIT 01 /* exit after evaluating tree */ +#undef EV_TESTED +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#undef EV_BACKCMD +#define EV_BACKCMD 04 /* command executing within back quotes */ +#undef CMDTABLESIZE +#define CMDTABLESIZE 31 /* should be prime */ +#undef ARB +#define ARB 1 /* actual size determined at run time */ +#undef NEWARGS +#define NEWARGS 5 +#undef EOF_NLEFT +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ +#undef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#undef PROFILE +#define PROFILE 0 +#undef SIGSSIZE +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) +#undef MINSIZE +#define MINSIZE 504 /* minimum size of a block */ +#undef DEFINE_OPTIONS +#define DEFINE_OPTIONS +#undef EOFMARKLEN +#define EOFMARKLEN 79 +#undef OPENBRACE +#define OPENBRACE '{' +#undef CLOSEBRACE +#define CLOSEBRACE '}' +#undef EMPTY +#define EMPTY -2 /* marks an unused slot in redirtab */ +#undef S_DFL +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#undef S_CATCH +#define S_CATCH 2 /* signal is caught */ +#undef S_IGN +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#undef S_HARD_IGN +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#undef S_RESET +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ +#undef OUTBUFSIZ +#define OUTBUFSIZ BUFSIZ +#undef BLOCK_OUT +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#undef MEM_OUT +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#undef OUTPUT_ERR +#define OUTPUT_ERR 01 /* error occurred on output */ +#undef TEMPSIZE +#define TEMPSIZE 24 +#undef HAVE_VASPRINTF +#define HAVE_VASPRINTF 1 +#undef VTABSIZE +#define VTABSIZE 39 +#undef VTABSIZE +#define VTABSIZE 517 +#undef main +#define main echocmd + + + +extern void rmaliases(void); + +extern int loopnest; /* current loop nesting level */ + +extern void deletefuncs(void); +extern void hash_special_builtins(void); + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; + int prevlleft; + struct alias *ap; /* if push was associated with an alias */ +}; + +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of chars left in this buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + +extern int parselleft; /* copy of parsefile->lleft */ +extern struct parsefile basepf; /* top level input file */ +extern char basebuf[BUFSIZ]; /* buffer for top level input file */ + +extern pid_t backgndpid; /* pid of last background process */ +extern int jobctl; + +extern int tokpushback; /* last token pushed back */ +extern int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ + +struct redirtab { + struct redirtab *next; + short renamed[10]; +}; + +extern struct redirtab *redirlist; + +extern char sigmode[NSIG]; /* current value of signal */ + +extern char **environ; + + + +/* + * Initialization code. + */ + +void +init() { + + /* from exec.c: */ + { + hash_special_builtins(); + } + + /* from input.c: */ + { + basepf.nextc = basepf.buf = basebuf; + } + + /* from var.c: */ + { + char **envp; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } + } +} + + + +/* + * This routine is called when an error or an interrupt occurs in an + * interactive shell and control is returned to the main command loop. + */ + +void +reset() { + + /* from eval.c: */ + { + evalskip = 0; + loopnest = 0; + funcnest = 0; + } + + /* from input.c: */ + { + if (exception != EXSHELLPROC) + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); + } + + /* from parser.c: */ + { + tokpushback = 0; + checkkwd = 0; + } + + /* from redir.c: */ + { + while (redirlist) + popredir(); + } + + /* from output.c: */ + { + out1 = &output; + out2 = &errout; + if (memout.buf != NULL) { + ckfree(memout.buf); + memout.buf = NULL; + } + } +} + + + +/* + * This routine is called to initialize the shell to run a shell procedure. + */ + +void +initshellproc() { + + /* from alias.c: */ + { + rmaliases(); + } + + /* from eval.c: */ + { + exitstatus = 0; + } + + /* from exec.c: */ + { + deletefuncs(); + } + + /* from input.c: */ + { + popallfiles(); + } + + /* from jobs.c: */ + { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif + } + + /* from options.c: */ + { + int i; + + for (i = 0; optlist[i].name; i++) + optlist[i].val = 0; + optschanged(); + + } + + /* from redir.c: */ + { + clearredir(0); + } + + /* from trap.c: */ + { + char *sm; + + clear_traps(0); + for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } + } + + /* from var.c: */ + { + shprocvar(); + } +} diff --git a/sh/init.h b/sh/init.h new file mode 100644 index 0000000..60d924e --- /dev/null +++ b/sh/init.h @@ -0,0 +1,39 @@ +/* $NetBSD: init.h,v 1.10 2003/08/07 09:05:32 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)init.h 8.2 (Berkeley) 5/4/95 + */ + +void init(void); +void reset(void); +void initshellproc(void); diff --git a/sh/input.c b/sh/input.c new file mode 100644 index 0000000..a81fd7b --- /dev/null +++ b/sh/input.c @@ -0,0 +1,531 @@ +/* $NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; +#else +__RCSID("$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $"); +#endif +#endif /* not lint */ + +#include <stdio.h> /* defines BUFSIZ */ +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +/* + * This file implements the input routines used by the parser. + */ + +#include "shell.h" +#include "redir.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "options.h" +#include "memalloc.h" +#include "error.h" +#include "alias.h" +#include "parser.h" +#include "myhistedit.h" + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + +MKINIT +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; + int prevlleft; + struct alias *ap; /* if push was associated with an alias */ +}; + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +MKINIT +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of chars left in this buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + + +int plinno = 1; /* input line number */ +int parsenleft; /* copy of parsefile->nleft */ +MKINIT int parselleft; /* copy of parsefile->lleft */ +char *parsenextc; /* copy of parsefile->nextc */ +MKINIT struct parsefile basepf; /* top level input file */ +MKINIT char basebuf[BUFSIZ]; /* buffer for top level input file */ +struct parsefile *parsefile = &basepf; /* current input file */ +int init_editline = 0; /* editline library initialized? */ +int whichprompt; /* 1 == PS1, 2 == PS2 */ + +#if WITH_HISTORY +EditLine *el; /* cookie for editline package */ +#endif + +STATIC void pushfile(void); +static int preadfd(void); + +#ifdef mkinit +INCLUDE <stdio.h> +INCLUDE "input.h" +INCLUDE "error.h" + +INIT { + basepf.nextc = basepf.buf = basebuf; +} + +RESET { + if (exception != EXSHELLPROC) + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); +} + +SHELLPROC { + popallfiles(); +} +#endif + + +/* + * Read a line from the script. + */ + +char * +pfgets(char *line, int len) +{ + char *p = line; + int nleft = len; + int c; + + while (--nleft > 0) { + c = pgetc_macro(); + if (c == PEOF) { + if (p == line) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = '\0'; + return line; +} + + + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +int +pgetc(void) +{ + return pgetc_macro(); +} + + +static int +preadfd(void) +{ + int nr; + char *buf = parsefile->buf; + parsenextc = buf; + +retry: +#ifdef WITH_HISTORY + if (parsefile->fd == 0 && el) { + static const char *rl_cp; + static int el_len; + + if (rl_cp == NULL) + rl_cp = el_gets(el, &el_len); + if (rl_cp == NULL) + nr = 0; + else { + nr = el_len; + if (nr > BUFSIZ - 8) + nr = BUFSIZ - 8; + memcpy(buf, rl_cp, nr); + if (nr != el_len) { + el_len -= nr; + rl_cp += nr; + } else + rl_cp = 0; + } + + } else +#endif + nr = read(parsefile->fd, buf, BUFSIZ - 8); + + + if (nr <= 0) { + if (nr < 0) { + if (errno == EINTR) + goto retry; + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } + } + nr = -1; + } + return nr; +} + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, pop it; + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) If the is more stuff in this buffer, use it else call read to fill it. + * 4) Process input up to the next newline, deleting nul characters. + */ + +int +preadbuffer(void) +{ + char *p, *q; + int more; + int something; + char savec; + + if (parsefile->strpush) { + popstring(); + if (--parsenleft >= 0) + return (*parsenextc++); + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushout(&output); + flushout(&errout); + +again: + if (parselleft <= 0) { + if ((parselleft = preadfd()) == -1) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } + } + + q = p = parsenextc; + + /* delete nul characters */ + something = 0; + for (more = 1; more;) { + switch (*p) { + case '\0': + p++; /* Skip nul */ + goto check; + + case '\t': + case ' ': + break; + + case '\n': + parsenleft = q - parsenextc; + more = 0; /* Stop processing here */ + break; + + default: + something = 1; + break; + } + + *q++ = *p++; +check: + if (--parselleft <= 0) { + parsenleft = q - parsenextc - 1; + if (parsenleft < 0) + goto again; + *q = '\0'; + more = 0; + } + } + + savec = *q; + *q = '\0'; + +#ifdef WITH_HISTORY + if (parsefile->fd == 0 && hist && something) { + HistEvent he; + INTOFF; + history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, + parsenextc); + INTON; + } +#endif + + if (vflag) { + out2str(parsenextc); + flushout(out2); + } + + *q = savec; + + return *parsenextc++; +} + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc(void) +{ + parsenleft++; + parsenextc--; +} + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +void +pushstring(char *s, int len, void *ap) +{ + struct strpush *sp; + + INTOFF; +/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ + if (parsefile->strpush) { + sp = ckmalloc(sizeof (struct strpush)); + sp->prev = parsefile->strpush; + parsefile->strpush = sp; + } else + sp = parsefile->strpush = &(parsefile->basestrpush); + sp->prevstring = parsenextc; + sp->prevnleft = parsenleft; + sp->prevlleft = parselleft; + sp->ap = (struct alias *)ap; + if (ap) + ((struct alias *)ap)->flag |= ALIASINUSE; + parsenextc = s; + parsenleft = len; + INTON; +} + +void +popstring(void) +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; + parselleft = sp->prevlleft; +/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ + if (sp->ap) + sp->ap->flag &= ~ALIASINUSE; + parsefile->strpush = sp->prev; + if (sp != &(parsefile->basestrpush)) + ckfree(sp); + INTON; +} + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +void +setinputfile(const char *fname, int push) +{ + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + fd2 = copyfd(fd, 10); + close(fd); + if (fd2 < 0) + error("Out of file descriptors"); + fd = fd2; + } + setinputfd(fd, push); + INTON; +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +void +setinputfd(int fd, int push) +{ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (push) { + pushfile(); + parsefile->buf = ckmalloc(BUFSIZ); + } + if (parsefile->fd > 0) + close(parsefile->fd); + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(char *string, int push) +{ + INTOFF; + if (push) + pushfile(); + parsenextc = string; + parselleft = parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +STATIC void +pushfile(void) +{ + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->lleft = parselleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + pf->strpush = NULL; + pf->basestrpush.prev = NULL; + parsefile = pf; +} + + +void +popfile(void) +{ + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + while (pf->strpush) + popstring(); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parselleft = parsefile->lleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return to top level. + */ + +void +popallfiles(void) +{ + while (parsefile != &basepf) + popfile(); +} + + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + * + * Takes one arg, vfork, which tells it to not modify its global vars + * as it is still running in the parent. + * + * This code is (probably) unnecessary as the 'close on exec' flag is + * set and should be enough. In the vfork case it is definitely wrong + * to close the fds as another fork() may be done later to feed data + * from a 'here' document into a pipe and we don't want to close the + * pipe! + */ + +void +closescript(int vforked) +{ + if (vforked) + return; + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} diff --git a/sh/input.h b/sh/input.h new file mode 100644 index 0000000..a9d3a12 --- /dev/null +++ b/sh/input.h @@ -0,0 +1,62 @@ +/* $NetBSD: input.h,v 1.15 2003/08/07 09:05:33 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)input.h 8.2 (Berkeley) 5/4/95 + */ + +/* PEOF (the end of file marker) is defined in syntax.h */ + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ +extern int plinno; +extern int parsenleft; /* number of characters left in input buffer */ +extern char *parsenextc; /* next character in input buffer */ +extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */ + +char *pfgets(char *, int); +int pgetc(void); +int preadbuffer(void); +void pungetc(void); +void pushstring(char *, int, void *); +void popstring(void); +void setinputfile(const char *, int); +void setinputfd(int, int); +void setinputstring(char *, int); +void popfile(void); +void popallfiles(void); +void closescript(int); + +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) diff --git a/sh/jobs.c b/sh/jobs.c new file mode 100644 index 0000000..b9460b0 --- /dev/null +++ b/sh/jobs.c @@ -0,0 +1,1487 @@ +/* $NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $"); +#endif +#endif /* not lint */ + +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#define _PATH_DEVNULL "/dev/null" +#include <sys/types.h> +#include <sys/param.h> +#ifdef BSD +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif +#include <sys/wait.h> +#define killpg(s,i) kill(-(s),i) +#include <sys/ioctl.h> + +#include "shell.h" +#if JOBS +#if OLD_TTY_DRIVER +#include "sgtty.h" +#else +#include <termios.h> +#endif +#undef CEOF /* syntax.h redefines this */ +#endif +#include "redir.h" +#include "show.h" +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +// Use of process groups is disabled to allow adb shell children to terminate when the shell dies +#define USE_PROCESS_GROUPS + + +static struct job *jobtab; /* array of jobs */ +static int njobs; /* size of array */ +static int jobs_invalid; /* set in child */ +MKINIT pid_t backgndpid = -1; /* pid of last background process */ +#if JOBS +int initialpgrp; /* pgrp of shell on invocation */ +static int curjob = -1; /* current job */ +#endif +static int ttyfd = -1; + +STATIC void restartjob(struct job *); +STATIC void freejob(struct job *); +STATIC struct job *getjob(const char *, int); +STATIC int dowait(int, struct job *); +STATIC int onsigchild(void); +STATIC int waitproc(int, struct job *, int *); +STATIC void cmdtxt(union node *); +STATIC void cmdlist(union node *, int); +STATIC void cmdputs(const char *); + +#ifdef OLD_TTY_DRIVER +static pid_t tcgetpgrp(int fd); +static int tcsetpgrp(int fd, pid_t pgrp); + +static pid_t +tcgetpgrp(int fd) +{ + pid_t pgrp; + if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1) + return -1; + else + return pgrp; +} + +static int +tcsetpgrp(int fd, pid_tpgrp) +{ + return ioctl(fd, TIOCSPGRP, (char *)&pgrp); +} +#endif + +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + */ + +MKINIT int jobctl; + +void +setjobctl(int on) +{ +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif + + if (on == jobctl || rootshell == 0) + return; + if (on) { +#if defined(FIOCLEX) || defined(FD_CLOEXEC) + int err; + int i; + if (ttyfd != -1) + close(ttyfd); + if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) { + for (i = 0; i < 3; i++) { + if (isatty(i) && (ttyfd = dup(i)) != -1) + break; + } + if (i == 3) + goto out; + } + /* Move to a high fd */ + for (i = 10; i > 2; i--) { + if ((err = fcntl(ttyfd, F_DUPFD, (1 << i) - 1)) != -1) + break; + } + if (err != -1) { + close(ttyfd); + ttyfd = err; + } +#ifdef FIOCLEX + err = ioctl(ttyfd, FIOCLEX, 0); +#elif FD_CLOEXEC + err = fcntl(ttyfd, F_SETFD, + fcntl(ttyfd, F_GETFD, 0) | FD_CLOEXEC); +#endif + if (err == -1) { + close(ttyfd); + ttyfd = -1; + goto out; + } +#else + out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control"); + goto out; +#endif + do { /* while we are in the background */ + if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) { +out: + out2str("sh: can't access tty; job control turned off\n"); + mflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { + killpg(0, SIGTTIN); + continue; + } + } while (0); + +#ifdef OLD_TTY_DRIVER + if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0 + || ldisc != NTTYDISC) { + out2str("sh: need new tty driver to run job control; job control turned off\n"); + mflag = 0; + return; + } +#endif + setsignal(SIGTSTP, 0); + setsignal(SIGTTOU, 0); + setsignal(SIGTTIN, 0); +#ifdef USE_PROCESS_GROUPS + if (getpgid(0) != rootpid && setpgid(0, rootpid) == -1) + error("Cannot set process group (%s) at %d", + strerror(errno), __LINE__); + if (tcsetpgrp(ttyfd, rootpid) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); +#endif + } else { /* turning job control off */ +#ifdef USE_PROCESS_GROUPS + if (getpgid(0) != initialpgrp && setpgid(0, initialpgrp) == -1) + error("Cannot set process group (%s) at %d", + strerror(errno), __LINE__); + if (tcsetpgrp(ttyfd, initialpgrp) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); +#endif + close(ttyfd); + ttyfd = -1; + setsignal(SIGTSTP, 0); + setsignal(SIGTTOU, 0); + setsignal(SIGTTIN, 0); + } + jobctl = on; +} + + +#ifdef mkinit +INCLUDE <stdlib.h> + +SHELLPROC { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif +} + +#endif + + + +#if JOBS +int +fgcmd(int argc, char **argv) +{ + struct job *jp; + int i; + int status; + + nextopt(""); + jp = getjob(*argptr, 0); + if (jp->jobctl == 0) + error("job not created under job control"); + out1fmt("%s", jp->ps[0].cmd); + for (i = 1; i < jp->nprocs; i++) + out1fmt(" | %s", jp->ps[i].cmd ); + out1c('\n'); + flushall(); + + for (i = 0; i < jp->nprocs; i++) + if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1) + break; + + if (i >= jp->nprocs) { + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + +static void +set_curjob(struct job *jp, int mode) +{ + struct job *jp1, *jp2; + int i, ji; + + ji = jp - jobtab; + + /* first remove from list */ + if (ji == curjob) + curjob = jp->prev_job; + else { + for (i = 0; i < njobs; i++) { + if (jobtab[i].prev_job != ji) + continue; + jobtab[i].prev_job = jp->prev_job; + break; + } + } + + /* Then re-insert in correct position */ + switch (mode) { + case 0: /* job being deleted */ + jp->prev_job = -1; + break; + case 1: /* newly created job or backgrounded job, + put after all stopped jobs. */ + if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) { + for (jp1 = jobtab + curjob; ; jp1 = jp2) { + if (jp1->prev_job == -1) + break; + jp2 = jobtab + jp1->prev_job; + if (jp2->state != JOBSTOPPED) + break; + } + jp->prev_job = jp1->prev_job; + jp1->prev_job = ji; + break; + } + /* FALLTHROUGH */ + case 2: /* newly stopped job - becomes curjob */ + jp->prev_job = curjob; + curjob = ji; + break; + } +} + +int +bgcmd(int argc, char **argv) +{ + struct job *jp; + int i; + + nextopt(""); + do { + jp = getjob(*argptr, 0); + if (jp->jobctl == 0) + error("job not created under job control"); + set_curjob(jp, 1); + out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd); + for (i = 1; i < jp->nprocs; i++) + out1fmt(" | %s", jp->ps[i].cmd ); + out1c('\n'); + flushall(); + restartjob(jp); + } while (*argptr && *++argptr); + return 0; +} + + +STATIC void +restartjob(struct job *jp) +{ + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + for (i = 0; i < jp->nprocs; i++) + if (killpg(jp->ps[i].pid, SIGCONT) != -1) + break; + if (i >= jp->nprocs) + error("Cannot continue job (%s)", strerror(errno)); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = JOBRUNNING; + } + } + INTON; +} +#endif + +static void +showjob(struct output *out, struct job *jp, int mode) +{ + int procno; + int st; + struct procstat *ps; + int col; + char s[64]; + +#if JOBS + if (mode & SHOW_PGID) { + /* just output process (group) id of pipeline */ + outfmt(out, "%ld\n", (long)jp->ps->pid); + return; + } +#endif + + procno = jp->nprocs; + if (!procno) + return; + + if (mode & SHOW_PID) + mode |= SHOW_MULTILINE; + + if ((procno > 1 && !(mode & SHOW_MULTILINE)) + || (mode & SHOW_SIGNALLED)) { + /* See if we have more than one status to report */ + ps = jp->ps; + st = ps->status; + do { + int st1 = ps->status; + if (st1 != st) + /* yes - need multi-line output */ + mode |= SHOW_MULTILINE; + if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1)) + continue; + if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f) + && st1 != SIGINT && st1 != SIGPIPE)) + mode |= SHOW_ISSIG; + + } while (ps++, --procno); + procno = jp->nprocs; + } + + if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) { + if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) { + TRACE(("showjob: freeing job %d\n", jp - jobtab + 1)); + freejob(jp); + } + return; + } + + for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */ + if (ps == jp->ps) + fmtstr(s, 16, "[%ld] %c ", + (long)(jp - jobtab + 1), +#if JOBS + jp == jobtab + curjob ? '+' : + curjob != -1 && jp == jobtab + + jobtab[curjob].prev_job ? '-' : +#endif + ' '); + else + fmtstr(s, 16, " " ); + col = strlen(s); + if (mode & SHOW_PID) { + fmtstr(s + col, 16, "%ld ", (long)ps->pid); + col += strlen(s + col); + } + if (ps->status == -1) { + scopy("Running", s + col); + } else if (WIFEXITED(ps->status)) { + st = WEXITSTATUS(ps->status); + if (st) + fmtstr(s + col, 16, "Done(%d)", st); + else + fmtstr(s + col, 16, "Done"); + } else { +#if JOBS + if (WIFSTOPPED(ps->status)) + st = WSTOPSIG(ps->status); + else /* WIFSIGNALED(ps->status) */ +#endif + st = WTERMSIG(ps->status); + st &= 0x7f; + if (st < NSIG && sys_siglist[st]) + scopyn(sys_siglist[st], s + col, 32); + else + fmtstr(s + col, 16, "Signal %d", st); + if (WCOREDUMP(ps->status)) { + col += strlen(s + col); + scopyn(" (core dumped)", s + col, 64 - col); + } + } + col += strlen(s + col); + outstr(s, out); + do { + outc(' ', out); + col++; + } while (col < 30); + outstr(ps->cmd, out); + if (mode & SHOW_MULTILINE) { + if (procno > 0) { + outc(' ', out); + outc('|', out); + } + } else { + while (--procno >= 0) + outfmt(out, " | %s", (++ps)->cmd ); + } + outc('\n', out); + } + flushout(out); + jp->changed = 0; + if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) + freejob(jp); +} + + +int +jobscmd(int argc, char **argv) +{ + int mode, m; + int sv = jobs_invalid; + + jobs_invalid = 0; + mode = 0; + while ((m = nextopt("lp"))) + if (m == 'l') + mode = SHOW_PID; + else + mode = SHOW_PGID; + if (*argptr) + do + showjob(out1, getjob(*argptr,0), mode); + while (*++argptr); + else + showjobs(out1, mode); + jobs_invalid = sv; + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +void +showjobs(struct output *out, int mode) +{ + int jobno; + struct job *jp; + int silent = 0, gotpid; + + TRACE(("showjobs(%x) called\n", mode)); + + /* If not even one one job changed, there is nothing to do */ + gotpid = dowait(0, NULL); + while (dowait(0, NULL) > 0) + continue; +#ifdef JOBS + /* + * Check if we are not in our foreground group, and if not + * put us in it. + */ + if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) { + if (tcsetpgrp(ttyfd, getpid()) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + TRACE(("repaired tty process group\n")); + silent = 1; + } +#endif + if (jobs_invalid) + return; + + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (!jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if ((mode & SHOW_CHANGED) && !jp->changed) + continue; + if (silent && jp->changed) { + jp->changed = 0; + continue; + } + showjob(out, jp, mode); + } +} + +/* + * Mark a job structure as unused. + */ + +STATIC void +freejob(struct job *jp) +{ + INTOFF; + if (jp->ps != &jp->ps0) { + ckfree(jp->ps); + jp->ps = &jp->ps0; + } + jp->nprocs = 0; + jp->used = 0; +#if JOBS + set_curjob(jp, 0); +#endif + INTON; +} + + + +int +waitcmd(int argc, char **argv) +{ + struct job *job; + int status, retval = 127; + struct job *jp; + + nextopt(""); + + if (!*argptr) { + /* wait for all jobs */ + jp = jobtab; + if (jobs_invalid) + return 0; + for (;;) { + if (jp >= jobtab + njobs) { + /* no running procs */ + return 0; + } + if (!jp->used || jp->state != JOBRUNNING) { + jp++; + continue; + } + if (dowait(1, (struct job *)NULL) == -1) + return 128 + SIGINT; + jp = jobtab; + } + } + + for (; *argptr; argptr++) { + job = getjob(*argptr, 1); + if (!job) { + retval = 127; + continue; + } + /* loop until process terminated or stopped */ + while (job->state == JOBRUNNING) { + if (dowait(1, (struct job *)NULL) == -1) + return 128 + SIGINT; + } + status = job->ps[job->nprocs].status; + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + retval = WSTOPSIG(status) + 128; +#endif + else { + /* XXX: limits number of signals */ + retval = WTERMSIG(status) + 128; + } + if (!iflag) + freejob(job); + } + return retval; +} + + + +int +jobidcmd(int argc, char **argv) +{ + struct job *jp; + int i; + + nextopt(""); + jp = getjob(*argptr, 0); + for (i = 0 ; i < jp->nprocs ; ) { + out1fmt("%ld", (long)jp->ps[i].pid); + out1c(++i < jp->nprocs ? ' ' : '\n'); + } + return 0; +} + +int +getjobpgrp(const char *name) +{ + struct job *jp; + + jp = getjob(name, 1); + if (jp == 0) + return 0; + return -jp->ps[0].pid; +} + +/* + * Convert a job name to a job structure. + */ + +STATIC struct job * +getjob(const char *name, int noerror) +{ + int jobno = -1; + struct job *jp; + int pid; + int i; + const char *err_msg = "No such job: %s"; + + if (name == NULL) { +#if JOBS + jobno = curjob; +#endif + err_msg = "No current job"; + } else if (name[0] == '%') { + if (is_number(name + 1)) { + jobno = number(name + 1) - 1; + } else if (!name[2]) { + switch (name[1]) { +#if JOBS + case 0: + case '+': + case '%': + jobno = curjob; + err_msg = "No current job"; + break; + case '-': + jobno = curjob; + if (jobno != -1) + jobno = jobtab[jobno].prev_job; + err_msg = "No previous job"; + break; +#endif + default: + goto check_pattern; + } + } else { + struct job *found; + check_pattern: + found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (!jp->used || jp->nprocs <= 0) + continue; + if ((name[1] == '?' + && strstr(jp->ps[0].cmd, name + 2)) + || prefix(name + 1, jp->ps[0].cmd)) { + if (found) { + err_msg = "%s: ambiguous"; + found = 0; + break; + } + found = jp; + } + } + if (found) + return found; + } + + } else if (is_number(name)) { + pid = number(name); + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + + if (!jobs_invalid && jobno >= 0 && jobno < njobs) { + jp = jobtab + jobno; + if (jp->used) + return jp; + } + if (!noerror) + error(err_msg, name); + return 0; +} + + + +/* + * Return a new job structure, + */ + +struct job * +makejob(union node *node, int nprocs) +{ + int i; + struct job *jp; + + if (jobs_invalid) { + for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { + if (jp->used) + freejob(jp); + } + jobs_invalid = 0; + } + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + memcpy(jp, jobtab, njobs * sizeof jp[0]); + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = JOBRUNNING; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#if JOBS + jp->jobctl = jobctl; + set_curjob(jp, 1); +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jp - jobtab + 1)); + return jp; +} + + +/* + * Fork off a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + +int +forkshell(struct job *jp, union node *n, int mode) +{ + int pid; + + TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode)); + switch ((pid = fork())) { + case -1: + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + break; + case 0: + forkchild(jp, n, mode, 0); + return 0; + default: + return forkparent(jp, n, mode, pid); + } +} + +int +forkparent(struct job *jp, union node *n, int mode, pid_t pid) +{ + int pgrp; + + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; +#ifdef USE_PROCESS_GROUPS + /* This can fail because we are doing it in the child also */ + (void)setpgid(pid, pgrp); +#endif + } + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd[0] = 0; + if (/* iflag && rootshell && */ n) + commandtext(ps, n); + } + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + +void +forkchild(struct job *jp, union node *n, int mode, int vforked) +{ + int wasroot; + int pgrp; + const char *devnull = _PATH_DEVNULL; + const char *nullerr = "Can't open %s"; + + wasroot = rootshell; + TRACE(("Child shell %d\n", getpid())); + if (!vforked) + rootshell = 0; + + closescript(vforked); + clear_traps(vforked); +#if JOBS + if (!vforked) + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; +#ifdef USE_PROCESS_GROUPS + /* This can fail because we are doing it in the parent also */ + (void)setpgid(0, pgrp); + if (mode == FORK_FG) { + if (tcsetpgrp(ttyfd, pgrp) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } +#endif + setsignal(SIGTSTP, vforked); + setsignal(SIGTTOU, vforked); + } else if (mode == FORK_BG) { + ignoresig(SIGINT, vforked); + ignoresig(SIGQUIT, vforked); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT, vforked); + ignoresig(SIGQUIT, vforked); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#endif + if (wasroot && iflag) { + setsignal(SIGINT, vforked); + setsignal(SIGQUIT, vforked); + setsignal(SIGTERM, vforked); + } + + if (!vforked) + jobs_invalid = 1; +} + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +int +waitforjob(struct job *jp) +{ +#if JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == JOBRUNNING) { + dowait(1, jp); + } +#if JOBS + if (jp->jobctl) { + if (tcsetpgrp(ttyfd, mypgrp) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } + if (jp->state == JOBSTOPPED && curjob != jp - jobtab) + set_curjob(jp, 2); +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; + TRACE(("waitforjob: job %d, nproc %d, status %x, st %x\n", + jp - jobtab + 1, jp->nprocs, status, st )); +#if JOBS + if (jp->jobctl) { + /* + * This is truly gross. + * If we're doing job control, then we did a TIOCSPGRP which + * caused us (the shell) to no longer be in the controlling + * session -- so we wouldn't have seen any ^C/SIGINT. So, we + * intuit from the subprocess exit status whether a SIGINT + * occurred, and if so interrupt ourselves. Yuck. - mycroft + */ + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + raise(SIGINT); + } +#endif + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(int block, struct job *job) +{ + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + extern volatile char gotsig[]; + + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, job, &status); + TRACE(("wait returns pid %d, status %d\n", pid, status)); + } while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - jobtab + 1, pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if (WIFSTOPPED(sp->status)) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done ? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#if JOBS + if (done) + set_curjob(jp, 0); +#endif + } + } + } + } + + if (thisjob && thisjob->state != JOBRUNNING) { + int mode = 0; + if (!rootshell || !iflag) + mode = SHOW_SIGNALLED; + if (job == thisjob) + mode = SHOW_SIGNALLED | SHOW_NO_FREE; + if (mode) + showjob(out2, thisjob, mode); + else { + TRACE(("Not printing status, rootshell=%d, job=%p\n", + rootshell, job)); + thisjob->changed = 1; + } + } + + INTON; + return pid; +} + + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of it's + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +#ifdef SYSV +STATIC int gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(int block, struct job *jp, int *status) +{ +#ifdef BSD + int flags = 0; + +#if JOBS + if (jp != NULL && jp->jobctl) + flags |= WUNTRACED; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3(status, flags, (struct rusage *)NULL); +#else +#ifdef SYSV + int (*save)(); + + if (block == 0) { + gotsigchild = 0; + save = signal(SIGCLD, onsigchild); + signal(SIGCLD, save); + if (gotsigchild == 0) + return 0; + } + return wait(status); +#else + if (block == 0) + return 0; + return wait(status); +#endif +#endif +} + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +int job_warning = 0; +int +stoppedjobs(void) +{ + int jobno; + struct job *jp; + + if (job_warning || jobs_invalid) + return (0); + for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { + if (jp->used == 0) + continue; + if (jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + return (1); + } + } + + return (0); +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command). + */ + +STATIC char *cmdnextc; +STATIC int cmdnleft; + +void +commandtext(struct procstat *ps, union node *n) +{ + int len; + + cmdnextc = ps->cmd; + if (iflag || mflag || sizeof ps->cmd < 100) + len = sizeof(ps->cmd); + else + len = sizeof(ps->cmd) / 10; + cmdnleft = len; + cmdtxt(n); + if (cmdnleft <= 0) { + char *p = ps->cmd + len - 4; + p[0] = '.'; + p[1] = '.'; + p[2] = '.'; + p[3] = 0; + } else + *cmdnextc = '\0'; + TRACE(("commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n", + ps->cmd, cmdnextc, cmdnleft, ps->cmd)); +} + + +STATIC void +cmdtxt(union node *n) +{ + union node *np; + struct nodelist *lp; + const char *p; + int i; + char s[2]; + + if (n == NULL || cmdnleft <= 0) + return; + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + if (n->nif.elsepart) { + cmdputs("; else "); + cmdtxt(n->nif.elsepart); + } + cmdputs("; fi"); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in "); + cmdlist(n->nfor.args, 1); + cmdputs("; do "); + cmdtxt(n->nfor.body); + cmdputs("; done"); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in "); + for (np = n->ncase.cases; np; np = np->nclist.next) { + cmdtxt(np->nclist.pattern); + cmdputs(") "); + cmdtxt(np->nclist.body); + cmdputs(";; "); + } + cmdputs("esac"); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() { ... }"); + break; + case NCMD: + cmdlist(n->ncmd.args, 1); + cmdlist(n->ncmd.redirect, 0); + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NCLOBBER: + p = ">|"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; + case NFROMTO: + p = "<>"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + +STATIC void +cmdlist(union node *np, int sep) +{ + for (; np; np = np->narg.next) { + if (!sep) + cmdputs(" "); + cmdtxt(np); + if (sep && np->narg.next) + cmdputs(" "); + } +} + + +STATIC void +cmdputs(const char *s) +{ + const char *p, *str = 0; + char c, cc[2] = " "; + char *nextc; + int nleft; + int subtype = 0; + int quoted = 0; + static char vstype[16][4] = { "", "}", "-", "+", "?", "=", + "#", "##", "%", "%%" }; + + p = s; + nextc = cmdnextc; + nleft = cmdnleft; + while (nleft > 0 && (c = *p++) != 0) { + switch (c) { + case CTLESC: + c = *p++; + break; + case CTLVAR: + subtype = *p++; + if ((subtype & VSTYPE) == VSLENGTH) + str = "${#"; + else + str = "${"; + if (!(subtype & VSQUOTE) != !(quoted & 1)) { + quoted ^= 1; + c = '"'; + } else + c = *str++; + break; + case CTLENDVAR: + if (quoted & 1) { + c = '"'; + str = "}"; + } else + c = '}'; + quoted >>= 1; + subtype = 0; + break; + case CTLBACKQ: + c = '$'; + str = "(...)"; + break; + case CTLBACKQ+CTLQUOTE: + c = '"'; + str = "$(...)\""; + break; + case CTLARI: + c = '$'; + str = "(("; + break; + case CTLENDARI: + c = ')'; + str = ")"; + break; + case CTLQUOTEMARK: + quoted ^= 1; + c = '"'; + break; + case '=': + if (subtype == 0) + break; + str = vstype[subtype & VSTYPE]; + if (subtype & VSNUL) + c = ':'; + else + c = *str++; + if (c != '}') + quoted <<= 1; + break; + case '\'': + case '\\': + case '"': + case '$': + /* These can only happen inside quotes */ + cc[0] = c; + str = cc; + c = '\\'; + break; + default: + break; + } + do { + *nextc++ = c; + } while (--nleft > 0 && str && (c = *str++)); + str = 0; + } + if ((quoted & 1) && nleft) { + *nextc++ = '"'; + nleft--; + } + cmdnleft = nleft; + cmdnextc = nextc; +} diff --git a/sh/jobs.h b/sh/jobs.h new file mode 100644 index 0000000..47e76c2 --- /dev/null +++ b/sh/jobs.h @@ -0,0 +1,106 @@ +/* $NetBSD: jobs.h,v 1.19 2003/11/27 21:16:14 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)jobs.h 8.2 (Berkeley) 5/4/95 + */ + +#include "output.h" + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + +/* mode flags for showjob(s) */ +#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ +#define SHOW_MULTILINE 0x02 /* one line per process */ +#define SHOW_PID 0x04 /* include process pid */ +#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ +#define SHOW_SIGNALLED 0x10 /* only if stopped/exited on signal */ +#define SHOW_ISSIG 0x20 /* job was signalled */ +#define SHOW_NO_FREE 0x40 /* do not free job */ + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ +#define MAXCMDTEXT 200 + +struct procstat { + pid_t pid; /* process id */ + int status; /* last process status from wait() */ + char cmd[MAXCMDTEXT];/* text of command being run */ +}; + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + int nprocs; /* number of processes */ + pid_t pgrp; /* process group of this job */ + char state; +#define JOBRUNNING 0 /* at least one proc running */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ +#if JOBS + char jobctl; /* job running under job control */ + int prev_job; /* previous job index */ +#endif +}; + +extern pid_t backgndpid; /* pid of last background process */ +extern int job_warning; /* user was warned about stopped jobs */ + +void setjobctl(int); +int fgcmd(int, char **); +int bgcmd(int, char **); +int jobscmd(int, char **); +void showjobs(struct output *, int); +int waitcmd(int, char **); +int jobidcmd(int, char **); +struct job *makejob(union node *, int); +int forkshell(struct job *, union node *, int); +void forkchild(struct job *, union node *, int, int); +int forkparent(struct job *, union node *, int, pid_t); +int waitforjob(struct job *); +int stoppedjobs(void); +void commandtext(struct procstat *, union node *); +int getjobpgrp(const char *); + +#if ! JOBS +#define setjobctl(on) /* do nothing */ +#endif diff --git a/sh/machdep.h b/sh/machdep.h new file mode 100644 index 0000000..14e803b --- /dev/null +++ b/sh/machdep.h @@ -0,0 +1,47 @@ +/* $NetBSD: machdep.h,v 1.11 2003/08/07 09:05:33 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)machdep.h 8.2 (Berkeley) 5/4/95 + */ + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) +/* + * It appears that grabstackstr() will barf with such alignments + * because stalloc() will return a string allocated in a new stackblock. + */ +#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) diff --git a/sh/main.c b/sh/main.c new file mode 100644 index 0000000..43b154f --- /dev/null +++ b/sh/main.c @@ -0,0 +1,394 @@ +/* $NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; +#else +__RCSID("$NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $"); +#endif +#endif /* not lint */ + +#include <errno.h> +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#include "var.h" +#include "show.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include "exec.h" +#include "cd.h" + +#define PROFILE 0 + +int rootpid; +int rootshell; +STATIC union node *curcmd; +STATIC union node *prevcmd; +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif + +STATIC void read_profile(const char *); +STATIC char *find_dot_file(char *); +int main(int, char **); + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +int +main(int argc, char **argv) +{ + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + char *shinit; + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + switch (exception) { + case EXSHELLPROC: + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + break; + + case EXEXEC: + exitstatus = exerrno; + break; + + case EXERROR: + exitstatus = 2; + break; + + default: + break; + } + + if (exception != EXSHELLPROC) { + if (state == 0 || iflag == 0 || ! rootshell) + exitshell(exitstatus); + } + reset(); + if (exception == EXINT +#if ATTY + && (! attyset() || equal(termval(), "emacs")) +#endif + ) { + out2c('\n'); + flushout(&errout); + } + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (state == 1) + goto state1; + else if (state == 2) + goto state2; + else if (state == 3) + goto state3; + else + goto state4; + } + handler = &jmploc; +#ifdef DEBUG +#if DEBUG == 2 + debug = 1; +#endif + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile(".profile"); + } +state2: + state = 3; + if (getuid() == geteuid() && getgid() == getegid()) { + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + state = 3; + read_profile(shinit); + } + } +state3: + state = 4; + if (sflag == 0 || minusc) { + static int sigs[] = { + SIGINT, SIGQUIT, SIGHUP, +#ifdef SIGTSTP + SIGTSTP, +#endif + SIGPIPE + }; +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) + int i; + + for (i = 0; i < SIGSSIZE; i++) + setsignal(sigs[i], 0); + } + + if (minusc) + evalstring(minusc, 0); + + if (sflag || minusc == NULL) { +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); + /* NOTREACHED */ +} + + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +void +cmdloop(int top) +{ + union node *n; + struct stackmark smark; + int inter; + int numeof = 0; + + TRACE(("cmdloop(%d) called\n", top)); + setstackmark(&smark); + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter = 1; + showjobs(out2, SHOW_CHANGED); + flushout(&errout); + } + n = parsecmd(inter); + /* showtree(n); DEBUG */ + if (n == NEOF) { + if (!top || numeof >= 50) + break; + if (!stoppedjobs()) { + if (!Iflag) + break; + out2str("\nUse \"exit\" to leave shell.\n"); + } + numeof++; + } else if (n != NULL && nflag == 0) { + job_warning = (job_warning == 2) ? 1 : 0; + numeof = 0; + evaltree(n, 0); + } + popstackmark(&smark); + setstackmark(&smark); + if (evalskip == SKIPFILE) { + evalskip = 0; + break; + } + } + popstackmark(&smark); +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +STATIC void +read_profile(const char *name) +{ + int fd; + int xflag_set = 0; + int vflag_set = 0; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + /* -q turns off -x and -v just when executing init files */ + if (qflag) { + if (xflag) + xflag = 0, xflag_set = 1; + if (vflag) + vflag = 0, vflag_set = 1; + } + cmdloop(0); + if (qflag) { + if (xflag_set) + xflag = 1; + if (vflag_set) + vflag = 1; + } + popfile(); +} + + + +/* + * Read a file containing shell functions. + */ + +void +readcmdfile(char *name) +{ + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + else + error("Can't open %s", name); + INTON; + cmdloop(0); + popfile(); +} + + + +/* + * Take commands from a file. To be compatible we should do a path + * search for the file, which is necessary to find sub-commands. + */ + + +STATIC char * +find_dot_file(char *basename) +{ + char *fullname; + const char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if (strchr(basename, '/')) + return basename; + + while ((fullname = padvance(&path, basename)) != NULL) { + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + /* + * Don't bother freeing here, since it will + * be freed by the caller. + */ + return fullname; + } + stunalloc(fullname); + } + + /* not found in the PATH */ + error("%s: not found", basename); + /* NOTREACHED */ +} + +int +dotcmd(int argc, char **argv) +{ + exitstatus = 0; + + if (argc >= 2) { /* That's what SVR2 does */ + char *fullname; + struct stackmark smark; + + setstackmark(&smark); + fullname = find_dot_file(argv[1]); + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + popstackmark(&smark); + } + return exitstatus; +} + + +int +exitcmd(int argc, char **argv) +{ + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + exitshell(exitstatus); + /* NOTREACHED */ +} diff --git a/sh/main.h b/sh/main.h new file mode 100644 index 0000000..d198e2d --- /dev/null +++ b/sh/main.h @@ -0,0 +1,43 @@ +/* $NetBSD: main.h,v 1.10 2003/08/07 09:05:34 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)main.h 8.2 (Berkeley) 5/4/95 + */ + +extern int rootpid; /* pid of main shell */ +extern int rootshell; /* true if we aren't a child of the main shell */ + +void readcmdfile(char *); +void cmdloop(int); +int dotcmd(int, char **); +int exitcmd(int, char **); diff --git a/sh/memalloc.c b/sh/memalloc.c new file mode 100644 index 0000000..07c14db --- /dev/null +++ b/sh/memalloc.c @@ -0,0 +1,307 @@ +/* $NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> +#include <unistd.h> + +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "machdep.h" +#include "mystring.h" + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(int nbytes) +{ + pointer p; + + p = malloc(nbytes); + if (p == NULL) + error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(pointer p, int nbytes) +{ + p = realloc(p, nbytes); + if (p == NULL) + error("Out of space"); + return p; +} + + +/* + * Make a copy of a string in safe storage. + */ + +char * +savestr(const char *s) +{ + char *p; + + p = ckmalloc(strlen(s) + 1); + scopy(s, p); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. + */ + +#define MINSIZE 504 /* minimum size of a block */ + +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; + +struct stack_block stackbase; +struct stack_block *stackp = &stackbase; +struct stackmark *markp; +char *stacknxt = stackbase.space; +int stacknleft = MINSIZE; +int sstrnleft; +int herefd = -1; + +pointer +stalloc(int nbytes) +{ + char *p; + + nbytes = SHELL_ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +void +stunalloc(pointer p) +{ + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + + +void +setstackmark(struct stackmark *mark) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + mark->marknext = markp; + markp = mark; +} + + +void +popstackmark(struct stackmark *mark) +{ + struct stack_block *sp; + + INTOFF; + markp = mark->marknext; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +void +growstackblock(void) +{ + int newlen = SHELL_ALIGN(stacknleft * 2 + 100); + + if (stacknxt == stackp->space && stackp != &stackbase) { + struct stack_block *oldstackp; + struct stackmark *xmark; + struct stack_block *sp; + + INTOFF; + oldstackp = stackp; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, + sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + + /* + * Stack marks pointing to the start of the old block + * must be relocated to point to the new block + */ + xmark = markp; + while (xmark != NULL && xmark->stackp == oldstackp) { + xmark->stackp = stackp; + xmark->stacknxt = stacknxt; + xmark->stacknleft = stacknleft; + xmark = xmark->marknext; + } + INTON; + } else { + char *oldspace = stacknxt; + int oldlen = stacknleft; + char *p = stalloc(newlen); + + (void)memcpy(p, oldspace, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + +void +grabstackblock(int len) +{ + len = SHELL_ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + +/* + * The following routines are somewhat easier to use than the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + +char * +growstackstr(void) +{ + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); + } + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; +} + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace(void) +{ + int len = stackblocksize() - sstrnleft; + growstackblock(); + sstrnleft = stackblocksize() - len; + return stackblock() + len; +} + +void +ungrabstackstr(char *s, char *p) +{ + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); + +} diff --git a/sh/memalloc.h b/sh/memalloc.h new file mode 100644 index 0000000..e793880 --- /dev/null +++ b/sh/memalloc.h @@ -0,0 +1,77 @@ +/* $NetBSD: memalloc.h,v 1.14 2003/08/07 09:05:34 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 + */ + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; + struct stackmark *marknext; +}; + + +extern char *stacknxt; +extern int stacknleft; +extern int sstrnleft; +extern int herefd; + +pointer ckmalloc(int); +pointer ckrealloc(pointer, int); +char *savestr(const char *); +pointer stalloc(int); +void stunalloc(pointer); +void setstackmark(struct stackmark *); +void popstackmark(struct stackmark *); +void growstackblock(void); +void grabstackblock(int); +char *growstackstr(void); +char *makestrspace(void); +void ungrabstackstr(char *, char *); + + + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(); } +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) + +#define ckfree(p) free((pointer)(p)) diff --git a/sh/miscbltin.c b/sh/miscbltin.c new file mode 100644 index 0000000..1a8e252 --- /dev/null +++ b/sh/miscbltin.c @@ -0,0 +1,447 @@ +/* $NetBSD: miscbltin.c,v 1.34.2.1 2005/04/07 11:34:20 tron Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: miscbltin.c,v 1.34.2.1 2005/04/07 11:34:20 tron Exp $"); +#endif +#endif /* not lint */ + +/* + * Miscelaneous builtins. + */ + +#include <sys/types.h> /* quad_t */ +#include <sys/param.h> /* BSD4_4 */ +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "miscbltin.h" +#include "mystring.h" + +#undef rflag + + + +/* + * The read builtin. + * Backslahes escape the next char unless -r is specified. + * + * This uses unbuffered input, which may be avoidable in some cases. + * + * Note that if IFS=' :' then read x y should work so that: + * 'a b' x='a', y='b' + * ' a b ' x='a', y='b' + * ':b' x='', y='b' + * ':' x='', y='' + * '::' x='', y='' + * ': :' x='', y='' + * ':::' x='', y='::' + * ':b c:' x='', y='b c:' + */ + +int +readcmd(int argc, char **argv) +{ + char **ap; + char c; + int rflag; + char *prompt; + const char *ifs; + char *p; + int startword; + int status; + int i; + int is_ifs; + int saveall = 0; + + rflag = 0; + prompt = NULL; + while ((i = nextopt("p:r")) != '\0') { + if (i == 'p') + prompt = optionarg; + else + rflag = 1; + } + + if (prompt && isatty(0)) { + out2str(prompt); + flushall(); + } + + if (*(ap = argptr) == NULL) + error("arg count"); + + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = " \t\n"; + + status = 0; + startword = 2; + STARTSTACKSTR(p); + for (;;) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + if (c == '\0') + continue; + if (c == '\\' && !rflag) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + if (c != '\n') + STPUTC(c, p); + continue; + } + if (c == '\n') + break; + if (strchr(ifs, c)) + is_ifs = strchr(" \t\n", c) ? 1 : 2; + else + is_ifs = 0; + + if (startword != 0) { + if (is_ifs == 1) { + /* Ignore leading IFS whitespace */ + if (saveall) + STPUTC(c, p); + continue; + } + if (is_ifs == 2 && startword == 1) { + /* Only one non-whitespace IFS per word */ + startword = 2; + if (saveall) + STPUTC(c, p); + continue; + } + } + + if (is_ifs == 0) { + /* append this character to the current variable */ + startword = 0; + if (saveall) + /* Not just a spare terminator */ + saveall++; + STPUTC(c, p); + continue; + } + + /* end of variable... */ + startword = is_ifs; + + if (ap[1] == NULL) { + /* Last variable needs all IFS chars */ + saveall++; + STPUTC(c, p); + continue; + } + + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + STARTSTACKSTR(p); + } + STACKSTRNUL(p); + + /* Remove trailing IFS chars */ + for (; stackblock() <= --p; *p = 0) { + if (!strchr(ifs, *p)) + break; + if (strchr(" \t\n", *p)) + /* Always remove whitespace */ + continue; + if (saveall > 1) + /* Don't remove non-whitespace unless it was naked */ + break; + } + setvar(*ap, stackblock(), 0); + + /* Set any remaining args to "" */ + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + + +int +umaskcmd(int argc, char **argv) +{ + char *ap; + int mask; + int i; + int symbolic_mode = 0; + + while ((i = nextopt("S")) != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + out1fmt("u=%s,g=%s,o=%s\n", u, g, o); + } else { + out1fmt("%.4o\n", mask); + } + } else { + if (isdigit((unsigned char)*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else + error("Illegal mode: %s", ap); + } + return 0; +} + +typedef unsigned long rlim_t; + +#if 1 +/* + * ulimit builtin + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. + */ + +struct limits { + const char *name; + int cmd; + int factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; + +static const struct limits limits[] = { +#ifdef RLIMIT_CPU + { "time(seconds)", RLIMIT_CPU, 1, 't' }, +#endif +#ifdef RLIMIT_FSIZE + { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, +#endif +#ifdef RLIMIT_DATA + { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, +#endif +#ifdef RLIMIT_STACK + { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, +#endif +#ifdef RLIMIT_CORE + { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, +#endif +#ifdef RLIMIT_RSS + { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_MEMLOCK + { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NPROC + { "process(processes)", RLIMIT_NPROC, 1, 'p' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_VMEM + { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, +#endif +#ifdef RLIMIT_SWAP + { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, +#endif +#ifdef RLIMIT_SBSIZE + { "sbsize(bytes)", RLIMIT_SBSIZE, 1, 'b' }, +#endif + { (char *) 0, 0, 0, '\0' } +}; + +int +ulimitcmd(int argc, char **argv) +{ + int c; + rlim_t val = 0; + enum { SOFT = 0x1, HARD = 0x2 } + how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSabtfdsmcnpl")) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->name && l->option != what; l++) + ; + if (!l->name) + error("internal error (%c)", what); + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("too many arguments"); + if (strcmp(p, "unlimited") == 0) + val = RLIM_INFINITY; + else { + val = (rlim_t) 0; + + while ((c = *p++) >= '0' && c <= '9') + { + val = (val * 10) + (long)(c - '0'); + if ((long)val < 0) + break; + } + if (c) + error("bad number"); + val *= l->factor; + } + } + if (all) { + for (l = limits; l->name; l++) { + getrlimit(l->cmd, &limit); + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + out1fmt("%-20s ", l->name); + if (val == RLIM_INFINITY) + out1fmt("unlimited\n"); + else + { + val /= l->factor; +#ifdef BSD4_4 + out1fmt("%lld\n", (long long) val); +#else + out1fmt("%ld\n", (long) val); +#endif + } + } + return 0; + } + + getrlimit(l->cmd, &limit); + if (set) { + if (how & HARD) + limit.rlim_max = val; + if (how & SOFT) + limit.rlim_cur = val; + if (setrlimit(l->cmd, &limit) < 0) + error("error setting limit (%s)", strerror(errno)); + } else { + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + if (val == RLIM_INFINITY) + out1fmt("unlimited\n"); + else + { + val /= l->factor; +#ifdef BSD4_4 + out1fmt("%lld\n", (long long) val); +#else + out1fmt("%ld\n", (long) val); +#endif + } + } + return 0; +} +#endif diff --git a/sh/miscbltin.h b/sh/miscbltin.h new file mode 100644 index 0000000..4c12c82 --- /dev/null +++ b/sh/miscbltin.h @@ -0,0 +1,31 @@ +/* $NetBSD: miscbltin.h,v 1.3 2003/08/21 17:57:53 christos Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +int readcmd(int, char **); +int umaskcmd(int, char **); +int ulimitcmd(int, char **); diff --git a/sh/mkbuiltins b/sh/mkbuiltins new file mode 100644 index 0000000..5b19269 --- /dev/null +++ b/sh/mkbuiltins @@ -0,0 +1,136 @@ +#!/bin/sh - +# $NetBSD: mkbuiltins,v 1.21 2004/06/06 07:03:11 christos Exp $ +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 + +havehist=1 +if [ "X$1" = "X-h" ]; then + havehist=0 + shift +fi + +shell=$1 +builtins=$2 +objdir=$3 + +havejobs=0 +if grep '^#define JOBS[ ]*1' ${shell} > /dev/null +then + havejobs=1 +fi + +exec <$builtins 3> ${objdir}/builtins.c 4> ${objdir}/builtins.h + +echo '/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +const struct builtincmd builtincmd[] = { +' >&3 + +echo '/* + * This file was generated by the mkbuiltins program. + */ + +#include <sys/cdefs.h> + +struct builtincmd { + const char *name; + int (*builtin)(int, char **); +}; + +extern const struct builtincmd builtincmd[]; +extern const struct builtincmd splbltincmd[]; + +' >&4 + +specials= + +while read line +do + set -- $line + [ -z "$1" ] && continue + case "$1" in + \#if*|\#def*|\#end*) + echo $line >&3 + echo $line >&4 + continue + ;; + esac + l1="${line###}" + [ "$l1" != "$line" ] && continue + + + func=$1 + shift + [ x"$1" = x'-j' ] && { + [ $havejobs = 0 ] && continue + shift + } + [ x"$1" = x'-h' ] && { + [ $havehist = 0 ] && continue + shift + } + echo 'int '"$func"'(int, char **);' >&4 + while + [ $# != 0 -a "$1" != '#' ] + do + [ "$1" = '-s' ] && { + specials="$specials $2 $func" + shift 2 + continue; + } + [ "$1" = '-u' ] && shift + echo ' { "'$1'", '"$func"' },' >&3 + shift + done +done + +echo ' { 0, 0 },' >&3 +echo '};' >&3 +echo >&3 +echo 'const struct builtincmd splbltincmd[] = {' >&3 + +set -- $specials +while + [ $# != 0 ] +do + echo ' { "'$1'", '"$2"' },' >&3 + shift 2 +done + +echo ' { 0, 0 },' >&3 +echo "};" >&3 diff --git a/sh/mkinit.sh b/sh/mkinit.sh new file mode 100644 index 0000000..cae27dd --- /dev/null +++ b/sh/mkinit.sh @@ -0,0 +1,197 @@ +#! /bin/sh +# $NetBSD: mkinit.sh,v 1.2 2004/06/15 23:09:54 dsl Exp $ + +# Copyright (c) 2003 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by David Laight. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The NetBSD Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +srcs="$*" + +nl=' +' +openparen='(' +backslash='\' + +includes=' "shell.h" "mystring.h" "init.h" ' +defines= +decles= +event_init= +event_reset= +event_shellproc= + +for src in $srcs; do + exec <$src + decnl="$nl" + while IFS=; read -r line; do + [ "$line" = x ] + case "$line " in + INIT["{ "]* ) event=init;; + RESET["{ "]* ) event=reset;; + SHELLPROC["{ "]* ) event=shellproc;; + INCLUDE[\ \ ]* ) + IFS=' ' + set -- $line + # ignore duplicates + [ "${includes}" != "${includes%* $2 }" ] && continue + includes="$includes$2 " + continue + ;; + MKINIT\ ) + # struct declaration + decles="$decles$nl" + while + read -r line + decles="${decles}${line}${nl}" + [ "$line" != "};" ] + do + : + done + decnl="$nl" + continue + ;; + MKINIT["{ "]* ) + # strip initialiser + def=${line#MKINIT} + comment="${def#*;}" + def="${def%;$comment}" + def="${def%%=*}" + def="${def% }" + decles="${decles}${decnl}extern${def};${comment}${nl}" + decnl= + continue + ;; + \#define[\ \ ]* ) + IFS=' ' + set -- $line + # Ignore those with arguments + [ "$2" = "${2##*$openparen}" ] || continue + # and multiline definitions + [ "$line" = "${line%$backslash}" ] || continue + defines="${defines}#undef $2${nl}${line}${nl}" + continue + ;; + * ) continue;; + esac + # code for events + ev="${nl} /* from $src: */${nl} {${nl}" + while + read -r line + [ "$line" != "}" ] + do + # The C program indented by an extra 6 chars using + # tabs then spaces. I need to compare the output :-( + indent=6 + while + l=${line# } + [ "$l" != "$line" ] + do + indent=$(($indent + 8)) + line="$l" + done + while + l=${line# } + [ "$l" != "$line" ] + do + indent=$(($indent + 1)) + line="$l" + done + [ -z "$line" -o "$line" != "${line###}" ] && indent=0 + while + [ $indent -ge 8 ] + do + ev="$ev " + indent="$(($indent - 8))" + done + while + [ $indent -gt 0 ] + do + ev="$ev " + indent="$(($indent - 1))" + done + ev="${ev}${line}${nl}" + done + ev="${ev} }${nl}" + eval event_$event=\"\$event_$event\$ev\" + done +done + +exec >init.c.tmp + +echo "/*" +echo " * This file was generated by the mkinit program." +echo " */" +echo + +IFS=' ' +for f in $includes; do + echo "#include $f" +done + +echo +echo +echo +echo "$defines" +echo +echo "$decles" +echo +echo +echo "/*" +echo " * Initialization code." +echo " */" +echo +echo "void" +echo "init() {" +echo "${event_init%$nl}" +echo "}" +echo +echo +echo +echo "/*" +echo " * This routine is called when an error or an interrupt occurs in an" +echo " * interactive shell and control is returned to the main command loop." +echo " */" +echo +echo "void" +echo "reset() {" +echo "${event_reset%$nl}" +echo "}" +echo +echo +echo +echo "/*" +echo " * This routine is called to initialize the shell to run a shell procedure." +echo " */" +echo +echo "void" +echo "initshellproc() {" +echo "${event_shellproc%$nl}" +echo "}" + +exec >&- +mv init.c.tmp init.c diff --git a/sh/mknodes.sh b/sh/mknodes.sh new file mode 100644 index 0000000..54d2e3d --- /dev/null +++ b/sh/mknodes.sh @@ -0,0 +1,217 @@ +#! /bin/sh +# $NetBSD: mknodes.sh,v 1.1 2004/01/16 23:24:38 dsl Exp $ + +# Copyright (c) 2003 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by David Laight. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The NetBSD Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +nodetypes=$1 +nodes_pat=$2 +objdir="$3" + +exec <$nodetypes +exec >$objdir/nodes.h.tmp + +echo "/*" +echo " * This file was generated by mknodes.sh" +echo " */" +echo + +tagno=0 +while IFS=; read -r line; do + line="${line%%#*}" + IFS=' ' + set -- $line + IFS= + [ -z "$2" ] && continue + case "$line" in + [" "]* ) + IFS=' ' + [ $field = 0 ] && struct_list="$struct_list $struct" + eval field_${struct}_$field=\"\$*\" + eval numfld_$struct=\$field + field=$(($field + 1)) + ;; + * ) + define=$1 + struct=$2 + echo "#define $define $tagno" + tagno=$(($tagno + 1)) + eval define_$struct=\"\$define_$struct \$define\" + struct_define="$struct_define $struct" + field=0 + ;; + esac +done + +echo + +IFS=' ' +for struct in $struct_list; do + echo + echo + echo "struct $struct {" + field=0 + while + eval line=\"\$field_${struct}_$field\" + field=$(($field + 1)) + [ -n "$line" ] + do + IFS=' ' + set -- $line + name=$1 + case $2 in + nodeptr ) type="union node *";; + nodelist ) type="struct nodelist *";; + string ) type="char *";; + int ) type="int ";; + * ) name=; shift 2; type="$*";; + esac + echo " $type$name;" + done + echo "};" +done + +echo +echo +echo "union node {" +echo " int type;" +for struct in $struct_list; do + echo " struct $struct $struct;" +done +echo "};" +echo +echo +echo "struct nodelist {" +echo " struct nodelist *next;" +echo " union node *n;" +echo "};" +echo +echo +echo "union node *copyfunc(union node *);" +echo "void freefunc(union node *);" + +mv $objdir/nodes.h.tmp $objdir/nodes.h || exit 1 + +exec <$nodes_pat +exec >$objdir/nodes.c.tmp + +echo "/*" +echo " * This file was generated by mknodes.sh" +echo " */" +echo + +while IFS=; read -r line; do + IFS=' ' + set -- $line + IFS= + case "$1" in + '%SIZES' ) + echo "static const short nodesize[$tagno] = {" + IFS=' ' + for struct in $struct_define; do + echo " SHELL_ALIGN(sizeof (struct $struct))," + done + echo "};" + ;; + '%CALCSIZE' ) + echo " if (n == NULL)" + echo " return;" + echo " funcblocksize += nodesize[n->type];" + echo " switch (n->type) {" + IFS=' ' + for struct in $struct_list; do + eval defines=\"\$define_$struct\" + for define in $defines; do + echo " case $define:" + done + eval field=\$numfld_$struct + while + [ $field != 0 ] + do + eval line=\"\$field_${struct}_$field\" + field=$(($field - 1)) + IFS=' ' + set -- $line + name=$1 + cl=")" + case $2 in + nodeptr ) fn=calcsize;; + nodelist ) fn=sizenodelist;; + string ) fn="funcstringsize += strlen" + cl=") + 1";; + * ) continue;; + esac + echo " ${fn}(n->$struct.$name${cl};" + done + echo " break;" + done + echo " };" + ;; + '%COPY' ) + echo " if (n == NULL)" + echo " return NULL;" + echo " new = funcblock;" + echo " funcblock = (char *) funcblock + nodesize[n->type];" + echo " switch (n->type) {" + IFS=' ' + for struct in $struct_list; do + eval defines=\"\$define_$struct\" + for define in $defines; do + echo " case $define:" + done + eval field=\$numfld_$struct + while + [ $field != 0 ] + do + eval line=\"\$field_${struct}_$field\" + field=$(($field - 1)) + IFS=' ' + set -- $line + name=$1 + case $2 in + nodeptr ) fn="copynode(";; + nodelist ) fn="copynodelist(";; + string ) fn="nodesavestr(";; + int ) fn=;; + * ) continue;; + esac + f="$struct.$name" + echo " new->$f = ${fn}n->$f${fn:+)};" + done + echo " break;" + done + echo " };" + echo " new->type = n->type;" + ;; + * ) echo "$line";; + esac +done + +mv $objdir/nodes.c.tmp $objdir/nodes.c || exit 1 diff --git a/sh/mktokens b/sh/mktokens new file mode 100644 index 0000000..25f2e6e --- /dev/null +++ b/sh/mktokens @@ -0,0 +1,92 @@ +#!/bin/sh - +# $NetBSD: mktokens,v 1.10 2003/08/22 11:22:23 agc Exp $ +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mktokens 8.1 (Berkeley) 5/31/93 + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +cat > /tmp/ka$$ <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TENDBQUOTE 1 "`" +TREDIR 0 redirection +TWORD 0 word +TIF 0 "if" +TTHEN 1 "then" +TELSE 1 "else" +TELIF 1 "elif" +TFI 1 "fi" +TWHILE 0 "while" +TUNTIL 0 "until" +TFOR 0 "for" +TDO 1 "do" +TDONE 1 "done" +TBEGIN 0 "{" +TEND 1 "}" +TCASE 0 "case" +TESAC 1 "esac" +TNOT 0 "!" +! +nl=`wc -l /tmp/ka$$` +exec > token.h +awk '{print "#define " $1 " " NR-1}' /tmp/ka$$ +echo ' +/* Array indicating which tokens mark the end of a list */ +const char tokendlist[] = {' +awk '{print "\t" $2 ","}' /tmp/ka$$ +echo '}; + +const char *const tokname[] = {' +sed -e 's/"/\\"/g' \ + -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ + /tmp/ka$$ +echo '}; +' +sed 's/"//g' /tmp/ka$$ | awk ' +/TIF/{print "#define KWDOFFSET " NR-1; print ""; + print "const char *const parsekwd[] = {"} +/TIF/,/neverfound/{print " \"" $3 "\","}' +echo ' 0 +};' + +rm /tmp/ka$$ diff --git a/sh/myhistedit.h b/sh/myhistedit.h new file mode 100644 index 0000000..603a27b --- /dev/null +++ b/sh/myhistedit.h @@ -0,0 +1,49 @@ +/* $NetBSD: myhistedit.h,v 1.10 2003/08/07 09:05:35 agc Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 + */ + +#ifdef WITH_HISTORY +#include <histedit.h> + +extern History *hist; +extern EditLine *el; +extern int displayhist; + +void histedit(void); +void sethistsize(const char *); +void setterm(const char *); +int histcmd(int, char **); +int inputrc(int, char **); +int not_fcnumber(char *); +int str_to_event(const char *, int); +#endif + diff --git a/sh/mystring.c b/sh/mystring.c new file mode 100644 index 0000000..aecf83e --- /dev/null +++ b/sh/mystring.c @@ -0,0 +1,133 @@ +/* $NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $"); +#endif +#endif /* not lint */ + +/* + * String functions. + * + * equal(s1, s2) Return true if strings are equal. + * scopy(from, to) Copy a string. + * scopyn(from, to, n) Like scopy, but checks for overflow. + * number(s) Convert a string of digits to an integer. + * is_number(s) Return true if s is a string of digits. + */ + +#include <stdlib.h> +#include "shell.h" +#include "syntax.h" +#include "error.h" +#include "mystring.h" + + +char nullstr[1]; /* zero length string */ + +/* + * equal - #defined in mystring.h + */ + +/* + * scopy - #defined in mystring.h + */ + + +/* + * scopyn - copy a string from "from" to "to", truncating the string + * if necessary. "To" is always nul terminated, even if + * truncation is performed. "Size" is the size of "to". + */ + +void +scopyn(const char *from, char *to, int size) +{ + + while (--size > 0) { + if ((*to++ = *from++) == '\0') + return; + } + *to = '\0'; +} + + +/* + * prefix -- see if pfx is a prefix of string. + */ + +int +prefix(const char *pfx, const char *string) +{ + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return 1; +} + + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(const char *s) +{ + + if (! is_number(s)) + error("Illegal number: %s", s); + return atoi(s); +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(const char *p) +{ + do { + if (! is_digit(*p)) + return 0; + } while (*++p != '\0'); + return 1; +} diff --git a/sh/mystring.h b/sh/mystring.h new file mode 100644 index 0000000..08a73e9 --- /dev/null +++ b/sh/mystring.h @@ -0,0 +1,45 @@ +/* $NetBSD: mystring.h,v 1.11 2003/08/07 09:05:35 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mystring.h 8.2 (Berkeley) 5/4/95 + */ + +#include <string.h> + +void scopyn(const char *, char *, int); +int prefix(const char *, const char *); +int number(const char *); +int is_number(const char *); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) +#define scopy(s1, s2) ((void)strcpy(s2, s1)) diff --git a/sh/nodes.c b/sh/nodes.c new file mode 100644 index 0000000..8a2c718 --- /dev/null +++ b/sh/nodes.c @@ -0,0 +1,347 @@ +/* + * This file was generated by mknodes.sh + */ + +/* $NetBSD: nodes.c.pat,v 1.12 2004/06/15 22:57:27 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 + */ + +#include <stdlib.h> +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "machdep.h" +#include "mystring.h" + + +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +pointer funcblock; /* block to allocate function from */ +char *funcstring; /* block to allocate strings from */ + +static const short nodesize[26] = { + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct ncmd)), + SHELL_ALIGN(sizeof (struct npipe)), + SHELL_ALIGN(sizeof (struct nredir)), + SHELL_ALIGN(sizeof (struct nredir)), + SHELL_ALIGN(sizeof (struct nredir)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nif)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nfor)), + SHELL_ALIGN(sizeof (struct ncase)), + SHELL_ALIGN(sizeof (struct nclist)), + SHELL_ALIGN(sizeof (struct narg)), + SHELL_ALIGN(sizeof (struct narg)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct ndup)), + SHELL_ALIGN(sizeof (struct ndup)), + SHELL_ALIGN(sizeof (struct nhere)), + SHELL_ALIGN(sizeof (struct nhere)), + SHELL_ALIGN(sizeof (struct nnot)), +}; + + +STATIC void calcsize(union node *); +STATIC void sizenodelist(struct nodelist *); +STATIC union node *copynode(union node *); +STATIC struct nodelist *copynodelist(struct nodelist *); +STATIC char *nodesavestr(char *); + + + +/* + * Make a copy of a parse tree. + */ + +union node * +copyfunc(n) + union node *n; +{ + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = (char *) funcblock + funcblocksize; + return copynode(n); +} + + + +STATIC void +calcsize(n) + union node *n; +{ + if (n == NULL) + return; + funcblocksize += nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + calcsize(n->nbinary.ch2); + calcsize(n->nbinary.ch1); + break; + case NCMD: + calcsize(n->ncmd.redirect); + calcsize(n->ncmd.args); + break; + case NPIPE: + sizenodelist(n->npipe.cmdlist); + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + calcsize(n->nredir.redirect); + calcsize(n->nredir.n); + break; + case NIF: + calcsize(n->nif.elsepart); + calcsize(n->nif.ifpart); + calcsize(n->nif.test); + break; + case NFOR: + funcstringsize += strlen(n->nfor.var) + 1; + calcsize(n->nfor.body); + calcsize(n->nfor.args); + break; + case NCASE: + calcsize(n->ncase.cases); + calcsize(n->ncase.expr); + break; + case NCLIST: + calcsize(n->nclist.body); + calcsize(n->nclist.pattern); + calcsize(n->nclist.next); + break; + case NDEFUN: + case NARG: + sizenodelist(n->narg.backquote); + funcstringsize += strlen(n->narg.text) + 1; + calcsize(n->narg.next); + break; + case NTO: + case NCLOBBER: + case NFROM: + case NFROMTO: + case NAPPEND: + calcsize(n->nfile.fname); + calcsize(n->nfile.next); + break; + case NTOFD: + case NFROMFD: + calcsize(n->ndup.vname); + calcsize(n->ndup.next); + break; + case NHERE: + case NXHERE: + calcsize(n->nhere.doc); + calcsize(n->nhere.next); + break; + case NNOT: + calcsize(n->nnot.com); + break; + }; +} + + + +STATIC void +sizenodelist(lp) + struct nodelist *lp; +{ + while (lp) { + funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + + +STATIC union node * +copynode(n) + union node *n; +{ + union node *new; + + if (n == NULL) + return NULL; + new = funcblock; + funcblock = (char *) funcblock + nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + new->nbinary.ch2 = copynode(n->nbinary.ch2); + new->nbinary.ch1 = copynode(n->nbinary.ch1); + break; + case NCMD: + new->ncmd.redirect = copynode(n->ncmd.redirect); + new->ncmd.args = copynode(n->ncmd.args); + new->ncmd.backgnd = n->ncmd.backgnd; + break; + case NPIPE: + new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); + new->npipe.backgnd = n->npipe.backgnd; + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + new->nredir.redirect = copynode(n->nredir.redirect); + new->nredir.n = copynode(n->nredir.n); + break; + case NIF: + new->nif.elsepart = copynode(n->nif.elsepart); + new->nif.ifpart = copynode(n->nif.ifpart); + new->nif.test = copynode(n->nif.test); + break; + case NFOR: + new->nfor.var = nodesavestr(n->nfor.var); + new->nfor.body = copynode(n->nfor.body); + new->nfor.args = copynode(n->nfor.args); + break; + case NCASE: + new->ncase.cases = copynode(n->ncase.cases); + new->ncase.expr = copynode(n->ncase.expr); + break; + case NCLIST: + new->nclist.body = copynode(n->nclist.body); + new->nclist.pattern = copynode(n->nclist.pattern); + new->nclist.next = copynode(n->nclist.next); + break; + case NDEFUN: + case NARG: + new->narg.backquote = copynodelist(n->narg.backquote); + new->narg.text = nodesavestr(n->narg.text); + new->narg.next = copynode(n->narg.next); + break; + case NTO: + case NCLOBBER: + case NFROM: + case NFROMTO: + case NAPPEND: + new->nfile.fname = copynode(n->nfile.fname); + new->nfile.fd = n->nfile.fd; + new->nfile.next = copynode(n->nfile.next); + break; + case NTOFD: + case NFROMFD: + new->ndup.vname = copynode(n->ndup.vname); + new->ndup.dupfd = n->ndup.dupfd; + new->ndup.fd = n->ndup.fd; + new->ndup.next = copynode(n->ndup.next); + break; + case NHERE: + case NXHERE: + new->nhere.doc = copynode(n->nhere.doc); + new->nhere.fd = n->nhere.fd; + new->nhere.next = copynode(n->nhere.next); + break; + case NNOT: + new->nnot.com = copynode(n->nnot.com); + break; + }; + new->type = n->type; + return new; +} + + +STATIC struct nodelist * +copynodelist(lp) + struct nodelist *lp; +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + + SHELL_ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +STATIC char * +nodesavestr(s) + char *s; +{ + register char *p = s; + register char *q = funcstring; + char *rtn = funcstring; + + while ((*q++ = *p++) != 0) + continue; + funcstring = q; + return rtn; +} + + + +/* + * Free a parse tree. + */ + +void +freefunc(n) + union node *n; +{ + if (n) + ckfree(n); +} diff --git a/sh/nodes.c.pat b/sh/nodes.c.pat new file mode 100644 index 0000000..e619a01 --- /dev/null +++ b/sh/nodes.c.pat @@ -0,0 +1,166 @@ +/* $NetBSD: nodes.c.pat,v 1.12 2004/06/15 22:57:27 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 + */ + +#include <stdlib.h> +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "machdep.h" +#include "mystring.h" + + +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +pointer funcblock; /* block to allocate function from */ +char *funcstring; /* block to allocate strings from */ + +%SIZES + + +STATIC void calcsize(union node *); +STATIC void sizenodelist(struct nodelist *); +STATIC union node *copynode(union node *); +STATIC struct nodelist *copynodelist(struct nodelist *); +STATIC char *nodesavestr(char *); + + + +/* + * Make a copy of a parse tree. + */ + +union node * +copyfunc(n) + union node *n; +{ + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = (char *) funcblock + funcblocksize; + return copynode(n); +} + + + +STATIC void +calcsize(n) + union node *n; +{ + %CALCSIZE +} + + + +STATIC void +sizenodelist(lp) + struct nodelist *lp; +{ + while (lp) { + funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + + +STATIC union node * +copynode(n) + union node *n; +{ + union node *new; + + %COPY + return new; +} + + +STATIC struct nodelist * +copynodelist(lp) + struct nodelist *lp; +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + + SHELL_ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +STATIC char * +nodesavestr(s) + char *s; +{ + register char *p = s; + register char *q = funcstring; + char *rtn = funcstring; + + while ((*q++ = *p++) != 0) + continue; + funcstring = q; + return rtn; +} + + + +/* + * Free a parse tree. + */ + +void +freefunc(n) + union node *n; +{ + if (n) + ckfree(n); +} diff --git a/sh/nodes.h b/sh/nodes.h new file mode 100644 index 0000000..aa750ed --- /dev/null +++ b/sh/nodes.h @@ -0,0 +1,159 @@ +/* + * This file was generated by mknodes.sh + */ + +#define NSEMI 0 +#define NCMD 1 +#define NPIPE 2 +#define NREDIR 3 +#define NBACKGND 4 +#define NSUBSHELL 5 +#define NAND 6 +#define NOR 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#define NCLOBBER 17 +#define NFROM 18 +#define NFROMTO 19 +#define NAPPEND 20 +#define NTOFD 21 +#define NFROMFD 22 +#define NHERE 23 +#define NXHERE 24 +#define NNOT 25 + + + +struct nbinary { + int type; + union node *ch1; + union node *ch2; +}; + + +struct ncmd { + int type; + int backgnd; + union node *args; + union node *redirect; +}; + + +struct npipe { + int type; + int backgnd; + struct nodelist *cmdlist; +}; + + +struct nredir { + int type; + union node *n; + union node *redirect; +}; + + +struct nif { + int type; + union node *test; + union node *ifpart; + union node *elsepart; +}; + + +struct nfor { + int type; + union node *args; + union node *body; + char *var; +}; + + +struct ncase { + int type; + union node *expr; + union node *cases; +}; + + +struct nclist { + int type; + union node *next; + union node *pattern; + union node *body; +}; + + +struct narg { + int type; + union node *next; + char *text; + struct nodelist *backquote; +}; + + +struct nfile { + int type; + union node *next; + int fd; + union node *fname; + char *expfname; +}; + + +struct ndup { + int type; + union node *next; + int fd; + int dupfd; + union node *vname; +}; + + +struct nhere { + int type; + union node *next; + int fd; + union node *doc; +}; + + +struct nnot { + int type; + union node *com; +}; + + +union node { + int type; + struct nbinary nbinary; + struct ncmd ncmd; + struct npipe npipe; + struct nredir nredir; + struct nif nif; + struct nfor nfor; + struct ncase ncase; + struct nclist nclist; + struct narg narg; + struct nfile nfile; + struct ndup ndup; + struct nhere nhere; + struct nnot nnot; +}; + + +struct nodelist { + struct nodelist *next; + union node *n; +}; + + +union node *copyfunc(union node *); +void freefunc(union node *); diff --git a/sh/nodetypes b/sh/nodetypes new file mode 100644 index 0000000..4adebc0 --- /dev/null +++ b/sh/nodetypes @@ -0,0 +1,143 @@ +# $NetBSD: nodetypes,v 1.12 2003/08/22 11:22:23 agc Exp $ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)nodetypes 8.2 (Berkeley) 5/4/95 + +# This file describes the nodes used in parse trees. Unindented lines +# contain a node type followed by a structure tag. Subsequent indented +# lines specify the fields of the structure. Several node types can share +# the same structure, in which case the fields of the structure should be +# specified only once. +# +# A field of a structure is described by the name of the field followed +# by a type. The currently implemented types are: +# nodeptr - a pointer to a node +# nodelist - a pointer to a list of nodes +# string - a pointer to a nul terminated string +# int - an integer +# other - any type that can be copied by assignment +# temp - a field that doesn't have to be copied when the node is copied +# The last two types should be followed by the text of a C declaration for +# the field. + +NSEMI nbinary # two commands separated by a semicolon + type int + ch1 nodeptr # the first child + ch2 nodeptr # the second child + +NCMD ncmd # a simple command + type int + backgnd int # set to run command in background + args nodeptr # the arguments + redirect nodeptr # list of file redirections + +NPIPE npipe # a pipeline + type int + backgnd int # set to run pipeline in background + cmdlist nodelist # the commands in the pipeline + +NREDIR nredir # redirection (of a complex command) + type int + n nodeptr # the command + redirect nodeptr # list of file redirections + +NBACKGND nredir # run command in background +NSUBSHELL nredir # run command in a subshell + +NAND nbinary # the && operator +NOR nbinary # the || operator + +NIF nif # the if statement. Elif clauses are handled + type int # using multiple if nodes. + test nodeptr # if test + ifpart nodeptr # then ifpart + elsepart nodeptr # else elsepart + +NWHILE nbinary # the while statement. First child is the test +NUNTIL nbinary # the until statement + +NFOR nfor # the for statement + type int + args nodeptr # for var in args + body nodeptr # do body; done + var string # the for variable + +NCASE ncase # a case statement + type int + expr nodeptr # the word to switch on + cases nodeptr # the list of cases (NCLIST nodes) + +NCLIST nclist # a case + type int + next nodeptr # the next case in list + pattern nodeptr # list of patterns for this case + body nodeptr # code to execute for this case + + +NDEFUN narg # define a function. The "next" field contains + # the body of the function. + +NARG narg # represents a word + type int + next nodeptr # next word in list + text string # the text of the word + backquote nodelist # list of commands in back quotes + +NTO nfile # fd> fname +NCLOBBER nfile # fd>| fname +NFROM nfile # fd< fname +NFROMTO nfile # fd<> fname +NAPPEND nfile # fd>> fname + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + fname nodeptr # file name, in a NARG node + expfname temp char *expfname # actual file name + +NTOFD ndup # fd<&dupfd +NFROMFD ndup # fd>&dupfd + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + dupfd int # file descriptor to duplicate + vname nodeptr # file name if fd>&$var + + +NHERE nhere # fd<<\! +NXHERE nhere # fd<<! + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + doc nodeptr # input to command (NARG node) + +NNOT nnot # ! command (actually pipeline) + type int + com nodeptr diff --git a/sh/options.c b/sh/options.c new file mode 100644 index 0000000..bc833c7 --- /dev/null +++ b/sh/options.c @@ -0,0 +1,530 @@ +/* $NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $"); +#endif +#endif /* not lint */ + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#define DEFINE_OPTIONS +#include "options.h" +#undef DEFINE_OPTIONS +#include "nodes.h" /* for other header files */ +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#ifndef SMALL +#include "myhistedit.h" +#endif +#include "show.h" + +char *arg0; /* value of $0 */ +struct shparam shellparam; /* current positional parameters */ +char **argptr; /* argument list for builtin commands */ +char *optionarg; /* set by nextopt (like getopt) */ +char *optptr; /* used by nextopt */ + +char *minusc; /* argument to -c option */ + + +STATIC void options(int); +STATIC void minus_o(char *, int); +STATIC void setoption(int, int); +STATIC int getopts(char *, char *, char **, char ***, char **); + + +/* + * Process the shell command line arguments. + */ + +void +procargs(int argc, char **argv) +{ + int i; + + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optlist[i].val = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + iflag = 1; + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optlist[i].val == 2) + optlist[i].val = 0; +#if DEBUG == 2 + debug = 1; +#endif + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + commandname = argv[0]; + arg0 = *argptr++; + setinputfile(arg0, 0); + commandname = arg0; + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (minusc != NULL) { + if (argptr == NULL || *argptr == NULL) + error("Bad -c option"); + minusc = *argptr++; + if (*argptr != 0) + arg0 = *argptr++; + } + + shellparam.p = argptr; + shellparam.reset = 1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +} + + +void +optschanged(void) +{ + setinteractive(iflag); +#ifdef WITH_HISTORY + histedit(); +#endif + setjobctl(mflag); +} + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +STATIC void +options(int cmdline) +{ + static char empty[] = ""; + char *p; + int val; + int c; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + /* command is after shell args*/ + minusc = empty; + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else { + setoption(c, val); + } + } + } +} + +static void +set_opt_val(int i, int val) +{ + int j; + int flag; + + if (val && (flag = optlist[i].opt_set)) { + /* some options (eg vi/emacs) are mutually exclusive */ + for (j = 0; j < NOPTS; j++) + if (optlist[j].opt_set == flag) + optlist[j].val = 0; + } + optlist[i].val = val; +#ifdef DEBUG + if (&optlist[i].val == &debug) + opentrace(); +#endif +} + +STATIC void +minus_o(char *name, int val) +{ + int i; + + if (name == NULL) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + out1fmt("%-16s%s\n", optlist[i].name, + optlist[i].val ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optlist[i].name)) { + set_opt_val(i, val); + return; + } + error("Illegal option -o %s", name); + } +} + + +STATIC void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NOPTS; i++) + if (optlist[i].letter == flag) { + set_opt_val( i, val ); + return; + } + error("Illegal option -%c", flag); + /* NOTREACHED */ +} + + + +#ifdef mkinit +INCLUDE "options.h" + +SHELLPROC { + int i; + + for (i = 0; optlist[i].name; i++) + optlist[i].val = 0; + optschanged(); + +} +#endif + + +/* + * Set the shell parameters. + */ + +void +setparam(char **argv) +{ + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optnext = NULL; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(volatile struct shparam *param) +{ + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } +} + + + +/* + * The shift builtin command. + */ + +int +shiftcmd(int argc, char **argv) +{ + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + error("can't shift that many"); + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); + } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + shellparam.optnext = NULL; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +int +setcmd(int argc, char **argv) +{ + if (argc == 1) + return showvars(0, 0, 1); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +void +getoptsreset(value) + const char *value; +{ + if (number(value) == 1) { + shellparam.optnext = NULL; + shellparam.reset = 1; + } +} + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +int +getoptscmd(int argc, char **argv) +{ + char **optbase; + + if (argc < 3) + error("usage: getopts optstring var [arg]"); + else if (argc == 3) + optbase = shellparam.p; + else + optbase = &argv[3]; + + if (shellparam.reset == 1) { + shellparam.optnext = optbase; + shellparam.optptr = NULL; + shellparam.reset = 0; + } + + return getopts(argv[1], argv[2], optbase, &shellparam.optnext, + &shellparam.optptr); +} + +STATIC int +getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr) +{ + char *p, *q; + char c = '?'; + int done = 0; + int ind = 0; + int err = 0; + char s[12]; + + if ((p = *optpptr) == NULL || *p == '\0') { + /* Current word is done, advance */ + if (*optnext == NULL) + return 1; + p = **optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + ind = *optnext - optfirst + 1; + *optnext = NULL; + p = NULL; + done = 1; + goto out; + } + (*optnext)++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + } else { + outfmt(&errout, "Illegal option -%c\n", c); + (void) unsetvar("OPTARG", 0); + } + c = '?'; + goto bad; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = **optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + c = ':'; + } else { + outfmt(&errout, "No arg for -%c option\n", c); + (void) unsetvar("OPTARG", 0); + c = '?'; + } + goto bad; + } + + if (p == **optnext) + (*optnext)++; + err |= setvarsafe("OPTARG", p, 0); + p = NULL; + } else + err |= setvarsafe("OPTARG", "", 0); + ind = *optnext - optfirst + 1; + goto out; + +bad: + ind = 1; + *optnext = NULL; + p = NULL; +out: + *optpptr = p; + fmtstr(s, sizeof(s), "%d", ind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *optnext = NULL; + *optpptr = NULL; + flushall(); + exraise(EXERROR); + } + return done; +} + +/* + * XXX - should get rid of. have all builtins use getopt(3). the + * library getopt must have the BSD extension static variable "optreset" + * otherwise it can't be used within the shell safely. + * + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +int +nextopt(const char *optstring) +{ + char *p; + const char *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + optionarg = p; + p = NULL; + } + optptr = p; + return c; +} diff --git a/sh/options.h b/sh/options.h new file mode 100644 index 0000000..4cc7dbe --- /dev/null +++ b/sh/options.h @@ -0,0 +1,131 @@ +/* $NetBSD: options.h,v 1.17 2003/08/07 09:05:36 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h 8.2 (Berkeley) 5/4/95 + */ + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + unsigned char reset; /* if getopts has been reset */ + char **p; /* parameter list */ + char **optnext; /* next parameter to be processed by getopts */ + char *optptr; /* used by getopts */ +}; + + +struct optent { + const char *name; /* for set -o <name> */ + const char letter; /* set [+/-]<letter> and $- */ + const char opt_set; /* mutually exclusive option set */ + char val; /* value of <letter>flag */ +}; + +/* Those marked [U] are required by posix, but have no effect! */ + +#ifdef DEFINE_OPTIONS +#define DEF_OPTS(name, letter, opt_set) {name, letter, opt_set, 0}, +struct optent optlist[] = { +#else +#define DEF_OPTS(name, letter, opt_set) +#endif +#define DEF_OPT(name,letter) DEF_OPTS(name, letter, 0) + +DEF_OPT( "errexit", 'e' ) /* exit on error */ +#define eflag optlist[0].val +DEF_OPT( "noglob", 'f' ) /* no pathname expansion */ +#define fflag optlist[1].val +DEF_OPT( "ignoreeof", 'I' ) /* do not exit on EOF */ +#define Iflag optlist[2].val +DEF_OPT( "interactive",'i' ) /* interactive shell */ +#define iflag optlist[3].val +DEF_OPT( "monitor", 'm' ) /* job control */ +#define mflag optlist[4].val +DEF_OPT( "noexec", 'n' ) /* [U] do not exec commands */ +#define nflag optlist[5].val +DEF_OPT( "stdin", 's' ) /* read from stdin */ +#define sflag optlist[6].val +DEF_OPT( "xtrace", 'x' ) /* trace after expansion */ +#define xflag optlist[7].val +DEF_OPT( "verbose", 'v' ) /* trace read input */ +#define vflag optlist[8].val +DEF_OPTS( "vi", 'V', 'V' ) /* vi style editing */ +#define Vflag optlist[9].val +DEF_OPTS( "emacs", 'E', 'V' ) /* emacs style editing */ +#define Eflag optlist[10].val +DEF_OPT( "noclobber", 'C' ) /* do not overwrite files with > */ +#define Cflag optlist[11].val +DEF_OPT( "allexport", 'a' ) /* export all variables */ +#define aflag optlist[12].val +DEF_OPT( "notify", 'b' ) /* [U] report completion of background jobs */ +#define bflag optlist[13].val +DEF_OPT( "nounset", 'u' ) /* error expansion of unset variables */ +#define uflag optlist[14].val +DEF_OPT( "quietprofile", 'q' ) +#define qflag optlist[15].val +DEF_OPT( "nolog", 0 ) /* [U] no functon defs in command history */ +#define nolog optlist[16].val +DEF_OPT( "cdprint", 0 ) /* always print result of cd */ +#define cdprint optlist[17].val +#ifdef DEBUG +DEF_OPT( "debug", 0 ) /* enable debug prints */ +#define debug optlist[18].val +#endif + +#ifdef DEFINE_OPTIONS + { 0, 0, 0, 0 }, +}; +#define NOPTS (sizeof optlist / sizeof optlist[0] - 1) +int sizeof_optlist = sizeof optlist; +#else +extern struct optent optlist[]; +extern int sizeof_optlist; +#endif + + +extern char *minusc; /* argument to -c option */ +extern char *arg0; /* $0 */ +extern struct shparam shellparam; /* $@ */ +extern char **argptr; /* argument list for builtin commands */ +extern char *optionarg; /* set by nextopt */ +extern char *optptr; /* used by nextopt */ + +void procargs(int, char **); +void optschanged(void); +void setparam(char **); +void freeparam(volatile struct shparam *); +int shiftcmd(int, char **); +int setcmd(int, char **); +int getoptscmd(int, char **); +int nextopt(const char *); +void getoptsreset(const char *); diff --git a/sh/output.c b/sh/output.c new file mode 100644 index 0000000..b0e669e --- /dev/null +++ b/sh/output.c @@ -0,0 +1,516 @@ +/* $NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $"); +#endif +#endif /* not lint */ + +/* + * Shell output routines. We use our own output routines because: + * When a builtin command is interrupted we have to discard + * any pending output. + * When a builtin command appears in back quotes, we want to + * save the output of the command in a region obtained + * via malloc, rather than doing a fork and reading the + * output of the command via a pipe. + * Our output routines may be smaller than the stdio routines. + */ + +#include <sys/types.h> /* quad_t */ +#include <sys/param.h> /* BSD4_4 */ +#include <sys/ioctl.h> + +#include <stdio.h> /* defines BUFSIZ */ +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" + + +#define OUTBUFSIZ BUFSIZ +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#define OUTPUT_ERR 01 /* error occurred on output */ + + +struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; +struct output errout = {NULL, 0, NULL, 100, 2, 0}; +struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; +struct output *out1 = &output; +struct output *out2 = &errout; + + + +#ifdef mkinit + +INCLUDE "output.h" +INCLUDE "memalloc.h" + +RESET { + out1 = &output; + out2 = &errout; + if (memout.buf != NULL) { + ckfree(memout.buf); + memout.buf = NULL; + } +} + +#endif + + +#ifdef notdef /* no longer used */ +/* + * Set up an output file to write to memory rather than a file. + */ + +void +open_mem(char *block, int length, struct output *file) +{ + file->nextc = block; + file->nleft = --length; + file->fd = BLOCK_OUT; + file->flags = 0; +} +#endif + + +void +out1str(const char *p) +{ + outstr(p, out1); +} + + +void +out2str(const char *p) +{ + outstr(p, out2); +} + + +void +outstr(const char *p, struct output *file) +{ + while (*p) + outc(*p++, file); + if (file == out2) + flushout(file); +} + + +char out_junk[16]; + + +void +emptyoutbuf(struct output *dest) +{ + int offset; + + if (dest->fd == BLOCK_OUT) { + dest->nextc = out_junk; + dest->nleft = sizeof out_junk; + dest->flags |= OUTPUT_ERR; + } else if (dest->buf == NULL) { + INTOFF; + dest->buf = ckmalloc(dest->bufsize); + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; + INTON; + } else if (dest->fd == MEM_OUT) { + offset = dest->bufsize; + INTOFF; + dest->bufsize <<= 1; + dest->buf = ckrealloc(dest->buf, dest->bufsize); + dest->nleft = dest->bufsize - offset; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } + dest->nleft--; +} + + +void +flushall(void) +{ + flushout(&output); + flushout(&errout); +} + + +void +flushout(struct output *dest) +{ + + if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) + return; + if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) + dest->flags |= OUTPUT_ERR; + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; +} + + +void +freestdout(void) +{ + INTOFF; + if (output.buf) { + ckfree(output.buf); + output.buf = NULL; + output.nleft = 0; + } + INTON; +} + + +void +outfmt(struct output *file, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(out1, fmt, ap); + va_end(ap); +} + +void +dprintf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(out2, fmt, ap); + va_end(ap); + flushout(out2); +} + +void +fmtstr(char *outbuf, size_t length, const char *fmt, ...) +{ + va_list ap; + struct output strout; + + va_start(ap, fmt); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; + va_end(ap); +} + +/* + * Formatted output. This routine handles a subset of the printf formats: + * - Formats supported: d, u, o, p, X, s, and c. + * - The x format is also accepted but is treated like X. + * - The l, ll and q modifiers are accepted. + * - The - and # flags are accepted; # only works with the o format. + * - Width and precision may be specified with any format except c. + * - An * may be given for the width or precision. + * - The obsolete practice of preceding the width with a zero to get + * zero padding is not supported; use the precision field. + * - A % may be printed by writing %% in the format string. + */ + +#define TEMPSIZE 24 + +#ifdef BSD4_4 +#define HAVE_VASPRINTF 1 +#endif + +void +doformat(struct output *dest, const char *f, va_list ap) +{ +#if HAVE_VASPRINTF + char *s; + + vasprintf(&s, f, ap); + outstr(s, dest); + free(s); +#else /* !HAVE_VASPRINTF */ + static const char digit[] = "0123456789ABCDEF"; + char c; + char temp[TEMPSIZE]; + int flushleft; + int sharp; + int width; + int prec; + int islong; + int isquad; + char *p; + int sign; +#ifdef BSD4_4 + quad_t l; + u_quad_t num; +#else + long l; + u_long num; +#endif + unsigned base; + int len; + int size; + int pad; + + while ((c = *f++) != '\0') { + if (c != '%') { + outc(c, dest); + continue; + } + flushleft = 0; + sharp = 0; + width = 0; + prec = -1; + islong = 0; + isquad = 0; + for (;;) { + if (*f == '-') + flushleft++; + else if (*f == '#') + sharp++; + else + break; + f++; + } + if (*f == '*') { + width = va_arg(ap, int); + f++; + } else { + while (is_digit(*f)) { + width = 10 * width + digit_val(*f++); + } + } + if (*f == '.') { + if (*++f == '*') { + prec = va_arg(ap, int); + f++; + } else { + prec = 0; + while (is_digit(*f)) { + prec = 10 * prec + digit_val(*f++); + } + } + } + if (*f == 'l') { + f++; + if (*f == 'l') { + isquad++; + f++; + } else + islong++; + } else if (*f == 'q') { + isquad++; + f++; + } + switch (*f) { + case 'd': +#ifdef BSD4_4 + if (isquad) + l = va_arg(ap, quad_t); + else +#endif + if (islong) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + sign = 0; + num = l; + if (l < 0) { + num = -l; + sign = 1; + } + base = 10; + goto number; + case 'u': + base = 10; + goto uns_number; + case 'o': + base = 8; + goto uns_number; + case 'p': + outc('0', dest); + outc('x', dest); + /*FALLTHROUGH*/ + case 'x': + /* we don't implement 'x'; treat like 'X' */ + case 'X': + base = 16; +uns_number: /* an unsigned number */ + sign = 0; +#ifdef BSD4_4 + if (isquad) + num = va_arg(ap, u_quad_t); + else +#endif + if (islong) + num = va_arg(ap, unsigned long); + else + num = va_arg(ap, unsigned int); +number: /* process a number */ + p = temp + TEMPSIZE - 1; + *p = '\0'; + while (num) { + *--p = digit[num % base]; + num /= base; + } + len = (temp + TEMPSIZE - 1) - p; + if (prec < 0) + prec = 1; + if (sharp && *f == 'o' && prec <= len) + prec = len + 1; + pad = 0; + if (width) { + size = len; + if (size < prec) + size = prec; + size += sign; + pad = width - size; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + if (sign) + outc('-', dest); + prec -= len; + while (--prec >= 0) + outc('0', dest); + while (*p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 's': + p = va_arg(ap, char *); + pad = 0; + if (width) { + len = strlen(p); + if (prec >= 0 && len > prec) + len = prec; + pad = width - len; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + prec++; + while (--prec != 0 && *p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 'c': + c = va_arg(ap, int); + outc(c, dest); + break; + default: + outc(*f, dest); + break; + } + f++; + } +#endif /* !HAVE_VASPRINTF */ +} + + + +/* + * Version of write which resumes after a signal is caught. + */ + +int +xwrite(int fd, char *buf, int nbytes) +{ + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * Version of ioctl that retries after a signal is caught. + * XXX unused function + */ + +int +xioctl(int fd, unsigned long request, char *arg) +{ + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; +} diff --git a/sh/output.h b/sh/output.h new file mode 100644 index 0000000..9a199a0 --- /dev/null +++ b/sh/output.h @@ -0,0 +1,81 @@ +/* $NetBSD: output.h,v 1.17 2003/08/07 09:05:36 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)output.h 8.2 (Berkeley) 5/4/95 + */ + +#ifndef OUTPUT_INCL + +#include <stdarg.h> + +struct output { + char *nextc; + int nleft; + char *buf; + int bufsize; + short fd; + short flags; +}; + +extern struct output output; +extern struct output errout; +extern struct output memout; +extern struct output *out1; +extern struct output *out2; + +void open_mem(char *, int, struct output *); +void out1str(const char *); +void out2str(const char *); +void outstr(const char *, struct output *); +void emptyoutbuf(struct output *); +void flushall(void); +void flushout(struct output *); +void freestdout(void); +void outfmt(struct output *, const char *, ...) + __attribute__((__format__(__printf__,2,3))); +void out1fmt(const char *, ...) + __attribute__((__format__(__printf__,1,2))); +void dprintf(const char *, ...) + __attribute__((__format__(__printf__,1,2))); +void fmtstr(char *, size_t, const char *, ...) + __attribute__((__format__(__printf__,3,4))); +void doformat(struct output *, const char *, va_list); +int xwrite(int, char *, int); +int xioctl(int, unsigned long, char *); + +#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) +#define out1c(c) outc(c, out1); +#define out2c(c) outc(c, out2); + +#define OUTPUT_INCL +#endif diff --git a/sh/parser.c b/sh/parser.c new file mode 100644 index 0000000..67de58e --- /dev/null +++ b/sh/parser.c @@ -0,0 +1,1651 @@ +/* $NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; +#else +__RCSID("$NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" /* defines rmescapes() */ +#include "eval.h" /* defines commandname */ +#include "redir.h" /* defines copyfd() */ +#include "syntax.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "var.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "show.h" +#ifndef SMALL +#include "myhistedit.h" +#endif + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + +/* values returned by readtoken */ +#include "token.h" + +#define OPENBRACE '{' +#define CLOSEBRACE '}' + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + + + +static int noalias = 0; /* when set, don't handle aliases */ +struct heredoc *heredoclist; /* list of here documents to read */ +int parsebackquote; /* nonzero if we are inside backquotes */ +int doprompt; /* if set, prompt the user */ +int needprompt; /* true if interactive and at start of line */ +int lasttoken; /* last token read */ +MKINIT int tokpushback; /* last token pushed back */ +char *wordtext; /* text of last word returned by readtoken */ +MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ +struct nodelist *backquotelist; +union node *redirnode; +struct heredoc *heredoc; +int quoteflag; /* set if (part of) last token was quoted */ +int startlinno; /* line # where last token started */ + + +STATIC union node *list(int); +STATIC union node *andor(void); +STATIC union node *pipeline(void); +STATIC union node *command(void); +STATIC union node *simplecmd(union node **, union node *); +STATIC union node *makename(void); +STATIC void parsefname(void); +STATIC void parseheredoc(void); +STATIC int peektoken(void); +STATIC int readtoken(void); +STATIC int xxreadtoken(void); +STATIC int readtoken1(int, char const *, char *, int); +STATIC int noexpand(char *); +STATIC void synexpect(int) __attribute__((__noreturn__)); +STATIC void synerror(const char *) __attribute__((__noreturn__)); +STATIC void setprompt(int); + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(int interact) +{ + int t; + + tokpushback = 0; + doprompt = interact; + if (doprompt) + setprompt(1); + else + setprompt(0); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +STATIC union node * +list(int nlflag) +{ + union node *n1, *n2, *n3; + int tok; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2->type == NCMD || n2->type == NPIPE) { + n2->ncmd.backgnd = 1; + } else if (n2->type == NREDIR) { + n2->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + } + if (n1 == NULL) { + n1 = n2; + } + else { + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } + switch (tok) { + case TBACKGND: + case TSEMI: + tok = readtoken(); + /* fall through */ + case TNL: + if (tok == TNL) { + parseheredoc(); + if (nlflag) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +STATIC union node * +andor(void) +{ + union node *n1, *n2, *n3; + int t; + + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + n2 = pipeline(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = t; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } +} + + + +STATIC union node * +pipeline(void) +{ + union node *n1, *n2, *pipenode; + struct nodelist *lp, *prev; + int negate; + + negate = 0; + TRACE(("pipeline: entered\n")); + while (readtoken() == TNOT) + negate = !negate; + tokpushback++; + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + if (negate) { + n2 = (union node *)stalloc(sizeof (struct nnot)); + n2->type = NNOT; + n2->nnot.com = n1; + return n2; + } else + return n1; +} + + + +STATIC union node * +command(void) +{ + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t, negate = 0; + + checkkwd = 2; + redir = NULL; + n1 = NULL; + rpp = &redir; + + /* Check for redirection which may precede command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + + while (readtoken() == TNOT) { + TRACE(("command: TNOT recognized\n")); + negate = !negate; + } + tokpushback++; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + if (readtoken() != TFI) + synexpect(TFI); + checkkwd = 1; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { + app = ≈ + while (readtoken() == TWORD) { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { + static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + '@', '=', '\0'}; + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = argvars; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* + * Newline or semicolon here is optional (but note + * that the original Bourne shell only allowed NL). + */ + if (lasttoken != TNL && lasttoken != TSEMI) + tokpushback++; + } + checkkwd = 2; + if ((t = readtoken()) == TDO) + t = TDONE; + else if (t == TBEGIN) + t = TEND; + else + synexpect(-1); + n1->nfor.body = list(0); + if (readtoken() != t) + synexpect(t); + checkkwd = 1; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + while (readtoken() == TNL); + if (lasttoken != TWORD || ! equal(wordtext, "in")) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + noalias = 1; + checkkwd = 2, readtoken(); + do { + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + for (;;) { + *app = ap = (union node *)stalloc(sizeof (struct narg)); + ap->type = NARG; + ap->narg.text = wordtext; + ap->narg.backquote = backquotelist; + if (checkkwd = 2, readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + noalias = 0; + if (lasttoken != TRP) { + synexpect(TRP); + } + cp->nclist.body = list(0); + + checkkwd = 2; + if ((t = readtoken()) != TESAC) { + if (t != TENDCASE) { + noalias = 0; + synexpect(TENDCASE); + } else { + noalias = 1; + checkkwd = 2; + readtoken(); + } + } + cpp = &cp->nclist.next; + } while(lasttoken != TESAC); + noalias = 0; + *cpp = NULL; + checkkwd = 1; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + if (readtoken() != TRP) + synexpect(TRP); + checkkwd = 1; + break; + case TBEGIN: + n1 = list(0); + if (readtoken() != TEND) + synexpect(TEND); + checkkwd = 1; + break; + /* Handle an empty command like other simple commands. */ + case TSEMI: + /* + * An empty command before a ; doesn't make much sense, and + * should certainly be disallowed in the case of `if ;'. + */ + if (!redir) + synexpect(-1); + case TAND: + case TOR: + case TNL: + case TEOF: + case TWORD: + case TRP: + tokpushback++; + n1 = simplecmd(rpp, redir); + goto checkneg; + default: + synexpect(-1); + /* NOTREACHED */ + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + +checkneg: + if (negate) { + n2 = (union node *)stalloc(sizeof (struct nnot)); + n2->type = NNOT; + n2->nnot.com = n1; + return n2; + } + else + return n1; +} + + +STATIC union node * +simplecmd(union node **rpp, union node *redir) +{ + union node *args, **app; + union node **orig_rpp = rpp; + union node *n = NULL, *n2; + int negate = 0; + + /* If we don't have any redirections already, then we must reset */ + /* rpp to be the address of the local redir variable. */ + if (redir == 0) + rpp = &redir; + + args = NULL; + app = &args; + /* + * We save the incoming value, because we need this for shell + * functions. There can not be a redirect or an argument between + * the function name and the open parenthesis. + */ + orig_rpp = rpp; + + while (readtoken() == TNOT) { + TRACE(("command: TNOT recognized\n")); + negate = !negate; + } + tokpushback++; + + for (;;) { + if (readtoken() == TWORD) { + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + *app = n; + app = &n->narg.next; + } else if (lasttoken == TREDIR) { + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + } else if (lasttoken == TLP && app == &args->narg.next + && rpp == orig_rpp) { + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); +#ifdef notdef + if (! goodname(n->narg.text)) + synerror("Bad function name"); +#endif + n->type = NDEFUN; + n->narg.next = command(); + goto checkneg; + } else { + tokpushback++; + break; + } + } + *app = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.backgnd = 0; + n->ncmd.args = args; + n->ncmd.redirect = redir; + +checkneg: + if (negate) { + n2 = (union node *)stalloc(sizeof (struct nnot)); + n2->type = NNOT; + n2->nnot.com = n; + return n2; + } + else + return n; +} + +STATIC union node * +makename(void) +{ + union node *n; + + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + return n; +} + +void fixredir(union node *n, const char *text, int err) + { + TRACE(("Fix redir %s %d\n", text, err)); + if (!err) + n->ndup.vname = NULL; + + if (is_digit(text[0]) && text[1] == '\0') + n->ndup.dupfd = digit_val(text[0]); + else if (text[0] == '-' && text[1] == '\0') + n->ndup.dupfd = -1; + else { + + if (err) + synerror("Bad fd number"); + else + n->ndup.vname = makename(); + } +} + + +STATIC void +parsefname(void) +{ + union node *n = redirnode; + + if (readtoken() != TWORD) + synexpect(-1); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + int i; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + if (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * Input any here documents. + */ + +STATIC void +parseheredoc(void) +{ + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + if (needprompt) { + setprompt(2); + needprompt = 0; + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = (union node *)stalloc(sizeof (struct narg)); + n->narg.type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + here->here->nhere.doc = n; + } +} + +STATIC int +peektoken(void) +{ + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +STATIC int +readtoken(void) +{ + int t; + int savecheckkwd = checkkwd; +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + struct alias *ap; + + top: + t = xxreadtoken(); + + if (checkkwd) { + /* + * eat newlines + */ + if (checkkwd == 2) { + checkkwd = 0; + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } else + checkkwd = 0; + /* + * check for keywords and aliases + */ + if (t == TWORD && !quoteflag) + { + const char *const *pp; + + for (pp = parsekwd; *pp; pp++) { + if (**pp == *wordtext && equal(*pp, wordtext)) + { + lasttoken = t = pp - + parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + goto out; + } + } + if(!noalias && + (ap = lookupalias(wordtext, 1)) != NULL) { + pushstring(ap->val, strlen(ap->val), ap); + checkkwd = savecheckkwd; + goto top; + } + } +out: + checkkwd = (t == TNOT) ? savecheckkwd : 0; + } +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +STATIC int +xxreadtoken(void) +{ + int c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + setprompt(2); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + if (c == ' ' || c == '\t') + continue; /* quick check for white space first */ + switch (c) { + case ' ': case '\t': + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + startlinno = ++plinno; + if (doprompt) + setprompt(2); + else + setprompt(0); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +/* + * Keep track of nested doublequotes in dblquote and doublequotep. + * We use dblquote for the first 32 levels, and we expand to a malloc'ed + * region for levels above that. Usually we never need to malloc. + * This code assumes that an int is 32 bits. We don't use uint32_t, + * because the rest of the code does not. + */ +#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \ + (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32)))) + +#define SETDBLQUOTE() \ + if (varnest < 32) \ + dblquote |= (1 << varnest); \ + else \ + dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32)) + +#define CLRDBLQUOTE() \ + if (varnest < 32) \ + dblquote &= ~(1 << varnest); \ + else \ + dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32)) + +STATIC int +readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) +{ + int c = firstc; + char *out; + int len; + char line[EOFMARKLEN + 1]; + struct nodelist *bqlist; + int quotef; + int *dblquotep = NULL; + size_t maxnest = 32; + int dblquote; + int varnest; /* levels of variables expansion */ + int arinest; /* levels of arithmetic expansion */ + int parenlevel; /* levels of parens in arithmetic */ + int oldstyle; + char const *prevsyntax = NULL; /* syntax before arithmetic */ +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &maxnest; + (void) &dblquotep; + (void) &out; + (void) "ef; + (void) &dblquote; + (void) &varnest; + (void) &arinest; + (void) &parenlevel; + (void) &oldstyle; + (void) &prevsyntax; + (void) &syntax; +#endif + + startlinno = plinno; + dblquote = 0; + varnest = 0; + if (syntax == DQSYNTAX) { + SETDBLQUOTE(); + } + quotef = 0; + bqlist = NULL; + arinest = 0; + parenlevel = 0; + + STARTSTACKSTR(out); + loop: { /* for each line, until end of word */ +#if ATTY + if (c == '\034' && doprompt + && attyset() && ! equal(termval(), "emacs")) { + attyline(); + if (syntax == BASESYNTAX) + return readtoken(); + c = pgetc(); + goto loop; + } +#endif + CHECKEND(); /* set c to PEOF if at end of here document */ + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || ISDBLQUOTE()) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + break; + } + if (c == '\n') { + if (doprompt) + setprompt(2); + else + setprompt(0); + break; + } + quotef = 1; + if (ISDBLQUOTE() && c != '\\' && + c != '`' && c != '$' && + (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + else if (eofmark == NULL) { + USTPUTC(CTLQUOTEMARK, out); + USTPUTC(c, out); + if (varnest != 0) + USTPUTC(CTLQUOTEEND, out); + break; + } + USTPUTC(c, out); + break; + case CSQUOTE: + if (syntax != SQSYNTAX) { + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + quotef = 1; + syntax = SQSYNTAX; + break; + } + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + /* Ignore inside quoted here document */ + USTPUTC(c, out); + break; + } + /* End of single quotes... */ + if (arinest) + syntax = ARISYNTAX; + else { + syntax = BASESYNTAX; + if (varnest != 0) + USTPUTC(CTLQUOTEEND, out); + } + break; + case CDQUOTE: + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + /* Ignore inside here document */ + USTPUTC(c, out); + break; + } + quotef = 1; + if (arinest) { + if (ISDBLQUOTE()) { + syntax = ARISYNTAX; + CLRDBLQUOTE(); + } else { + syntax = DQSYNTAX; + SETDBLQUOTE(); + USTPUTC(CTLQUOTEMARK, out); + } + break; + } + if (eofmark != NULL) + break; + if (ISDBLQUOTE()) { + if (varnest != 0) + USTPUTC(CTLQUOTEEND, out); + syntax = BASESYNTAX; + CLRDBLQUOTE(); + } else { + syntax = DQSYNTAX; + SETDBLQUOTE(); + USTPUTC(CTLQUOTEMARK, out); + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* CLOSEBRACE */ + if (varnest > 0 && !ISDBLQUOTE()) { + varnest--; + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; + case CLP: /* '(' in arithmetic */ + parenlevel++; + USTPUTC(c, out); + break; + case CRP: /* ')' in arithmetic */ + if (parenlevel > 0) { + USTPUTC(c, out); + --parenlevel; + } else { + if (pgetc() == ')') { + if (--arinest == 0) { + USTPUTC(CTLENDARI, out); + syntax = prevsyntax; + if (syntax == DQSYNTAX) + SETDBLQUOTE(); + else + CLRDBLQUOTE(); + } else + USTPUTC(')', out); + } else { + /* + * unbalanced parens + * (don't 2nd guess - no error) + */ + pungetc(); + USTPUTC(')', out); + } + } + break; + case CBQUOTE: /* '`' */ + PARSEBACKQOLD(); + break; + case CEOF: + goto endword; /* exit outer loop */ + default: + if (varnest == 0) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + } + c = pgetc_macro(); + } + } +endword: + if (syntax == ARISYNTAX) + synerror("Missing '))'"); + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + /* { */ + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + if (dblquotep != NULL) + ckfree(dblquotep); + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { + if (striptabs) { + while (c == '\t') + c = pgetc(); + } + if (c == *eofmark) { + if (pfgets(line, sizeof line) != NULL) { + char *p, *q; + + p = line; + for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); + if (*p == '\n' && *q == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; + } else { + pushstring(line, strlen(line), NULL); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '|') + np->type = NCLOBBER; + else if (c == '&') + np->type = NTOFD; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + switch (c = pgetc()) { + case '<': + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + break; + + case '&': + np->type = NFROMFD; + break; + + case '>': + np->type = NFROMTO; + break; + + default: + np->type = NFROM; + pungetc(); + break; + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; + goto parseredir_return; +} + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + int flags; + char *p; + static const char types[] = "}-+?="; + + c = pgetc(); + if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc() == '(') { + PARSEARITH(); + } else { + pungetc(); + PARSEBACKQNEW(); + } + } else { + USTPUTC(CTLVAR, out); + typeloc = out - stackblock(); + USTPUTC(VSNORMAL, out); + subtype = VSNORMAL; + if (c == OPENBRACE) { + c = pgetc(); + if (c == '#') { + if ((c = pgetc()) == CLOSEBRACE) + c = '#'; + else + subtype = VSLENGTH; + } + else + subtype = 0; + } + if (is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_in_name(c)); + } else if (is_digit(c)) { + do { + USTPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } + else if (is_special(c)) { + USTPUTC(c, out); + c = pgetc(); + } + else +badsub: synerror("Bad substitution"); + + STPUTC('=', out); + flags = 0; + if (subtype == 0) { + switch (c) { + case ':': + flags = VSNUL; + c = pgetc(); + /*FALLTHROUGH*/ + default: + p = strchr(types, c); + if (p == NULL) + goto badsub; + subtype = p - types + VSNORMAL; + break; + case '%': + case '#': + { + int cc = c; + subtype = c == '#' ? VSTRIMLEFT : + VSTRIMRIGHT; + c = pgetc(); + if (c == cc) + subtype++; + else + pungetc(); + break; + } + } + } else { + pungetc(); + } + if (ISDBLQUOTE() || arinest) + flags |= VSQUOTE; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) { + varnest++; + if (varnest >= maxnest) { + dblquotep = ckrealloc(dblquotep, maxnest / 8); + dblquotep[(maxnest / 32) - 1] = 0; + maxnest += 32; + } + } + } + goto parsesub_return; +} + + +/* + * Called to parse command substitutions. Newstyle is set if the command + * is enclosed inside $(...); nlpp is a pointer to the head of the linked + * list of commands (passed by reference), and savelen is the number of + * characters on the top of the stack which must be preserved. + */ + +parsebackq: { + struct nodelist **nlpp; + int savepbq; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int savelen; + int saveprompt; +#ifdef __GNUC__ + (void) &saveprompt; +#endif + + savepbq = parsebackquote; + if (setjmp(jmploc.loc)) { + if (str) + ckfree(str); + parsebackquote = 0; + handler = savehandler; + longjmp(handler->loc, 1); + } + INTOFF; + str = NULL; + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + memcpy(str, stackblock(), savelen); + } + savehandler = handler; + handler = &jmploc; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + char *pout; + int pc; + int psavelen; + char *pstr; + + + STARTSTACKSTR(pout); + for (;;) { + if (needprompt) { + setprompt(2); + needprompt = 0; + } + switch (pc = pgetc()) { + case '`': + goto done; + + case '\\': + if ((pc = pgetc()) == '\n') { + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + /* + * If eating a newline, avoid putting + * the newline into the new character + * stream (via the STPUTC after the + * switch). + */ + continue; + } + if (pc != '\\' && pc != '`' && pc != '$' + && (!ISDBLQUOTE() || pc != '"')) + STPUTC('\\', pout); + break; + + case '\n': + plinno++; + needprompt = doprompt; + break; + + case PEOF: + startlinno = plinno; + synerror("EOF in backquote substitution"); + break; + + default: + break; + } + STPUTC(pc, pout); + } +done: + STPUTC('\0', pout); + psavelen = pout - stackblock(); + if (psavelen > 0) { + pstr = grabstackstr(pout); + setinputstring(pstr, 1); + } + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + parsebackquote = oldstyle; + + if (oldstyle) { + saveprompt = doprompt; + doprompt = 0; + } + + n = list(0); + + if (oldstyle) + doprompt = saveprompt; + else { + if (readtoken() != TRP) + synexpect(TRP); + } + + (*nlpp)->n = n; + if (oldstyle) { + /* + * Start reading from old file again, ignoring any pushed back + * tokens left from the backquote parsing + */ + popfile(); + tokpushback = 0; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + memcpy(out, str, savelen); + STADJUST(savelen, out); + INTOFF; + ckfree(str); + str = NULL; + INTON; + } + parsebackquote = savepbq; + handler = savehandler; + if (arinest || ISDBLQUOTE()) + USTPUTC(CTLBACKQ | CTLQUOTE, out); + else + USTPUTC(CTLBACKQ, out); + if (oldstyle) + goto parsebackq_oldreturn; + else + goto parsebackq_newreturn; +} + +/* + * Parse an arithmetic expansion (indicate start of one and set state) + */ +parsearith: { + + if (++arinest == 1) { + prevsyntax = syntax; + syntax = ARISYNTAX; + USTPUTC(CTLARI, out); + if (ISDBLQUOTE()) + USTPUTC('"',out); + else + USTPUTC(' ',out); + } else { + /* + * we collapse embedded arithmetic expansion to + * parenthesis, which should be equivalent + */ + USTPUTC('(', out); + } + goto parsearith_return; +} + +} /* end of readtoken */ + + + +#ifdef mkinit +RESET { + tokpushback = 0; + checkkwd = 0; +} +#endif + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +STATIC int +noexpand(char *text) +{ + char *p; + char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + p++; + else if (BASESYNTAX[(int)c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +int +goodname(char *name) + { + char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +STATIC void +synexpect(int token) +{ + char msg[64]; + + if (token >= 0) { + fmtstr(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); + /* NOTREACHED */ +} + + +STATIC void +synerror(const char *msg) +{ + if (commandname) + outfmt(&errout, "%s: %d: ", commandname, startlinno); + outfmt(&errout, "Syntax error: %s\n", msg); + error((char *)NULL); + /* NOTREACHED */ +} + +STATIC void +setprompt(int which) +{ + whichprompt = which; + +#ifdef WITH_HISTORY + if (!el) +#endif + out2str(getprompt(NULL)); +} + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +const char * +getprompt(void *unused) + { + switch (whichprompt) { + case 0: + return ""; + case 1: + return ps1val(); + case 2: + return ps2val(); + default: + return "<internal prompt error>"; + } +} diff --git a/sh/parser.h b/sh/parser.h new file mode 100644 index 0000000..b343c71 --- /dev/null +++ b/sh/parser.h @@ -0,0 +1,82 @@ +/* $NetBSD: parser.h,v 1.17 2004/06/26 22:09:49 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)parser.h 8.3 (Berkeley) 5/4/95 + */ + +/* control characters in argument strings */ +#define CTL_FIRST '\201' /* first 'special' character */ +#define CTLESC '\201' /* escape next character */ +#define CTLVAR '\202' /* variable defn */ +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\205' */ +#define CTLARI '\206' /* arithmetic expression */ +#define CTLENDARI '\207' +#define CTLQUOTEMARK '\210' +#define CTLQUOTEEND '\211' /* only inside ${...} */ +#define CTL_LAST '\211' /* last 'special' character */ + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMLEFT 0x6 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ +#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ +#define VSLENGTH 0xa /* ${#var} */ + + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +extern int tokpushback; +#define NEOF ((union node *)&tokpushback) +extern int whichprompt; /* 1 == PS1, 2 == PS2 */ + + +union node *parsecmd(int); +void fixredir(union node *, const char *, int); +int goodname(char *); +const char *getprompt(void *); diff --git a/sh/redir.c b/sh/redir.c new file mode 100644 index 0000000..5c4c286 --- /dev/null +++ b/sh/redir.c @@ -0,0 +1,389 @@ +/* $NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> /* PIPE_BUF */ +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +/* + * Code for dealing with input/output redirection. + */ + +#include "main.h" +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "expand.h" +#include "redir.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" + + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif + +#define signal bsd_signal + +MKINIT +struct redirtab { + struct redirtab *next; + short renamed[10]; +}; + + +MKINIT struct redirtab *redirlist; + +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. +*/ +int fd0_redirected = 0; + +STATIC void openredirect(union node *, char[10], int); +STATIC int openhere(union node *); + + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv = NULL; + int i; + int fd; + int try; + char memory[10]; /* file descriptors to write to memory */ + + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; + if (flags & REDIR_PUSH) { + /* We don't have to worry about REDIR_VFORK here, as + * flags & REDIR_PUSH is never true if REDIR_VFORK is set. + */ + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + try = 0; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + INTOFF; +again: + if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { + switch (errno) { + case EBADF: + if (!try) { + openredirect(n, memory, flags); + try++; + goto again; + } + /* FALLTHROUGH*/ + default: + INTON; + error("%d: %s", fd, strerror(errno)); + /* NOTREACHED */ + } + } + if (!try) { + sv->renamed[fd] = i; + close(fd); + } + INTON; + } else { + close(fd); + } + if (fd == 0) + fd0_redirected++; + if (!try) + openredirect(n, memory, flags); + } + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; +} + + +STATIC void +openredirect(union node *redir, char memory[10], int flags) +{ + int fd = redir->nfile.fd; + char *fname; + int f; + int oflags = O_WRONLY|O_CREAT|O_TRUNC, eflags; + + /* + * We suppress interrupts so that we won't leave open file + * descriptors around. This may not be such a good idea because + * an open of a device or a fifo can block indefinitely. + */ + INTOFF; + memory[fd] = 0; + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if (flags & REDIR_VFORK) + eflags = O_NONBLOCK; + else + eflags = 0; + if ((f = open(fname, O_RDONLY|eflags)) < 0) + goto eopen; + if (eflags) + (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags); + break; + case NFROMTO: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; + break; + case NTO: + if (Cflag) + oflags |= O_EXCL; + /* FALLTHROUGH */ + case NCLOBBER: + fname = redir->nfile.expfname; + if ((f = open(fname, oflags, 0666)) < 0) + goto ecreate; + break; + case NAPPEND: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + goto ecreate; + break; + case NTOFD: + case NFROMFD: + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (memory[redir->ndup.dupfd]) + memory[fd] = 1; + else + copyfd(redir->ndup.dupfd, fd); + } + INTON; + return; + case NHERE: + case NXHERE: + f = openhere(redir); + break; + default: + abort(); + } + + if (f != fd) { + copyfd(f, fd); + close(f); + } + INTON; + return; +ecreate: + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +eopen: + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +STATIC int +openhere(union node *redir) +{ + int pip[2]; + int len = 0; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(pip[1], redir->nhere.doc->narg.text, len); + goto out; + } + } + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + xwrite(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir(void) +{ + struct redirtab *rp = redirlist; + int i; + + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (i == 0) + fd0_redirected--; + close(i); + if (rp->renamed[i] >= 0) { + copyfd(rp->renamed[i], i); + close(rp->renamed[i]); + } + } + } + INTOFF; + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* + * Undo all redirections. Called on error or interrupt. + */ + +#ifdef mkinit + +INCLUDE "redir.h" + +RESET { + while (redirlist) + popredir(); +} + +SHELLPROC { + clearredir(0); +} + +#endif + +/* Return true if fd 0 has already been redirected at least once. */ +int +fd0_redirected_p () { + return fd0_redirected != 0; +} + +/* + * Discard all saved file descriptors. + */ + +void +clearredir(vforked) + int vforked; +{ + struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + if (!vforked) + rp->renamed[i] = EMPTY; + } + } +} + + + +/* + * Copy a file descriptor to be >= to. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +int +copyfd(int from, int to) +{ + int newfd; + + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0) { + if (errno == EMFILE) + return EMPTY; + else + error("%d: %s", from, strerror(errno)); + } + return newfd; +} diff --git a/sh/redir.h b/sh/redir.h new file mode 100644 index 0000000..c9709e9 --- /dev/null +++ b/sh/redir.h @@ -0,0 +1,48 @@ +/* $NetBSD: redir.h,v 1.15 2003/08/07 09:05:37 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)redir.h 8.2 (Berkeley) 5/4/95 + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output in memory */ +#define REDIR_VFORK 04 /* running under vfork(2), be careful */ + +union node; +void redirect(union node *, int); +void popredir(void); +int fd0_redirected_p(void); +void clearredir(int); +int copyfd(int, int); + @@ -0,0 +1,1928 @@ +.\" $NetBSD: sh.1,v 1.78 2004/06/03 19:54:37 hubertf Exp $ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sh.1 8.6 (Berkeley) 5/4/95 +.\" +.Dd April 17, 2004 +.Os +.Dt SH 1 +.Sh NAME +.Nm sh +.Nd command interpreter (shell) +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Op Ar command_file Oo Ar argument ... Oc +.Ek +.Nm +.Fl c +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Ar command_string +.Op Ar command_name Oo Ar argument ... Oc +.Ek +.Nm +.Fl s +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Op Ar argument ... +.Ek +.Sh DESCRIPTION +.Nm +is the standard command interpreter for the system. +The current version of +.Nm +is in the process of being changed to conform with the +.Tn POSIX +1003.2 and 1003.2a specifications for the shell. +This version has many +features which make it appear similar in some respects to the Korn shell, +but it is not a Korn shell clone (see +.Xr ksh 1 ) . +Only features designated by +.Tn POSIX , +plus a few Berkeley extensions, are being incorporated into this shell. +.\" We expect +.\" .Tn POSIX +.\" conformance by the time 4.4 BSD is released. +This man page is not intended +to be a tutorial or a complete specification of the shell. +.Ss Overview +The shell is a command that reads lines from either a file or the +terminal, interprets them, and generally executes other commands. +It is the program that is running when a user logs into the system +(although a user can select a different shell with the +.Xr chsh 1 +command). +The shell implements a language that has flow control +constructs, a macro facility that provides a variety of features in +addition to data storage, along with built in history and line editing +capabilities. +It incorporates many features to aid interactive use and +has the advantage that the interpretative language is common to both +interactive and non-interactive use (shell scripts). +That is, commands +can be typed directly to the running shell or can be put into a file and +the file can be executed directly by the shell. +.Ss Invocation +If no args are present and if the standard input of the shell +is connected to a terminal (or if the +.Fl i +flag is set), +and the +.Fl c +option is not present, the shell is considered an interactive shell. +An interactive shell generally prompts before each command and handles +programming and command errors differently (as described below). +When first starting, +the shell inspects argument 0, and if it begins with a dash +.Sq - , +the shell is also considered +a login shell. +This is normally done automatically by the system +when the user first logs in. +A login shell first reads commands +from the files +.Pa /etc/profile +and +.Pa .profile +if they exist. +If the environment variable +.Ev ENV +is set on entry to a shell, or is set in the +.Pa .profile +of a login shell, the shell next reads +commands from the file named in +.Ev ENV . +Therefore, a user should place commands that are to be executed only at +login time in the +.Pa .profile +file, and commands that are executed for every shell inside the +.Ev ENV +file. +To set the +.Ev ENV +variable to some file, place the following line in your +.Pa .profile +of your home directory +.Pp +.Dl ENV=$HOME/.shinit; export ENV +.Pp +substituting for +.Dq .shinit +any filename you wish. +Since the +.Ev ENV +file is read for every invocation of the shell, including shell scripts +and non-interactive shells, the following paradigm is useful for +restricting commands in the +.Ev ENV +file to interactive invocations. +Place commands within the +.Dq case +and +.Dq esac +below (these commands are described later): +.Pp +.Bl -item -compact -offset indent +.It +.Li case $- in *i*) +.Bl -item -compact -offset indent +.It +.Li # commands for interactive use only +.It +.Li ... +.El +.It +.Li esac +.El +.Pp +If command line arguments besides the options have been specified, then +the shell treats the first argument as the name of a file from which to +read commands (a shell script), and the remaining arguments are set as the +positional parameters of the shell ($1, $2, etc). +Otherwise, the shell +reads commands from its standard input. +.Ss Argument List Processing +All of the single letter options have a corresponding name that can be +used as an argument to the +.Fl o +option. +The set +.Fl o +name is provided next to the single letter option in +the description below. +Specifying a dash +.Dq - +turns the option on, while using a plus +.Dq + +disables the option. +The following options can be set from the command line or +with the +.Ic set +builtin (described later). +.Bl -tag -width aaaallexportfoo -offset indent +.It Fl a Em allexport +Export all variables assigned to. +.It Fl c +Read commands from the +.Ar command_string +operand instead of from the standard input. +Special parameter 0 will be set from the +.Ar command_name +operand and the positional parameters ($1, $2, etc.) +set from the remaining argument operands. +.It Fl C Em noclobber +Don't overwrite existing files with +.Dq \*[Gt] . +.It Fl e Em errexit +If not interactive, exit immediately if any untested command fails. +The exit status of a command is considered to be +explicitly tested if the command is used to control an +.Ic if , +.Ic elif , +.Ic while , +or +.Ic until ; +or if the command is the left hand operand of an +.Dq \*[Am]\*[Am] +or +.Dq || +operator. +.It Fl f Em noglob +Disable pathname expansion. +.It Fl n Em noexec +If not interactive, read commands but do not execute them. +This is useful for checking the syntax of shell scripts. +.It Fl u Em nounset +Write a message to standard error when attempting to expand a variable +that is not set, and if the shell is not interactive, exit immediately. +.It Fl v Em verbose +The shell writes its input to standard error as it is read. +Useful for debugging. +.It Fl x Em xtrace +Write each command to standard error (preceded by a +.Sq +\ ) +before it is executed. +Useful for debugging. +.It Fl q Em quietprofile +If the +.Fl v +or +.Fl x +options have been set, do not apply them when reading +initialization files, these being +.Pa /etc/profile , +.Pa .profile , +and the file specified by the +.Ev ENV +environment variable. +.It Fl I Em ignoreeof +Ignore EOF's from input when interactive. +.It Fl i Em interactive +Force the shell to behave interactively. +.It Fl m Em monitor +Turn on job control (set automatically when interactive). +.It Fl s Em stdin +Read commands from standard input (set automatically if no file arguments +are present). +This option has no effect when set after the shell has +already started running (i.e. with +.Ic set ) . +.It Fl V Em vi +Enable the built-in +.Xr vi 1 +command line editor (disables +.Fl E +if it has been set). +(See the +.Sx Command Line Editing +section below.) +.It Fl E Em emacs +Enable the built-in emacs style +command line editor (disables +.Fl V +if it has been set). +(See the +.Sx Command Line Editing +section below.) +.It Fl b Em notify +Enable asynchronous notification of background job completion. +(UNIMPLEMENTED for 4.4alpha) +.It "\ \ " Em cdprint +Make an interactive shell always print the new directory name when +changed by the +.Ic cd +command. +.El +.Ss Lexical Structure +The shell reads input in terms of lines from a file and breaks it up into +words at whitespace (blanks and tabs), and at certain sequences of +characters that are special to the shell called +.Dq operators . +There are two types of operators: control operators and redirection +operators (their meaning is discussed later). +Following is a list of operators: +.Bl -ohang -offset indent +.It "Control operators:" +.Dl \*[Am] \*[Am]\*[Am] \&( \&) \&; ;; | || \*[Lt]newline\*[Gt] +.It "Redirection operators:" +.Dl \*[Lt] \*[Gt] \*[Gt]| \*[Lt]\*[Lt] \*[Gt]\*[Gt] \*[Lt]\*[Am] \*[Gt]\*[Am] \*[Lt]\*[Lt]- \*[Lt]\*[Gt] +.El +.Ss Quoting +Quoting is used to remove the special meaning of certain characters or +words to the shell, such as operators, whitespace, or keywords. +There are three types of quoting: matched single quotes, +matched double quotes, and backslash. +.Ss Backslash +A backslash preserves the literal meaning of the following +character, with the exception of +.Aq newline . +A backslash preceding a +.Aq newline +is treated as a line continuation. +.Ss Single Quotes +Enclosing characters in single quotes preserves the literal meaning of all +the characters (except single quotes, making it impossible to put +single-quotes in a single-quoted string). +.Ss Double Quotes +Enclosing characters within double quotes preserves the literal +meaning of all characters except dollarsign +.Pq $ , +backquote +.Pq ` , +and backslash +.Pq \e . +The backslash inside double quotes is historically weird, and serves to +quote only the following characters: +.Dl $ ` \*q \e \*[Lt]newline\*[Gt] . +Otherwise it remains literal. +.Ss Reserved Words +Reserved words are words that have special meaning to the +shell and are recognized at the beginning of a line and +after a control operator. +The following are reserved words: +.Bl -column while while while while while -offset indent +.It ! Ta elif Ta fi Ta while Ta case +.It else Ta for Ta then Ta { Ta } +.It do Ta done Ta until Ta if Ta esac +.El +.Pp +Their meaning is discussed later. +.Ss Aliases +An alias is a name and corresponding value set using the +.Ic alias +builtin command. +Whenever a reserved word may occur (see above), +and after checking for reserved words, the shell +checks the word to see if it matches an alias. +If it does, it replaces it in the input stream with its value. +For example, if there is an alias called +.Dq lf +with the value +.Dq "ls -F" , +then the input: +.Pp +.Dl lf foobar Aq return +.Pp +would become +.Pp +.Dl ls -F foobar Aq return +.Pp +Aliases provide a convenient way for naive users to create shorthands for +commands without having to learn how to create functions with arguments. +They can also be used to create lexically obscure code. +This use is discouraged. +.Ss Commands +The shell interprets the words it reads according to a language, the +specification of which is outside the scope of this man page (refer to the +BNF in the +.Tn POSIX +1003.2 document). +Essentially though, a line is read and if the first +word of the line (or after a control operator) is not a reserved word, +then the shell has recognized a simple command. +Otherwise, a complex +command or some other special construct may have been recognized. +.Ss Simple Commands +If a simple command has been recognized, the shell performs +the following actions: +.Bl -enum -offset indent +.It +Leading words of the form +.Dq name=value +are stripped off and assigned to the environment of the simple command. +Redirection operators and their arguments (as described below) are +stripped off and saved for processing. +.It +The remaining words are expanded as described in +the section called +.Dq Expansions , +and the first remaining word is considered the command name and the +command is located. +The remaining words are considered the arguments of the command. +If no command name resulted, then the +.Dq name=value +variable assignments recognized in item 1 affect the current shell. +.It +Redirections are performed as described in the next section. +.El +.Ss Redirections +Redirections are used to change where a command reads its input or sends +its output. +In general, redirections open, close, or duplicate an +existing reference to a file. +The overall format used for redirection is: +.Pp +.Dl [n] Va redir-op Ar file +.Pp +where +.Va redir-op +is one of the redirection operators mentioned previously. +Following is a list of the possible redirections. +The +.Bq n +is an optional number, as in +.Sq 3 +(not +.Sq Bq 3 ) , +that refers to a file descriptor. +.Bl -tag -width aaabsfiles -offset indent +.It [n] Ns \*[Gt] file +Redirect standard output (or n) to file. +.It [n] Ns \*[Gt]| file +Same, but override the +.Fl C +option. +.It [n] Ns \*[Gt]\*[Gt] file +Append standard output (or n) to file. +.It [n] Ns \*[Lt] file +Redirect standard input (or n) from file. +.It [n1] Ns \*[Lt]\*[Am] Ns n2 +Duplicate standard input (or n1) from file descriptor n2. +.It [n] Ns \*[Lt]\*[Am]- +Close standard input (or n). +.It [n1] Ns \*[Gt]\*[Am] Ns n2 +Duplicate standard output (or n1) to n2. +.It [n] Ns \*[Gt]\*[Am]- +Close standard output (or n). +.It [n] Ns \*[Lt]\*[Gt] file +Open file for reading and writing on standard input (or n). +.El +.Pp +The following redirection is often called a +.Dq here-document . +.Bl -item -offset indent +.It +.Li [n]\*[Lt]\*[Lt] delimiter +.Dl here-doc-text ... +.Li delimiter +.El +.Pp +All the text on successive lines up to the delimiter is saved away and +made available to the command on standard input, or file descriptor n if +it is specified. +If the delimiter as specified on the initial line is +quoted, then the here-doc-text is treated literally, otherwise the text is +subjected to parameter expansion, command substitution, and arithmetic +expansion (as described in the section on +.Dq Expansions ) . +If the operator is +.Dq \*[Lt]\*[Lt]- +instead of +.Dq \*[Lt]\*[Lt] , +then leading tabs in the here-doc-text are stripped. +.Ss Search and Execution +There are three types of commands: shell functions, builtin commands, and +normal programs -- and the command is searched for (by name) in that order. +They each are executed in a different way. +.Pp +When a shell function is executed, all of the shell positional parameters +(except $0, which remains unchanged) are set to the arguments of the shell +function. +The variables which are explicitly placed in the environment of +the command (by placing assignments to them before the function name) are +made local to the function and are set to the values given. +Then the command given in the function definition is executed. +The positional parameters are restored to their original values +when the command completes. +This all occurs within the current shell. +.Pp +Shell builtins are executed internally to the shell, without spawning a +new process. +.Pp +Otherwise, if the command name doesn't match a function or builtin, the +command is searched for as a normal program in the file system (as +described in the next section). +When a normal program is executed, the shell runs the program, +passing the arguments and the environment to the program. +If the program is not a normal executable file (i.e., if it does +not begin with the "magic number" whose +.Tn ASCII +representation is "#!", so +.Xr execve 2 +returns +.Er ENOEXEC +then) the shell will interpret the program in a subshell. +The child shell will reinitialize itself in this case, +so that the effect will be as if a +new shell had been invoked to handle the ad-hoc shell script, except that +the location of hashed commands located in the parent shell will be +remembered by the child. +.Pp +Note that previous versions of this document and the source code itself +misleadingly and sporadically refer to a shell script without a magic +number as a "shell procedure". +.Ss Path Search +When locating a command, the shell first looks to see if it has a shell +function by that name. +Then it looks for a builtin command by that name. +If a builtin command is not found, one of two things happen: +.Bl -enum +.It +Command names containing a slash are simply executed without performing +any searches. +.It +The shell searches each entry in +.Ev PATH +in turn for the command. +The value of the +.Ev PATH +variable should be a series of entries separated by colons. +Each entry consists of a directory name. +The current directory may be indicated +implicitly by an empty directory name, or explicitly by a single period. +.El +.Ss Command Exit Status +Each command has an exit status that can influence the behavior +of other shell commands. +The paradigm is that a command exits +with zero for normal or success, and non-zero for failure, +error, or a false indication. +The man page for each command +should indicate the various exit codes and what they mean. +Additionally, the builtin commands return exit codes, as does +an executed shell function. +.Pp +If a command consists entirely of variable assignments then the +exit status of the command is that of the last command substitution +if any, otherwise 0. +.Ss Complex Commands +Complex commands are combinations of simple commands with control +operators or reserved words, together creating a larger complex command. +More generally, a command is one of the following: +.Bl -bullet +.It +simple command +.It +pipeline +.It +list or compound-list +.It +compound command +.It +function definition +.El +.Pp +Unless otherwise stated, the exit status of a command is that of the last +simple command executed by the command. +.Ss Pipelines +A pipeline is a sequence of one or more commands separated +by the control operator |. +The standard output of all but +the last command is connected to the standard input +of the next command. +The standard output of the last +command is inherited from the shell, as usual. +.Pp +The format for a pipeline is: +.Pp +.Dl [!] command1 [ | command2 ...] +.Pp +The standard output of command1 is connected to the standard input of +command2. +The standard input, standard output, or both of a command is +considered to be assigned by the pipeline before any redirection specified +by redirection operators that are part of the command. +.Pp +If the pipeline is not in the background (discussed later), the shell +waits for all commands to complete. +.Pp +If the reserved word ! does not precede the pipeline, the exit status is +the exit status of the last command specified in the pipeline. +Otherwise, the exit status is the logical NOT of the exit status of the +last command. +That is, if the last command returns zero, the exit status +is 1; if the last command returns greater than zero, the exit status is +zero. +.Pp +Because pipeline assignment of standard input or standard output or both +takes place before redirection, it can be modified by redirection. +For example: +.Pp +.Dl $ command1 2\*[Gt]\*[Am]1 | command2 +.Pp +sends both the standard output and standard error of command1 +to the standard input of command2. +.Pp +A ; or +.Aq newline +terminator causes the preceding AND-OR-list (described +next) to be executed sequentially; a \*[Am] causes asynchronous execution of +the preceding AND-OR-list. +.Pp +Note that unlike some other shells, each process in the pipeline is a +child of the invoking shell (unless it is a shell builtin, in which case +it executes in the current shell -- but any effect it has on the +environment is wiped). +.Ss Background Commands -- \*[Am] +If a command is terminated by the control operator ampersand (\*[Am]), the +shell executes the command asynchronously -- that is, the shell does not +wait for the command to finish before executing the next command. +.Pp +The format for running a command in background is: +.Pp +.Dl command1 \*[Am] [command2 \*[Am] ...] +.Pp +If the shell is not interactive, the standard input of an asynchronous +command is set to +.Pa /dev/null . +.Ss Lists -- Generally Speaking +A list is a sequence of zero or more commands separated by newlines, +semicolons, or ampersands, and optionally terminated by one of these three +characters. +The commands in a list are executed in the order they are written. +If command is followed by an ampersand, the shell starts the +command and immediately proceed onto the next command; otherwise it waits +for the command to terminate before proceeding to the next one. +.Ss Short-Circuit List Operators +.Dq \*[Am]\*[Am] +and +.Dq || +are AND-OR list operators. +.Dq \*[Am]\*[Am] +executes the first command, and then executes the second command if and only +if the exit status of the first command is zero. +.Dq || +is similar, but executes the second command if and only if the exit status +of the first command is nonzero. +.Dq \*[Am]\*[Am] +and +.Dq || +both have the same priority. +Note that these operators are left-associative, so +.Dq true || echo bar && echo baz +writes +.Dq baz +and nothing else. +This is not the way it works in C. +.Ss Flow-Control Constructs -- if, while, for, case +The syntax of the if command is +.Bd -literal -offset indent +if list +then list +[ elif list +then list ] ... +[ else list ] +fi +.Ed +.Pp +The syntax of the while command is +.Bd -literal -offset indent +while list +do list +done +.Ed +.Pp +The two lists are executed repeatedly while the exit status of the +first list is zero. +The until command is similar, but has the word +until in place of while, which causes it to +repeat until the exit status of the first list is zero. +.Pp +The syntax of the for command is +.Bd -literal -offset indent +for variable in word ... +do list +done +.Ed +.Pp +The words are expanded, and then the list is executed repeatedly with the +variable set to each word in turn. +do and done may be replaced with +.Dq { +and +.Dq } . +.Pp +The syntax of the break and continue command is +.Bd -literal -offset indent +break [ num ] +continue [ num ] +.Ed +.Pp +Break terminates the num innermost for or while loops. +Continue continues with the next iteration of the innermost loop. +These are implemented as builtin commands. +.Pp +The syntax of the case command is +.Bd -literal -offset indent +case word in +pattern) list ;; +\&... +esac +.Ed +.Pp +The pattern can actually be one or more patterns (see +.Sx Shell Patterns +described later), separated by +.Dq \*(Ba +characters. +.Ss Grouping Commands Together +Commands may be grouped by writing either +.Pp +.Dl (list) +.Pp +or +.Pp +.Dl { list; } +.Pp +The first of these executes the commands in a subshell. +Builtin commands grouped into a (list) will not affect the current shell. +The second form does not fork another shell so is slightly more efficient. +Grouping commands together this way allows you to redirect +their output as though they were one program: +.Pp +.Bd -literal -offset indent +{ echo -n \*q hello \*q ; echo \*q world" ; } \*[Gt] greeting +.Ed +.Pp +Note that +.Dq } +must follow a control operator (here, +.Dq \&; ) +so that it is recognized as a reserved word and not as another command argument. +.Ss Functions +The syntax of a function definition is +.Pp +.Dl name ( ) command +.Pp +A function definition is an executable statement; when executed it +installs a function named name and returns an exit status of zero. +The command is normally a list enclosed between +.Dq { +and +.Dq } . +.Pp +Variables may be declared to be local to a function by using a local +command. +This should appear as the first statement of a function, and the syntax is +.Pp +.Dl local [ variable | - ] ... +.Pp +Local is implemented as a builtin command. +.Pp +When a variable is made local, it inherits the initial value and exported +and readonly flags from the variable with the same name in the surrounding +scope, if there is one. +Otherwise, the variable is initially unset. +The shell uses dynamic scoping, so that if you make the variable x local to +function f, which then calls function g, references to the variable x made +inside g will refer to the variable x declared inside f, not to the global +variable named x. +.Pp +The only special parameter that can be made local is +.Dq - . +Making +.Dq - +local any shell options that are changed via the set command inside the +function to be restored to their original values when the function +returns. +.Pp +The syntax of the return command is +.Pp +.Dl return [ exitstatus ] +.Pp +It terminates the currently executing function. +Return is implemented as a builtin command. +.Ss Variables and Parameters +The shell maintains a set of parameters. +A parameter denoted by a name is called a variable. +When starting up, the shell turns all the environment +variables into shell variables. +New variables can be set using the form +.Pp +.Dl name=value +.Pp +Variables set by the user must have a name consisting solely of +alphabetics, numerics, and underscores - the first of which must not be +numeric. +A parameter can also be denoted by a number or a special +character as explained below. +.Ss Positional Parameters +A positional parameter is a parameter denoted by a number (n \*[Gt] 0). +The shell sets these initially to the values of its command line arguments +that follow the name of the shell script. +The +.Ic set +builtin can also be used to set or reset them. +.Ss Special Parameters +A special parameter is a parameter denoted by one of the following special +characters. +The value of the parameter is listed next to its character. +.Bl -tag -width thinhyphena +.It * +Expands to the positional parameters, starting from one. +When the +expansion occurs within a double-quoted string it expands to a single +field with the value of each parameter separated by the first character of +the +.Ev IFS +variable, or by a +.Aq space +if +.Ev IFS +is unset. +.It @ +Expands to the positional parameters, starting from one. +When the expansion occurs within double-quotes, each positional +parameter expands as a separate argument. +If there are no positional parameters, the +expansion of @ generates zero arguments, even when @ is +double-quoted. +What this basically means, for example, is +if $1 is +.Dq abc +and $2 is +.Dq def ghi , +then +.Qq $@ +expands to +the two arguments: +.Pp +.Sm off +.Dl \*q abc \*q \ \*q def\ ghi \*q +.Sm on +.It # +Expands to the number of positional parameters. +.It \&? +Expands to the exit status of the most recent pipeline. +.It - (Hyphen.) +Expands to the current option flags (the single-letter +option names concatenated into a string) as specified on +invocation, by the set builtin command, or implicitly +by the shell. +.It $ +Expands to the process ID of the invoked shell. +A subshell retains the same value of $ as its parent. +.It \&! +Expands to the process ID of the most recent background +command executed from the current shell. +For a pipeline, the process ID is that of the last command in the pipeline. +.It 0 (Zero.) +Expands to the name of the shell or shell script. +.El +.Ss Word Expansions +This clause describes the various expansions that are performed on words. +Not all expansions are performed on every word, as explained later. +.Pp +Tilde expansions, parameter expansions, command substitutions, arithmetic +expansions, and quote removals that occur within a single word expand to a +single field. +It is only field splitting or pathname expansion that can +create multiple fields from a single word. +The single exception to this +rule is the expansion of the special parameter @ within double-quotes, as +was described above. +.Pp +The order of word expansion is: +.Bl -enum +.It +Tilde Expansion, Parameter Expansion, Command Substitution, +Arithmetic Expansion (these all occur at the same time). +.It +Field Splitting is performed on fields +generated by step (1) unless the +.Ev IFS +variable is null. +.It +Pathname Expansion (unless set +.Fl f +is in effect). +.It +Quote Removal. +.El +.Pp +The $ character is used to introduce parameter expansion, command +substitution, or arithmetic evaluation. +.Ss Tilde Expansion (substituting a user's home directory) +A word beginning with an unquoted tilde character (~) is +subjected to tilde expansion. +All the characters up to +a slash (/) or the end of the word are treated as a username +and are replaced with the user's home directory. +If the username is missing (as in +.Pa ~/foobar ) , +the tilde is replaced with the value of the +.Va HOME +variable (the current user's home directory). +.Ss Parameter Expansion +The format for parameter expansion is as follows: +.Pp +.Dl ${expression} +.Pp +where expression consists of all characters until the matching +.Dq } . +Any +.Dq } +escaped by a backslash or within a quoted string, and characters in +embedded arithmetic expansions, command substitutions, and variable +expansions, are not examined in determining the matching +.Dq } . +.Pp +The simplest form for parameter expansion is: +.Pp +.Dl ${parameter} +.Pp +The value, if any, of parameter is substituted. +.Pp +The parameter name or symbol can be enclosed in braces, which are +optional except for positional parameters with more than one digit or +when parameter is followed by a character that could be interpreted as +part of the name. +If a parameter expansion occurs inside double-quotes: +.Bl -enum +.It +Pathname expansion is not performed on the results of the expansion. +.It +Field splitting is not performed on the results of the +expansion, with the exception of @. +.El +.Pp +In addition, a parameter expansion can be modified by using one of the +following formats. +.Bl -tag -width aaparameterwordaaaaa +.It ${parameter:-word} +Use Default Values. +If parameter is unset or null, the expansion of word +is substituted; otherwise, the value of parameter is substituted. +.It ${parameter:=word} +Assign Default Values. +If parameter is unset or null, the expansion of +word is assigned to parameter. +In all cases, the final value of parameter is substituted. +Only variables, not positional parameters or special +parameters, can be assigned in this way. +.It ${parameter:?[word]} +Indicate Error if Null or Unset. +If parameter is unset or null, the +expansion of word (or a message indicating it is unset if word is omitted) +is written to standard error and the shell exits with a nonzero exit status. +Otherwise, the value of parameter is substituted. +An interactive shell need not exit. +.It ${parameter:+word} +Use Alternative Value. +If parameter is unset or null, null is +substituted; otherwise, the expansion of word is substituted. +.El +.Pp +In the parameter expansions shown previously, use of the colon in the +format results in a test for a parameter that is unset or null; omission +of the colon results in a test for a parameter that is only unset. +.Bl -tag -width aaparameterwordaaaaa +.It ${#parameter} +String Length. +The length in characters of the value of parameter. +.El +.Pp +The following four varieties of parameter expansion provide for substring +processing. +In each case, pattern matching notation (see +.Sx Shell Patterns ) , +rather than regular expression notation, is used to evaluate the patterns. +If parameter is * or @, the result of the expansion is unspecified. +Enclosing the full parameter expansion string in double-quotes does not +cause the following four varieties of pattern characters to be quoted, +whereas quoting characters within the braces has this effect. +.Bl -tag -width aaparameterwordaaaaa +.It ${parameter%word} +Remove Smallest Suffix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the +smallest portion of the suffix matched by the pattern deleted. +.It ${parameter%%word} +Remove Largest Suffix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the largest +portion of the suffix matched by the pattern deleted. +.It ${parameter#word} +Remove Smallest Prefix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the +smallest portion of the prefix matched by the pattern deleted. +.It ${parameter##word} +Remove Largest Prefix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the largest +portion of the prefix matched by the pattern deleted. +.El +.Ss Command Substitution +Command substitution allows the output of a command to be substituted in +place of the command name itself. +Command substitution occurs when the command is enclosed as follows: +.Pp +.Dl $(command) +.Pp +or +.Po +.Dq backquoted +version +.Pc : +.Pp +.Dl `command` +.Pp +The shell expands the command substitution by executing command in a +subshell environment and replacing the command substitution with the +standard output of the command, removing sequences of one or more +.Ao newline Ac Ns s +at the end of the substitution. +(Embedded +.Ao newline Ac Ns s +before +the end of the output are not removed; however, during field splitting, +they may be translated into +.Ao space Ac Ns s , +depending on the value of +.Ev IFS +and quoting that is in effect.) +.Ss Arithmetic Expansion +Arithmetic expansion provides a mechanism for evaluating an arithmetic +expression and substituting its value. +The format for arithmetic expansion is as follows: +.Pp +.Dl $((expression)) +.Pp +The expression is treated as if it were in double-quotes, except +that a double-quote inside the expression is not treated specially. +The shell expands all tokens in the expression for parameter expansion, +command substitution, and quote removal. +.Pp +Next, the shell treats this as an arithmetic expression and +substitutes the value of the expression. +.Ss White Space Splitting (Field Splitting) +After parameter expansion, command substitution, and +arithmetic expansion the shell scans the results of +expansions and substitutions that did not occur in double-quotes for +field splitting and multiple fields can result. +.Pp +The shell treats each character of the +.Ev IFS +as a delimiter and use the delimiters to split the results of parameter +expansion and command substitution into fields. +.Ss Pathname Expansion (File Name Generation) +Unless the +.Fl f +flag is set, file name generation is performed after word splitting is +complete. +Each word is viewed as a series of patterns, separated by slashes. +The process of expansion replaces the word with the names of all +existing files whose names can be formed by replacing each pattern with a +string that matches the specified pattern. +There are two restrictions on +this: first, a pattern cannot match a string containing a slash, and +second, a pattern cannot match a string starting with a period unless the +first character of the pattern is a period. +The next section describes the +patterns used for both Pathname Expansion and the +.Ic case +command. +.Ss Shell Patterns +A pattern consists of normal characters, which match themselves, +and meta-characters. +The meta-characters are +.Dq \&! , +.Dq * , +.Dq \&? , +and +.Dq \&[ . +These characters lose their special meanings if they are quoted. +When command or variable substitution is performed +and the dollar sign or back quotes are not double quoted, +the value of the variable or the output of +the command is scanned for these characters and they are turned into +meta-characters. +.Pp +An asterisk +.Pq Dq * +matches any string of characters. +A question mark matches any single character. +A left bracket +.Pq Dq \&[ +introduces a character class. +The end of the character class is indicated by a +.Pq Dq \&] ; +if the +.Dq \&] +is missing then the +.Dq \&[ +matches a +.Dq \&[ +rather than introducing a character class. +A character class matches any of the characters between the square brackets. +A range of characters may be specified using a minus sign. +The character class may be complemented +by making an exclamation point the first character of the character class. +.Pp +To include a +.Dq \&] +in a character class, make it the first character listed (after the +.Dq \&! , +if any). +To include a minus sign, make it the first or last character listed. +.Ss Builtins +This section lists the builtin commands which are builtin because they +need to perform some operation that can't be performed by a separate +process. +In addition to these, there are several other commands that may +be builtin for efficiency (e.g. +.Xr printf 1 , +.Xr echo 1 , +.Xr test 1 , +etc). +.Bl -tag -width 5n +.It : +A null command that returns a 0 (true) exit value. +.It \&. file +The commands in the specified file are read and executed by the shell. +.It alias Op Ar name Ns Op Ar "=string ..." +If +.Ar name=string +is specified, the shell defines the alias +.Ar name +with value +.Ar string . +If just +.Ar name +is specified, the value of the alias +.Ar name +is printed. +With no arguments, the +.Ic alias +builtin prints the +names and values of all defined aliases (see +.Ic unalias ) . +.It bg [ Ar job ] ... +Continue the specified jobs (or the current job if no +jobs are given) in the background. +.It Xo command +.Op Fl p +.Op Fl v +.Op Fl V +.Ar command +.Op Ar arg ... +.Xc +Execute the specified command but ignore shell functions when searching +for it. +(This is useful when you +have a shell function with the same name as a builtin command.) +.Bl -tag -width 5n +.It Fl p +search for command using a +.Ev PATH +that guarantees to find all the standard utilities. +.It Fl V +Do not execute the command but +search for the command and print the resolution of the +command search. +This is the same as the type builtin. +.It Fl v +Do not execute the command but +search for the command and print the absolute pathname +of utilities, the name for builtins or the expansion of aliases. +.El +.It cd Op Ar directory Op Ar replace +Switch to the specified directory (default +.Ev $HOME ) . +If +.Ar replace +is specified, then the new directory name is generated by replacing +the first occurrence of +.Ar directory +in the current directory name with +.Ar replace . +Otherwise if an entry for +.Ev CDPATH +appears in the environment of the +.Ic cd +command or the shell variable +.Ev CDPATH +is set and the directory name does not begin with a slash, then the +directories listed in +.Ev CDPATH +will be searched for the specified directory. +The format of +.Ev CDPATH +is the same as that of +.Ev PATH . +In an interactive shell, the +.Ic cd +command will print out the name of the +directory that it actually switched to if this is different from the name +that the user gave. +These may be different either because the +.Ev CDPATH +mechanism was used or because a symbolic link was crossed. +.It eval Ar string ... +Concatenate all the arguments with spaces. +Then re-parse and execute the command. +.It exec Op Ar command arg ... +Unless command is omitted, the shell process is replaced with the +specified program (which must be a real program, not a shell builtin or +function). +Any redirections on the +.Ic exec +command are marked as permanent, so that they are not undone when the +.Ic exec +command finishes. +.It exit Op Ar exitstatus +Terminate the shell process. +If +.Ar exitstatus +is given it is used as the exit status of the shell; otherwise the +exit status of the preceding command is used. +.It export Ar name ... +.It export Fl p +The specified names are exported so that they will appear in the +environment of subsequent commands. +The only way to un-export a variable is to unset it. +The shell allows the value of a variable to be set at the +same time it is exported by writing +.Pp +.Dl export name=value +.Pp +With no arguments the export command lists the names of all exported variables. +With the +.Fl p +option specified the output will be formatted suitably for non-interactive use. +.It Xo fc Op Fl e Ar editor +.Op Ar first Op Ar last +.Xc +.It Xo fc Fl l +.Op Fl nr +.Op Ar first Op Ar last +.Xc +.It Xo fc Fl s Op Ar old=new +.Op Ar first +.Xc +The +.Ic fc +builtin lists, or edits and re-executes, commands previously entered +to an interactive shell. +.Bl -tag -width 5n +.It Fl e No editor +Use the editor named by editor to edit the commands. +The editor string is a command name, subject to search via the +.Ev PATH +variable. +The value in the +.Ev FCEDIT +variable is used as a default when +.Fl e +is not specified. +If +.Ev FCEDIT +is null or unset, the value of the +.Ev EDITOR +variable is used. +If +.Ev EDITOR +is null or unset, +.Xr ed 1 +is used as the editor. +.It Fl l No (ell) +List the commands rather than invoking an editor on them. +The commands are written in the sequence indicated by +the first and last operands, as affected by +.Fl r , +with each command preceded by the command number. +.It Fl n +Suppress command numbers when listing with -l. +.It Fl r +Reverse the order of the commands listed (with +.Fl l ) +or edited (with neither +.Fl l +nor +.Fl s ) . +.It Fl s +Re-execute the command without invoking an editor. +.It first +.It last +Select the commands to list or edit. +The number of previous commands that +can be accessed are determined by the value of the +.Ev HISTSIZE +variable. +The value of first or last or both are one of the following: +.Bl -tag -width 5n +.It [+]number +A positive number representing a command number; command numbers can be +displayed with the +.Fl l +option. +.It Fl number +A negative decimal number representing the command that was executed +number of commands previously. +For example, \-1 is the immediately previous command. +.El +.It string +A string indicating the most recently entered command that begins with +that string. +If the old=new operand is not also specified with +.Fl s , +the string form of the first operand cannot contain an embedded equal sign. +.El +.Pp +The following environment variables affect the execution of fc: +.Bl -tag -width HISTSIZE +.It Ev FCEDIT +Name of the editor to use. +.It Ev HISTSIZE +The number of previous commands that are accessible. +.El +.It fg Op Ar job +Move the specified job or the current job to the foreground. +.It getopts Ar optstring var +The +.Tn POSIX +.Ic getopts +command, not to be confused with the +.Em Bell Labs +-derived +.Xr getopt 1 . +.Pp +The first argument should be a series of letters, each of which may be +optionally followed by a colon to indicate that the option requires an +argument. +The variable specified is set to the parsed option. +.Pp +The +.Ic getopts +command deprecates the older +.Xr getopt 1 +utility due to its handling of arguments containing whitespace. +.Pp +The +.Ic getopts +builtin may be used to obtain options and their arguments +from a list of parameters. +When invoked, +.Ic getopts +places the value of the next option from the option string in the list in +the shell variable specified by +.Va var +and its index in the shell variable +.Ev OPTIND . +When the shell is invoked, +.Ev OPTIND +is initialized to 1. +For each option that requires an argument, the +.Ic getopts +builtin will place it in the shell variable +.Ev OPTARG . +If an option is not allowed for in the +.Va optstring , +then +.Ev OPTARG +will be unset. +.Pp +.Va optstring +is a string of recognized option letters (see +.Xr getopt 3 ) . +If a letter is followed by a colon, the option is expected to have an +argument which may or may not be separated from it by white space. +If an option character is not found where expected, +.Ic getopts +will set the variable +.Va var +to a +.Dq \&? ; +.Ic getopts +will then unset +.Ev OPTARG +and write output to standard error. +By specifying a colon as the first character of +.Va optstring +all errors will be ignored. +.Pp +A nonzero value is returned when the last option is reached. +If there are no remaining arguments, +.Ic getopts +will set +.Va var +to the special option, +.Dq -- , +otherwise, it will set +.Va var +to +.Dq \&? . +.Pp +The following code fragment shows how one might process the arguments +for a command that can take the options +.Op a +and +.Op b , +and the option +.Op c , +which requires an argument. +.Pp +.Bd -literal -offset indent +while getopts abc: f +do + case $f in + a | b) flag=$f;; + c) carg=$OPTARG;; + \\?) echo $USAGE; exit 1;; + esac +done +shift `expr $OPTIND - 1` +.Ed +.Pp +This code will accept any of the following as equivalent: +.Pp +.Bd -literal -offset indent +cmd \-acarg file file +cmd \-a \-c arg file file +cmd \-carg -a file file +cmd \-a \-carg \-\- file file +.Ed +.It hash Fl rv Ar command ... +The shell maintains a hash table which remembers the +locations of commands. +With no arguments whatsoever, +the +.Ic hash +command prints out the contents of this table. +Entries which have not been looked at since the last +.Ic cd +command are marked with an asterisk; it is possible for these entries +to be invalid. +.Pp +With arguments, the +.Ic hash +command removes the specified commands from the hash table (unless +they are functions) and then locates them. +With the +.Fl v +option, hash prints the locations of the commands as it finds them. +The +.Fl r +option causes the hash command to delete all the entries in the hash table +except for functions. +.It inputrc Ar file +Read the +.Va file +to set keybindings as defined by +.Xr editrc 5 . +.It jobid Op Ar job +Print the process id's of the processes in the job. +If the +.Ar job +argument is omitted, the current job is used. +.It jobs +This command lists out all the background processes +which are children of the current shell process. +.It pwd Op Fl LP +Print the current directory. +If +.Fl L +is specified the cached value (initially set from +.Ev PWD ) +is checked to see if it refers to the current directory, if it does +the value is printed. +Otherwise the current directory name is found using +.Xr getcwd(3) . +The environment variable +.Ev PWD +is set to printed value. +.Pp +The default is +.Ic pwd +.Fl L , +but note that the builtin +.Ic cd +command doesn't currently support +.Fl L +or +.Fl P +and will cache (almost) the absolute path. +If +.Ic cd +is changed, +.Ic pwd +may be changed to default to +.Ic pwd +.Fl P . +.Pp +If the current directory is renamed and replaced by a symlink to the +same directory, or the initial +.Ev PWD +value followed a symbolic link, then the cached value may not +be the absolute path. +.Pp +The builtin command may differ from the program of the same name because +the program will use +.Ev PWD +and the builtin uses a separately cached value. +.It Xo read Op Fl p Ar prompt +.Op Fl r +.Ar variable +.Op Ar ... +.Xc +The prompt is printed if the +.Fl p +option is specified and the standard input is a terminal. +Then a line is read from the standard input. +The trailing newline is deleted from the +line and the line is split as described in the section on word splitting +above, and the pieces are assigned to the variables in order. +If there are more pieces than variables, the remaining pieces +(along with the characters in +.Ev IFS +that separated them) are assigned to the last variable. +If there are more variables than pieces, +the remaining variables are assigned the null string. +The +.Ic read +builtin will indicate success unless EOF is encountered on input, in +which case failure is returned. +.Pp +By default, unless the +.Fl r +option is specified, the backslash +.Dq \e +acts as an escape character, causing the following character to be treated +literally. +If a backslash is followed by a newline, the backslash and the +newline will be deleted. +.It readonly Ar name ... +.It readonly Fl p +The specified names are marked as read only, so that they cannot be +subsequently modified or unset. +The shell allows the value of a variable +to be set at the same time it is marked read only by writing +.Pp +.Dl readonly name=value +.Pp +With no arguments the readonly command lists the names of all read only +variables. +With the +.Fl p +option specified the output will be formatted suitably for non-interactive use. +.Pp +.It Xo set +.Oo { +.Fl options | Cm +options | Cm -- } +.Oc Ar arg ... +.Xc +The +.Ic set +command performs three different functions. +.Pp +With no arguments, it lists the values of all shell variables. +.Pp +If options are given, it sets the specified option +flags, or clears them as described in the section called +.Sx Argument List Processing . +.Pp +The third use of the set command is to set the values of the shell's +positional parameters to the specified args. +To change the positional +parameters without changing any options, use +.Dq -- +as the first argument to set. +If no args are present, the set command +will clear all the positional parameters (equivalent to executing +.Dq shift $# . ) +.It setvar Ar variable Ar value +Assigns value to variable. +(In general it is better to write +variable=value rather than using +.Ic setvar . +.Ic setvar +is intended to be used in +functions that assign values to variables whose names are passed as +parameters.) +.It shift Op Ar n +Shift the positional parameters n times. +A +.Ic shift +sets the value of +.Va $1 +to the value of +.Va $2 , +the value of +.Va $2 +to the value of +.Va $3 , +and so on, decreasing +the value of +.Va $# +by one. +If there are zero positional parameters, +.Ic shift +does nothing. +.It Xo trap +.Op Fl l +.Xc +.It Xo trap +.Op Ar action +.Ar signal ... +.Xc +Cause the shell to parse and execute action when any of the specified +signals are received. +The signals are specified by signal number or as the name of the signal. +If +.Ar signal +is +.Li 0 , +the action is executed when the shell exits. +.Ar action +may be null, which cause the specified signals to be ignored. +With +.Ar action +omitted or set to `-' the specified signals are set to their default action. +When the shell forks off a subshell, it resets trapped (but not ignored) +signals to the default action. +The +.Ic trap +command has no effect on signals that were +ignored on entry to the shell. +Issuing +.Ic trap +with option +.Ar -l +will print a list of valid signal names. +.Ic trap +without any arguments cause it to write a list of signals and their +associated action to the standard output in a format that is suitable +as an input to the shell that achieves the same trapping results. +.Pp +Examples: +.Pp +.Dl trap +.Pp +List trapped signals and their corresponding action +.Pp +.Dl trap -l +.Pp +Print a list of valid signals +.Pp +.Dl trap '' INT QUIT tstp 30 +.Pp +Ignore signals INT QUIT TSTP USR1 +.Pp +.Dl trap date INT +.Pp +Print date upon receiving signal INT +.It type Op Ar name ... +Interpret each name as a command and print the resolution of the command +search. +Possible resolutions are: +shell keyword, alias, shell builtin, +command, tracked alias and not found. +For aliases the alias expansion is +printed; for commands and tracked aliases the complete pathname of the +command is printed. +.It ulimit Xo +.Op Fl H \*(Ba Fl S +.Op Fl a \*(Ba Fl tfdscmlpn Op Ar value +.Xc +Inquire about or set the hard or soft limits on processes or set new +limits. +The choice between hard limit (which no process is allowed to +violate, and which may not be raised once it has been lowered) and soft +limit (which causes processes to be signaled but not necessarily killed, +and which may be raised) is made with these flags: +.Bl -tag -width Fl +.It Fl H +set or inquire about hard limits +.It Fl S +set or inquire about soft limits. +If neither +.Fl H +nor +.Fl S +is specified, the soft limit is displayed or both limits are set. +If both are specified, the last one wins. +.El +.Pp +.Bl -tag -width Fl +The limit to be interrogated or set, then, is chosen by specifying +any one of these flags: +.It Fl a +show all the current limits +.It Fl b +show or set the limit on the socket buffer size of a process (in bytes) +.It Fl t +show or set the limit on CPU time (in seconds) +.It Fl f +show or set the limit on the largest file that can be created +(in 512-byte blocks) +.It Fl d +show or set the limit on the data segment size of a process (in kilobytes) +.It Fl s +show or set the limit on the stack size of a process (in kilobytes) +.It Fl c +show or set the limit on the largest core dump size that can be produced +(in 512-byte blocks) +.It Fl m +show or set the limit on the total physical memory that can be +in use by a process (in kilobytes) +.It Fl l +show or set the limit on how much memory a process can lock with +.Xr mlock 2 +(in kilobytes) +.It Fl p +show or set the limit on the number of processes this user can +have at one time +.It Fl n +show or set the limit on the number of files a process can have open at once +.El +.Pp +If none of these is specified, it is the limit on file size that is shown +or set. +If value is specified, the limit is set to that number; otherwise +the current limit is displayed. +.Pp +Limits of an arbitrary process can be displayed or set using the +.Xr sysctl 8 +utility. +.Pp +.It umask Op Ar mask +Set the value of umask (see +.Xr umask 2 ) +to the specified octal value. +If the argument is omitted, the umask value is printed. +.It unalias Xo +.Op Fl a +.Op Ar name +.Xc +If +.Ar name +is specified, the shell removes that alias. +If +.Fl a +is specified, all aliases are removed. +.It unset Ar name ... +The specified variables and functions are unset and unexported. +If a given name corresponds to both a variable and a function, both +the variable and the function are unset. +.It wait Op Ar job +Wait for the specified job to complete and return the exit status of the +last process in the job. +If the argument is omitted, wait for all jobs to +complete and then return an exit status of zero. +.El +.Ss Command Line Editing +When +.Nm +is being used interactively from a terminal, the current command +and the command history (see +.Ic fc +in +.Sx Builtins ) +can be edited using emacs-mode or vi-mode command-line editing. +The command +.Ql set -o emacs +enables emacs-mode editing. +The command +.Ql set -o vi +enables vi-mode editing and places sh into vi insert mode. +(See the +.Sx Argument List Processing +section above.) +.Pp +The vi mode uses commands similar to a subset of those described in the +.Xr vi 1 +man page. +With vi-mode +enabled, sh can be switched between insert mode and command mode. +It's similar to vi: typing +.Aq ESC +will throw you into command VI command mode. +Hitting +.Aq return +while in command mode will pass the line to the shell. +.Pp +The emacs mode uses commands similar to a subset available in +the emacs editor. +With emacs-mode enabled, special keys can be used to modify the text +in the buffer using the control key. +.Pp +.Nm +uses the +.Xr editline 3 +library. +.Sh EXIT STATUS +Errors that are detected by the shell, such as a syntax error, will cause the +shell to exit with a non-zero exit status. +If the shell is not an +interactive shell, the execution of the shell file will be aborted. +Otherwise +the shell will return the exit status of the last command executed, or +if the exit builtin is used with a numeric argument, it will return the +argument. +.Sh ENVIRONMENT +.Bl -tag -width MAILCHECK +.It Ev HOME +Set automatically by +.Xr login 1 +from the user's login directory in the password file +.Pq Xr passwd 5 . +This environment variable also functions as the default argument for the +cd builtin. +.It Ev PATH +The default search path for executables. +See the above section +.Sx Path Search . +.It Ev CDPATH +The search path used with the cd builtin. +.It Ev LANG +The string used to specify localization information that allows users +to work with different culture-specific and language conventions. +See +.Xr nls 7 . +.It Ev MAIL +The name of a mail file, that will be checked for the arrival of new mail. +Overridden by +.Ev MAILPATH . +.It Ev MAILCHECK +The frequency in seconds that the shell checks for the arrival of mail +in the files specified by the +.Ev MAILPATH +or the +.Ev MAIL +file. +If set to 0, the check will occur at each prompt. +.It Ev MAILPATH +A colon +.Dq \&: +separated list of file names, for the shell to check for incoming mail. +This environment setting overrides the +.Ev MAIL +setting. +There is a maximum of 10 mailboxes that can be monitored at once. +.It Ev PS1 +The primary prompt string, which defaults to +.Dq $ \ , +unless you are the superuser, in which case it defaults to +.Dq # \ . +.It Ev PS2 +The secondary prompt string, which defaults to +.Dq \*[Gt] \ . +.It Ev PS4 +Output before each line when execution trace (set -x) is enabled, +defaults to +.Dq + \ . +.It Ev IFS +Input Field Separators. +This is normally set to +.Aq space , +.Aq tab , +and +.Aq newline . +See the +.Sx White Space Splitting +section for more details. +.It Ev TERM +The default terminal setting for the shell. +This is inherited by +children of the shell, and is used in the history editing modes. +.It Ev HISTSIZE +The number of lines in the history buffer for the shell. +.El +.Sh FILES +.Bl -item -width HOMEprofilexxxx +.It +.Pa $HOME/.profile +.It +.Pa /etc/profile +.El +.Sh SEE ALSO +.Xr csh 1 , +.Xr echo 1 , +.Xr getopt 1 , +.Xr ksh 1 , +.Xr login 1 , +.Xr printf 1 , +.Xr test 1 , +.Xr editline 3 , +.Xr getopt 3 , +.\" .Xr profile 4 , +.Xr editrc 5 , +.Xr passwd 5 , +.Xr environ 7 , +.Xr nls 7 , +.Xr sysctl 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . +It was, however, unmaintainable so we wrote this one. +.Sh BUGS +Setuid shell scripts should be avoided at all costs, as they are a +significant security risk. +.Pp +PS1, PS2, and PS4 should be subject to parameter expansion before +being displayed. diff --git a/sh/shell.h b/sh/shell.h new file mode 100644 index 0000000..94be27a --- /dev/null +++ b/sh/shell.h @@ -0,0 +1,83 @@ +/* $NetBSD: shell.h,v 1.17 2003/08/07 09:05:38 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)shell.h 8.2 (Berkeley) 5/4/95 + */ + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * SHORTNAMES -> 1 if your linker cannot handle long names. + * define BSD if you are running 4.2 BSD or later. + * define SYSV if you are running under System V. + * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * define DO_SHAREDVFORK to indicate that vfork(2) shares its address + * with its parent. + * + * When debugging is on, debugging info will be written to ./trace and + * a quit signal will generate a core dump. + */ + +#include <sys/param.h> + +#define JOBS 1 +#ifndef BSD +#define BSD 1 +#endif + +#ifndef DO_SHAREDVFORK +#if __NetBSD_Version__ >= 104000000 +#define DO_SHAREDVFORK +#endif +#endif + +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif +#define STATIC /* empty */ +#define MKINIT /* empty */ + +#include <sys/cdefs.h> + +extern char nullstr[1]; /* null string */ + + +#ifdef DEBUG +#define TRACE(param) trace param +#define TRACEV(param) tracev param +#else +#define TRACE(param) +#define TRACEV(param) +#endif diff --git a/sh/show.c b/sh/show.c new file mode 100644 index 0000000..e92aa51 --- /dev/null +++ b/sh/show.c @@ -0,0 +1,425 @@ +/* $NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $"); +#endif +#endif /* not lint */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" +#include "show.h" +#include "options.h" + + +#ifdef DEBUG +static void shtree(union node *, int, char *, FILE*); +static void shcmd(union node *, FILE *); +static void sharg(union node *, FILE *); +static void indent(int, char *, FILE *); +static void trstring(char *); + + +void +showtree(union node *n) +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(union node *n, int ind, char *pfx, FILE *fp) +{ + struct nodelist *lp; + const char *s; + + if (n == NULL) + return; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "<node type %d>", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static void +shcmd(union node *cmd, FILE *fp) +{ + union node *np; + int first; + const char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NCLOBBER: s = ">|"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + case NFROMTO: s = "<>"; dftfd = 0; break; + default: s = "*error*"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + fprintf(fp, "%d", np->ndup.dupfd); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static void +sharg(union node *arg, FILE *fp) +{ + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("<node type %d>\n", arg->type); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + if (subtype == VSLENGTH) + putc('#', fp); + + while (*p != '=') + putc(*p++, fp); + + if (subtype & VSNUL) + putc(':', fp); + + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + break; + default: + printf("<subtype %d>", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static void +indent(int amount, char *pfx, FILE *fp) +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} +#endif + + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + + +#ifdef DEBUG +void +trputc(int c) +{ + if (debug != 1) + return; + putc(c, tracefile); +} +#endif + +void +trace(const char *fmt, ...) +{ +#ifdef DEBUG + va_list va; + + if (debug != 1) + return; + va_start(va, fmt); + (void) vfprintf(tracefile, fmt, va); + va_end(va); +#endif +} + +void +tracev(const char *fmt, va_list va) +{ +#ifdef DEBUG + if (debug != 1) + return; + (void) vfprintf(tracefile, fmt, va); +#endif +} + + +#ifdef DEBUG +void +trputs(const char *s) +{ + if (debug != 1) + return; + fputs(s, tracefile); +} + + +static void +trstring(char *s) +{ + char *p; + char c; + + if (debug != 1) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +} +#endif + + +void +trargs(char **ap) +{ +#ifdef DEBUG + if (debug != 1) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } +#endif +} + + +#ifdef DEBUG +void +opentrace(void) +{ + char s[100]; +#ifdef O_APPEND + int flags; +#endif + + if (debug != 1) { + if (tracefile) + fflush(tracefile); + /* leave open because libedit might be using it */ + return; + } +#ifdef not_this_way + { + char *p; + if ((p = getenv("HOME")) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + scopy(p, s); + strcat(s, "/trace"); + } +#else + scopy("./trace", s); +#endif /* not_this_way */ + if (tracefile) { + if (!freopen(s, "a", tracefile)) { + fprintf(stderr, "Can't re-open %s\n", s); + debug = 0; + return; + } + } else { + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + debug = 0; + return; + } + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + setlinebuf(tracefile); + fputs("\nTracing started.\n", tracefile); +} +#endif /* DEBUG */ diff --git a/sh/show.h b/sh/show.h new file mode 100644 index 0000000..3152ff2 --- /dev/null +++ b/sh/show.h @@ -0,0 +1,45 @@ +/* $NetBSD: show.h,v 1.7 2003/08/07 09:05:38 agc Exp $ */ + +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)show.h 1.1 (Berkeley) 5/4/95 + */ + +#include <stdarg.h> + +union node; +void showtree(union node *); +void trace(const char *, ...); +void tracev(const char *, va_list); +void trargs(char **); +#ifdef DEBUG +void trputc(int); +void trputs(const char *); +void opentrace(void); +#endif diff --git a/sh/syntax.c b/sh/syntax.c new file mode 100644 index 0000000..094f674 --- /dev/null +++ b/sh/syntax.c @@ -0,0 +1,102 @@ +/* $NetBSD: syntax.c,v 1.1 2004/01/17 17:38:12 dsl Exp $ */ + +#include "shell.h" +#include "syntax.h" +#include "parser.h" +#include <limits.h> + +#if CWORD != 0 +#error initialisation assumes 'CWORD' is zero +#endif + +#define ndx(ch) (ch + 1 - CHAR_MIN) +#define set(ch, val) [ndx(ch)] = val, +#define set_range(s, e, val) [ndx(s) ... ndx(e)] = val, + +/* syntax table used when not in quotes */ +const char basesyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\\', CBACK) + set('\'', CSQUOTE) + set('"', CDQUOTE) + set('`', CBQUOTE) + set('$', CVAR) + set('}', CENDVAR) + set('<', CSPCL) + set('>', CSPCL) + set('(', CSPCL) + set(')', CSPCL) + set(';', CSPCL) + set('&', CSPCL) + set('|', CSPCL) + set(' ', CSPCL) + set('\t', CSPCL) +}; + +/* syntax table used when in double quotes */ +const char dqsyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\\', CBACK) + set('"', CDQUOTE) + set('`', CBQUOTE) + set('$', CVAR) + set('}', CENDVAR) + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + set('!', CCTL) + set('*', CCTL) + set('?', CCTL) + set('[', CCTL) + set('=', CCTL) + set('~', CCTL) + set(':', CCTL) + set('/', CCTL) + set('-', CCTL) +}; + +/* syntax table used when in single quotes */ +const char sqsyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\'', CSQUOTE) + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + set('!', CCTL) + set('*', CCTL) + set('?', CCTL) + set('[', CCTL) + set('=', CCTL) + set('~', CCTL) + set(':', CCTL) + set('/', CCTL) + set('-', CCTL) +}; + +/* syntax table used when in arithmetic */ +const char arisyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\\', CBACK) + set('`', CBQUOTE) + set('\'', CSQUOTE) + set('"', CDQUOTE) + set('$', CVAR) + set('}', CENDVAR) + set('(', CLP) + set(')', CRP) +}; + +/* character classification table */ +const char is_type[257] = { 0, + set_range('0', '9', ISDIGIT) + set_range('a', 'z', ISLOWER) + set_range('A', 'Z', ISUPPER) + set('_', ISUNDER) + set('#', ISSPECL) + set('?', ISSPECL) + set('$', ISSPECL) + set('!', ISSPECL) + set('-', ISSPECL) + set('*', ISSPECL) + set('@', ISSPECL) +}; diff --git a/sh/syntax.h b/sh/syntax.h new file mode 100644 index 0000000..89a32dc --- /dev/null +++ b/sh/syntax.h @@ -0,0 +1,83 @@ +/* $NetBSD: syntax.h,v 1.2 2004/01/17 17:38:12 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <ctype.h> + +/* Syntax classes */ +#define CWORD 0 /* character is nothing special */ +#define CNL 1 /* newline character */ +#define CBACK 2 /* a backslash character */ +#define CSQUOTE 3 /* single quote */ +#define CDQUOTE 4 /* double quote */ +#define CBQUOTE 5 /* backwards single quote */ +#define CVAR 6 /* a dollar sign */ +#define CENDVAR 7 /* a '}' character */ +#define CLP 8 /* a left paren in arithmetic */ +#define CRP 9 /* a right paren in arithmetic */ +#define CEOF 10 /* end of file */ +#define CCTL 11 /* like CWORD, except it must be escaped */ +#define CSPCL 12 /* these terminate a word */ + +/* Syntax classes for is_ functions */ +#define ISDIGIT 01 /* a digit */ +#define ISUPPER 02 /* an upper case letter */ +#define ISLOWER 04 /* a lower case letter */ +#define ISUNDER 010 /* an underscore */ +#define ISSPECL 020 /* the name of a special parameter */ + +#define PEOF (CHAR_MIN - 1) +#define SYNBASE (-PEOF) +/* XXX UPEOF is CHAR_MAX, so is a valid 'char' value... */ +#define UPEOF ((char)PEOF) + + +#define BASESYNTAX (basesyntax + SYNBASE) +#define DQSYNTAX (dqsyntax + SYNBASE) +#define SQSYNTAX (sqsyntax + SYNBASE) +#define ARISYNTAX (arisyntax + SYNBASE) + +/* These defines assume that the digits are contiguous */ +#define is_digit(c) ((unsigned)((c) - '0') <= 9) +#define is_alpha(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && isalpha((unsigned char)(c))) +#define is_name(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalpha((unsigned char)(c)))) +#define is_in_name(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalnum((unsigned char)(c)))) +#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT)) +#define digit_val(c) ((c) - '0') + +extern const char basesyntax[]; +extern const char dqsyntax[]; +extern const char sqsyntax[]; +extern const char arisyntax[]; +extern const char is_type[]; diff --git a/sh/token.h b/sh/token.h new file mode 100644 index 0000000..c961f01 --- /dev/null +++ b/sh/token.h @@ -0,0 +1,112 @@ +#define TEOF 0 +#define TNL 1 +#define TSEMI 2 +#define TBACKGND 3 +#define TAND 4 +#define TOR 5 +#define TPIPE 6 +#define TLP 7 +#define TRP 8 +#define TENDCASE 9 +#define TENDBQUOTE 10 +#define TREDIR 11 +#define TWORD 12 +#define TIF 13 +#define TTHEN 14 +#define TELSE 15 +#define TELIF 16 +#define TFI 17 +#define TWHILE 18 +#define TUNTIL 19 +#define TFOR 20 +#define TDO 21 +#define TDONE 22 +#define TBEGIN 23 +#define TEND 24 +#define TCASE 25 +#define TESAC 26 +#define TNOT 27 + +/* Array indicating which tokens mark the end of a list */ +const char tokendlist[] = { + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, +}; + +const char *const tokname[] = { + "end of file", + "newline", + "\";\"", + "\"&\"", + "\"&&\"", + "\"||\"", + "\"|\"", + "\"(\"", + "\")\"", + "\";;\"", + "\"`\"", + "redirection", + "word", + "\"if\"", + "\"then\"", + "\"else\"", + "\"elif\"", + "\"fi\"", + "\"while\"", + "\"until\"", + "\"for\"", + "\"do\"", + "\"done\"", + "\"{\"", + "\"}\"", + "\"case\"", + "\"esac\"", + "\"!\"", +}; + +#define KWDOFFSET 13 + +const char *const parsekwd[] = { + "if", + "then", + "else", + "elif", + "fi", + "while", + "until", + "for", + "do", + "done", + "{", + "}", + "case", + "esac", + "!", + 0 +}; diff --git a/sh/trap.c b/sh/trap.c new file mode 100644 index 0000000..b3b2db4 --- /dev/null +++ b/sh/trap.c @@ -0,0 +1,470 @@ +/* $NetBSD: trap.c,v 1.31 2005/01/11 19:38:57 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; +#else +__RCSID("$NetBSD: trap.c,v 1.31 2005/01/11 19:38:57 christos Exp $"); +#endif +#endif /* not lint */ + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "show.h" +#include "options.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" +#include "var.h" + +static const char *sys_signame[NSIG] = { + "Unused", + "HUP", "INT", "QUIT", "ILL", + "TRAP", "ABRT", "BUS", "FPE", + "KILL", "USR1", "SEGV", "USR2", + "PIPE", "ALRM", "TERM", + "Unknown", + "CHLD", + "CONT", "STOP", "TSTP", "TTIN", + "TTOU", "URG", "XCPU", "XFSZ", + "VTALRM", "PROF", "WINCH", "IO", + "PWR", "SYS" +}; + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + +char *trap[NSIG+1]; /* trap handler commands */ +MKINIT char sigmode[NSIG]; /* current value of signal */ +char gotsig[NSIG]; /* indicates specified signal received */ +int pendingsigs; /* indicates some signal received */ + +static int getsigaction(int, sig_t *); + +/* + * return the signal number described by `p' (as a number or a name) + * or -1 if it isn't one + */ + +static int +signame_to_signum(const char *p) +{ + int i; + + if (is_number(p)) + return number(p); + + if (strcasecmp(p, "exit") == 0 ) + return 0; + + if (strncasecmp(p, "sig", 3) == 0) + p += 3; + + for (i = 0; i < NSIG; ++i) + if (strcasecmp (p, sys_signame[i]) == 0) + return i; + return -1; +} + +/* + * Print a list of valid signal names + */ +static void +printsignals(void) +{ + int n; + + out1str("EXIT "); + + for (n = 1; n < NSIG; n++) { + out1fmt("%s", sys_signame[n]); + if ((n == NSIG/2) || n == (NSIG - 1)) + out1str("\n"); + else + out1c(' '); + } +} + +/* + * The trap builtin. + */ + +int +trapcmd(int argc, char **argv) +{ + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo <= NSIG ; signo++) + if (trap[signo] != NULL) { + out1fmt("trap -- "); + print_quoted(trap[signo]); + out1fmt(" %s\n", + (signo) ? sys_signame[signo] : "EXIT"); + } + return 0; + } + ap = argv + 1; + + action = NULL; + + if (strcmp(*ap, "--") == 0) + if (*++ap == NULL) + return 0; + + if (signame_to_signum(*ap) == -1) { + if ((*ap)[0] == '-') { + if ((*ap)[1] == '\0') + ap++; + else if ((*ap)[1] == 'l' && (*ap)[2] == '\0') { + printsignals(); + return 0; + } + else + error("bad option %s\n", *ap); + } + else + action = *ap++; + } + + while (*ap) { + if (is_number(*ap)) + signo = number(*ap); + else + signo = signame_to_signum(*ap); + + if (signo < 0 || signo > NSIG) + error("%s: bad trap", *ap); + + INTOFF; + if (action) + action = savestr(action); + + if (trap[signo]) + ckfree(trap[signo]); + + trap[signo] = action; + + if (signo != 0) + setsignal(signo, 0); + INTON; + ap++; + } + return 0; +} + + + +/* + * Clear traps on a fork or vfork. + * Takes one arg vfork, to tell it to not be destructive of + * the parents variables. + */ + +void +clear_traps(int vforked) +{ + char **tp; + + for (tp = trap ; tp <= &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + if (!vforked) { + ckfree(*tp); + *tp = NULL; + } + if (tp != &trap[0]) + setsignal(tp - trap, vforked); + INTON; + } + } +} + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +long +setsignal(int signo, int vforked) +{ + int action; + sig_t sigact = SIG_DFL; + struct sigaction act, oact; + char *t, tsig; + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && !vforked && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag || minusc || sflag == 0) + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + if (debug) + break; +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo - 1]; + tsig = *t; + if (tsig == 0) { + /* + * current setting unknown + */ + if (!getsigaction(signo, &sigact)) { + /* + * Pretend it worked; maybe we should give a warning + * here, but other shells don't. We don't alter + * sigmode, so that we retry every time. + */ + return 0; + } + if (sigact == SIG_IGN) { + if (mflag && (signo == SIGTSTP || + signo == SIGTTIN || signo == SIGTTOU)) { + tsig = S_IGN; /* don't hard ignore these */ + } else + tsig = S_HARD_IGN; + } else { + tsig = S_RESET; /* force to be set */ + } + } + if (tsig == S_HARD_IGN || tsig == action) + return 0; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + if (!vforked) + *t = action; + act.sa_handler = sigact; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_INTERRUPT + act.sa_flags |= SA_INTERRUPT; +#endif + if(sigaction(signo, &act, &oact) < 0) + return (long) SIG_ERR; + return (long) oact.sa_handler; +} + +/* + * Return the current setting for sig w/o changing it. + */ +static int +getsigaction(int signo, sig_t *sigact) +{ + struct sigaction sa; + + if (sigaction(signo, (struct sigaction *)0, &sa) == -1) + return 0; + *sigact = (sig_t) sa.sa_handler; + return 1; +} + +/* + * Ignore a signal. + */ + +void +ignoresig(int signo, int vforked) +{ + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) + bsd_signal(signo, SIG_IGN); + if (!vforked) + sigmode[signo - 1] = S_HARD_IGN; +} + + +#ifdef mkinit +INCLUDE <signal.h> +INCLUDE "trap.h" + +SHELLPROC { + char *sm; + + clear_traps(0); + for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } +} +#endif + + + +/* + * Signal handler. + */ + +void +onsig(int signo) +{ + bsd_signal(signo, onsig); + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo - 1] = 1; + pendingsigs++; +} + + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void +dotrap(void) +{ + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (gotsig[i - 1]) + break; + if (i >= NSIG) + goto done; + } + gotsig[i - 1] = 0; + savestatus=exitstatus; + evalstring(trap[i], 0); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + + + +/* + * Controls whether the shell is interactive or not. + */ + + +void +setinteractive(int on) +{ + static int is_interactive; + + if (on == is_interactive) + return; + setsignal(SIGINT, 0); + setsignal(SIGQUIT, 0); + setsignal(SIGTERM, 0); + is_interactive = on; +} + + + +/* + * Called to exit the shell. + */ + +void +exitshell(int status) +{ + struct jmploc loc1, loc2; + char *p; + + TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); + if (setjmp(loc1.loc)) { + goto l1; + } + if (setjmp(loc2.loc)) { + goto l2; + } + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p, 0); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#if JOBS + setjobctl(0); +#endif +l2: _exit(status); + /* NOTREACHED */ +} diff --git a/sh/trap.h b/sh/trap.h new file mode 100644 index 0000000..125ef40 --- /dev/null +++ b/sh/trap.h @@ -0,0 +1,46 @@ +/* $NetBSD: trap.h,v 1.17 2003/08/07 09:05:39 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trap.h 8.3 (Berkeley) 6/5/95 + */ + +extern int pendingsigs; + +int trapcmd(int, char **); +void clear_traps(int); +long setsignal(int, int); +void ignoresig(int, int); +void onsig(int); +void dotrap(void); +void setinteractive(int); +void exitshell(int) __attribute__((__noreturn__)); diff --git a/sh/var.c b/sh/var.c new file mode 100644 index 0000000..a1f1689 --- /dev/null +++ b/sh/var.c @@ -0,0 +1,825 @@ +/* $NetBSD: var.c,v 1.36 2004/10/06 10:23:43 enami Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: var.c,v 1.36 2004/10/06 10:23:43 enami Exp $"); +#endif +#endif /* not lint */ + +#include <unistd.h> +#include <stdlib.h> +#include <paths.h> + +/* + * Shell variables. + */ + +#include "shell.h" +#include "output.h" +#include "expand.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" /* defines cmdenviron */ +#include "exec.h" +#include "syntax.h" +#include "options.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "parser.h" +#include "show.h" +#ifndef SMALL +#include "myhistedit.h" +#endif + +#ifdef SMALL +#define VTABSIZE 39 +#else +#define VTABSIZE 517 +#endif + + +struct varinit { + struct var *var; + int flags; + const char *text; + void (*func)(const char *); +}; + + +#if ATTY +struct var vatty; +#endif +#ifdef WITH_HISTORY +struct var vhistsize; +struct var vterm; +#endif +struct var vifs; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vps4; +struct var vvers; +struct var voptind; + +const struct varinit varinit[] = { +#if ATTY + { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", + NULL }, +#endif +#ifdef WITH_HISTORY + { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", + sethistsize }, +#endif + { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", + NULL }, + { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", + NULL }, + { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, + changepath }, + /* + * vps1 depends on uid + */ + { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", + NULL }, + { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ", + NULL }, +#ifdef WITH_HISTORY + { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", + setterm }, +#endif + { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1", + getoptsreset }, + { NULL, 0, NULL, + NULL } +}; + +struct var *vartab[VTABSIZE]; + +STATIC int strequal(const char *, const char *); +STATIC struct var *find_var(const char *, struct var ***, int *); + +/* + * Initialize the varable symbol tables and import the environment + */ + +#ifdef mkinit +INCLUDE "var.h" +MKINIT char **environ; +INIT { + char **envp; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } +} +#endif + + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized and again when a shell procedure is spawned. + */ + +void +initvar(void) +{ + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if (find_var(ip->text, &vpp, &vp->name_len) != NULL) + continue; + vp->next = *vpp; + *vpp = vp; + vp->text = strdup(ip->text); + vp->flags = ip->flags; + vp->func = ip->func; + } + /* + * PS1 depends on uid + */ + if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ + +int +setvarsafe(const char *name, const char *val, int flags) +{ + struct jmploc jmploc; + struct jmploc *volatile savehandler = handler; + int err = 0; +#ifdef __GNUC__ + (void) &err; +#endif + + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + } + handler = savehandler; + return err; +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +void +setvar(const char *name, const char *val, int flags) +{ + const char *p; + const char *q; + char *d; + int len; + int namelen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p)) + isbad = 1; + p++; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: bad variable name", namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + } else { + len += strlen(val); + } + d = nameeq = ckmalloc(len); + q = name; + while (--namelen >= 0) + *d++ = *q++; + *d++ = '='; + *d = '\0'; + if (val) + scopy(val, d); + setvareq(nameeq, flags); +} + + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + */ + +void +setvareq(char *s, int flags) +{ + struct var *vp, **vpp; + int nlen; + + if (aflag) + flags |= VEXPORT; + vp = find_var(s, &vpp, &nlen); + if (vp != NULL) { + if (vp->flags & VREADONLY) + error("%.*s: is read only", vp->name_len, s); + if (flags & VNOSET) + return; + INTOFF; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(s + vp->name_len + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags & ~VNOFUNC; + vp->text = s; + + INTON; + return; + } + /* not found */ + if (flags & VNOSET) + return; + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags & ~VNOFUNC; + vp->text = s; + vp->name_len = nlen; + vp->next = *vpp; + vp->func = NULL; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(struct strlist *list, int flags) +{ + struct strlist *lp; + + INTOFF; + for (lp = list ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), flags); + } + INTON; +} + +void +listmklocal(struct strlist *list, int flags) +{ + struct strlist *lp; + + for (lp = list ; lp ; lp = lp->next) + mklocal(lp->text, flags); +} + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(const char *name) +{ + struct var *v; + + v = find_var(name, NULL, NULL); + if (v == NULL || v->flags & VUNSET) + return NULL; + return v->text + v->name_len + 1; +} + + + +/* + * Search the environment of a builtin command. If the second argument + * is nonzero, return the value of a variable even if it hasn't been + * exported. + */ + +char * +bltinlookup(const char *name, int doall) +{ + struct strlist *sp; + struct var *v; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (strequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + + v = find_var(name, NULL, NULL); + + if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT))) + return NULL; + return v->text + v->name_len + 1; +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +char ** +environment(void) +{ + int nenv; + struct var **vpp; + struct var *vp; + char **env; + char **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +/* + * Called when a shell procedure is invoked to clear out nonexported + * variables. It is also necessary to reallocate variables of with + * VSTACK set since these are currently allocated on the stack. + */ + +#ifdef mkinit +void shprocvar(void); + +SHELLPROC { + shprocvar(); +} +#endif + +void +shprocvar(void) +{ + struct var **vpp; + struct var *vp, **prev; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (prev = vpp ; (vp = *prev) != NULL ; ) { + if ((vp->flags & VEXPORT) == 0) { + *prev = vp->next; + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + if ((vp->flags & VSTRFIXED) == 0) + ckfree(vp); + } else { + if (vp->flags & VSTACK) { + vp->text = savestr(vp->text); + vp->flags &=~ VSTACK; + } + prev = &vp->next; + } + } + } + initvar(); +} + + + +/* + * Command to list all variables which are set. Currently this command + * is invoked from the set command when the set command is called without + * any variables. + */ + +void +print_quoted(const char *p) +{ + const char *q; + + if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) { + out1fmt("%s", p); + return; + } + while (*p) { + if (*p == '\'') { + out1fmt("\\'"); + p++; + continue; + } + q = index(p, '\''); + if (!q) { + out1fmt("'%s'", p ); + return; + } + out1fmt("'%.*s'", (int)(q - p), p ); + p = q; + } +} + +static int +sort_var(const void *v_v1, const void *v_v2) +{ + const struct var * const *v1 = v_v1; + const struct var * const *v2 = v_v2; + + /* XXX Will anyone notice we include the '=' of the shorter name? */ + return strcmp((*v1)->text, (*v2)->text); +} + +/* + * POSIX requires that 'set' (but not export or readonly) output the + * variables in lexicographic order - by the locale's collating order (sigh). + * Maybe we could keep them in an ordered balanced binary tree + * instead of hashed lists. + * For now just roll 'em through qsort for printing... + */ + +int +showvars(const char *name, int flag, int show_value) +{ + struct var **vpp; + struct var *vp; + const char *p; + + static struct var **list; /* static in case we are interrupted */ + static int list_len; + int count = 0; + + if (!list) { + list_len = 32; + list = ckmalloc(list_len * sizeof *list); + } + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if (flag && !(vp->flags & flag)) + continue; + if (vp->flags & VUNSET && !(show_value & 2)) + continue; + if (count >= list_len) { + list = ckrealloc(list, + (list_len << 1) * sizeof *list); + list_len <<= 1; + } + list[count++] = vp; + } + } + + qsort(list, count, sizeof *list, sort_var); + + for (vpp = list; count--; vpp++) { + vp = *vpp; + if (name) + out1fmt("%s ", name); + for (p = vp->text ; *p != '=' ; p++) + out1c(*p); + if (!(vp->flags & VUNSET) && show_value) { + out1fmt("="); + print_quoted(++p); + } + out1c('\n'); + } + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(int argc, char **argv) +{ + struct var *vp; + char *name; + const char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int pflag; + + pflag = nextopt("p") == 'p' ? 3 : 0; + if (argc <= 1 || pflag) { + showvars( pflag ? argv[0] : 0, flag, pflag ); + return 0; + } + + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vp = find_var(name, NULL, NULL); + if (vp != NULL) { + vp->flags |= flag; + continue; + } + } + setvar(name, p, flag); + } + return 0; +} + + +/* + * The "local" command. + */ + +int +localcmd(int argc, char **argv) +{ + char *name; + + if (! in_function()) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name, 0); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +void +mklocal(const char *name, int flags) +{ + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + char *p; + p = ckmalloc(sizeof_optlist); + lvp->text = memcpy(p, optlist, sizeof_optlist); + vp = NULL; + } else { + vp = find_var(name, &vpp, NULL); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED|flags); + else + setvar(name, NULL, VSTRFIXED|flags); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (name[vp->name_len] == '=') + setvareq(savestr(name), flags); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +void +poplocalvars(void) +{ + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + TRACE(("poplocalvar %s", vp ? vp->text : "-")); + if (vp == NULL) { /* $- saved */ + memcpy(optlist, lvp->text, sizeof_optlist); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + (void)unsetvar(vp->text, 0); + } else { + if (vp->func && (vp->flags & VNOFUNC) == 0) + (*vp->func)(lvp->text + vp->name_len + 1); + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +int +setvarcmd(int argc, char **argv) +{ + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("List assignment not implemented"); + return 0; +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +int +unsetcmd(int argc, char **argv) +{ + char **ap; + int i; + int flg_func = 0; + int flg_var = 0; + int ret = 0; + + while ((i = nextopt("evf")) != '\0') { + if (i == 'f') + flg_func = 1; + else + flg_var = i; + } + if (flg_func == 0 && flg_var == 0) + flg_var = 1; + + for (ap = argptr; *ap ; ap++) { + if (flg_func) + ret |= unsetfunc(*ap); + if (flg_var) + ret |= unsetvar(*ap, flg_var == 'e'); + } + return ret; +} + + +/* + * Unset the specified variable. + */ + +int +unsetvar(const char *s, int unexport) +{ + struct var **vpp; + struct var *vp; + + vp = find_var(s, &vpp, NULL); + if (vp == NULL) + return 1; + + if (vp->flags & VREADONLY) + return (1); + + INTOFF; + if (unexport) { + vp->flags &= ~VEXPORT; + } else { + if (vp->text[vp->name_len + 1] != '\0') + setvar(s, nullstr, 0); + vp->flags &= ~VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + } + INTON; + return 0; +} + + +/* + * Returns true if the two strings specify the same varable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +STATIC int +strequal(const char *p, const char *q) +{ + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} + +/* + * Search for a variable. + * 'name' may be terminated by '=' or a NUL. + * vppp is set to the pointer to vp, or the list head if vp isn't found + * lenp is set to the number of charactets in 'name' + */ + +STATIC struct var * +find_var(const char *name, struct var ***vppp, int *lenp) +{ + unsigned int hashval; + int len; + struct var *vp, **vpp; + const char *p = name; + + hashval = 0; + while (*p && *p != '=') + hashval = 2 * hashval + (unsigned char)*p++; + len = p - name; + + if (lenp) + *lenp = len; + vpp = &vartab[hashval % VTABSIZE]; + if (vppp) + *vppp = vpp; + + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (vp->name_len != len) + continue; + if (memcmp(vp->text, name, len) != 0) + continue; + if (vppp) + *vppp = vpp; + return vp; + } + return NULL; +} diff --git a/sh/var.h b/sh/var.h new file mode 100644 index 0000000..b7b7db8 --- /dev/null +++ b/sh/var.h @@ -0,0 +1,131 @@ +/* $NetBSD: var.h,v 1.23 2004/10/02 12:16:53 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)var.h 8.2 (Berkeley) 5/4/95 + */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is statically allocated */ +#define VTEXTFIXED 0x08 /* text is statically allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ +#define VNOSET 0x80 /* do not set variable - just readonly test */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + char *text; /* name=value */ + int name_len; /* length of name */ + void (*func)(const char *); + /* function to be called when */ + /* the variable gets set/unset */ +}; + + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + char *text; /* saved text */ +}; + + +struct localvar *localvars; + +#if ATTY +extern struct var vatty; +#endif +extern struct var vifs; +extern struct var vmpath; +extern struct var vpath; +extern struct var vps1; +extern struct var vps2; +extern struct var vps4; +#ifdef WITH_HISTORY +extern struct var vterm; +extern struct var vtermcap; +extern struct var vhistsize; +#endif + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define ifsset() ((vifs.flags & VUNSET) == 0) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#define ps4val() (vps4.text + 4) +#define optindval() (voptind.text + 7) +#ifdef WITH_HISTORY +#define histsizeval() (vhistsize.text + 9) +#define termval() (vterm.text + 5) +#endif + +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif +#define mpathset() ((vmpath.flags & VUNSET) == 0) + +void initvar(void); +void setvar(const char *, const char *, int); +void setvareq(char *, int); +struct strlist; +void listsetvar(struct strlist *, int); +char *lookupvar(const char *); +char *bltinlookup(const char *, int); +char **environment(void); +void shprocvar(void); +int showvars(const char *, int, int); +int exportcmd(int, char **); +int localcmd(int, char **); +void mklocal(const char *, int); +void listmklocal(struct strlist *, int); +void poplocalvars(void); +int setvarcmd(int, char **); +int unsetcmd(int, char **); +int unsetvar(const char *, int); +int setvarsafe(const char *, const char *, int); +void print_quoted(const char *); diff --git a/toolbox/Android.mk b/toolbox/Android.mk new file mode 100644 index 0000000..5a8dc0b --- /dev/null +++ b/toolbox/Android.mk @@ -0,0 +1,91 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +TOOLS := \ + ls \ + mount \ + cat \ + ps \ + kill \ + ln \ + insmod \ + rmmod \ + lsmod \ + ifconfig \ + setconsole \ + rm \ + mkdir \ + rmdir \ + reboot \ + getevent \ + sendevent \ + date \ + wipe \ + sync \ + umount \ + start \ + stop \ + notify \ + cmp \ + dmesg \ + route \ + hd \ + dd \ + df \ + getprop \ + setprop \ + watchprops \ + log \ + sleep \ + renice \ + printenv \ + smd \ + chmod \ + chown \ + mkdosfs \ + netstat \ + ioctl \ + mv \ + schedtop \ + top \ + iftop \ + id \ + vmstat + +LOCAL_SRC_FILES:= \ + toolbox.c \ + $(patsubst %,%.c,$(TOOLS)) + +LOCAL_SHARED_LIBRARIES := libcutils libc + +LOCAL_MODULE:= toolbox + +# Including this will define $(intermediates). +# +include $(BUILD_EXECUTABLE) + +$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h + +TOOLS_H := $(intermediates)/tools.h +$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS) +$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done +$(TOOLS_H): $(LOCAL_PATH)/Android.mk +$(TOOLS_H): + $(transform-generated-source) + +# Make #!/system/bin/toolbox launchers for each tool. +# +SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS)) +$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE) +$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(TOOLBOX_BINARY) $@ + +ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS) + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ + $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS) diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/toolbox/MODULE_LICENSE_BSD diff --git a/toolbox/NOTICE b/toolbox/NOTICE new file mode 100644 index 0000000..12f28b9 --- /dev/null +++ b/toolbox/NOTICE @@ -0,0 +1,131 @@ + +Copyright (c) 2008, The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 1998 Robert Nordier +Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by +Kevin Fall. +This code is derived from software contributed to Berkeley by +Keith Muller of the University of California, San Diego and Lance +Visser of Convex Computer Corporation. +This code is derived from software contributed to Berkeley by +Mike Muuss. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. + + This code is derived from software contributed to Berkeley by + Kevin Fall. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + + Copyright (c) 1991, 1993, 1994 + The Regents of the University of California. All rights reserved. + + This code is derived from software contributed to Berkeley by + Keith Muller of the University of California, San Diego and Lance + Visser of Convex Computer Corporation. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + diff --git a/toolbox/alarm.c b/toolbox/alarm.c new file mode 100644 index 0000000..9bd58aa --- /dev/null +++ b/toolbox/alarm.c @@ -0,0 +1,190 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <asm/ioctl.h> +//#include <linux/rtc.h> +#include <linux/android_alarm.h> + +int alarm_main(int argc, char *argv[]) +{ + int c; + int res; + struct tm tm; + time_t t; + struct timespec ts; +// struct rtc_time rtc_time; + char strbuf[26]; + int afd; + int nfd; +// struct timeval timeout = { 0, 0 }; + int wait = 0; + fd_set rfds; + const char wake_lock_id[] = "alarm_test"; + int waitalarmmask = 0; + + int useutc = 0; + android_alarm_type_t alarmtype_low = ANDROID_ALARM_RTC_WAKEUP; + android_alarm_type_t alarmtype_high = ANDROID_ALARM_RTC_WAKEUP; + android_alarm_type_t alarmtype = 0; + + do { + //c = getopt(argc, argv, "uw:"); + c = getopt(argc, argv, "uwat:"); + if (c == EOF) + break; + switch (c) { + case 'u': + useutc = 1; + break; + case 't': + alarmtype_low = alarmtype_high = strtol(optarg, NULL, 0); + break; + case 'a': + alarmtype_low = ANDROID_ALARM_RTC_WAKEUP; + alarmtype_high = ANDROID_ALARM_TYPE_COUNT - 1; + break; + case 'w': + //timeout.tv_sec = strtol(optarg, NULL, 0); + wait = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(optind + 2 < argc) { + fprintf(stderr,"%s [-uwa] [-t type] [seconds]\n", argv[0]); + return 1; + } + + afd = open("/dev/alarm", O_RDWR); + if(afd < 0) { + fprintf(stderr, "Unable to open rtc: %s\n", strerror(errno)); + return 1; + } + + if(optind == argc) { + for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) { + waitalarmmask |= 1U << alarmtype; + } +#if 0 + res = ioctl(fd, RTC_ALM_READ, &tm); + if(res < 0) { + fprintf(stderr, "Unable to read alarm: %s\n", strerror(errno)); + return 1; + } +#endif +#if 0 + t = timegm(&tm); + if(useutc) + gmtime_r(&t, &tm); + else + localtime_r(&t, &tm); +#endif +#if 0 + asctime_r(&tm, strbuf); + printf("%s", strbuf); +#endif + } + else if(optind + 1 == argc) { +#if 0 + res = ioctl(fd, RTC_RD_TIME, &tm); + if(res < 0) { + fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno)); + return 1; + } + asctime_r(&tm, strbuf); + printf("Now: %s", strbuf); + time(&tv.tv_sec); +#endif +#if 0 + time(&ts.tv_sec); + ts.tv_nsec = 0; + + //strptime(argv[optind], NULL, &tm); + //tv.tv_sec = mktime(&tm); + //tv.tv_usec = 0; +#endif + for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) { + waitalarmmask |= 1U << alarmtype; + res = ioctl(afd, ANDROID_ALARM_GET_TIME(alarmtype), &ts); + if(res < 0) { + fprintf(stderr, "Unable to get current time: %s\n", strerror(errno)); + return 1; + } + ts.tv_sec += strtol(argv[optind], NULL, 0); + //strtotimeval(argv[optind], &tv); + gmtime_r(&ts.tv_sec, &tm); + printf("time %s -> %ld.%09ld\n", argv[optind], ts.tv_sec, ts.tv_nsec); + asctime_r(&tm, strbuf); + printf("Requested %s", strbuf); + + res = ioctl(afd, ANDROID_ALARM_SET(alarmtype), &ts); + if(res < 0) { + fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno)); + return 1; + } + } +#if 0 + res = ioctl(fd, RTC_ALM_SET, &tm); + if(res < 0) { + fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno)); + return 1; + } + res = ioctl(fd, RTC_AIE_ON); + if(res < 0) { + fprintf(stderr, "Unable to enable alarm: %s\n", strerror(errno)); + return 1; + } +#endif + } + else { + fprintf(stderr,"%s [-u] [date]\n", argv[0]); + return 1; + } + + if(wait) { + while(waitalarmmask) { + printf("wait for alarm %x\n", waitalarmmask); + res = ioctl(afd, ANDROID_ALARM_WAIT); + if(res < 0) { + fprintf(stderr, "alarm wait failed\n"); + } + printf("got alarm %x\n", res); + waitalarmmask &= ~res; + nfd = open("/sys/android_power/acquire_full_wake_lock", O_RDWR); + write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1); + close(nfd); + //sleep(5); + nfd = open("/sys/android_power/release_wake_lock", O_RDWR); + write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1); + close(nfd); + } + printf("done\n"); + } +#if 0 + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + res = select(fd + 1, &rfds, NULL, NULL, &timeout); + if(res < 0) { + fprintf(stderr, "select failed: %s\n", strerror(errno)); + return 1; + } + if(res > 0) { + int event; + read(fd, &event, sizeof(event)); + fprintf(stderr, "got %x\n", event); + } + else { + fprintf(stderr, "timeout waiting for alarm\n"); + } +#endif + + close(afd); + + return 0; +} diff --git a/toolbox/cat.c b/toolbox/cat.c new file mode 100644 index 0000000..6ac31f8 --- /dev/null +++ b/toolbox/cat.c @@ -0,0 +1,291 @@ +/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define CAT_BUFSIZ (4096) + +static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag; +static int rval; +static const char *filename; + +static void +cook_buf(FILE *fp) +{ + int ch, gobble, line, prev; + int stdout_err = 0; + + line = gobble = 0; + for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { + if (prev == '\n') { + if (ch == '\n') { + if (sflag) { + if (!gobble && putchar(ch) == EOF) + break; + gobble = 1; + continue; + } + if (nflag) { + if (!bflag) { + if (fprintf(stdout, + "%6d\t", ++line) < 0) { + stdout_err++; + break; + } + } else if (eflag) { + if (fprintf(stdout, + "%6s\t", "") < 0) { + stdout_err++; + break; + } + } + } + } else if (nflag) { + if (fprintf(stdout, "%6d\t", ++line) < 0) { + stdout_err++; + break; + } + } + } + gobble = 0; + if (ch == '\n') { + if (eflag) + if (putchar('$') == EOF) + break; + } else if (ch == '\t') { + if (tflag) { + if (putchar('^') == EOF || putchar('I') == EOF) + break; + continue; + } + } else if (vflag) { + if (!isascii(ch)) { + if (putchar('M') == EOF || putchar('-') == EOF) + break; + ch = (ch) & 0x7f; + } + if (iscntrl(ch)) { + if (putchar('^') == EOF || + putchar(ch == '\177' ? '?' : + ch | 0100) == EOF) + break; + continue; + } + } + if (putchar(ch) == EOF) + break; + } + if (stdout_err) { + perror(filename); + rval = 1; + } +} + +static void +cook_args(char **argv) +{ + FILE *fp; + + fp = stdin; + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fp = stdin; + else if ((fp = fopen(*argv, + fflag ? "rf" : "r")) == NULL) { + perror("fopen"); + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + cook_buf(fp); + if (fp != stdin) + fclose(fp); + } while (*argv); +} + +static void +raw_cat(int rfd) +{ + static char *buf; + static char fb_buf[CAT_BUFSIZ]; + static size_t bsize; + + struct stat sbuf; + ssize_t nr, nw, off; + int wfd; + + wfd = fileno(stdout); + if (buf == NULL) { + if (fstat(wfd, &sbuf) == 0) { + bsize = sbuf.st_blksize > CAT_BUFSIZ ? + sbuf.st_blksize : CAT_BUFSIZ; + buf = malloc(bsize); + } + if (buf == NULL) { + buf = fb_buf; + bsize = CAT_BUFSIZ; + } + } + while ((nr = read(rfd, buf, bsize)) > 0) + for (off = 0; nr; nr -= nw, off += nw) + if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) + { + perror("write"); + exit(EXIT_FAILURE); + } + if (nr < 0) { + fprintf(stderr,"%s: invalid length\n", filename); + rval = 1; + } +} + +static void +raw_args(char **argv) +{ + int fd; + + fd = fileno(stdin); + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fd = fileno(stdin); + else if (fflag) { + struct stat st; + fd = open(*argv, O_RDONLY|O_NONBLOCK, 0); + if (fd < 0) + goto skip; + + if (fstat(fd, &st) == -1) { + close(fd); + goto skip; + } + if (!S_ISREG(st.st_mode)) { + close(fd); + errno = EINVAL; + goto skipnomsg; + } + } + else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { +skip: + perror(*argv); +skipnomsg: + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + raw_cat(fd); + if (fd != fileno(stdin)) + close(fd); + } while (*argv); +} + +int +cat_main(int argc, char *argv[]) +{ + int ch; + struct flock stdout_lock; + + while ((ch = getopt(argc, argv, "beflnstv")) != -1) + switch (ch) { + case 'b': + bflag = nflag = 1; /* -b implies -n */ + break; + case 'e': + eflag = vflag = 1; /* -e implies -v */ + break; + case 'f': + fflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = vflag = 1; /* -t implies -v */ + break; + case 'v': + vflag = 1; + break; + default: + case '?': + fprintf(stderr, + "usage: cat [-beflnstv] [-] [file ...]\n"); + exit(EXIT_FAILURE); + } + argv += optind; + + if (lflag) { + stdout_lock.l_len = 0; + stdout_lock.l_start = 0; + stdout_lock.l_type = F_WRLCK; + stdout_lock.l_whence = SEEK_SET; + if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) + { + perror("fcntl"); + exit(EXIT_FAILURE); + } + } + + if (bflag || eflag || nflag || sflag || tflag || vflag) + cook_args(argv); + else + raw_args(argv); + if (fclose(stdout)) + { + perror("fclose"); + exit(EXIT_FAILURE); + } + exit(rval); +} diff --git a/toolbox/chmod.c b/toolbox/chmod.c new file mode 100644 index 0000000..31a53bf --- /dev/null +++ b/toolbox/chmod.c @@ -0,0 +1,40 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> + +#include <unistd.h> +#include <time.h> + +int chmod_main(int argc, char **argv) +{ + int i; + + if (argc < 3) { + fprintf(stderr, "Usage: chmod <MODE> <FILE>\n"); + return 10; + } + + int mode = 0; + const char* s = argv[1]; + while (*s) { + if (*s >= '0' && *s <= '7') { + mode = (mode<<3) | (*s-'0'); + } + else { + fprintf(stderr, "Bad mode\n"); + return 10; + } + s++; + } + for (i = 2; i < argc; i++) { + if (chmod(argv[i], mode) < 0) { + fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno)); + return 10; + } + } + return 0; +} + diff --git a/toolbox/chown.c b/toolbox/chown.c new file mode 100644 index 0000000..13617db --- /dev/null +++ b/toolbox/chown.c @@ -0,0 +1,62 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> + +#include <unistd.h> +#include <time.h> + +int chown_main(int argc, char **argv) +{ + int i; + + if (argc < 3) { + fprintf(stderr, "Usage: chown <USER>[.GROUP] <FILE1> [FILE2] ...\n"); + return 10; + } + + // Copy argv[1] to 'user' so we can truncate it at the period + // if a group id specified. + char user[32]; + char *group = NULL; + strncpy(user, argv[1], sizeof(user)); + if ((group = strchr(user, '.')) != NULL) { + *group++ = '\0'; + } + + // Lookup uid (and gid if specified) + struct passwd *pw; + struct group *grp = NULL; + uid_t uid; + gid_t gid = -1; // passing -1 to chown preserves current group + + pw = getpwnam(user); + if (pw == NULL) { + fprintf(stderr, "No such user '%s'\n", user); + return 10; + } + uid = pw->pw_uid; + + if (group != NULL) { + grp = getgrnam(group); + if (grp == NULL) { + fprintf(stderr, "No such group '%s'\n", group); + return 10; + } + gid = grp->gr_gid; + } + + for (i = 2; i < argc; i++) { + if (chown(argv[i], uid, gid) < 0) { + fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno)); + return 10; + } + } + + return 0; +} + diff --git a/toolbox/cmp.c b/toolbox/cmp.c new file mode 100644 index 0000000..9bd2e19 --- /dev/null +++ b/toolbox/cmp.c @@ -0,0 +1,90 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +int cmp_main(int argc, char *argv[]) +{ + int c; + int fd1, fd2; + char buf1[4096], buf2[4096]; + int res, res1, res2; + int rv = 0; + int i; + int filepos = 0; + + int show_byte = 0; + int show_all = 0; + int limit = 0; + + do { + c = getopt(argc, argv, "bln:"); + if (c == EOF) + break; + switch (c) { + case 'b': + show_byte = 1; + break; + case 'l': + show_all = 1; + break; + case 'n': + limit = atoi(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (optind + 2 != argc) { + fprintf(stderr, "Usage: %s [-b] [-l] [-n count] file1 file2\n", argv[0]); + exit(1); + } + + fd1 = open(argv[optind], O_RDONLY); + if(fd1 < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + + fd2 = open(argv[optind+1], O_RDONLY); + if(fd2 < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind+1], strerror(errno)); + return 1; + } + + while(1) { + res1 = read(fd1, &buf1, sizeof(buf1)); + res2 = read(fd2, &buf2, sizeof(buf2)); + res = res1 < res2 ? res1 : res2; + if(res1 == 0 && res2 == 0) { + return rv; + } + for(i = 0; i < res; i++) { + if(buf1[i] != buf2[i]) { + printf("%s %s differ byte %d", argv[optind], argv[optind+1], filepos + i); + if(show_byte) + printf(" 0x%02x 0x%02x", buf1[i], buf2[i]); + printf("\n"); + if(!show_all) + return 1; + rv = 1; + } + if(limit) { + limit--; + if(limit == 0) + return rv; + } + } + if(res1 != res2 || res < 0) { + printf("%s on %s\n", res < 0 ? "Read error" : "EOF", res1 < res2 ? argv[optind] : argv[optind+1]); + return 1; + } + filepos += res; + } +} diff --git a/toolbox/date.c b/toolbox/date.c new file mode 100644 index 0000000..13b5210 --- /dev/null +++ b/toolbox/date.c @@ -0,0 +1,132 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <linux/android_alarm.h> + +static void settime(char *s) { + struct tm tm; + int day = atoi(s); + int hour; + time_t t; + int fd; + struct timespec ts; + + while (*s && *s != '.') + s++; + + if (*s) + s++; + + hour = atoi(s); + + tm.tm_year = day / 10000 - 1900; + tm.tm_mon = (day % 10000) / 100 - 1; + tm.tm_mday = (day % 100); + tm.tm_hour = hour / 10000; + tm.tm_min = (hour % 10000) / 100; + tm.tm_sec = (hour % 100); + tm.tm_isdst = -1; + + t = mktime(&tm); + + fd = open("/dev/alarm", O_RDWR); + ts.tv_sec = t; + ts.tv_nsec = 0; + ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); +} + +int date_main(int argc, char *argv[]) +{ + int c; + int res; + struct tm tm; + time_t t; + struct timeval tv; + struct timespec ts; + char strbuf[260]; + int fd; + + int useutc = 0; + + tzset(); + + do { + c = getopt(argc, argv, "us:"); + if (c == EOF) + break; + switch (c) { + case 'u': + useutc = 1; + break; + case 's': + settime(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(optind + 2 < argc) { + fprintf(stderr,"%s [-u] [date]\n", argv[0]); + return 1; + } + + int hasfmt = argc == optind + 1 && argv[optind][0] == '+'; + if(optind == argc || hasfmt) { + char buf[2000]; + time(&t); + if (useutc) { + gmtime_r(&t, &tm); + strftime(strbuf, sizeof(strbuf), + (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S GMT %Y"), + &tm); + } else { + localtime_r(&t, &tm); + strftime(strbuf, sizeof(strbuf), + (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S %Z %Y"), + &tm); + } + printf("%s\n", strbuf); + } + else if(optind + 1 == argc) { +#if 0 + struct tm *tmptr; + tmptr = getdate(argv[optind]); + if(tmptr == NULL) { + fprintf(stderr,"getdate_r failed\n"); + return 1; + } + tm = *tmptr; +#if 0 + if(getdate_r(argv[optind], &tm) < 0) { + fprintf(stderr,"getdate_r failed %s\n", strerror(errno)); + return 1; + } +#endif +#endif + //strptime(argv[optind], NULL, &tm); + //tv.tv_sec = mktime(&tm); + //tv.tv_usec = 0; + strtotimeval(argv[optind], &tv); + printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec); + fd = open("/dev/alarm", O_RDWR); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + //res = settimeofday(&tv, NULL); + if(res < 0) { + fprintf(stderr,"settimeofday failed %s\n", strerror(errno)); + return 1; + } + } + else { + fprintf(stderr,"%s [-s 20070325.123456] [-u] [date]\n", argv[0]); + return 1; + } + + return 0; +} diff --git a/toolbox/dd.c b/toolbox/dd.c new file mode 100644 index 0000000..c6af3ea --- /dev/null +++ b/toolbox/dd.c @@ -0,0 +1,1358 @@ +/* $NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" + +#define NO_CONV + +//#include "extern.h" +void block(void); +void block_close(void); +void dd_out(int); +void def(void); +void def_close(void); +void jcl(char **); +void pos_in(void); +void pos_out(void); +void summary(void); +void summaryx(int); +void terminate(int); +void unblock(void); +void unblock_close(void); +ssize_t bwrite(int, const void *, size_t); + +extern IO in, out; +extern STAT st; +extern void (*cfunc)(void); +extern uint64_t cpy_cnt; +extern uint64_t cbsz; +extern u_int ddflags; +extern u_int files_cnt; +extern int progress; +extern const u_char *ctab; +extern const u_char a2e_32V[], a2e_POSIX[]; +extern const u_char e2a_32V[], e2a_POSIX[]; +extern const u_char a2ibm_32V[], a2ibm_POSIX[]; +extern u_char casetab[]; + + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static void dd_close(void); +static void dd_in(void); +static void getfdtype(IO *); +static int redup_clean_fd(int); +static void setup(void); + + +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc)(void); /* conversion function */ +uint64_t cpy_cnt; /* # of blocks to copy */ +static off_t pending = 0; /* pending seek if sparse */ +u_int ddflags; /* conversion options */ +uint64_t cbsz; /* conversion block size */ +u_int files_cnt = 1; /* # of files to copy */ +int progress = 0; /* display sign of life */ +const u_char *ctab; /* conversion table */ +sigset_t infoset; /* a set blocking SIGINFO */ + +int +dd_main(int argc, char *argv[]) +{ + int ch; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + fprintf(stderr, "usage: dd [operand ...]\n"); + exit(1); + /* NOTREACHED */ + } + } + argc -= (optind - 1); + argv += (optind - 1); + + jcl(argv); + setup(); + +// (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + (void)sigemptyset(&infoset); +// (void)sigaddset(&infoset, SIGINFO); + + (void)atexit(summary); + + while (files_cnt--) + dd_in(); + + dd_close(); + exit(0); + /* NOTREACHED */ +} + +static void +setup(void) +{ + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd < 0) { + fprintf(stderr, "%s: cannot open for read: %s\n", + in.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + /* Ensure in.fd is outside the stdio descriptor range */ + in.fd = redup_clean_fd(in.fd); + } + + getfdtype(&in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) { + fprintf(stderr, + "files is not supported for non-tape devices\n"); + exit(1); + /* NOTREACHED */ + } + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd < 0) { + out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */); + out.flags |= NOREAD; + } + if (out.fd < 0) { + fprintf(stderr, "%s: cannot open for write: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + /* Ensure out.fd is outside the stdio descriptor range */ + out.fd = redup_clean_fd(out.fd); + } + + getfdtype(&out); + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) { + exit(1); + /* NOTREACHED */ + } + out.db = in.db; + } else if ((in.db = + malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || + (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) { + exit(1); + /* NOTREACHED */ + } + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file; ignore errors because it fails on some + * kinds of output files, tapes, for example. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK)) + (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE|C_UCASE)) { +#ifdef NO_CONV + /* Should not get here, but just in case... */ + fprintf(stderr, "case conv and -DNO_CONV\n"); + exit(1); + /* NOTREACHED */ +#else /* NO_CONV */ + u_int cnt; + + if (ddflags & C_ASCII || ddflags & C_EBCDIC) { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = toupper(ctab[cnt]); + } + } else { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = tolower(cnt); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = toupper(cnt); + } + } + + ctab = casetab; +#endif /* NO_CONV */ + } + + (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */ +} + +static void +getfdtype(IO *io) +{ +// struct mtget mt; + struct stat sb; + + if (fstat(io->fd, &sb)) { + fprintf(stderr, "%s: cannot fstat: %s\n", + io->name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + if (S_ISCHR(sb.st_mode)) + io->flags |= /*ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; */ ISCHR; + else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */ +} + +/* + * Move the parameter file descriptor to a descriptor that is outside the + * stdio descriptor range, if necessary. This is required to avoid + * accidentally outputting completion or error messages into the + * output file that were intended for the tty. + */ +static int +redup_clean_fd(int fd) +{ + int newfd; + + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && + fd != STDERR_FILENO) + /* File descriptor is ok, return immediately. */ + return fd; + + /* + * 3 is the first descriptor greater than STD*_FILENO. Any + * free descriptor valued 3 or above is acceptable... + */ + newfd = fcntl(fd, F_DUPFD, 3); + if (newfd < 0) { + fprintf(stderr, "dupfd IO: %s\n", strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + close(fd); + + return newfd; +} + +static void +dd_in(void) +{ + int flags; + int64_t n; + + for (flags = ddflags;;) { + if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) + return; + + /* + * Clear the buffer first if doing "sync" on input. + * If doing block operations use spaces. This will + * affect not only the C_NOERROR case, but also the + * last partial input block which should be padded + * with zero and not garbage. + */ + if (flags & C_SYNC) { + if (flags & (C_BLOCK|C_UNBLOCK)) + (void)memset(in.dbp, ' ', in.dbsz); + else + (void)memset(in.dbp, 0, in.dbsz); + } + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n < 0) { + + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + fprintf(stderr, "%s: read error: %s\n", + in.name, strerror(errno)); + if (!(flags & C_NOERROR)) { + exit(1); + /* NOTREACHED */ + } + summary(); + + /* + * If it's not a tape drive or a pipe, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (!(in.flags & (ISPIPE|ISTAPE)) && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + fprintf(stderr, "%s: seek error: %s\n", + in.name, strerror(errno)); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if (n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + +/* if (ddflags & C_SWAB) { + if ((n = in.dbrcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, n); + } +*/ + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Cleanup any remaining I/O and flush output. If necesssary, output file + * is truncated. + */ +static void +dd_close(void) +{ + + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) { + (void)memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + /* If there are pending sparse blocks, make sure + * to write out the final block un-sparse + */ + if ((out.dbcnt == 0) && pending) { + memset(out.db, 0, out.dbsz); + out.dbcnt = out.dbsz; + out.dbp = out.db + out.dbcnt; + pending -= out.dbsz; + } + if (out.dbcnt) + dd_out(1); + + /* + * Reporting nfs write error may be defered until next + * write(2) or close(2) system call. So, we need to do an + * extra check. If an output is stdout, the file structure + * may be shared among with other processes and close(2) just + * decreases the reference count. + */ + if (out.fd == STDOUT_FILENO && fsync(out.fd) == -1 && errno != EINVAL) { + fprintf(stderr, "fsync stdout: %s\n", strerror(errno)); + exit(1); + /* NOTREACHED */ + } + if (close(out.fd) == -1) { + fprintf(stderr, "close: %s\n", strerror(errno)); + exit(1); + /* NOTREACHED */ + } +} + +void +dd_out(int force) +{ + static int warned; + int64_t cnt, n, nw; + u_char *outp; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + + if (!force && ddflags & C_SPARSE) { + int sparse, i; + sparse = 1; /* Is buffer sparse? */ + for (i = 0; i < cnt; i++) + if (outp[i] != 0) { + sparse = 0; + break; + } + if (sparse) { + pending += cnt; + outp += cnt; + nw = 0; + break; + } + } + if (pending != 0) { + if (lseek(out.fd, pending, SEEK_CUR) == + -1) { + fprintf(stderr, + "%s: seek error creating " + "sparse file: %s\n", + out.name, strerror(errno)); + exit(1); + } + } + nw = bwrite(out.fd, outp, cnt); + if (nw <= 0) { + if (nw == 0) { + fprintf(stderr, "%s: end of device\n", + out.name); + exit(1); + /* NOTREACHED */ + } + if (errno != EINTR) { + fprintf(stderr, "%s: write error: %s\n", + out.name, strerror(errno)); + /* NOTREACHED */ + exit(1); + } + nw = 0; + } + if (pending) { + st.bytes += pending; + st.sparse += pending/out.dbsz; + st.out_full += pending/out.dbsz; + pending = 0; + } + outp += nw; + st.bytes += nw; + if (nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if (nw == cnt) + break; + if (out.flags & ISCHR && !warned) { + warned = 1; + fprintf(stderr, "%s: short write on character " + "device\n", out.name); + } + if (out.flags & ISTAPE) { + fprintf(stderr, + "%s: short write on tape device", + out.name); + exit(1); + /* NOTREACHED */ + } + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; + + if (progress) + (void)write(STDERR_FILENO, ".", 1); +} + +/* + * A protected against SIGINFO write + */ +ssize_t +bwrite(int fd, const void *buf, size_t len) +{ + sigset_t oset; + ssize_t rv; + int oerrno; + + (void)sigprocmask(SIG_BLOCK, &infoset, &oset); + rv = write(fd, buf, len); + oerrno = errno; + (void)sigprocmask(SIG_SETMASK, &oset, NULL); + errno = oerrno; + return (rv); +} + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in(void) +{ + int bcnt, cnt, nr, warned; + + /* If not a pipe or tape device, try to seek on it. */ + if (!(in.flags & (ISPIPE|ISTAPE))) { + if (lseek(in.fd, + (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) { + fprintf(stderr, "%s: seek error: %s", + in.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + return; + /* NOTREACHED */ + } + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + fprintf(stderr, "skip reached end of input\n"); + exit(1); + /* NOTREACHED */ + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + + fprintf(stderr, "%s: error occurred\n", + in.name); + warned = 1; + summary(); + } + continue; + } + fprintf(stderr, "%s: read error: %s", in.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } +} + +void +pos_out(void) +{ +// struct mtop t_op; + int cnt, n; + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (!(out.flags & ISTAPE)) { + if (lseek(out.fd, + (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) { + fprintf(stderr, "%s: seek error: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + return; + } + + /* If no read access, try using mtio. */ + if (out.flags & NOREAD) { +/* t_op.mt_op = MTFSR; + t_op.mt_count = out.offset; + + if (ioctl(out.fd, MTIOCTOP, &t_op) < 0)*/ + fprintf(stderr, "%s: cannot read", out.name); + exit(1); + /* NOTREACHED */ + return; + } + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n < 0) { + fprintf(stderr, "%s: cannot position by reading: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + /* + * If reach EOF, fill with NUL characters; first, back up over + * the EOF mark. Note, cnt has not yet been incremented, so + * the EOF read does not count as a seek'd block. + */ +/* t_op.mt_op = MTBSR; + t_op.mt_count = 1; + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) */ { + fprintf(stderr, "%s: cannot position\n", out.name); + exit(1); + /* NOTREACHED */ + } + + while (cnt++ < out.offset) + if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz) { + fprintf(stderr, "%s: cannot position " + "by writing: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + break; + } +} + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def(void) +{ + uint64_t cnt; + u_char *inp; + const u_char *t; + + if ((t = ctab) != NULL) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close(void) +{ + + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +#ifdef NO_CONV +/* Build a smaller version (i.e. for a miniroot) */ +/* These can not be called, but just in case... */ +static const char no_block[] = "unblock and -DNO_CONV?\n"; +void block(void) { fprintf(stderr, "%s", no_block + 2); exit(1); } +void block_close(void) { fprintf(stderr, "%s", no_block + 2); exit(1); } +void unblock(void) { fprintf(stderr, "%s", no_block); exit(1); } +void unblock_close(void) { fprintf(stderr, "%s", no_block); exit(1); } +#else /* NO_CONV */ + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block(void) +{ + static int intrunc; + int ch = 0; /* pacify gcc */ + uint64_t cnt, maxlen; + u_char *inp, *outp; + const u_char *t; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; + cnt && *inp++ != '\n'; --cnt); + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if ((t = ctab) != NULL) + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt); + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close(void) +{ + + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, + ctab ? ctab[' '] : ' ', cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock(void) +{ + uint64_t cnt; + u_char *inp; + const u_char *t; + + /* Translation and case conversion. */ + if ((t = ctab) != NULL) + for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--) + *inp = t[*inp]; + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t); + if (t >= inp) { + cnt = t - inp + 1; + (void)memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + if (out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close(void) +{ + uint64_t cnt; + u_char *t; + + if (in.dbcnt) { + warnx("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t); + if (t >= in.db) { + cnt = t - in.db + 1; + (void)memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} + +#endif /* NO_CONV */ + +#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000) + +void +summary(void) +{ + char buf[100]; + int64_t mS; + struct timeval tv; + + if (progress) + (void)write(STDERR_FILENO, "\n", 1); + + (void)gettimeofday(&tv, NULL); + mS = tv2mS(tv) - tv2mS(st.start); + if (mS == 0) + mS = 1; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%llu+%llu records in\n%llu+%llu records out\n", + (unsigned long long)st.in_full, (unsigned long long)st.in_part, + (unsigned long long)st.out_full, (unsigned long long)st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n", + (unsigned long long)st.swab, + (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n", + (unsigned long long)st.trunc, + (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.sparse) { + (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n", + (unsigned long long)st.sparse, + (st.sparse == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n", + (unsigned long long) st.bytes, + (long) (mS / 1000), + (int) (mS % 1000), + (unsigned long long) (st.bytes * 1000LL / mS)); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +void +terminate(int notused) +{ + + exit(0); + /* NOTREACHED */ +} + +static int c_arg(const void *, const void *); +#ifndef NO_CONV +static int c_conv(const void *, const void *); +#endif +static void f_bs(char *); +static void f_cbs(char *); +static void f_conv(char *); +static void f_count(char *); +static void f_files(char *); +static void f_ibs(char *); +static void f_if(char *); +static void f_obs(char *); +static void f_of(char *); +static void f_seek(char *); +static void f_skip(char *); +static void f_progress(char *); + +static const struct arg { + const char *name; + void (*f)(char *); + u_int set, noset; +} args[] = { + /* the array needs to be sorted by the first column so + bsearch() can be used to find commands quickly */ + { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, + { "cbs", f_cbs, C_CBS, C_CBS }, + { "conv", f_conv, 0, 0 }, + { "count", f_count, C_COUNT, C_COUNT }, + { "files", f_files, C_FILES, C_FILES }, + { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, + { "if", f_if, C_IF, C_IF }, + { "obs", f_obs, C_OBS, C_BS|C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "progress", f_progress, 0, 0 }, + { "seek", f_seek, C_SEEK, C_SEEK }, + { "skip", f_skip, C_SKIP, C_SKIP }, +}; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(char **argv) +{ + struct arg *ap, tmp; + char *oper, *arg; + + in.dbsz = out.dbsz = 512; + + while ((oper = *++argv) != NULL) { + if ((arg = strchr(oper, '=')) == NULL) { + fprintf(stderr, "unknown operand %s\n", oper); + exit(1); + /* NOTREACHED */ + } + *arg++ = '\0'; + if (!*arg) { + fprintf(stderr, "no value specified for %s\n", oper); + exit(1); + /* NOTREACHED */ + } + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) { + fprintf(stderr, "unknown operand %s\n", tmp.name); + exit(1); + /* NOTREACHED */ + } + if (ddflags & ap->noset) { + fprintf(stderr, + "%s: illegal argument combination or already set\n", + tmp.name); + exit(1); + /* NOTREACHED */ + } + ddflags |= ap->set; + ap->f(arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | + C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) { + ddflags &= ~C_BS; + ddflags |= C_IBS|C_OBS; + } + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) + fprintf(stderr, "bs supersedes ibs and obs\n"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK|C_UNBLOCK)) { + if (!(ddflags & C_CBS)) { + fprintf(stderr, "record operations require cbs\n"); + exit(1); + /* NOTREACHED */ + } + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII|C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else { + fprintf(stderr, + "cbs meaningless if not doing record operations\n"); + exit(1); + /* NOTREACHED */ + } + } else + cfunc = def; + + /* Read, write and seek calls take off_t as arguments. + * + * The following check is not done because an off_t is a quad + * for current NetBSD implementations. + * + * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz) + * errx(1, "seek offsets cannot be larger than %d", INT_MAX); + */ +} + +static int +c_arg(const void *a, const void *b) +{ + + return (strcmp(((const struct arg *)a)->name, + ((const struct arg *)b)->name)); +} + +static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max) +{ + long long result; + + if (sscanf(arg, "%lld", &result) == 0) + result = def; + return result; +} + +static void +f_bs(char *arg) +{ + + in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX); +} + +static void +f_cbs(char *arg) +{ + + cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX); +} + +static void +f_count(char *arg) +{ + + cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX); + if (!cpy_cnt) + terminate(0); +} + +static void +f_files(char *arg) +{ + + files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX); + if (!files_cnt) + terminate(0); +} + +static void +f_ibs(char *arg) +{ + + if (!(ddflags & C_BS)) + in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX); +} + +static void +f_if(char *arg) +{ + + in.name = arg; +} + +static void +f_obs(char *arg) +{ + + if (!(ddflags & C_BS)) + out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX); +} + +static void +f_of(char *arg) +{ + + out.name = arg; +} + +static void +f_seek(char *arg) +{ + + out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX); +} + +static void +f_skip(char *arg) +{ + + in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX); +} + +static void +f_progress(char *arg) +{ + + if (*arg != '0') + progress = 1; +} + +#ifdef NO_CONV +/* Build a small version (i.e. for a ramdisk root) */ +static void +f_conv(char *arg) +{ + + fprintf(stderr, "conv option disabled\n"); + exit(1); + /* NOTREACHED */ +} +#else /* NO_CONV */ + +static const struct conv { + const char *name; + u_int set, noset; + const u_char *ctab; +} clist[] = { + { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, + { "block", C_BLOCK, C_UNBLOCK, NULL }, + { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, + { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, + { "lcase", C_LCASE, C_UCASE, NULL }, + { "noerror", C_NOERROR, 0, NULL }, + { "notrunc", C_NOTRUNC, 0, NULL }, + { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, + { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, + { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, + { "osync", C_OSYNC, C_BS, NULL }, + { "sparse", C_SPARSE, 0, NULL }, + { "swab", C_SWAB, 0, NULL }, + { "sync", C_SYNC, 0, NULL }, + { "ucase", C_UCASE, C_LCASE, NULL }, + { "unblock", C_UNBLOCK, C_BLOCK, NULL }, + /* If you add items to this table, be sure to add the + * conversions to the C_BS check in the jcl routine above. + */ +}; + +static void +f_conv(char *arg) +{ + struct conv *cp, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + if (!(cp = (struct conv *)bsearch(&tmp, clist, + sizeof(clist)/sizeof(struct conv), sizeof(struct conv), + c_conv))) { + errx(EXIT_FAILURE, "unknown conversion %s", tmp.name); + /* NOTREACHED */ + } + if (ddflags & cp->noset) { + errx(EXIT_FAILURE, "%s: illegal conversion combination", tmp.name); + /* NOTREACHED */ + } + ddflags |= cp->set; + if (cp->ctab) + ctab = cp->ctab; + } +} + +static int +c_conv(const void *a, const void *b) +{ + + return (strcmp(((const struct conv *)a)->name, + ((const struct conv *)b)->name)); +} + +#endif /* NO_CONV */ + + diff --git a/toolbox/dd.h b/toolbox/dd.h new file mode 100644 index 0000000..794a464 --- /dev/null +++ b/toolbox/dd.h @@ -0,0 +1,91 @@ +/* $NetBSD: dd.h,v 1.12 2004/01/17 20:48:57 dbj Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dd.h 8.3 (Berkeley) 4/2/94 + */ + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + uint64_t dbcnt; /* current buffer byte count */ + int64_t dbrcnt; /* last read byte count */ + uint64_t dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe (not truncatable) */ +#define ISTAPE 0x04 /* tape (not seekable) */ +#define NOREAD 0x08 /* not readable */ + u_int flags; + + const char *name; /* name */ + int fd; /* file descriptor */ + uint64_t offset; /* # of blocks to skip */ +} IO; + +typedef struct { + uint64_t in_full; /* # of full input blocks */ + uint64_t in_part; /* # of partial input blocks */ + uint64_t out_full; /* # of full output blocks */ + uint64_t out_part; /* # of partial output blocks */ + uint64_t trunc; /* # of truncated records */ + uint64_t swab; /* # of odd-length swab blocks */ + uint64_t sparse; /* # of sparse output blocks */ + uint64_t bytes; /* # of bytes written */ + struct timeval start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_SEEK 0x04000 +#define C_SKIP 0x08000 +#define C_SWAB 0x10000 +#define C_SYNC 0x20000 +#define C_UCASE 0x40000 +#define C_UNBLOCK 0x80000 +#define C_OSYNC 0x100000 +#define C_SPARSE 0x200000 diff --git a/toolbox/df.c b/toolbox/df.c new file mode 100644 index 0000000..90476bd --- /dev/null +++ b/toolbox/df.c @@ -0,0 +1,63 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/statfs.h> + +static int ok = EXIT_SUCCESS; + +static void df(char *s, int always) { + struct statfs st; + + if (statfs(s, &st) < 0) { + fprintf(stderr, "%s: %s\n", s, strerror(errno)); + ok = EXIT_FAILURE; + } else { + if (st.f_blocks == 0 && !always) + return; + + printf("%s: %lldK total, %lldK used, %lldK available (block size %d)\n", + s, + ((long long)st.f_blocks * (long long)st.f_bsize) / 1024, + ((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize) / 1024, + ((long long)st.f_bfree * (long long)st.f_bsize) / 1024, + (int) st.f_bsize); + } +} + +int df_main(int argc, char *argv[]) { + if (argc == 1) { + char s[2000]; + FILE *f = fopen("/proc/mounts", "r"); + + while (fgets(s, 2000, f)) { + char *c, *e = s; + + for (c = s; *c; c++) { + if (*c == ' ') { + e = c + 1; + break; + } + } + + for (c = e; *c; c++) { + if (*c == ' ') { + *c = '\0'; + break; + } + } + + df(e, 0); + } + + fclose(f); + } else { + int i; + + for (i = 1; i < argc; i++) { + df(argv[i], 1); + } + } + + exit(ok); +} diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c new file mode 100644 index 0000000..e57f607 --- /dev/null +++ b/toolbox/dmesg.c @@ -0,0 +1,43 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <sys/klog.h> +#include <string.h> + +#define KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */ +#define KLOG_BUF_LEN (1 << KLOG_BUF_SHIFT) + +int dmesg_main(int argc, char **argv) +{ + char buffer[KLOG_BUF_LEN + 1]; + char *p = buffer; + ssize_t ret; + int n, op; + + if((argc == 2) && (!strcmp(argv[1],"-c"))) { + op = KLOG_READ_CLEAR; + } else { + op = KLOG_READ_ALL; + } + + n = klogctl(op, buffer, KLOG_BUF_LEN); + if (n < 0) { + perror("klogctl"); + return EXIT_FAILURE; + } + buffer[n] = '\0'; + + while((ret = write(STDOUT_FILENO, p, n))) { + if (ret == -1) { + if (errno == EINTR) + continue; + perror("write"); + return EXIT_FAILURE; + } + p += ret; + n -= ret; + } + + return 0; +} diff --git a/toolbox/exists.c b/toolbox/exists.c new file mode 100644 index 0000000..e348668 --- /dev/null +++ b/toolbox/exists.c @@ -0,0 +1,16 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +int exists_main(int argc, char *argv[]) +{ + struct stat s; + + if(argc < 2) return 1; + + if(stat(argv[1], &s)) { + return 1; + } else { + return 0; + } +} diff --git a/toolbox/getevent.c b/toolbox/getevent.c new file mode 100644 index 0000000..14372cb --- /dev/null +++ b/toolbox/getevent.c @@ -0,0 +1,427 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <sys/limits.h> +#include <sys/poll.h> +#include <linux/input.h> // this does not compile +#include <errno.h> + +static struct pollfd *ufds; +static char **device_names; +static int nfds; + +enum { + PRINT_DEVICE_ERRORS = 1U << 0, + PRINT_DEVICE = 1U << 1, + PRINT_DEVICE_NAME = 1U << 2, + PRINT_DEVICE_INFO = 1U << 3, + PRINT_VERSION = 1U << 4, + PRINT_POSSIBLE_EVENTS = 1U << 5, +}; + +static int print_possible_events(int fd) +{ + uint8_t *bits = NULL; + ssize_t bits_size = 0; + int i, j, k; + int res, res2; + + printf(" events:\n"); + for(i = 0; i <= EV_MAX; i++) { + int count = 0; + while(1) { + res = ioctl(fd, EVIOCGBIT(i, bits_size), bits); + if(res < bits_size) + break; + bits_size = res + 16; + bits = realloc(bits, bits_size * 2); + if(bits == NULL) { + fprintf(stderr, "failed to allocate buffer of size %d\n", bits_size); + return 1; + } + } + switch(i) { + case EV_KEY: + res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size); + break; + case EV_LED: + res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size); + break; + case EV_SND: + res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size); + break; + case EV_SW: + res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size); + break; + default: + res2 = 0; + } + for(j = 0; j < res; j++) { + for(k = 0; k < 8; k++) + if(bits[j] & 1 << k) { + char down; + if(j < res2 && (bits[j + bits_size] & 1 << k)) + down = '*'; + else + down = ' '; + if(count == 0) + printf(" type %04x:", i); + else if((count & 0x7) == 0 || i == EV_ABS) + printf("\n "); + printf(" %04x%c", j * 8 + k, down); + if(i == EV_ABS) { + struct input_absinfo abs; + if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) { + printf(" value %d, min %d, max %d, fuzz %d flat %d", abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat); + } + } + count++; + } + } + if(count) + printf("\n"); + } + free(bits); + return 0; +} + +static int open_device(const char *device, int print_flags) +{ + int version; + int fd; + struct pollfd *new_ufds; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if(fd < 0) { + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "could not open %s, %s\n", device, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno)); + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno)); + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); + idstr[0] = '\0'; + } + + new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); + if(new_ufds == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + ufds = new_ufds; + new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1)); + if(new_device_names == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + device_names = new_device_names; + + if(print_flags & PRINT_DEVICE) + printf("add device %d: %s\n", nfds, device); + if(print_flags & PRINT_DEVICE_INFO) + printf(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + id.bustype, id.vendor, id.product, id.version); + if(print_flags & PRINT_DEVICE_NAME) + printf(" name: \"%s\"\n", name); + if(print_flags & PRINT_DEVICE_INFO) + printf(" location: \"%s\"\n" + " id: \"%s\"\n", location, idstr); + if(print_flags & PRINT_VERSION) + printf(" version: %d.%d.%d\n", + version >> 16, (version >> 8) & 0xff, version & 0xff); + + if(print_flags & PRINT_POSSIBLE_EVENTS) { + print_possible_events(fd); + } + + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; +} + +int close_device(const char *device, int print_flags) +{ + int i; + for(i = 1; i < nfds; i++) { + if(strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + if(print_flags & PRINT_DEVICE) + printf("remove device %d: %s\n", i, device); + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "remote device: %s not found\n", device); + return -1; +} + +static int read_notify(const char *dirname, int nfd, int print_flags) +{ + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname, print_flags); + } + else { + close_device(devname, print_flags); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +static int scan_dir(const char *dirname, int print_flags) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname, print_flags); + } + closedir(dir); + return 0; +} + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-q] [-c count] [-r] [device]\n", argv[0]); +} + +int getevent_main(int argc, char *argv[]) +{ + int c; + int i; + int res; + int pollres; + int get_time = 0; + int print_device = 0; + char *newline = "\n"; + uint16_t get_switch = 0; + struct input_event event; + int version; + int print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME; + int print_flags_set = 0; + int dont_block = -1; + int event_count = 0; + int sync_rate = 0; + int64_t last_sync_time = 0; + const char *device = NULL; + const char *device_path = "/dev/input"; + + opterr = 0; + do { + c = getopt(argc, argv, "tns:Sv::qc:rh"); + if (c == EOF) + break; + switch (c) { + case 't': + get_time = 1; + break; + case 'n': + newline = ""; + break; + case 's': + get_switch = strtoul(optarg, NULL, 0); + if(dont_block == -1) + dont_block = 1; + break; + case 'S': + get_switch = ~0; + if(dont_block == -1) + dont_block = 1; + break; + case 'v': + if(optarg) + print_flags = strtoul(optarg, NULL, 0); + else + print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION; + print_flags_set = 1; + break; + case 'q': + print_flags = 0; + print_flags_set = 1; + break; + case 'c': + event_count = atoi(optarg); + dont_block = 0; + break; + case 'r': + sync_rate = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + case 'h': + usage(argc, argv); + exit(1); + } + } while (1); + if(dont_block == -1) + dont_block = 0; + + if (optind + 1 == argc) { + device = argv[optind]; + optind++; + } + if (optind != argc) { + usage(argc, argv); + exit(1); + } + nfds = 1; + ufds = calloc(1, sizeof(ufds[0])); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + if(device) { + if(!print_flags_set) + print_flags = PRINT_DEVICE_ERRORS; + res = open_device(device, print_flags); + if(res < 0) { + return 1; + } + } + else { + print_device = 1; + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno)); + return 1; + } + res = scan_dir(device_path, print_flags); + if(res < 0) { + fprintf(stderr, "scan dir failed for %s\n", device_path); + return 1; + } + } + + if(get_switch) { + for(i = 1; i < nfds; i++) { + uint16_t sw; + res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw); + if(res < 0) { + fprintf(stderr, "could not get switch state, %s\n", strerror(errno)); + return 1; + } + sw &= get_switch; + printf("%04x%s", sw, newline); + } + } + + if(dont_block) + return 0; + + while(1) { + pollres = poll(ufds, nfds, -1); + //printf("poll %d, returned %d\n", nfds, pollres); + if(ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd, print_flags); + } + for(i = 1; i < nfds; i++) { + if(ufds[i].revents) { + if(ufds[i].revents & POLLIN) { + res = read(ufds[i].fd, &event, sizeof(event)); + if(res < (int)sizeof(event)) { + fprintf(stderr, "could not get event\n"); + return 1; + } + if(get_time) { + printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec); + } + if(print_device) + printf("%s: ", device_names[i]); + printf("%04x %04x %08x", event.type, event.code, event.value); + if(sync_rate && event.type == 0 && event.code == 0) { + int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec; + if(last_sync_time) + printf(" rate %lld", 1000000LL / (now - last_sync_time)); + last_sync_time = now; + } + printf("%s", newline); + if(event_count && --event_count == 0) + return 0; + } + } + } + } + + return 0; +} diff --git a/toolbox/getprop.c b/toolbox/getprop.c new file mode 100644 index 0000000..fc80a4d --- /dev/null +++ b/toolbox/getprop.c @@ -0,0 +1,34 @@ +#include <stdio.h> + +#include <cutils/properties.h> + +#include <sys/system_properties.h> + +static void proplist(const char *key, const char *name, + void *user __attribute__((unused))) +{ + printf("[%s]: [%s]\n", key, name); +} + +int __system_property_wait(prop_info *pi); + +int getprop_main(int argc, char *argv[]) +{ + int n = 0; + + if (argc == 1) { + (void)property_list(proplist, NULL); + } else { + char value[PROPERTY_VALUE_MAX]; + char *default_value; + if(argc > 2) { + default_value = argv[2]; + } else { + default_value = ""; + } + + property_get(argv[1], value, default_value); + printf("%s\n", value); + } + return 0; +} diff --git a/toolbox/hd.c b/toolbox/hd.c new file mode 100644 index 0000000..1f7d179 --- /dev/null +++ b/toolbox/hd.c @@ -0,0 +1,95 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +int hd_main(int argc, char *argv[]) +{ + int c; + int fd; + unsigned char buf[4096]; + int res; + int read_len; + int rv = 0; + int i; + int filepos = 0; + int sum; + int lsum; + + int base = -1; + int count = 0; + int repeat = 0; + + do { + c = getopt(argc, argv, "b:c:r:"); + if (c == EOF) + break; + switch (c) { + case 'b': + base = strtol(optarg, NULL, 0); + break; + case 'c': + count = strtol(optarg, NULL, 0); + break; + case 'r': + repeat = strtol(optarg, NULL, 0); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (optind + 1 != argc) { + fprintf(stderr, "Usage: %s [-b base] [-c count] [-r delay] file\n", argv[0]); + exit(1); + } + + fd = open(argv[optind], O_RDONLY); + if(fd < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + + do { + if(base >= 0) { + lseek(fd, base, SEEK_SET); + filepos = base; + } + sum = 0; + lsum = 0; + while(1) { + read_len = sizeof(buf); + if(count > 0 && base + count - filepos < read_len) + read_len = base + count - filepos; + res = read(fd, &buf, read_len); + for(i = 0; i < res; i++) { + if((i & 15) == 0) { + printf("%08x: ", filepos + i); + } + lsum += buf[i]; + sum += buf[i]; + printf("%02x ", buf[i]); + if(((i & 15) == 15) || (i == res - 1)) { + printf("s %x\n", lsum); + lsum = 0; + } + } + if(res <= 0) { + printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno)); + return 1; + } + filepos += res; + if(filepos == base + count) + break; + } + printf("sum %x\n", sum); + if(repeat) + sleep(repeat); + } while(repeat); + return 0; +} diff --git a/toolbox/id.c b/toolbox/id.c new file mode 100644 index 0000000..bb03cad --- /dev/null +++ b/toolbox/id.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> + +static void print_uid(uid_t uid) +{ + struct passwd *pw = getpwuid(uid); + + if (pw) { + printf("%d(%s)", uid, pw->pw_name); + } else { + printf("%d",uid); + } +} + +static void print_gid(gid_t gid) +{ + struct group *gr = getgrgid(gid); + if (gr) { + printf("%d(%s)", gid, gr->gr_name); + } else { + printf("%d",gid); + } +} + +int id_main(int argc, char **argv) +{ + gid_t list[64]; + int n, max; + + max = getgroups(64, list); + if (max < 0) max = 0; + + printf("uid="); + print_uid(getuid()); + printf(" gid="); + print_gid(getgid()); + if (max) { + printf(" groups="); + print_gid(list[0]); + for(n = 1; n < max; n++) { + printf(","); + print_gid(list[n]); + } + } + printf("\n"); + return 0; +} diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c new file mode 100644 index 0000000..e83cd8b --- /dev/null +++ b/toolbox/ifconfig.c @@ -0,0 +1,139 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/sockios.h> +#include <arpa/inet.h> + +static void die(const char *s) +{ + fprintf(stderr,"error: %s (%s)\n", s, strerror(errno)); + exit(-1); +} + +static void setflags(int s, struct ifreq *ifr, int set, int clr) +{ + if(ioctl(s, SIOCGIFFLAGS, ifr) < 0) die("SIOCGIFFLAGS"); + ifr->ifr_flags = (ifr->ifr_flags & (~clr)) | set; + if(ioctl(s, SIOCSIFFLAGS, ifr) < 0) die("SIOCSIFFLAGS"); +} + +static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) +{ + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(addr); +} + +static void setnetmask(int s, struct ifreq *ifr, const char *addr) +{ + init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr); + if(ioctl(s, SIOCSIFNETMASK, ifr) < 0) die("SIOCSIFNETMASK"); +} + +static void setaddr(int s, struct ifreq *ifr, const char *addr) +{ + init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr); + if(ioctl(s, SIOCSIFADDR, ifr) < 0) die("SIOCSIFADDR"); +} + +int ifconfig_main(int argc, char *argv[]) +{ + struct ifreq ifr; + int s; + unsigned int addr, mask, flags; + char astring[20]; + char mstring[20]; + char *updown, *brdcst, *loopbk, *ppp, *running, *multi; + + argc--; + argv++; + + if(argc == 0) return 0; + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, argv[0], IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + argc--, argv++; + + if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + die("cannot open control socket\n"); + } + + if (argc == 0) { + if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { + perror(ifr.ifr_name); + return -1; + } else + addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + + if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0) { + perror(ifr.ifr_name); + return -1; + } else + mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { + perror(ifr.ifr_name); + return -1; + } else + flags = ifr.ifr_flags; + + sprintf(astring, "%d.%d.%d.%d", + addr & 0xff, + ((addr >> 8) & 0xff), + ((addr >> 16) & 0xff), + ((addr >> 24) & 0xff)); + sprintf(mstring, "%d.%d.%d.%d", + mask & 0xff, + ((mask >> 8) & 0xff), + ((mask >> 16) & 0xff), + ((mask >> 24) & 0xff)); + printf("%s: ip %s mask %s flags [", ifr.ifr_name, + astring, + mstring + ); + + updown = (flags & IFF_UP) ? "up" : "down"; + brdcst = (flags & IFF_BROADCAST) ? " broadcast" : ""; + loopbk = (flags & IFF_LOOPBACK) ? " loopback" : ""; + ppp = (flags & IFF_POINTOPOINT) ? " point-to-point" : ""; + running = (flags & IFF_RUNNING) ? " running" : ""; + multi = (flags & IFF_MULTICAST) ? " multicast" : ""; + printf("%s%s%s%s%s%s]\n", updown, brdcst, loopbk, ppp, running, multi); + + + +/* char *updown, *brdcst, *loopbk, *ppp, *running, *multi; */ + + return 0; + } + + while(argc > 0){ + if(!strcmp(argv[0], "up")) { + setflags(s, &ifr, IFF_UP, 0); + } else if(!strcmp(argv[0], "down")) { + setflags(s, &ifr, 0, IFF_UP); + } else if(!strcmp(argv[0], "netmask")) { + argc--, argv++; + if (0 == argc) { + errno = EINVAL; + die("expecting an IP address for parameter \"netmask\""); + } + setnetmask(s, &ifr, argv[0]); + } else if(isdigit(argv[0][0])){ + setaddr(s, &ifr, argv[0]); + } + argc--, argv++; + } + + return 0; +} diff --git a/toolbox/iftop.c b/toolbox/iftop.c new file mode 100644 index 0000000..800c0f0 --- /dev/null +++ b/toolbox/iftop.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#define PROC_NET_DEV "/proc/net/dev" + +#define MAX_IF 8 /* max interfaces we can handle */ + +#ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +#endif + +#define _STR(s) #s +#define STR(s) _STR(s) + +struct if_stats { + char name[IFNAMSIZ]; + + unsigned int mtu; + + unsigned int rx_bytes; + unsigned int rx_packets; + unsigned int rx_errors; + unsigned int rx_dropped; + + unsigned int tx_bytes; + unsigned int tx_packets; + unsigned int tx_errors; + unsigned int tx_dropped; +}; + +static int get_mtu(const char *if_name) +{ + struct ifreq ifr; + int s, ret; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_addr.sa_family = AF_INET; + strcpy(ifr.ifr_name, if_name); + + ret = ioctl(s, SIOCGIFMTU, &ifr); + if (ret < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + ret = close(s); + if (ret < 0) { + perror("close"); + exit(EXIT_FAILURE); + } + + return ifr.ifr_mtu; +} + +static int get_interfaces(struct if_stats *ifs) +{ + char buf[PAGE_SIZE]; + char *p; + int ret, nr, fd; + + fd = open(PROC_NET_DEV, O_RDONLY); + if (fd < 0) { + perror("open"); + exit(EXIT_FAILURE); + } + + ret = read(fd, buf, sizeof(buf) - 1); + if (ret < 0) { + perror("read"); + exit(EXIT_FAILURE); + } else if (!ret) { + fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n"); + exit(EXIT_FAILURE); + } + buf[ret] = '\0'; + + /* skip down to the third line */ + p = strchr(buf, '\n'); + if (!p) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + p = strchr(p + 1, '\n'); + if (!p) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + p += 1; + + /* + * Key: + * if: (Rx) bytes packets errs drop fifo frame compressed multicast \ + * (Tx) bytes packets errs drop fifo colls carrier compressed + */ + for (nr = 0; nr < MAX_IF; nr++) { + char *c; + + ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name); + if (ret != 1) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + + /* + * This works around a bug in the proc file where large interface names + * or Rx byte counts eat the delimiter, breaking sscanf. + */ + c = strchr(ifs->name, ':'); + if (c) + *c = '\0'; + + p = strchr(p, ':') + 1; + + ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u " + "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets, + &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes, + &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped); + if (ret != 8) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + + ifs->mtu = get_mtu(ifs->name); + + p = strchr(p, '\n') + 1; + if (*p == '\0') { + nr++; + break; + } + + ifs++; + } + + ret = close(fd); + if (ret) { + perror("close"); + exit(EXIT_FAILURE); + } + + return nr; +} + +static void print_header(void) +{ + printf(" Rx Tx\n"); + printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n", + "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes", + "packets", "errs", "drpd"); +} + +static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr) +{ + int i = 0; + + while (nr--) { + if (old->rx_packets || old->tx_packets) { + printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n", + new->name, new->mtu, + new->rx_bytes - old->rx_bytes, + new->rx_packets - old->rx_packets, + new->rx_errors - old->rx_errors, + new->rx_dropped - old->rx_dropped, + new->tx_bytes - old->tx_bytes, + new->tx_packets - old->tx_packets, + new->tx_errors - old->tx_errors, + new->tx_dropped - old->tx_dropped); + i++; + } + old++; + new++; + } + + return i; +} + +static void usage(const char *cmd) +{ + fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd); +} + +int iftop_main(int argc, char *argv[]) +{ + struct if_stats ifs[2][MAX_IF]; + int count = 0, header_interval = 22, delay = 1, i; + unsigned int toggle = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -d requires an argument.\n"); + exit(EXIT_FAILURE); + } + delay = atoi(argv[i++]); + if (!delay) + delay = 1; + continue; + } + if (!strcmp(argv[i], "-r")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -r requires an argument.\n"); + exit(EXIT_FAILURE); + } + header_interval = atoi(argv[i++]); + if (header_interval < MAX_IF) + header_interval = MAX_IF; + continue; + } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + usage(argv[0]); + exit(EXIT_FAILURE); + } + + get_interfaces(ifs[!toggle]); + if (header_interval) + print_header(); + while (1) { + int nr; + + sleep(delay); + nr = get_interfaces(ifs[toggle]); + if (header_interval && count + nr > header_interval) { + print_header(); + count = 0; + } + count += print_interfaces(ifs[!toggle], ifs[toggle], nr); + toggle = !toggle; + } + + return 0; +} diff --git a/toolbox/insmod.c b/toolbox/insmod.c new file mode 100644 index 0000000..44b9847 --- /dev/null +++ b/toolbox/insmod.c @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +extern int init_module(void *, unsigned long, const char *); + +static void *read_file(const char *filename, ssize_t *_size) +{ + int ret, fd; + struct stat sb; + ssize_t size; + void *buffer = NULL; + + /* open the file */ + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + /* find out how big it is */ + if (fstat(fd, &sb) < 0) + goto bail; + size = sb.st_size; + + /* allocate memory for it to be read into */ + buffer = malloc(size); + if (!buffer) + goto bail; + + /* slurp it into our buffer */ + ret = read(fd, buffer, size); + if (ret != size) + goto bail; + + /* let the caller know how big it is */ + *_size = size; + +bail: + close(fd); + return buffer; +} + +#define min(x,y) ((x) < (y) ? (x) : (y)) +int insmod_main(int argc, char **argv) +{ + void *file; + ssize_t size = 0; + char opts[1024]; + int ret; + + /* make sure we've got an argument */ + if (argc < 2) { + fprintf(stderr, "usage: insmod <module.o>\n"); + return -1; + } + + /* read the file into memory */ + file = read_file(argv[1], &size); + if (!file) { + fprintf(stderr, "insmod: can't open '%s'\n", argv[1]); + return -1; + } + + opts[0] = '\0'; + if (argc > 2) { + int i, len; + char *end = opts + sizeof(opts) - 1; + char *ptr = opts; + + for (i = 2; (i < argc) && (ptr < end); i++) { + len = min(strlen(argv[i]), end - ptr); + memcpy(ptr, argv[i], len); + ptr += len; + *ptr++ = ' '; + *ptr++ = '\0'; + } + *(ptr - 1) = '\0'; + } + + /* pass it to the kernel */ + ret = init_module(file, size, opts); + if (ret != 0) { + fprintf(stderr, + "insmod: init_module '%s' failed (%s)\n", + argv[1], strerror(errno)); + } + + /* free the file buffer */ + free(file); + + return ret; +} + diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c new file mode 100644 index 0000000..e28f2a4 --- /dev/null +++ b/toolbox/ioctl.c @@ -0,0 +1,125 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include <errno.h> +#include <pthread.h> + +int ioctl_main(int argc, char *argv[]) +{ + int c; + int fd; + int res; + + int read_only = 0; + int length = -1; + int arg_size = 4; + int direct_arg = 0; + uint32_t ioctl_nr; + void *ioctl_args; + uint8_t *ioctl_argp; + uint8_t *ioctl_argp_save; + int rem; + + do { + c = getopt(argc, argv, "rdl:a:h"); + if (c == EOF) + break; + switch (c) { + case 'r': + read_only = 1; + break; + case 'd': + direct_arg = 1; + break; + case 'l': + length = strtol(optarg, NULL, 0); + break; + case 'a': + arg_size = strtol(optarg, NULL, 0); + break; + case 'h': + fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n" + " -l <lenght> Length of io buffer\n" + " -a <argsize> Size of each argument (1-8)\n" + " -r Open device in read only mode\n" + " -d Direct argument (no iobuffer)\n" + " -h Print help\n", argv[0]); + return -1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if(optind + 2 > argc) { + fprintf(stderr, "%s: too few arguments\n", argv[0]); + exit(1); + } + + fd = open(argv[optind], O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", argv[optind]); + return 1; + } + optind++; + + ioctl_nr = strtol(argv[optind], NULL, 0); + optind++; + + if(direct_arg) { + arg_size = 4; + length = 4; + } + + if(length < 0) { + length = (argc - optind) * arg_size; + } + if(length) { + ioctl_args = calloc(1, length); + + ioctl_argp_save = ioctl_argp = ioctl_args; + rem = length; + while(optind < argc) { + uint64_t tmp = strtoull(argv[optind], NULL, 0); + if(rem < arg_size) { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + exit(1); + } + memcpy(ioctl_argp, &tmp, arg_size); + ioctl_argp += arg_size; + rem -= arg_size; + optind++; + } + } + printf("sending ioctl 0x%x", ioctl_nr); + rem = length; + while(rem--) { + printf(" 0x%02x", *ioctl_argp_save++); + } + printf("\n"); + + if(direct_arg) + res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args); + else if(length) + res = ioctl(fd, ioctl_nr, ioctl_args); + else + res = ioctl(fd, ioctl_nr, 0); + if (res < 0) { + fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res); + return 1; + } + if(length) { + printf("return buf:"); + ioctl_argp = ioctl_args; + rem = length; + while(rem--) { + printf(" %02x", *ioctl_argp++); + } + printf("\n"); + } + return 0; +} diff --git a/toolbox/kill.c b/toolbox/kill.c new file mode 100644 index 0000000..4d0e479 --- /dev/null +++ b/toolbox/kill.c @@ -0,0 +1,35 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <sys/types.h> +#include <signal.h> + +int kill_main(int argc, char **argv) +{ + int sig = SIGTERM; + int result = 0; + + argc--; + argv++; + + if(argc >= 2 && argv[0][0] == '-'){ + sig = atoi(argv[0] + 1); + argc--; + argv++; + } + + while(argc > 0){ + int pid = atoi(argv[0]); + int err = kill(pid, sig); + if (err < 0) { + result = err; + fprintf(stderr, "could not kill pid %d: %s\n", pid, strerror(errno)); + } + + argc--; + argv++; + } + + return result; +} diff --git a/toolbox/ln.c b/toolbox/ln.c new file mode 100644 index 0000000..dcd5e3a --- /dev/null +++ b/toolbox/ln.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +static int usage() +{ + fprintf(stderr,"ln [-s] <target> <name>\n"); + return -1; +} + +int ln_main(int argc, char *argv[]) +{ + int symbolic = 0; + int ret; + if(argc < 2) return usage(); + + if(!strcmp(argv[1],"-s")) { + symbolic = 1; + argc--; + argv++; + } + + if(argc < 3) return usage(); + + if(symbolic) { + ret = symlink(argv[1], argv[2]); + } else { + ret = link(argv[1], argv[2]); + } + if(ret < 0) + fprintf(stderr, "link failed %s\n", strerror(errno)); + return ret; +} diff --git a/toolbox/log.c b/toolbox/log.c new file mode 100644 index 0000000..f30e6a7 --- /dev/null +++ b/toolbox/log.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <cutils/logd.h> +#include <ctype.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <stdlib.h> +#include <cutils/sockets.h> +#include <unistd.h> + +/* + * Note: also accepts 0-9 priorities + * returns ANDROID_LOG_UNKNOWN if the character is unrecognized + */ +static android_LogPriority filterCharToPri (char c) +{ + android_LogPriority pri; + + c = tolower(c); + + if (c >= '0' && c <= '9') { + if (c >= ('0'+ANDROID_LOG_SILENT)) { + pri = ANDROID_LOG_VERBOSE; + } else { + pri = (android_LogPriority)(c - '0'); + } + } else if (c == 'v') { + pri = ANDROID_LOG_VERBOSE; + } else if (c == 'd') { + pri = ANDROID_LOG_DEBUG; + } else if (c == 'i') { + pri = ANDROID_LOG_INFO; + } else if (c == 'w') { + pri = ANDROID_LOG_WARN; + } else if (c == 'e') { + pri = ANDROID_LOG_ERROR; + } else if (c == 'f') { + pri = ANDROID_LOG_FATAL; + } else if (c == 's') { + pri = ANDROID_LOG_SILENT; + } else if (c == '*') { + pri = ANDROID_LOG_DEFAULT; + } else { + pri = ANDROID_LOG_UNKNOWN; + } + + return pri; +} + +static int usage(const char *s) +{ + fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s); + + fprintf(stderr, "\tpriorityChar should be one of:\n" + "\t\tv,d,i,w,e\n"); + exit(-1); +} + + +int log_main(int argc, char *argv[]) +{ + android_LogPriority priority; + const char *tag = "log"; + char buffer[4096]; + int i; + + priority = ANDROID_LOG_INFO; + + for (;;) { + int ret; + + ret = getopt(argc, argv, "t:p:h"); + + if (ret < 0) { + break; + } + + switch(ret) { + case 't': + tag = optarg; + break; + + case 'p': + priority = filterCharToPri(optarg[0]); + if (priority == ANDROID_LOG_UNKNOWN) { + usage(argv[0]); + } + break; + + case 'h': + usage(argv[0]); + break; + } + } + + if (optind == argc) { + usage(argv[0]); + } + + buffer[0] = '\0'; + + for (i = optind ; i < argc ; i++) { + strncat(buffer, argv[i], sizeof(buffer)-1); + strncat(buffer, " ", sizeof(buffer)-1); + } + + if(buffer[0] == 0) { + usage(argv[0]); + } + + __android_log_print(priority, tag, "%s", buffer); + + return 0; +} + diff --git a/toolbox/ls.c b/toolbox/ls.c new file mode 100644 index 0000000..f609df2 --- /dev/null +++ b/toolbox/ls.c @@ -0,0 +1,285 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> + +#include <sys/stat.h> +#include <unistd.h> +#include <time.h> + +#include <pwd.h> +#include <grp.h> + +#include <linux/kdev_t.h> + +// bits for flags argument +#define LIST_LONG (1 << 0) +#define LIST_ALL (1 << 1) +#define LIST_RECURSIVE (1 << 2) + +// fwd +static int listpath(const char *name, int flags); + +static char mode2kind(unsigned mode) +{ + switch(mode & S_IFMT){ + case S_IFSOCK: return 's'; + case S_IFLNK: return 'l'; + case S_IFREG: return '-'; + case S_IFDIR: return 'd'; + case S_IFBLK: return 'b'; + case S_IFCHR: return 'c'; + case S_IFIFO: return 'p'; + default: return '?'; + } +} + +static void mode2str(unsigned mode, char *out) +{ + *out++ = mode2kind(mode); + + *out++ = (mode & 0400) ? 'r' : '-'; + *out++ = (mode & 0200) ? 'w' : '-'; + if(mode & 04000) { + *out++ = (mode & 0100) ? 's' : 'S'; + } else { + *out++ = (mode & 0100) ? 'x' : '-'; + } + *out++ = (mode & 040) ? 'r' : '-'; + *out++ = (mode & 020) ? 'w' : '-'; + if(mode & 02000) { + *out++ = (mode & 010) ? 's' : 'S'; + } else { + *out++ = (mode & 010) ? 'x' : '-'; + } + *out++ = (mode & 04) ? 'r' : '-'; + *out++ = (mode & 02) ? 'w' : '-'; + if(mode & 01000) { + *out++ = (mode & 01) ? 't' : 'T'; + } else { + *out++ = (mode & 01) ? 'x' : '-'; + } + *out = 0; +} + +static void user2str(unsigned uid, char *out) +{ + struct passwd *pw = getpwuid(uid); + if(pw) { + strcpy(out, pw->pw_name); + } else { + sprintf(out, "%d", uid); + } +} + +static void group2str(unsigned gid, char *out) +{ + struct group *gr = getgrgid(gid); + if(gr) { + strcpy(out, gr->gr_name); + } else { + sprintf(out, "%d", gid); + } +} + +static int listfile(const char *path, int flags) +{ + struct stat s; + char date[32]; + char mode[16]; + char user[16]; + char group[16]; + const char *name; + + /* name is anything after the final '/', or the whole path if none*/ + name = strrchr(path, '/'); + if(name == 0) { + name = path; + } else { + name++; + } + + if(lstat(path, &s) < 0) { + return -1; + } + + mode2str(s.st_mode, mode); + user2str(s.st_uid, user); + group2str(s.st_gid, group); + + strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime)); + date[31] = 0; + +// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) + + switch(s.st_mode & S_IFMT) { + case S_IFBLK: + case S_IFCHR: + printf("%s %-8s %-8s %3d, %3d %s %s\n", + mode, user, group, + (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev), + date, name); + break; + case S_IFREG: + printf("%s %-8s %-8s %8d %s %s\n", + mode, user, group, (int) s.st_size, date, name); + break; + case S_IFLNK: { + char linkto[256]; + int len; + + len = readlink(path, linkto, 256); + if(len < 0) return -1; + + if(len > 255) { + linkto[252] = '.'; + linkto[253] = '.'; + linkto[254] = '.'; + linkto[255] = 0; + } else { + linkto[len] = 0; + } + + printf("%s %-8s %-8s %s %s -> %s\n", + mode, user, group, date, name, linkto); + break; + } + default: + printf("%s %-8s %-8s %s %s\n", + mode, user, group, date, name); + + } + return 0; +} + +static int listdir(const char *name, int flags) +{ + char tmp[4096]; + DIR *d; + struct dirent *de; + + d = opendir(name); + if(d == 0) { + fprintf(stderr, "opendir failed, %s\n", strerror(errno)); + return -1; + } + + while((de = readdir(d)) != 0){ + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; + if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; + if ((flags & LIST_LONG) != 0) { + sprintf(tmp, "%s/%s", name, de->d_name); + listfile(tmp, flags); + } else { + printf("%s\n", de->d_name); + } + } + + if (flags & LIST_RECURSIVE) { + rewinddir(d); + + while ((de = readdir(d)) != 0) { + struct stat s; + int err; + + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) + continue; + + if (!strcmp(name, "/")) sprintf(tmp, "/%s", de->d_name); + else sprintf(tmp, "%s/%s", name, de->d_name); + + /* + * If the name ends in a '/', use stat() so we treat it like a + * directory even if it's a symlink. + */ + if (tmp[strlen(tmp)-1] == '/') + err = stat(tmp, &s); + else + err = lstat(tmp, &s); + + if (err < 0) { + perror(tmp); + closedir(d); + return -1; + } + + if (S_ISDIR(s.st_mode)) { + printf("\n%s:\n", tmp); + listdir(tmp, flags); + } + } + } + + closedir(d); + return 0; +} + +static int listpath(const char *name, int flags) +{ + struct stat s; + int err; + + /* + * If the name ends in a '/', use stat() so we treat it like a + * directory even if it's a symlink. + */ + if (name[strlen(name)-1] == '/') + err = stat(name, &s); + else + err = lstat(name, &s); + + if (err < 0) { + perror(name); + return -1; + } + + if (S_ISDIR(s.st_mode)) { + if (flags & LIST_RECURSIVE) + printf("\n%s:\n", name); + return listdir(name, flags); + } else { + if ((flags & LIST_LONG) != 0) { + /* yeah this calls stat() again*/ + return listfile(name, flags); + } else { + printf("%s\n", name); + return 0; + } + } +} + +int ls_main(int argc, char **argv) +{ + int flags = 0; + int listed = 0; + + if(argc > 1) { + int i; + int err = 0; + + for (i = 1; i < argc; i++) { + if(!strcmp(argv[i], "-l")) { + flags |= LIST_LONG; + } else if (!strcmp(argv[i], "-a")) { + flags |= LIST_ALL; + } else if (!strcmp(argv[i], "-R")) { + flags |= LIST_RECURSIVE; + } else { + listed++; + if(listpath(argv[i], flags) != 0) { + err = EXIT_FAILURE; + } + } + } + + if (listed > 0) return err; + } + + // list working directory if no files or directories were specified + return listpath(".", flags); +} diff --git a/toolbox/lsmod.c b/toolbox/lsmod.c new file mode 100644 index 0000000..8b55ee6 --- /dev/null +++ b/toolbox/lsmod.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern int cat_main(int argc, char **argv); + +int lsmod_main(int argc, char **argv) +{ + char *cat_argv[] = { "cat", "/proc/modules", NULL }; + return cat_main(2, cat_argv); +} + diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c new file mode 100644 index 0000000..121adab --- /dev/null +++ b/toolbox/mkdir.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +static int usage() +{ + fprintf(stderr,"mkdir <target>\n"); + return -1; +} + +int mkdir_main(int argc, char *argv[]) +{ + int symbolic = 0; + int ret; + if(argc < 2) return usage(); + + while(argc > 1) { + argc--; + argv++; + ret = mkdir(argv[0], 0777); + if(ret < 0) { + fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno)); + return ret; + } + } + + return 0; +} diff --git a/toolbox/mkdosfs.c b/toolbox/mkdosfs.c new file mode 100644 index 0000000..744aad1 --- /dev/null +++ b/toolbox/mkdosfs.c @@ -0,0 +1,848 @@ +/* $NetBSD: newfs_msdos.c,v 1.18.2.1 2005/05/01 18:44:02 tron Exp $ */ + +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __USE_FILE_OFFSET64 + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <sys/param.h> +#ifdef __FreeBSD__ +#include <sys/diskslice.h> +#endif +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#ifdef __NetBSD__ +#include <disktab.h> +#include <util.h> +#endif + +#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ +#define BPN 4 /* bits per nibble */ +#define NPB 2 /* nibbles per byte */ + +#define DOSMAGIC 0xaa55 /* DOS magic number */ +#define MINBPS 128 /* minimum bytes per sector */ +#define MAXSPC 128 /* maximum sectors per cluster */ +#define MAXNFT 16 /* maximum number of FATs */ +#define DEFBLK 4096 /* default block size */ +#define DEFBLK16 2048 /* default block size FAT16 */ +#define DEFRDE 512 /* default root directory entries */ +#define RESFTE 2 /* reserved FAT entries */ +#define MINCLS12 1 /* minimum FAT12 clusters */ +#define MINCLS16 0x1000 /* minimum FAT16 clusters */ +#define MINCLS32 2 /* minimum FAT32 clusters */ +#define MAXCLS12 0xfed /* maximum FAT12 clusters */ +#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */ +#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */ + +#define mincls(fat) ((fat) == 12 ? MINCLS12 : \ + (fat) == 16 ? MINCLS16 : \ + MINCLS32) + +#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ + (fat) == 16 ? MAXCLS16 : \ + MAXCLS32) + +#define mk1(p, x) \ + (p) = (u_int8_t)(x) + +#define mk2(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010) + +#define mk4(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010), \ + (p)[2] = (u_int8_t)((x) >> 020), \ + (p)[3] = (u_int8_t)((x) >> 030) + +#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) +#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) +#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) +#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) + +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +static int powerof2(int x) { + int i; + for (i = 0; i < 32; i++) { + if (x & 1) { + x >>= 1; + // if x is zero, then original x was a power of two + return (x == 0); + } + x >>= 1; + } + + return 0; +} + +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif + +#pragma pack(push, 1) +struct bs { + u_int8_t jmp[3]; /* bootstrap entry point */ + u_int8_t oem[8]; /* OEM name and version */ +}; +#define BS_SIZE 11 + +struct bsbpb { + u_int8_t bps[2]; /* bytes per sector */ + u_int8_t spc; /* sectors per cluster */ + u_int8_t res[2]; /* reserved sectors */ + u_int8_t nft; /* number of FATs */ + u_int8_t rde[2]; /* root directory entries */ + u_int8_t sec[2]; /* total sectors */ + u_int8_t mid; /* media descriptor */ + u_int8_t spf[2]; /* sectors per FAT */ + u_int8_t spt[2]; /* sectors per track */ + u_int8_t hds[2]; /* drive heads */ + u_int8_t hid[4]; /* hidden sectors */ + u_int8_t bsec[4]; /* big total sectors */ +}; +#define BSBPB_SIZE 25 + +struct bsxbpb { + u_int8_t bspf[4]; /* big sectors per FAT */ + u_int8_t xflg[2]; /* FAT control flags */ + u_int8_t vers[2]; /* file system version */ + u_int8_t rdcl[4]; /* root directory start cluster */ + u_int8_t infs[2]; /* file system info sector */ + u_int8_t bkbs[2]; /* backup boot sector */ + u_int8_t rsvd[12]; /* reserved */ +}; +#define BSXBPB_SIZE 28 + +struct bsx { + u_int8_t drv; /* drive number */ + u_int8_t rsvd; /* reserved */ + u_int8_t sig; /* extended boot signature */ + u_int8_t volid[4]; /* volume ID number */ + u_int8_t label[11]; /* volume label */ + u_int8_t type[8]; /* file system type */ +}; +#define BSX_SIZE 26 + +struct de { + u_int8_t namext[11]; /* name and extension */ + u_int8_t attr; /* attributes */ + u_int8_t rsvd[10]; /* reserved */ + u_int8_t time[2]; /* creation time */ + u_int8_t date[2]; /* creation date */ + u_int8_t clus[2]; /* starting cluster */ + u_int8_t size[4]; /* size */ +#define DE_SIZE 32 +}; +#pragma pack(pop) + +struct bpb { + u_int bps; /* bytes per sector */ + u_int spc; /* sectors per cluster */ + u_int res; /* reserved sectors */ + u_int nft; /* number of FATs */ + u_int rde; /* root directory entries */ + u_int sec; /* total sectors */ + u_int mid; /* media descriptor */ + u_int spf; /* sectors per FAT */ + u_int spt; /* sectors per track */ + u_int hds; /* drive heads */ + u_int hid; /* hidden sectors */ + u_int bsec; /* big total sectors */ + u_int bspf; /* big sectors per FAT */ + u_int rdcl; /* root directory start cluster */ + u_int infs; /* file system info sector */ + u_int bkbs; /* backup boot sector */ +}; + +static u_int8_t bootcode[] = { + 0xfa, /* cli */ + 0x31, 0xc0, /* xor ax,ax */ + 0x8e, 0xd0, /* mov ss,ax */ + 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ + 0xfb, /* sti */ + 0x8e, 0xd8, /* mov ds,ax */ + 0xe8, 0x00, 0x00, /* call $ + 3 */ + 0x5e, /* pop si */ + 0x83, 0xc6, 0x19, /* add si,+19h */ + 0xbb, 0x07, 0x00, /* mov bx,0007h */ + 0xfc, /* cld */ + 0xac, /* lodsb */ + 0x84, 0xc0, /* test al,al */ + 0x74, 0x06, /* jz $ + 8 */ + 0xb4, 0x0e, /* mov ah,0eh */ + 0xcd, 0x10, /* int 10h */ + 0xeb, 0xf5, /* jmp $ - 9 */ + 0x30, 0xe4, /* xor ah,ah */ + 0xcd, 0x16, /* int 16h */ + 0xcd, 0x19, /* int 19h */ + 0x0d, 0x0a, + 'N', 'o', 'n', '-', 's', 'y', 's', 't', + 'e', 'm', ' ', 'd', 'i', 's', 'k', + 0x0d, 0x0a, + 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', + 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', + ' ', 'r', 'e', 'b', 'o', 'o', 't', + 0x0d, 0x0a, + 0 +}; + +static void print_bpb(struct bpb *); +static u_int ckgeom(const char *, u_int, const char *); +static u_int argtou(const char *, u_int, u_int, const char *); +static int oklabel(const char *); +static void mklabel(u_int8_t *, const char *); +static void setstr(u_int8_t *, const char *, size_t); +static void usage(char* progname); + +/* + * Construct a FAT12, FAT16, or FAT32 file system. + */ +int +mkdosfs_main(int argc, char *argv[]) +{ + static char opts[] = "NB:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; + static const char *opt_B, *opt_L, *opt_O; + static u_int opt_F, opt_I, opt_S, opt_a, opt_b, opt_c, opt_e; + static u_int opt_h, opt_i, opt_k, opt_m, opt_n, opt_o, opt_r; + static u_int opt_s, opt_u; + static int opt_N; + static int Iflag, mflag, oflag; + char buf[MAXPATHLEN]; + struct stat sb; + struct timeval tv; + struct bpb bpb; + struct tm *tm; + struct bs *bs; + struct bsbpb *bsbpb; + struct bsxbpb *bsxbpb; + struct bsx *bsx; + struct de *de; + u_int8_t *img; + const char *fname, *dtype, *bname; + ssize_t n; + time_t now; + u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; + int ch, fd, fd1; + char* progname = argv[0]; + + while ((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case 'N': + opt_N = 1; + break; + case 'B': + opt_B = optarg; + break; + case 'F': + if (strcmp(optarg, "12") && + strcmp(optarg, "16") && + strcmp(optarg, "32")) + fprintf(stderr, "%s: bad FAT type\n", optarg); + opt_F = atoi(optarg); + break; + case 'I': + opt_I = argto4(optarg, 0, "volume ID"); + Iflag = 1; + break; + case 'L': + if (!oklabel(optarg)) + fprintf(stderr, "%s: bad volume label\n", optarg); + opt_L = optarg; + break; + case 'O': + if (strlen(optarg) > 8) + fprintf(stderr, "%s: bad OEM string\n", optarg); + opt_O = optarg; + break; + case 'S': + opt_S = argto2(optarg, 1, "bytes/sector"); + break; + case 'a': + opt_a = argto4(optarg, 1, "sectors/FAT"); + break; + case 'b': + opt_b = argtox(optarg, 1, "block size"); + opt_c = 0; + break; + case 'c': + opt_c = argto1(optarg, 1, "sectors/cluster"); + opt_b = 0; + break; + case 'e': + opt_e = argto2(optarg, 1, "directory entries"); + break; + case 'h': + opt_h = argto2(optarg, 1, "drive heads"); + break; + case 'i': + opt_i = argto2(optarg, 1, "info sector"); + break; + case 'k': + opt_k = argto2(optarg, 1, "backup sector"); + break; + case 'm': + opt_m = argto1(optarg, 0, "media descriptor"); + mflag = 1; + break; + case 'n': + opt_n = argto1(optarg, 1, "number of FATs"); + break; + case 'o': + opt_o = argto4(optarg, 0, "hidden sectors"); + oflag = 1; + break; + case 'r': + opt_r = argto2(optarg, 1, "reserved sectors"); + break; + case 's': + opt_s = argto4(optarg, 1, "file system size"); + break; + case 'u': + opt_u = argto2(optarg, 1, "sectors/track"); + break; + default: + usage(progname); + } + argc -= optind; + argv += optind; + if (argc < 1 || argc > 2) + usage(progname); + fname = *argv++; + if (!strchr(fname, '/')) { + snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname); + if (!(fname = strdup(buf))) + fprintf(stderr, NULL); + } + dtype = *argv; + if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1 || + fstat(fd, &sb)) + fprintf(stderr, "%s\n", fname); + memset(&bpb, 0, sizeof(bpb)); + + if (opt_h) + bpb.hds = opt_h; + if (opt_u) + bpb.spt = opt_u; + if (opt_S) + bpb.bps = opt_S; + if (opt_s) + bpb.bsec = opt_s; + if (oflag) + bpb.hid = opt_o; + + bpb.bps = 512; // 512 bytes/sector + bpb.spc = 8; // 4K clusters + + + fprintf(stderr, "opening %s\n", fname); + if ((fd1 = open(fname, O_RDONLY)) == -1) { + fprintf(stderr, "failed to open %s\n", fname); + exit(1); + } + + lseek64(fd1, 0, SEEK_SET); + loff_t length = lseek64(fd1, 0, SEEK_END); + if (length > 0) { + bpb.bsec = length / bpb.bps; + bpb.spt = bpb.bsec; + // use FAT32 for 2 gig or greater + if (length >= 2 *1024 *1024 *1024) { + fat = 32; + } else { + fat = 16; + } + } + close(fd1); + fd1 = -1; + + if (!powerof2(bpb.bps)) + fprintf(stderr, "bytes/sector (%u) is not a power of 2\n", bpb.bps); + if (bpb.bps < MINBPS) + fprintf(stderr, "bytes/sector (%u) is too small; minimum is %u\n", + bpb.bps, MINBPS); + + if (!(fat = opt_F)) { + if (!opt_e && (opt_i || opt_k)) + fat = 32; + } + + if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k))) + fprintf(stderr, "-%c is not a legal FAT%s option\n", + fat == 32 ? 'e' : opt_i ? 'i' : 'k', + fat == 32 ? "32" : "12/16"); + if (fat == 32) + bpb.rde = 0; + if (opt_b) { + if (!powerof2(opt_b)) + fprintf(stderr, "block size (%u) is not a power of 2\n", opt_b); + if (opt_b < bpb.bps) + fprintf(stderr, "block size (%u) is too small; minimum is %u\n", + opt_b, bpb.bps); + if (opt_b > bpb.bps * MAXSPC) + fprintf(stderr, "block size (%u) is too large; maximum is %u\n", + opt_b, bpb.bps * MAXSPC); + bpb.spc = opt_b / bpb.bps; + } + if (opt_c) { + if (!powerof2(opt_c)) + fprintf(stderr, "sectors/cluster (%u) is not a power of 2\n", opt_c); + bpb.spc = opt_c; + } + if (opt_r) + bpb.res = opt_r; + if (opt_n) { + if (opt_n > MAXNFT) + fprintf(stderr, "number of FATs (%u) is too large; maximum is %u\n", + opt_n, MAXNFT); + bpb.nft = opt_n; + } + if (opt_e) + bpb.rde = opt_e; + if (mflag) { + if (opt_m < 0xf0) + fprintf(stderr, "illegal media descriptor (%#x)\n", opt_m); + bpb.mid = opt_m; + } + if (opt_a) + bpb.bspf = opt_a; + if (opt_i) + bpb.infs = opt_i; + if (opt_k) + bpb.bkbs = opt_k; + bss = 1; + bname = NULL; + fd1 = -1; + if (opt_B) { + bname = opt_B; + if (!strchr(bname, '/')) { + snprintf(buf, sizeof(buf), "/boot/%s", bname); + if (!(bname = strdup(buf))) + fprintf(stderr, NULL); + } + if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) + fprintf(stderr, "%s", bname); + if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps || + sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) + fprintf(stderr, "%s: inappropriate file type or format\n", bname); + bss = sb.st_size / bpb.bps; + } + if (!bpb.nft) + bpb.nft = 2; + if (!fat) { + if (bpb.bsec < (bpb.res ? bpb.res : bss) + + howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) * + ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) * + bpb.nft + + howmany(bpb.rde ? bpb.rde : DEFRDE, + bpb.bps / DE_SIZE) + + (bpb.spc ? MINCLS16 : MAXCLS12 + 1) * + (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps))) + fat = 12; + else if (bpb.rde || bpb.bsec < + (bpb.res ? bpb.res : bss) + + howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft + + howmany(DEFRDE, bpb.bps / DE_SIZE) + + (MAXCLS16 + 1) * + (bpb.spc ? bpb.spc : howmany(8192, bpb.bps))) + fat = 16; + else + fat = 32; + } + x = bss; + if (fat == 32) { + if (!bpb.infs) { + if (x == MAXU16 || x == bpb.bkbs) + fprintf(stderr, "no room for info sector\n"); + bpb.infs = x; + } + if (bpb.infs != MAXU16 && x <= bpb.infs) + x = bpb.infs + 1; + if (!bpb.bkbs) { + if (x == MAXU16) + fprintf(stderr, "no room for backup sector\n"); + bpb.bkbs = x; + } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) + fprintf(stderr, "backup sector would overwrite info sector\n"); + if (bpb.bkbs != MAXU16 && x <= bpb.bkbs) + x = bpb.bkbs + 1; + } + if (!bpb.res) + bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x; + else if (bpb.res < x) + fprintf(stderr, "too few reserved sectors (need %d have %d)\n", x, bpb.res); + if (fat != 32 && !bpb.rde) + bpb.rde = DEFRDE; + rds = howmany(bpb.rde, bpb.bps / DE_SIZE); + if (!bpb.spc) + for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps); + bpb.spc < MAXSPC && + bpb.res + + howmany((RESFTE + maxcls(fat)) * (fat / BPN), + bpb.bps * NPB) * bpb.nft + + rds + + (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec; + bpb.spc <<= 1); + if (fat != 32 && bpb.bspf > MAXU16) + fprintf(stderr, "too many sectors/FAT for FAT12/16\n"); + x1 = bpb.res + rds; + x = bpb.bspf ? bpb.bspf : 1; + if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) + fprintf(stderr, "meta data exceeds file system size\n"); + x1 += x * bpb.nft; + x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB / + (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft); + x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), + bpb.bps * NPB); + if (!bpb.bspf) { + bpb.bspf = x2; + x1 += (bpb.bspf - 1) * bpb.nft; + } + cls = (bpb.bsec - x1) / bpb.spc; + x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE; + if (cls > x) + cls = x; + if (bpb.bspf < x2) + fprintf(stderr, "warning: sectors/FAT limits file system to %u clusters\n", + cls); + if (cls < mincls(fat)) + fprintf(stderr, "%u clusters too few clusters for FAT%u, need %u\n", cls, fat, + mincls(fat)); + if (cls > maxcls(fat)) { + cls = maxcls(fat); + bpb.bsec = x1 + (cls + 1) * bpb.spc - 1; + fprintf(stderr, "warning: FAT type limits file system to %u sectors\n", + bpb.bsec); + } + printf("%s: %u sector%s in %u FAT%u cluster%s " + "(%u bytes/cluster)\n", fname, cls * bpb.spc, + cls * bpb.spc == 1 ? "" : "s", cls, fat, + cls == 1 ? "" : "s", bpb.bps * bpb.spc); + if (!bpb.mid) + bpb.mid = !bpb.hid ? 0xf0 : 0xf8; + if (fat == 32) + bpb.rdcl = RESFTE; + if (bpb.hid + bpb.bsec <= MAXU16) { + bpb.sec = bpb.bsec; + bpb.bsec = 0; + } + if (fat != 32) { + bpb.spf = bpb.bspf; + bpb.bspf = 0; + } + ch = 0; + if (fat == 12) + ch = 1; /* 001 Primary DOS with 12 bit FAT */ + else if (fat == 16) { + if (bpb.bsec == 0) + ch = 4; /* 004 Primary DOS with 16 bit FAT <32M */ + else + ch = 6; /* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */ + /* + * XXX: what about: + * 014 DOS (16-bit FAT) - LBA + * ? + */ + } else if (fat == 32) { + ch = 11; /* 011 Primary DOS with 32 bit FAT */ + /* + * XXX: what about: + * 012 Primary DOS with 32 bit FAT - LBA + * ? + */ + } + if (ch != 0) + printf("MBR type: %d\n", ch); + print_bpb(&bpb); + if (!opt_N) { + gettimeofday(&tv, NULL); + now = tv.tv_sec; + tm = localtime(&now); + if (!(img = malloc(bpb.bps))) + fprintf(stderr, NULL); + dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft; + + for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) { + x = lsn; + if (opt_B && + fat == 32 && bpb.bkbs != MAXU16 && + bss <= bpb.bkbs && x >= bpb.bkbs) { + x -= bpb.bkbs; + if (!x && lseek64(fd1, 0, SEEK_SET)) + fprintf(stderr, "lseek64 failed for %s\n", bname); + } + if (opt_B && x < bss) { + if ((n = read(fd1, img, bpb.bps)) == -1) + fprintf(stderr, "%s\n", bname); + if (n != bpb.bps) + fprintf(stderr, "%s: can't read sector %u\n", bname, x); + } else + memset(img, 0, bpb.bps); + if (!lsn || + (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) { + x1 = BS_SIZE; + bsbpb = (struct bsbpb *)(img + x1); + mk2(bsbpb->bps, bpb.bps); + mk1(bsbpb->spc, bpb.spc); + mk2(bsbpb->res, bpb.res); + mk1(bsbpb->nft, bpb.nft); + mk2(bsbpb->rde, bpb.rde); + mk2(bsbpb->sec, bpb.sec); + mk1(bsbpb->mid, bpb.mid); + mk2(bsbpb->spf, bpb.spf); + mk2(bsbpb->spt, bpb.spt); + mk2(bsbpb->hds, bpb.hds); + mk4(bsbpb->hid, bpb.hid); + mk4(bsbpb->bsec, bpb.bsec); + x1 += BSBPB_SIZE; + if (fat == 32) { + bsxbpb = (struct bsxbpb *)(img + x1); + mk4(bsxbpb->bspf, bpb.bspf); + mk2(bsxbpb->xflg, 0); + mk2(bsxbpb->vers, 0); + mk4(bsxbpb->rdcl, bpb.rdcl); + mk2(bsxbpb->infs, bpb.infs); + mk2(bsxbpb->bkbs, bpb.bkbs); + x1 += BSXBPB_SIZE; + } + bsx = (struct bsx *)(img + x1); + mk1(bsx->sig, 0x29); + if (Iflag) + x = opt_I; + else + x = (((u_int)(1 + tm->tm_mon) << 8 | + (u_int)tm->tm_mday) + + ((u_int)tm->tm_sec << 8 | + (u_int)(tv.tv_usec / 10))) << 16 | + ((u_int)(1900 + tm->tm_year) + + ((u_int)tm->tm_hour << 8 | + (u_int)tm->tm_min)); + mk4(bsx->volid, x); + mklabel(bsx->label, opt_L ? opt_L : "NO_NAME"); + snprintf(buf, sizeof(buf), "FAT%u", fat); + setstr(bsx->type, buf, sizeof(bsx->type)); + if (!opt_B) { + x1 += BSX_SIZE; + bs = (struct bs *)img; + mk1(bs->jmp[0], 0xeb); + mk1(bs->jmp[1], x1 - 2); + mk1(bs->jmp[2], 0x90); + setstr(bs->oem, opt_O ? opt_O : "NetBSD", + sizeof(bs->oem)); + memcpy(img + x1, bootcode, sizeof(bootcode)); + mk2(img + bpb.bps - 2, DOSMAGIC); + } + } else if (fat == 32 && bpb.infs != MAXU16 && + (lsn == bpb.infs || + (bpb.bkbs != MAXU16 && + lsn == bpb.bkbs + bpb.infs))) { + mk4(img, 0x41615252); + mk4(img + bpb.bps - 28, 0x61417272); + mk4(img + bpb.bps - 24, 0xffffffff); + mk4(img + bpb.bps - 20, bpb.rdcl); + mk2(img + bpb.bps - 2, DOSMAGIC); + } else if (lsn >= bpb.res && lsn < dir && + !((lsn - bpb.res) % + (bpb.spf ? bpb.spf : bpb.bspf))) { + mk1(img[0], bpb.mid); + for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) + mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); + } else if (lsn == dir && opt_L) { + de = (struct de *)img; + mklabel(de->namext, opt_L); + mk1(de->attr, 050); + x = (u_int)tm->tm_hour << 11 | + (u_int)tm->tm_min << 5 | + (u_int)tm->tm_sec >> 1; + mk2(de->time, x); + x = (u_int)(tm->tm_year - 80) << 9 | + (u_int)(tm->tm_mon + 1) << 5 | + (u_int)tm->tm_mday; + mk2(de->date, x); + } + if ((n = write(fd, img, bpb.bps)) == -1) + fprintf(stderr, "%s\n", fname); + if (n != bpb.bps) + fprintf(stderr, "%s: can't write sector %u\n", fname, lsn); + } + } + return 0; +} + +/* + * Print out BPB values. + */ +static void +print_bpb(struct bpb *bpb) +{ + printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res, + bpb->nft); + if (bpb->rde) + printf(" rde=%u", bpb->rde); + if (bpb->sec) + printf(" sec=%u", bpb->sec); + printf(" mid=%#x", bpb->mid); + if (bpb->spf) + printf(" spf=%u", bpb->spf); + printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid); + if (bpb->bsec) + printf(" bsec=%u", bpb->bsec); + if (!bpb->spf) { + printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl); + printf(" infs="); + printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs); + printf(" bkbs="); + printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs); + } + printf("\n"); +} + +/* + * Check a disk geometry value. + */ +static u_int +ckgeom(const char *fname, u_int val, const char *msg) +{ + if (!val) + fprintf(stderr, "%s: no default %s\n", fname, msg); + if (val > MAXU16) + fprintf(stderr, "%s: illegal %s\n", fname, msg); + return val; +} + +/* + * Convert and check a numeric option argument. + */ +static u_int +argtou(const char *arg, u_int lo, u_int hi, const char *msg) +{ + char *s; + u_long x; + + errno = 0; + x = strtoul(arg, &s, 0); + if (errno || !*arg || *s || x < lo || x > hi) + fprintf(stderr, "%s: bad %s\n", arg, msg); + return x; +} + +/* + * Check a volume label. + */ +static int +oklabel(const char *src) +{ + int c, i; + + for (i = 0; i <= 11; i++) { + c = (u_char)*src++; + if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) + break; + } + return i && !c; +} + +/* + * Make a volume label. + */ +static void +mklabel(u_int8_t *dest, const char *src) +{ + int c, i; + + for (i = 0; i < 11; i++) { + c = *src ? toupper((unsigned char)*src++) : ' '; + *dest++ = !i && c == '\xe5' ? 5 : c; + } +} + +/* + * Copy string, padding with spaces. + */ +static void +setstr(u_int8_t *dest, const char *src, size_t len) +{ + while (len--) + *dest++ = *src ? *src++ : ' '; +} + +/* + * Print usage message. + */ +static void +usage(char* progname) +{ + fprintf(stderr, + "usage: %s [ -options ] special [disktype]\n", progname); + fprintf(stderr, "where the options are:\n"); + fprintf(stderr, "\t-N don't create file system: " + "just print out parameters\n"); + fprintf(stderr, "\t-B get bootstrap from file\n"); + fprintf(stderr, "\t-F FAT type (12, 16, or 32)\n"); + fprintf(stderr, "\t-I volume ID\n"); + fprintf(stderr, "\t-L volume label\n"); + fprintf(stderr, "\t-O OEM string\n"); + fprintf(stderr, "\t-S bytes/sector\n"); + fprintf(stderr, "\t-a sectors/FAT\n"); + fprintf(stderr, "\t-b block size\n"); + fprintf(stderr, "\t-c sectors/cluster\n"); + fprintf(stderr, "\t-e root directory entries\n"); + fprintf(stderr, "\t-h drive heads\n"); + fprintf(stderr, "\t-i file system info sector\n"); + fprintf(stderr, "\t-k backup boot sector\n"); + fprintf(stderr, "\t-m media descriptor\n"); + fprintf(stderr, "\t-n number of FATs\n"); + fprintf(stderr, "\t-o hidden sectors\n"); + fprintf(stderr, "\t-r reserved sectors\n"); + fprintf(stderr, "\t-s file system size (sectors)\n"); + fprintf(stderr, "\t-u sectors/track\n"); + exit(1); +} + + diff --git a/toolbox/mount.c b/toolbox/mount.c new file mode 100644 index 0000000..ef13e1f --- /dev/null +++ b/toolbox/mount.c @@ -0,0 +1,273 @@ +/* + * mount.c, by rmk + */ + +#include <sys/mount.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/loop.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +// FIXME - only one loop mount is supported at a time +#define LOOP_DEVICE "/dev/block/loop0" + +struct mount_opts { + const char str[8]; + unsigned long rwmask; + unsigned long rwset; + unsigned long rwnoset; +}; + +struct extra_opts { + char *str; + char *end; + int used_size; + int alloc_size; +}; + +/* + * These options define the function of "mount(2)". + */ +#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE) + + +static const struct mount_opts options[] = { + /* name mask set noset */ + { "async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS }, + { "atime", MS_NOATIME, 0, MS_NOATIME }, + { "bind", MS_TYPE, MS_BIND, 0, }, + { "dev", MS_NODEV, 0, MS_NODEV }, + { "diratime", MS_NODIRATIME, 0, MS_NODIRATIME }, + { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0 }, + { "exec", MS_NOEXEC, 0, MS_NOEXEC }, + { "move", MS_TYPE, MS_MOVE, 0 }, + { "recurse", MS_REC, MS_REC, 0 }, + { "remount", MS_TYPE, MS_REMOUNT, 0 }, + { "ro", MS_RDONLY, MS_RDONLY, 0 }, + { "rw", MS_RDONLY, 0, MS_RDONLY }, + { "suid", MS_NOSUID, 0, MS_NOSUID }, + { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 }, + { "verbose", MS_VERBOSE, MS_VERBOSE, 0 }, +}; + +static void add_extra_option(struct extra_opts *extra, char *s) +{ + int len = strlen(s); + int newlen = extra->used_size + len; + + if (extra->str) + len++; /* +1 for ',' */ + + if (newlen >= extra->alloc_size) { + char *new; + + new = realloc(extra->str, newlen + 1); /* +1 for NUL */ + if (!new) + return; + + extra->str = new; + extra->end = extra->str + extra->used_size; + extra->alloc_size = newlen; + } + + if (extra->used_size) { + *extra->end = ','; + extra->end++; + } + strcpy(extra->end, s); + extra->used_size += len; + +} + +static unsigned long +parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop) +{ + char *s; + + *loop = 0; + while ((s = strsep(&arg, ",")) != NULL) { + char *opt = s; + unsigned int i; + int res, no = s[0] == 'n' && s[1] == 'o'; + + if (no) + s += 2; + + if (strcmp(s, "loop") == 0) { + *loop = 1; + continue; + } + for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) { + res = strcmp(s, options[i].str); + + if (res == 0) { + rwflag &= ~options[i].rwmask; + if (no) + rwflag |= options[i].rwnoset; + else + rwflag |= options[i].rwset; + } + if (res <= 0) + break; + } + + if (res != 0 && s[0]) + add_extra_option(extra, opt); + } + + return rwflag; +} + +static char *progname; + +static struct extra_opts extra; +static unsigned long rwflag; + +static int +do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop) +{ + char *s; + int error = 0; + + if (loop) { + int file_fd, device_fd; + + // FIXME - only one loop mount supported at a time + file_fd = open(dev, O_RDWR); + if (file_fd < -1) { + perror("open backing file failed"); + return 1; + } + device_fd = open(LOOP_DEVICE, O_RDWR); + if (device_fd < -1) { + perror("open loop device failed"); + close(file_fd); + return 1; + } + if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) { + perror("ioctl LOOP_SET_FD failed"); + close(file_fd); + close(device_fd); + return 1; + } + + close(file_fd); + close(device_fd); + dev = LOOP_DEVICE; + } + + while ((s = strsep(&type, ",")) != NULL) { +retry: + if (mount(dev, dir, s, rwflag, data) == -1) { + error = errno; + /* + * If the filesystem is not found, or the + * superblock is invalid, try the next. + */ + if (error == ENODEV || error == EINVAL) + continue; + + /* + * If we get EACCESS, and we're trying to + * mount readwrite and this isn't a remount, + * try read only. + */ + if (error == EACCES && + (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) { + rwflag |= MS_RDONLY; + goto retry; + } + break; + } + } + + if (error) { + errno = error; + perror("mount"); + return 255; + } + + return 0; +} + +static int print_mounts() +{ + FILE* f; + int length; + char buffer[100]; + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stdout, "could not open /proc/mounts\n"); + return -1; + } + + do { + length = fread(buffer, 1, 100, f); + if (length > 0) + fwrite(buffer, 1, length, stdout); + } while (length > 0); + + fclose(f); + return 0; +} + +int mount_main(int argc, char *argv[]) +{ + char *type = NULL; + int c; + int loop; + + progname = argv[0]; + rwflag = MS_VERBOSE; + + // mount with no arguments is equivalent to "cat /proc/mounts" + if (argc == 1) return print_mounts(); + + do { + c = getopt(argc, argv, "o:rt:w"); + if (c == EOF) + break; + switch (c) { + case 'o': + rwflag = parse_mount_options(optarg, rwflag, &extra, &loop); + break; + case 'r': + rwflag |= MS_RDONLY; + break; + case 't': + type = optarg; + break; + case 'w': + rwflag &= ~MS_RDONLY; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + exit(1); + } + } while (1); + + /* + * If remount, bind or move was specified, then we don't + * have a "type" as such. Use the dummy "none" type. + */ + if (rwflag & MS_TYPE) + type = "none"; + + if (optind + 2 != argc || type == NULL) { + fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] " + "device directory\n", progname); + exit(1); + } + + return do_mount(argv[optind], argv[optind + 1], type, rwflag, + extra.str, loop); +} diff --git a/toolbox/mv.c b/toolbox/mv.c new file mode 100644 index 0000000..a5bc225 --- /dev/null +++ b/toolbox/mv.c @@ -0,0 +1,59 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> + + +int mv_main(int argc, char *argv[]) +{ + const char* dest; + struct stat st; + int i; + + if (argc < 3) { + fprintf(stderr,"USAGE: %s <source...> <destination>\n", argv[0]); + return -1; + } + + /* check if destination exists */ + dest = argv[argc - 1]; + if (stat(dest, &st)) { + /* an error, unless the destination was missing */ + if (errno != ENOENT) { + fprintf(stderr, "failed on %s - %s\n", dest, strerror(errno)); + return -1; + } + st.st_mode = 0; + } + + for (i = 1; i < argc - 1; i++) { + const char *source = argv[i]; + char fullDest[PATH_MAX + 1 + PATH_MAX + 1]; + /* assume we build "dest/source", and let rename() fail on pathsize */ + if (strlen(dest) + 1 + strlen(source) + 1 > sizeof(fullDest)) { + fprintf(stderr, "path too long\n"); + return -1; + } + strcpy(fullDest, dest); + + /* if destination is a directory, concat the source file name */ + if (S_ISDIR(st.st_mode)) { + const char *fileName = strrchr(source, '/'); + if (fullDest[strlen(fullDest)-1] != '/') { + strcat(fullDest, "/"); + } + strcat(fullDest, fileName ? fileName + 1 : source); + } + + /* attempt to move it */ + if (rename(source, fullDest)) { + fprintf(stderr, "failed on '%s' - %s\n", source, strerror(errno)); + return -1; + } + } + + return 0; +} + diff --git a/toolbox/netstat.c b/toolbox/netstat.c new file mode 100644 index 0000000..6404309 --- /dev/null +++ b/toolbox/netstat.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> + +typedef union iaddr iaddr; + +union iaddr { + unsigned u; + unsigned char b[4]; +}; + +static const char *state2str(unsigned state) +{ + switch(state){ + case 0x1: return "ESTABLISHED"; + case 0x2: return "SYN_SENT"; + case 0x3: return "SYN_RECV"; + case 0x4: return "FIN_WAIT1"; + case 0x5: return "FIN_WAIT2"; + case 0x6: return "TIME_WAIT"; + case 0x7: return "CLOSE"; + case 0x8: return "CLOSE_WAIT"; + case 0x9: return "LAST_ACK"; + case 0xA: return "LISTEN"; + case 0xB: return "CLOSING"; + default: return "UNKNOWN"; + } +} + +void addr2str(iaddr addr, unsigned port, char *buf) +{ + if(port) { + snprintf(buf, 64, "%d.%d.%d.%d:%d", + addr.b[0], addr.b[1], addr.b[2], addr.b[3], port); + } else { + snprintf(buf, 64, "%d.%d.%d.%d:*", + addr.b[0], addr.b[1], addr.b[2], addr.b[3]); + } +} + +int netstat_main(int argc, char *argv[]) +{ + char buf[512]; + char lip[64]; + char rip[64]; + iaddr laddr, raddr; + unsigned lport, rport, state, txq, rxq, num; + int n; + FILE *fp; + + printf("Proto Recv-Q Send-Q Local Address Foreign Address State\n"); + + fp = fopen("/proc/net/tcp", "r"); + if(fp != 0) { + fgets(buf, 512, fp); + while(fgets(buf, 512, fp)){ + n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x", + &num, &laddr.u, &lport, &raddr.u, &rport, + &state, &txq, &rxq); + if(n == 8) { + addr2str(laddr, lport, lip); + addr2str(raddr, rport, rip); + + printf("tcp %6d %6d %-22s %-22s %s\n", + txq, rxq, lip, rip, + state2str(state)); + } + } + fclose(fp); + } + fp = fopen("/proc/net/udp", "r"); + if(fp != 0) { + fgets(buf, 512, fp); + while(fgets(buf, 512, fp)){ + n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x", + &num, &laddr.u, &lport, &raddr.u, &rport, + &state, &txq, &rxq); + if(n == 8) { + addr2str(laddr, lport, lip); + addr2str(raddr, rport, rip); + + printf("udp %6d %6d %-22s %-22s\n", + txq, rxq, lip, rip); + } + } + fclose(fp); + } + + return 0; +} diff --git a/toolbox/notify.c b/toolbox/notify.c new file mode 100644 index 0000000..b1761d2 --- /dev/null +++ b/toolbox/notify.c @@ -0,0 +1,144 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <errno.h> + +int notify_main(int argc, char *argv[]) +{ + int c; + int nfd, ffd; + int res; + char event_buf[512]; + struct inotify_event *event; + int event_mask = IN_ALL_EVENTS; + int event_count = 1; + int print_files = 0; + int verbose = 2; + int width = 80; + char **file_names; + int file_count; + int id_offset = 0; + int i; + char *buf; + + do { + c = getopt(argc, argv, "m:c:pv:w:"); + if (c == EOF) + break; + switch (c) { + case 'm': + event_mask = strtol(optarg, NULL, 0); + break; + case 'c': + event_count = atoi(optarg); + break; + case 'p': + print_files = 1; + break; + case 'v': + verbose = atoi(optarg); + break; + case 'w': + width = atoi(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (argc <= optind) { + fprintf(stderr, "Usage: %s [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]\n", argv[0]); + return 1; + } + + nfd = inotify_init(); + if(nfd < 0) { + fprintf(stderr, "inotify_init failed, %s\n", strerror(errno)); + return 1; + } + file_names = argv + optind; + file_count = argc - optind; + for(i = 0; i < file_count; i++) { + res = inotify_add_watch(nfd, file_names[i], event_mask); + if(res < 0) { + fprintf(stderr, "inotify_add_watch failed for %s, %s\n", file_names[i], strerror(errno)); + return 1; + } + if(i == 0) + id_offset = -res; + if(res + id_offset != i) { + fprintf(stderr, "%s got unexpected id %d instead of %d\n", file_names[i], res, i); + return 1; + } + } + + buf = malloc(width + 2); + + while(1) { + int event_pos = 0; + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + continue; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + while(res >= (int)sizeof(*event)) { + int event_size; + event = (struct inotify_event *)(event_buf + event_pos); + if(verbose >= 2) + printf("%s: %08x %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->cookie, event->len ? event->name : ""); + else if(verbose >= 2) + printf("%s: %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->len ? event->name : ""); + else if(verbose >= 1) + printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(print_files && (event->mask & IN_MODIFY)) { + char filename[512]; + ssize_t read_len; + char *display_name; + int buflen; + strcpy(filename, file_names[event->wd + id_offset]); + if(event->len) { + strcat(filename, "/"); + strcat(filename, event->name); + } + ffd = open(filename, O_RDONLY); + display_name = (verbose >= 2 || event->len == 0) ? filename : event->name; + buflen = width - strlen(display_name); + read_len = read(ffd, buf, buflen); + if(read_len > 0) { + if(read_len < buflen && buf[read_len-1] != '\n') { + buf[read_len] = '\n'; + read_len++; + } + if(read_len == buflen) { + buf[--read_len] = '\0'; + buf[--read_len] = '\n'; + buf[--read_len] = '.'; + buf[--read_len] = '.'; + buf[--read_len] = '.'; + } + else { + buf[read_len] = '\0'; + } + printf("%s: %s", display_name, buf); + } + close(ffd); + } + if(event_count && --event_count == 0) + return 0; + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + } + + return 0; +} diff --git a/toolbox/powerd.c b/toolbox/powerd.c new file mode 100644 index 0000000..1f29a8b --- /dev/null +++ b/toolbox/powerd.c @@ -0,0 +1,441 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/select.h> +#include <sys/inotify.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +//#include <linux/input.h> // this does not compile + +// from <linux/input.h> + +struct input_event { + struct timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ +#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */ +#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ + +/* + * Event types + */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_SW 0x05 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f + +#define KEY_POWER 116 +#define KEY_SLEEP 142 +#define SW_0 0x00 + +// end <linux/input.h> + +struct notify_entry { + int id; + int (*handler)(struct notify_entry *entry, struct inotify_event *event); + const char *filename; +}; + +int charging_state_notify_handler(struct notify_entry *entry, struct inotify_event *event) +{ + static int state = -1; + int last_state; + char buf[40]; + int read_len; + int fd; + + last_state = state; + fd = open(entry->filename, O_RDONLY); + read_len = read(fd, buf, sizeof(buf)); + if(read_len > 0) { + //printf("charging_state_notify_handler: \"%s\"\n", buf); + state = !(strncmp(buf, "Unknown", 7) == 0 + || strncmp(buf, "Discharging", 11) == 0); + } + close(fd); + //printf("charging_state_notify_handler: %d -> %d\n", last_state, state); + return state > last_state; +} + +struct notify_entry watched_files[] = { + { + .filename = "/sys/android_power/charging_state", + .handler = charging_state_notify_handler + } +}; + +int call_notify_handler(struct inotify_event *event) +{ + unsigned int start, i; + start = event->wd - watched_files[0].id; + if(start >= ARRAY_SIZE(watched_files)) + start = 0; + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + for(i = start; i < ARRAY_SIZE(watched_files); i++) { + if(event->wd == watched_files[i].id) { + if(watched_files[i].handler) { + return watched_files[i].handler(&watched_files[i], event); + } + return 1; + } + } + for(i = 0; i < start; i++) { + if(event->wd == watched_files[i].id) { + if(watched_files[i].handler) { + return watched_files[i].handler(&watched_files[i], event); + } + return 1; + } + } + return 0; +} + +int handle_inotify_event(int nfd) +{ + int res; + int wake_up = 0; + struct inotify_event *event; + char event_buf[512]; + int event_pos = 0; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 0; + } + printf("got %d bytes of event information\n", res); + while(res >= (int)sizeof(*event)) { + int event_size; + event = (struct inotify_event *)(event_buf + event_pos); + wake_up |= call_notify_handler(event); + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return wake_up; +} + +int powerd_main(int argc, char *argv[]) +{ + int c; + unsigned int i; + int res; + struct timeval tv; + int eventfd; + int notifyfd; + int powerfd; + int powerfd_is_sleep; + int user_activity_fd; + int acquire_partial_wake_lock_fd; + int acquire_full_wake_lock_fd; + int release_wake_lock_fd; + char *eventdev = "/dev/input/event0"; + const char *android_sleepdev = "/sys/android_power/request_sleep"; + const char *android_autooff_dev = "/sys/android_power/auto_off_timeout"; + const char *android_user_activity_dev = "/sys/android_power/last_user_activity"; + const char *android_acquire_partial_wake_lock_dev = "/sys/android_power/acquire_partial_wake_lock"; + const char *android_acquire_full_wake_lock_dev = "/sys/android_power/acquire_full_wake_lock"; + const char *android_release_wake_lock_dev = "/sys/android_power/release_wake_lock"; + const char *powerdev = "/sys/power/state"; + const char suspendstring[] = "standby"; + const char wakelockstring[] = "powerd"; + fd_set rfds; + struct input_event event; + struct input_event light_event; + struct input_event light_event2; + int gotkey = 1; + time_t idle_time = 5; + const char *idle_time_string = "5"; + time_t lcd_light_time = 0; + time_t key_light_time = 0; + int verbose = 1; + int event_sleep = 0; + int got_power_key_down = 0; + struct timeval power_key_down_time = { 0, 0 }; + + light_event.type = EV_LED; + light_event.code = 4; // bright lcd backlight + light_event.value = 0; // light off -- sleep after timeout + + light_event2.type = EV_LED; + light_event2.code = 8; // keyboard backlight + light_event2.value = 0; // light off -- sleep after timeout + + do { + c = getopt(argc, argv, "e:ni:vql:k:"); + if (c == EOF) + break; + switch (c) { + case 'e': + eventdev = optarg; + break; + case 'n': + gotkey = 0; + break; + case 'i': + idle_time = atoi(optarg); + idle_time_string = optarg; + break; + case 'v': + verbose = 2; + break; + case 'q': + verbose = 0; + break; + case 'l': + lcd_light_time = atoi(optarg); + break; + case 'k': + key_light_time = atoi(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(optind != argc) { + fprintf(stderr,"%s [-e eventdev]\n", argv[0]); + return 1; + } + + eventfd = open(eventdev, O_RDWR | O_NONBLOCK); + if(eventfd < 0) { + fprintf(stderr, "could not open %s, %s\n", eventdev, strerror(errno)); + return 1; + } + if(key_light_time >= lcd_light_time) { + lcd_light_time = key_light_time + 1; + fprintf(stderr,"lcd bright backlight time must be longer than keyboard backlight time.\n" + "Setting lcd bright backlight time to %ld seconds\n", lcd_light_time); + } + + user_activity_fd = open(android_user_activity_dev, O_RDWR); + if(user_activity_fd >= 0) { + int auto_off_fd = open(android_autooff_dev, O_RDWR); + write(auto_off_fd, idle_time_string, strlen(idle_time_string)); + close(auto_off_fd); + } + + powerfd = open(android_sleepdev, O_RDWR); + if(powerfd >= 0) { + powerfd_is_sleep = 1; + if(verbose > 0) + printf("Using android sleep dev: %s\n", android_sleepdev); + } + else { + powerfd_is_sleep = 0; + powerfd = open(powerdev, O_RDWR); + if(powerfd >= 0) { + if(verbose > 0) + printf("Using linux power dev: %s\n", powerdev); + } + } + if(powerfd < 0) { + fprintf(stderr, "could not open %s, %s\n", powerdev, strerror(errno)); + return 1; + } + + notifyfd = inotify_init(); + if(notifyfd < 0) { + fprintf(stderr, "inotify_init failed, %s\n", strerror(errno)); + return 1; + } + fcntl(notifyfd, F_SETFL, O_NONBLOCK | fcntl(notifyfd, F_GETFL)); + for(i = 0; i < ARRAY_SIZE(watched_files); i++) { + watched_files[i].id = inotify_add_watch(notifyfd, watched_files[i].filename, IN_MODIFY); + printf("Watching %s, id %d\n", watched_files[i].filename, watched_files[i].id); + } + + acquire_partial_wake_lock_fd = open(android_acquire_partial_wake_lock_dev, O_RDWR); + acquire_full_wake_lock_fd = open(android_acquire_full_wake_lock_dev, O_RDWR); + release_wake_lock_fd = open(android_release_wake_lock_dev, O_RDWR); + + if(user_activity_fd >= 0) { + idle_time = 60*60*24; // driver handles real timeout + } + if(gotkey) { + tv.tv_sec = idle_time; + tv.tv_usec = 0; + } + else { + tv.tv_sec = 0; + tv.tv_usec = 500000; + } + + while(1) { + FD_ZERO(&rfds); + //FD_SET(0, &rfds); + FD_SET(eventfd, &rfds); + FD_SET(notifyfd, &rfds); + res = select(((notifyfd > eventfd) ? notifyfd : eventfd) + 1, &rfds, NULL, NULL, &tv); + if(res < 0) { + fprintf(stderr, "select failed, %s\n", strerror(errno)); + return 1; + } + if(res == 0) { + if(light_event2.value == 1) + goto light2_off; + if(light_event.value == 1) + goto light_off; + if(user_activity_fd < 0) { + if(gotkey && verbose > 0) + printf("Idle - sleep\n"); + if(!gotkey && verbose > 1) + printf("Reenter sleep\n"); + goto sleep; + } + else { + tv.tv_sec = 60*60*24; + tv.tv_usec = 0; + } + } + if(res > 0) { + //if(FD_ISSET(0, &rfds)) { + // printf("goto data on stdin quit\n"); + // return 0; + //} + if(FD_ISSET(notifyfd, &rfds)) { + write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + if(handle_inotify_event(notifyfd) > 0) { + write(acquire_full_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + } + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + } + if(FD_ISSET(eventfd, &rfds)) { + write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + res = read(eventfd, &event, sizeof(event)); + if(res < (int)sizeof(event)) { + fprintf(stderr, "could not get event\n"); + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + return 1; + } + if(event.type == EV_PWR && event.code == KEY_SLEEP) { + event_sleep = event.value; + } + if(event.type == EV_KEY || (event.type == EV_SW && event.code == SW_0 && event.value == 1)) { + gotkey = 1; + if(user_activity_fd >= 0) { + char buf[32]; + int len; + len = sprintf(buf, "%ld%06lu000", event.time.tv_sec, event.time.tv_usec); + write(user_activity_fd, buf, len); + } + if(lcd_light_time | key_light_time) { + tv.tv_sec = key_light_time; + light_event.value = 1; + write(eventfd, &light_event, sizeof(light_event)); + light_event2.value = 1; + write(eventfd, &light_event2, sizeof(light_event2)); + } + else { + tv.tv_sec = idle_time; + } + tv.tv_usec = 0; + if(verbose > 1) + printf("got %s %s %d%s\n", event.type == EV_KEY ? "key" : "switch", event.value ? "down" : "up", event.code, event_sleep ? " from sleep" : ""); + if(event.code == KEY_POWER) { + if(event.value == 0) { + int tmp_got_power_key_down = got_power_key_down; + got_power_key_down = 0; + if(tmp_got_power_key_down) { + // power key released + if(verbose > 0) + printf("Power key released - sleep\n"); + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + goto sleep; + } + } + else if(event_sleep == 0) { + got_power_key_down = 1; + power_key_down_time = event.time; + } + } + } + if(event.type == EV_SW && event.code == SW_0 && event.value == 0) { + if(verbose > 0) + printf("Flip closed - sleep\n"); + power_key_down_time = event.time; + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + goto sleep; + } + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + } + } + if(0) { +light_off: + light_event.value = 0; + write(eventfd, &light_event, sizeof(light_event)); + tv.tv_sec = idle_time - lcd_light_time; + } + if(0) { +light2_off: + light_event2.value = 0; + write(eventfd, &light_event2, sizeof(light_event2)); + tv.tv_sec = lcd_light_time - key_light_time; + } + if(0) { +sleep: + if(light_event.value == 1) { + light_event.value = 0; + write(eventfd, &light_event, sizeof(light_event)); + light_event2.value = 0; + write(eventfd, &light_event2, sizeof(light_event2)); + tv.tv_sec = idle_time - lcd_light_time; + } + if(powerfd_is_sleep) { + char buf[32]; + int len; + len = sprintf(buf, "%ld%06lu000", power_key_down_time.tv_sec, power_key_down_time.tv_usec); + write(powerfd, buf, len); + } + else + write(powerfd, suspendstring, sizeof(suspendstring) - 1); + gotkey = 0; + tv.tv_sec = 0; + tv.tv_usec = 500000; + } + } + + return 0; +} diff --git a/toolbox/printenv.c b/toolbox/printenv.c new file mode 100644 index 0000000..d5ea531 --- /dev/null +++ b/toolbox/printenv.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdlib.h> + +extern char** environ; + +int printenv_main (int argc, char **argv) +{ + char** e; + char* v; + int i; + + if (argc == 1) { + e = environ; + while (*e) { + printf("%s\n", *e); + e++; + } + } else { + for (i=1; i<argc; i++) { + v = getenv(argv[i]); + if (v) { + printf("%s\n", v); + } + } + } + + return 0; +} + diff --git a/toolbox/ps.c b/toolbox/ps.c new file mode 100644 index 0000000..3b86fa2 --- /dev/null +++ b/toolbox/ps.c @@ -0,0 +1,214 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> + +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> + +#include <pwd.h> + + +static char *nexttoksep(char **strp, char *sep) +{ + char *p = strsep(strp,sep); + return (p == 0) ? "" : p; +} +static char *nexttok(char **strp) +{ + return nexttoksep(strp, " "); +} + +#define SHOW_PRIO 1 +#define SHOW_TIME 2 + +static int display_flags = 0; + +static int ps_line(int pid, int tid, char *namefilter) +{ + char statline[1024]; + char cmdline[1024]; + char user[32]; + struct stat stats; + int fd, r; + char *ptr, *name, *state; + int ppid, tty; + unsigned wchan, rss, vss, eip; + unsigned utime, stime; + int prio, nice, rtprio, sched; + struct passwd *pw; + + sprintf(statline, "/proc/%d", pid); + stat(statline, &stats); + + if(tid) { + sprintf(statline, "/proc/%d/task/%d/stat", pid, tid); + cmdline[0] = 0; + } else { + sprintf(statline, "/proc/%d/stat", pid); + sprintf(cmdline, "/proc/%d/cmdline", pid); + fd = open(cmdline, O_RDONLY); + if(fd == 0) { + r = 0; + } else { + r = read(fd, cmdline, 1023); + close(fd); + if(r < 0) r = 0; + } + cmdline[r] = 0; + } + + fd = open(statline, O_RDONLY); + if(fd == 0) return -1; + r = read(fd, statline, 1023); + close(fd); + if(r < 0) return -1; + statline[r] = 0; + + ptr = statline; + nexttok(&ptr); // skip pid + ptr++; // skip "(" + + name = ptr; + ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')', + *ptr++ = '\0'; // and null-terminate name. + + ptr++; // skip " " + state = nexttok(&ptr); + ppid = atoi(nexttok(&ptr)); + nexttok(&ptr); // pgrp + nexttok(&ptr); // sid + tty = atoi(nexttok(&ptr)); + + nexttok(&ptr); // tpgid + nexttok(&ptr); // flags + nexttok(&ptr); // minflt + nexttok(&ptr); // cminflt + nexttok(&ptr); // majflt + nexttok(&ptr); // cmajflt +#if 1 + utime = atoi(nexttok(&ptr)); + stime = atoi(nexttok(&ptr)); +#else + nexttok(&ptr); // utime + nexttok(&ptr); // stime +#endif + nexttok(&ptr); // cutime + nexttok(&ptr); // cstime + prio = atoi(nexttok(&ptr)); + nice = atoi(nexttok(&ptr)); + nexttok(&ptr); // threads + nexttok(&ptr); // itrealvalue + nexttok(&ptr); // starttime + vss = strtoul(nexttok(&ptr), 0, 10); // vsize + rss = strtoul(nexttok(&ptr), 0, 10); // rss + nexttok(&ptr); // rlim + nexttok(&ptr); // startcode + nexttok(&ptr); // endcode + nexttok(&ptr); // startstack + nexttok(&ptr); // kstkesp + eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip + nexttok(&ptr); // signal + nexttok(&ptr); // blocked + nexttok(&ptr); // sigignore + nexttok(&ptr); // sigcatch + wchan = strtoul(nexttok(&ptr), 0, 10); // wchan + nexttok(&ptr); // nswap + nexttok(&ptr); // cnswap + nexttok(&ptr); // exit signal + nexttok(&ptr); // processor + rtprio = atoi(nexttok(&ptr)); // rt_priority + sched = atoi(nexttok(&ptr)); // scheduling policy + + tty = atoi(nexttok(&ptr)); + + if(tid != 0) { + ppid = pid; + pid = tid; + } + + pw = getpwuid(stats.st_uid); + if(pw == 0) { + sprintf(user,"%d",(int)stats.st_uid); + } else { + strcpy(user,pw->pw_name); + } + + if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) { + printf("%-8s %-5d %-5d %-5d %-5d", user, pid, ppid, vss / 1024, rss * 4); + if(display_flags&SHOW_PRIO) + printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched); + printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name); + if(display_flags&SHOW_TIME) + printf(" (u:%d, s:%d)", utime, stime); + printf("\n"); + } + return 0; +} + + +void ps_threads(int pid, char *namefilter) +{ + char tmp[128]; + DIR *d; + struct dirent *de; + + sprintf(tmp,"/proc/%d/task",pid); + d = opendir(tmp); + if(d == 0) return; + + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int tid = atoi(de->d_name); + if(tid == pid) continue; + ps_line(pid, tid, namefilter); + } + } + closedir(d); +} + +int ps_main(int argc, char **argv) +{ + DIR *d; + struct dirent *de; + char *namefilter = 0; + int pidfilter = 0; + int threads = 0; + + d = opendir("/proc"); + if(d == 0) return -1; + + while(argc > 1){ + if(!strcmp(argv[1],"-t")) { + threads = 1; + } else if(!strcmp(argv[1],"-x")) { + display_flags |= SHOW_TIME; + } else if(!strcmp(argv[1],"-p")) { + display_flags |= SHOW_PRIO; + } else if(isdigit(argv[1][0])){ + pidfilter = atoi(argv[1]); + } else { + namefilter = argv[1]; + } + argc--; + argv++; + } + + printf("USER PID PPID VSIZE RSS %sWCHAN PC NAME\n", + (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":""); + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int pid = atoi(de->d_name); + if(!pidfilter || (pidfilter == pid)) { + ps_line(pid, 0, namefilter); + if(threads) ps_threads(pid, namefilter); + } + } + } + closedir(d); + return 0; +} + diff --git a/toolbox/r.c b/toolbox/r.c new file mode 100644 index 0000000..5a82e20 --- /dev/null +++ b/toolbox/r.c @@ -0,0 +1,74 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> + +static int usage() +{ + fprintf(stderr,"r [-b|-s] <address> [<value>]\n"); + return -1; +} + +int r_main(int argc, char *argv[]) +{ + int width = 4, set = 0, fd; + unsigned addr, value; + void *page; + + if(argc < 2) return usage(); + + if(!strcmp(argv[1], "-b")) { + width = 1; + argc--; + argv++; + } else if(!strcmp(argv[1], "-s")) { + width = 2; + argc--; + argv++; + } + + if(argc < 2) return usage(); + addr = strtoul(argv[1], 0, 16); + + if(argc > 2) { + set = 1; + value = strtoul(argv[2], 0, 16); + } + + fd = open("/dev/mem", O_RDWR | O_SYNC); + if(fd < 0) { + fprintf(stderr,"cannot open /dev/mem\n"); + return -1; + } + + page = mmap(0, 8192, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, addr & (~4095)); + + if(page == MAP_FAILED){ + fprintf(stderr,"cannot mmap region\n"); + return -1; + } + + switch(width){ + case 4: { + unsigned *x = (unsigned*) (((unsigned) page) + (addr & 4095)); + if(set) *x = value; + fprintf(stderr,"%08x: %08x\n", addr, *x); + break; + } + case 2: { + unsigned short *x = (unsigned short*) (((unsigned) page) + (addr & 4095)); + if(set) *x = value; + fprintf(stderr,"%08x: %04x\n", addr, *x); + break; + } + case 1: { + unsigned char *x = (unsigned char*) (((unsigned) page) + (addr & 4095)); + if(set) *x = value; + fprintf(stderr,"%08x: %02x\n", addr, *x); + break; + } + } + return 0; +} diff --git a/toolbox/readtty.c b/toolbox/readtty.c new file mode 100644 index 0000000..2b27548 --- /dev/null +++ b/toolbox/readtty.c @@ -0,0 +1,183 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +struct { + char key; + char *chars; +} map[] = { + { '1', "_ -1?!,.:;\"'<=>()_" }, + { '2', "Cabc2ABC" }, + { '3', "Fdef3DEF" }, + { '4', "Ighi4GHI" }, + { '5', "Ljkl5JKL" }, + { '6', "Omno6MNO" }, + { '7', "Spqrs7PQRS" }, + { '8', "Vtuv8TUV" }, + { '9', "Zwxyz9WXYZ" }, + { '0', "*+&0@/#*" }, +}; + +char next_char(char key, char current) +{ + int i; + char *next; + for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) { + if(key == map[i].key) { + next = strchr(map[i].chars, current); + if(next && next[1]) + return next[1]; + return map[i].chars[1]; + } + } + return key; +} + +char prev_char(char key, char current) +{ + int i; + char *next; + for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) { + if(key == map[i].key) { + next = strchr(map[i].chars+1, current); + if(next && next[-1]) + return next[-1]; + return map[i].chars[1]; + } + } + return key; +} + +int readtty_main(int argc, char *argv[]) +{ + int c; + //int flags; + char buf[1]; + int res; + struct termios ttyarg; + struct termios savedttyarg; + int nonblock = 0; + int timeout = 0; + int flush = 0; + int phone = 0; + char *accept = NULL; + char *rejectstring = NULL; + char last_char_in = 0; + char current_char = 0; + char *exit_string = NULL; + int exit_match = 0; + + do { + c = getopt(argc, argv, "nt:fa:r:pe:"); + if (c == EOF) + break; + switch (c) { + case 't': + timeout = atoi(optarg); + break; + case 'n': + nonblock = 1; + break; + case 'f': + flush = 1; + break; + case 'a': + accept = optarg; + break; + case 'r': + rejectstring = optarg; + break; + case 'p': + phone = 1; + break; + case 'e': + exit_string = optarg; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if(flush) + tcflush(STDIN_FILENO, TCIFLUSH); + ioctl(STDIN_FILENO, TCGETS , &savedttyarg) ; /* set changed tty arguments */ + ttyarg = savedttyarg; + ttyarg.c_cc[VMIN] = (timeout > 0 || nonblock) ? 0 : 1; /* minimum of 0 chars */ + ttyarg.c_cc[VTIME] = timeout; /* wait max 15/10 sec */ + ttyarg.c_iflag = BRKINT | ICRNL; + ttyarg.c_lflag &= ~(ECHO | ICANON); + ioctl(STDIN_FILENO, TCSETS , &ttyarg); + + while (1) { + res = read(STDIN_FILENO, buf, 1); + if(res <= 0) { + if(phone) { + if(current_char) { + write(STDERR_FILENO, ¤t_char, 1); + write(STDOUT_FILENO, ¤t_char, 1); + if(exit_string && current_char == exit_string[exit_match]) { + exit_match++; + if(exit_string[exit_match] == '\0') + break; + } + else + exit_match = 0; + current_char = 0; + } + continue; + } + break; + } + if(accept && strchr(accept, buf[0]) == NULL) { + if(rejectstring) { + write(STDOUT_FILENO, rejectstring, strlen(rejectstring)); + break; + } + if(flush) + tcflush(STDIN_FILENO, TCIFLUSH); + continue; + } + if(phone) { + //if(!isprint(buf[0])) { + // fprintf(stderr, "got unprintable character 0x%x\n", buf[0]); + //} + if(buf[0] == '\0') { + if(current_char) { + current_char = prev_char(last_char_in, current_char); + write(STDERR_FILENO, ¤t_char, 1); + write(STDERR_FILENO, "\b", 1); + } + continue; + } + if(current_char && buf[0] != last_char_in) { + write(STDERR_FILENO, ¤t_char, 1); + write(STDOUT_FILENO, ¤t_char, 1); + if(exit_string && current_char == exit_string[exit_match]) { + exit_match++; + if(exit_string[exit_match] == '\0') + break; + } + else + exit_match = 0; + current_char = 0; + } + last_char_in = buf[0]; + current_char = next_char(last_char_in, current_char); + write(STDERR_FILENO, ¤t_char, 1); + write(STDERR_FILENO, "\b", 1); + continue; + } + write(STDOUT_FILENO, buf, 1); + break; + } + ioctl(STDIN_FILENO, TCSETS , &savedttyarg) ; /* set changed tty arguments */ + + return 0; +} diff --git a/toolbox/reboot.c b/toolbox/reboot.c new file mode 100644 index 0000000..aebe185 --- /dev/null +++ b/toolbox/reboot.c @@ -0,0 +1,56 @@ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/reboot.h> +#include <unistd.h> + +int reboot_main(int argc, char *argv[]) +{ + int ret; + int nosync = 0; + int poweroff = 0; + + opterr = 0; + do { + int c; + + c = getopt(argc, argv, "np"); + + if (c == EOF) { + break; + } + + switch (c) { + case 'n': + nosync = 1; + break; + case 'p': + poweroff = 1; + break; + case '?': + fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]); + exit(EXIT_FAILURE); + } + } while (1); + + if(argc > optind + 1) { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + exit(EXIT_FAILURE); + } + + if(!nosync) + sync(); + + if(poweroff) + ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL); + else if(argc > optind) + ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]); + else + ret = reboot(RB_AUTOBOOT); + if(ret < 0) { + perror("reboot"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "reboot returned\n"); + return 0; +} diff --git a/toolbox/renice.c b/toolbox/renice.c new file mode 100644 index 0000000..978b329 --- /dev/null +++ b/toolbox/renice.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sched.h> + +static void +usage(const char *s) +{ + fprintf(stderr, "USAGE: %s [[-r] priority pids ...] [-g pid]\n", s); + exit(EXIT_FAILURE); +} + +void print_prio(pid_t pid) +{ + int sched; + struct sched_param sp; + + printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid)); + + printf("scheduling class: "); + sched = sched_getscheduler(pid); + switch (sched) { + case SCHED_FIFO: + printf("FIFO\n"); + break; + case SCHED_RR: + printf("RR\n"); + break; + case SCHED_OTHER: + printf("Normal\n"); + break; + case -1: + perror("sched_getscheduler"); + break; + default: + printf("Unknown\n"); + } + + sched_getparam(pid, &sp); + printf("RT prio: %d (of %d to %d)\n", sp.sched_priority, + sched_get_priority_min(sched), sched_get_priority_max(sched)); +} + +int renice_main(int argc, char *argv[]) +{ + int prio; + int realtime = 0; + char *cmd = argv[0]; + + // consume command name + argc--; + argv++; + + if (argc < 1) + usage(cmd); + + if(strcmp("-r", argv[0]) == 0) { + // do realtime priority adjustment + realtime = 1; + argc--; + argv++; + } + + if(strcmp("-g", argv[0]) == 0) { + if (argc < 2) + usage(cmd); + print_prio(atoi(argv[1])); + return 0; + } + + if (argc < 1) + usage(cmd); + + prio = atoi(argv[0]); + argc--; + argv++; + + if (argc < 1) + usage(cmd); + + while(argc) { + pid_t pid; + + pid = atoi(argv[0]); + argc--; + argv++; + + if (realtime) { + struct sched_param sp = { .sched_priority = prio }; + int ret; + + ret = sched_setscheduler(pid, SCHED_RR, &sp); + if (ret) { + perror("sched_set_scheduler"); + exit(EXIT_FAILURE); + } + } else { + int ret; + + ret = setpriority(PRIO_PROCESS, pid, prio); + if (ret) { + perror("setpriority"); + exit(EXIT_FAILURE); + } + } + } + + return 0; +} + + diff --git a/toolbox/rm.c b/toolbox/rm.c new file mode 100644 index 0000000..bd66311 --- /dev/null +++ b/toolbox/rm.c @@ -0,0 +1,92 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> + +static int usage() +{ + fprintf(stderr,"rm [-rR] <target>\n"); + return -1; +} + +/* return -1 on failure, with errno set to the first error */ +static int unlink_recursive(const char* name) +{ + struct stat st; + DIR *dir; + struct dirent *de; + int fail = 0; + + /* is it a file or directory? */ + if (lstat(name, &st) < 0) + return -1; + + /* a file, so unlink it */ + if (!S_ISDIR(st.st_mode)) + return unlink(name); + + /* a directory, so open handle */ + dir = opendir(name); + if (dir == NULL) + return -1; + + /* recurse over components */ + errno = 0; + while ((de = readdir(dir)) != NULL) { + char dn[PATH_MAX]; + if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) + continue; + sprintf(dn, "%s/%s", name, de->d_name); + if (unlink_recursive(dn) < 0) { + fail = 1; + break; + } + errno = 0; + } + /* in case readdir or unlink_recursive failed */ + if (fail || errno < 0) { + int save = errno; + closedir(dir); + errno = save; + return -1; + } + + /* close directory handle */ + if (closedir(dir) < 0) + return -1; + + /* delete target directory */ + return rmdir(name); +} + +int rm_main(int argc, char *argv[]) +{ + int ret; + int i = 1; + int recursive = 0; + + if (argc < 2) + return usage(); + + /* check if recursive */ + if (argc >=2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "-R"))) { + recursive = 1; + i = 2; + } + + /* loop over the file/directory args */ + for (; i < argc; i++) { + int ret = recursive ? unlink_recursive(argv[i]) : unlink(argv[i]); + if (ret < 0) { + fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno)); + return -1; + } + } + + return 0; +} + diff --git a/toolbox/rmdir.c b/toolbox/rmdir.c new file mode 100644 index 0000000..06f3df2 --- /dev/null +++ b/toolbox/rmdir.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +static int usage() +{ + fprintf(stderr,"rmdir <directory>\n"); + return -1; +} + +int rmdir_main(int argc, char *argv[]) +{ + int symbolic = 0; + int ret; + if(argc < 2) return usage(); + + while(argc > 1) { + argc--; + argv++; + ret = rmdir(argv[0]); + if(ret < 0) { + fprintf(stderr, "rmdir failed for %s, %s\n", argv[0], strerror(errno)); + return ret; + } + } + + return 0; +} diff --git a/toolbox/rmmod.c b/toolbox/rmmod.c new file mode 100644 index 0000000..7e10c06 --- /dev/null +++ b/toolbox/rmmod.c @@ -0,0 +1,42 @@ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <errno.h> +#include <asm/unistd.h> + +extern int delete_module(const char *, unsigned int); + +int rmmod_main(int argc, char **argv) +{ + int ret; + char *modname, *dot; + + /* make sure we've got an argument */ + if (argc < 2) { + fprintf(stderr, "usage: rmmod <module>\n"); + return -1; + } + + /* if given /foo/bar/blah.ko, make a weak attempt + * to convert to "blah", just for convenience + */ + modname = strrchr(argv[1], '/'); + if (!modname) + modname = argv[1]; + dot = strchr(argv[1], '.'); + if (dot) + *dot = '\0'; + + /* pass it to the kernel */ + ret = delete_module(modname, O_NONBLOCK | O_EXCL); + if (ret != 0) { + fprintf(stderr, "rmmod: delete_module '%s' failed (errno %d)\n", + modname, errno); + return -1; + } + + return 0; +} + diff --git a/toolbox/rotatefb.c b/toolbox/rotatefb.c new file mode 100644 index 0000000..2ff4127 --- /dev/null +++ b/toolbox/rotatefb.c @@ -0,0 +1,71 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> +#include <linux/fb.h> + + +int rotatefb_main(int argc, char *argv[]) +{ + int c; + char *fbdev = "/dev/graphics/fb0"; + int rotation = 0; + int fd; + int res; + struct fb_var_screeninfo fbinfo; + + do { + c = getopt(argc, argv, "d:"); + if (c == EOF) + break; + switch (c) { + case 'd': + fbdev = optarg; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if(optind + 1 != argc) { + fprintf(stderr, "%s: specify rotation\n", argv[0]); + exit(1); + } + rotation = atoi(argv[optind]); + + fd = open(fbdev, O_RDWR); + if(fd < 0) { + fprintf(stderr, "cannot open %s\n", fbdev); + return 1; + } + + res = ioctl(fd, FBIOGET_VSCREENINFO, &fbinfo); + if(res < 0) { + fprintf(stderr, "failed to get fbinfo: %s\n", strerror(errno)); + return 1; + } + if((fbinfo.rotate ^ rotation) & 1) { + unsigned int xres = fbinfo.yres; + fbinfo.yres = fbinfo.xres; + fbinfo.xres = xres; + fbinfo.xres_virtual = fbinfo.xres; + fbinfo.yres_virtual = fbinfo.yres * 2; + if(fbinfo.yoffset == xres) + fbinfo.yoffset = fbinfo.yres; + } + fbinfo.rotate = rotation; + res = ioctl(fd, FBIOPUT_VSCREENINFO, &fbinfo); + if(res < 0) { + fprintf(stderr, "failed to set fbinfo: %s\n", strerror(errno)); + return 1; + } + + return 0; +} diff --git a/toolbox/route.c b/toolbox/route.c new file mode 100644 index 0000000..adf5c69 --- /dev/null +++ b/toolbox/route.c @@ -0,0 +1,97 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/sockios.h> +#include <arpa/inet.h> +#include <linux/route.h> + +static void die(const char *s) +{ + fprintf(stderr,"error: %s (%s)\n", s, strerror(errno)); + exit(-1); +} + +static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) +{ + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(addr); +} + +#define ADVANCE(argc, argv) do { argc--, argv++; } while(0) +#define EXPECT_NEXT(argc, argv) do { \ + ADVANCE(argc, argv); \ + if (0 == argc) { \ + errno = EINVAL; \ + die("expecting one more argument"); \ + } \ +} while(0) + +/* current support two kinds of usage */ +/* route add default dev wlan0 */ +/* route add default gw 192.168.20.1 dev wlan0 */ + +int route_main(int argc, char *argv[]) +{ + struct ifreq ifr; + int s,i; + struct rtentry rt; + struct sockaddr_in ina; + + if(argc == 0) return 0; + + strncpy(ifr.ifr_name, argv[0], IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + ADVANCE(argc, argv); + + if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + die("cannot open control socket\n"); + } + + while(argc > 0){ + if(!strcmp(argv[0], "add")) { + EXPECT_NEXT(argc, argv); + if(!strcmp(argv[0], "default")) { + EXPECT_NEXT(argc, argv); + memset((char *) &rt, 0, sizeof(struct rtentry)); + rt.rt_dst.sa_family = AF_INET; + if(!strcmp(argv[0], "dev")) { + EXPECT_NEXT(argc, argv); + rt.rt_flags = RTF_UP | RTF_HOST; + rt.rt_dev = argv[0]; + if (ioctl(s, SIOCADDRT, &rt) < 0) die("SIOCADDRT"); + }else if(!strcmp(argv[0], "gw")) { + EXPECT_NEXT(argc, argv); + rt.rt_flags = RTF_UP | RTF_GATEWAY; + init_sockaddr_in((struct sockaddr_in *)&(rt.rt_genmask), "0.0.0.0"); + if(isdigit(argv[0][0])){ + init_sockaddr_in((struct sockaddr_in *)&(rt.rt_gateway), argv[0]); + }else{ + die("expecting an IP address for parameter \"gw\""); + } + EXPECT_NEXT(argc, argv); + if(!strcmp(argv[0], "dev")) { + EXPECT_NEXT(argc, argv); + rt.rt_dev = argv[0]; + if (ioctl(s, SIOCADDRT, &rt) < 0){ + die("SIOCADDRT"); + } + } + } + } + } + ADVANCE(argc, argv); + } + + return 0; +} + diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c new file mode 100644 index 0000000..c0e0141 --- /dev/null +++ b/toolbox/schedtop.c @@ -0,0 +1,335 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> + +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <signal.h> + +#include <pwd.h> + +struct thread_info { + int pid; + int tid; + char name[64]; + uint64_t exec_time; + uint64_t delay_time; + uint32_t run_count; +}; + +struct thread_table { + size_t allocated; + size_t active; + struct thread_info *data; +}; + +enum { + FLAG_BATCH = 1U << 0, + FLAG_HIDE_IDLE = 1U << 1, + FLAG_SHOW_THREADS = 1U << 2, + FLAG_USE_ALTERNATE_SCREEN = 1U << 3, +}; + +static int time_dp = 9; +static int time_div = 1; +#define NS_TO_S_D(ns) \ + (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div) + +struct thread_table processes; +struct thread_table last_processes; +struct thread_table threads; +struct thread_table last_threads; + +static void grow_table(struct thread_table *table) +{ + size_t size = table->allocated; + struct thread_info *new_table; + if (size < 128) + size = 128; + else + size *= 2; + + new_table = realloc(table->data, size * sizeof(*table->data)); + if (new_table == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + table->data = new_table; + table->allocated = size; +} + +static struct thread_info *get_item(struct thread_table *table) +{ + if (table->active >= table->allocated) + grow_table(table); + return table->data + table->active; +} + +static void commit_item(struct thread_table *table) +{ + table->active++; +} + +static int read_line(char *line, size_t line_size) +{ + int fd; + int len; + fd = open(line, O_RDONLY); + if(fd == 0) + return -1; + len = read(fd, line, line_size - 1); + close(fd); + if (len <= 0) + return -1; + line[len] = '\0'; + return 0; +} + +static void add_thread(int pid, int tid, struct thread_info *proc_info) +{ + char line[1024]; + char *name, *name_end; + size_t name_len; + struct thread_info *info; + if(tid == 0) + info = get_item(&processes); + else + info = get_item(&threads); + info->pid = pid; + info->tid = tid; + + if(tid) + sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid); + else + sprintf(line, "/proc/%d/schedstat", pid); + if (read_line(line, sizeof(line))) + return; + if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3) + return; + if (proc_info) { + proc_info->exec_time += info->exec_time; + proc_info->delay_time += info->delay_time; + proc_info->run_count += info->run_count; + } + + name = NULL; + if (!tid) { + sprintf(line, "/proc/%d/cmdline", pid); + if (read_line(line, sizeof(line)) == 0 && line[0]) { + name = line; + name_len = strlen(name); + } + } + if (!name) { + if (tid) + sprintf(line, "/proc/%d/task/%d/stat", pid, tid); + else + sprintf(line, "/proc/%d/stat", pid); + if (read_line(line, sizeof(line))) + return; + name = strchr(line, '('); + if (name == NULL) + return; + name_end = strchr(name, ')'); + if (name_end == NULL) + return; + name++; + name_len = name_end - name; + } + if (name_len >= sizeof(info->name)) + name_len = sizeof(info->name) - 1; + memcpy(info->name, name, name_len); + info->name[name_len] = '\0'; + if(tid == 0) + commit_item(&processes); + else + commit_item(&threads); +} + +static void add_threads(int pid, struct thread_info *proc_info) +{ + char path[1024]; + DIR *d; + struct dirent *de; + sprintf(path, "/proc/%d/task", pid); + d = opendir(path); + if(d == 0) return; + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int tid = atoi(de->d_name); + add_thread(pid, tid, proc_info); + } + } + closedir(d); +} + +static void print_threads(int pid, uint32_t flags) +{ + size_t i, j; + for (i = 0; i < last_threads.active; i++) { + int epid = last_threads.data[i].pid; + int tid = last_threads.data[i].tid; + if (epid != pid) + continue; + for (j = 0; j < threads.active; j++) + if (tid == threads.data[j].tid) + break; + if (j == threads.active) + printf(" %5u died\n", tid); + else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count) + printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid, + NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time), + NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time), + threads.data[j].run_count - last_threads.data[i].run_count, + NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time), + threads.data[j].run_count, threads.data[j].name); + } +} + +static void update_table(DIR *d, uint32_t flags) +{ + size_t i, j; + struct dirent *de; + + rewinddir(d); + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int pid = atoi(de->d_name); + struct thread_info *proc_info; + add_thread(pid, 0, NULL); + proc_info = &processes.data[processes.active - 1]; + proc_info->exec_time = 0; + proc_info->delay_time = 0; + proc_info->run_count = 0; + add_threads(pid, proc_info); + } + } + if (!(flags & FLAG_BATCH)) + printf("\e[H\e[0J"); + printf("Processes: %d, Threads %d\n", processes.active, threads.active); + switch (time_dp) { + case 3: + printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n"); + printf(" PID EXEC_T DELAY SCHED EXEC_TIME DELAY_TIM SCHED NAME\n"); + break; + case 6: + printf(" TID ------ SINCE LAST ------- ------------ TOTAL -----------\n"); + printf(" PID EXEC_TIME DELAY_TIM SCHED EXEC_TIME DELAY_TIME SCHED NAME\n"); + break; + default: + printf(" TID -------- SINCE LAST -------- ------------- TOTAL -------------\n"); + printf(" PID EXEC_TIME DELAY_TIME SCHED EXEC_TIME DELAY_TIME SCHED NAME\n"); + break; + } + for (i = 0; i < last_processes.active; i++) { + int pid = last_processes.data[i].pid; + int tid = last_processes.data[i].tid; + for (j = 0; j < processes.active; j++) + if (pid == processes.data[j].pid) + break; + if (j == processes.active) + printf("%5u died\n", pid); + else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) { + printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid, + NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time), + NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time), + processes.data[j].run_count - last_processes.data[i].run_count, + NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time), + processes.data[j].run_count, processes.data[j].name); + if (flags & FLAG_SHOW_THREADS) + print_threads(pid, flags); + } + } + + { + struct thread_table tmp; + tmp = last_processes; + last_processes = processes; + processes = tmp; + processes.active = 0; + tmp = last_threads; + last_threads = threads; + threads = tmp; + threads.active = 0; + } +} + +void +sig_abort(int signum) +{ + printf("\e[?47l"); + exit(0); +} + + +int schedtop_main(int argc, char **argv) +{ + int c; + DIR *d; + struct dirent *de; + char *namefilter = 0; + int pidfilter = 0; + uint32_t flags = 0; + int delay = 3000000; + float delay_f; + + while(1) { + c = getopt(argc, argv, "d:ibtamun"); + if (c == EOF) + break; + switch (c) { + case 'd': + delay_f = atof(optarg); + delay = delay_f * 1000000; + break; + case 'b': + flags |= FLAG_BATCH; + break; + case 'i': + flags |= FLAG_HIDE_IDLE; + break; + case 't': + flags |= FLAG_SHOW_THREADS; + break; + case 'a': + flags |= FLAG_USE_ALTERNATE_SCREEN; + break; + case 'm': + time_dp = 3; + time_div = 1000000; + break; + case 'u': + time_dp = 6; + time_div = 1000; + break; + case 'n': + time_dp = 9; + time_div = 1; + break; + } + } + + d = opendir("/proc"); + if(d == 0) return -1; + + if (!(flags & FLAG_BATCH)) { + if(flags & FLAG_USE_ALTERNATE_SCREEN) { + signal(SIGINT, sig_abort); + signal(SIGPIPE, sig_abort); + signal(SIGTERM, sig_abort); + printf("\e7\e[?47h"); + } + printf("\e[2J"); + } + while (1) { + update_table(d, flags); + usleep(delay); + } + closedir(d); + return 0; +} + diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c new file mode 100644 index 0000000..1608e6c --- /dev/null +++ b/toolbox/sendevent.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <fcntl.h> +#include <sys/ioctl.h> +//#include <linux/input.h> // this does not compile +#include <errno.h> + + +// from <linux/input.h> + +struct input_event { + struct timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ +#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */ +#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ + +// end <linux/input.h> + + + +int sendevent_main(int argc, char *argv[]) +{ + int i; + int fd; + int ret; + int version; + struct input_event event; + + if(argc != 5) { + fprintf(stderr, "use: %s device type code value\n", argv[0]); + return 1; + } + + fd = open(argv[1], O_RDWR); + if(fd < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + if (ioctl(fd, EVIOCGVERSION, &version)) { + fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + memset(&event, 0, sizeof(event)); + event.type = atoi(argv[2]); + event.code = atoi(argv[3]); + event.value = atoi(argv[4]); + ret = write(fd, &event, sizeof(event)); + if(ret < sizeof(event)) { + fprintf(stderr, "write event failed, %s\n", strerror(errno)); + return -1; + } + return 0; +} diff --git a/toolbox/setconsole.c b/toolbox/setconsole.c new file mode 100644 index 0000000..b0ce13f --- /dev/null +++ b/toolbox/setconsole.c @@ -0,0 +1,164 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include <errno.h> +#include <pthread.h> + +static int activate_thread_switch_vc; +static void *activate_thread(void *arg) +{ + int res; + int fd = (int)arg; + while(activate_thread_switch_vc >= 0) { + do { + res = ioctl(fd, VT_ACTIVATE, (void*)activate_thread_switch_vc); + } while(res < 0 && errno == EINTR); + if (res < 0) { + fprintf(stderr, "ioctl( vcfd, VT_ACTIVATE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), activate_thread_switch_vc); + } + if(activate_thread_switch_vc >= 0) + sleep(1); + } + return NULL; +} + + +int setconsole_main(int argc, char *argv[]) +{ + int c; + int fd; + int res; + + int mode = -1; + int new_vc = 0; + int close_vc = 0; + int switch_vc = -1; + int printvc = 0; + char *ttydev = "/dev/tty0"; + + do { + c = getopt(argc, argv, "d:gtncv:poh"); + if (c == EOF) + break; + switch (c) { + case 'd': + ttydev = optarg; + break; + case 'g': + if(mode == KD_TEXT) { + fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]); + exit(1); + } + mode = KD_GRAPHICS; + break; + case 't': + if(mode == KD_GRAPHICS) { + fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]); + exit(1); + } + mode = KD_TEXT; + break; + case 'n': + new_vc = 1; + break; + case 'c': + close_vc = 1; + break; + case 'v': + switch_vc = atoi(optarg); + break; + case 'p': + printvc |= 1; + break; + case 'o': + printvc |= 2; + break; + case 'h': + fprintf(stderr, "%s [-d <dev>] [-v <vc>] [-gtncpoh]\n" + " -d <dev> Use <dev> instead of /dev/tty0\n" + " -v <vc> Switch to virtual console <vc>\n" + " -g Switch to graphics mode\n" + " -t Switch to text mode\n" + " -n Create and switch to new virtual console\n" + " -c Close unused virtual consoles\n" + " -p Print new virtual console\n" + " -o Print old virtual console\n" + " -h Print help\n", argv[0]); + return -1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(mode == -1 && new_vc == 0 && close_vc == 0 && switch_vc == -1 && printvc == 0) { + fprintf(stderr,"%s [-d <dev>] [-v <vc>] [-gtncpoh]\n", argv[0]); + return -1; + } + + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", ttydev); + return -1; + } + + if ((printvc && !new_vc) || (printvc & 2)) { + struct vt_stat vs; + + res = ioctl(fd, VT_GETSTATE, &vs); + if (res < 0) { + fprintf(stderr, "ioctl(vcfd, VT_GETSTATE, &vs) failed, %d\n", res); + } + printf("%d\n", vs.v_active); + } + + if (new_vc) { + int vtnum; + res = ioctl(fd, VT_OPENQRY, &vtnum); + if (res < 0 || vtnum == -1) { + fprintf(stderr, "ioctl(vcfd, VT_OPENQRY, &vtnum) failed, res %d, vtnum %d\n", res, vtnum); + } + switch_vc = vtnum; + } + if (switch_vc != -1) { + pthread_t thread; + pthread_attr_t attr; + activate_thread_switch_vc = switch_vc; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread, &attr, activate_thread, (void*)fd); + + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)switch_vc); + } while(res < 0 && errno == EINTR); + activate_thread_switch_vc = -1; + if (res < 0) { + fprintf(stderr, "ioctl( vcfd, VT_WAITACTIVE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), switch_vc); + } + if(printvc & 1) + printf("%d\n", switch_vc); + + close(fd); + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", ttydev); + return -1; + } + } + if (close_vc) { + res = ioctl(fd, VT_DISALLOCATE, 0); + if (res < 0) { + fprintf(stderr, "ioctl(vcfd, VT_DISALLOCATE, 0) failed, %d\n", res); + } + } + if (mode != -1) { + if (ioctl(fd, KDSETMODE, (void*)mode) < 0) { + fprintf(stderr, "KDSETMODE %d failed\n", mode); + return -1; + } + } + return 0; +} diff --git a/toolbox/setkey.c b/toolbox/setkey.c new file mode 100644 index 0000000..1ff2774 --- /dev/null +++ b/toolbox/setkey.c @@ -0,0 +1,89 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include <errno.h> + +static void setkey_usage(char *argv[]) +{ + fprintf(stderr, "%s [-t <table>] [-k <index>] [-v value] [-r] [-h]\n" + " -t <table> Select table\n" + " -k <index> Select key\n" + " -v <value> Set entry\n" + " -r Read current entry\n" + " -h Print help\n", argv[0]); +} + +#define TTYDEV "/dev/tty0" + +int setkey_main(int argc, char *argv[]) +{ + int fd; + struct kbentry kbe; + int did_something = 0; + + kbe.kb_table = 0; + kbe.kb_index = -1; + kbe.kb_value = 0; + + fd = open(TTYDEV, O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "open %s: %s\n", TTYDEV, strerror(errno)); + return 1; + } + + do { + int c, ret; + + c = getopt(argc, argv, "t:k:v:hr"); + if (c == EOF) + break; + + switch (c) { + case 't': + kbe.kb_table = strtol(optarg, NULL, 0); + break; + case 'k': + kbe.kb_index = strtol(optarg, NULL, 0); + break; + case 'v': + kbe.kb_value = strtol(optarg, NULL, 0); + ret = ioctl(fd, KDSKBENT, &kbe); + if (ret < 0) { + fprintf(stderr, "KDSKBENT %d %d %d failed: %s\n", + kbe.kb_table, kbe.kb_index, kbe.kb_value, + strerror(errno)); + return 1; + } + did_something = 1; + break; + case 'r': + ret = ioctl(fd, KDGKBENT, &kbe); + if (ret < 0) { + fprintf(stderr, "KDGKBENT %d %d failed: %s\n", + kbe.kb_table, kbe.kb_index, strerror(errno)); + return 1; + } + printf("0x%x 0x%x 0x%x\n", + kbe.kb_table, kbe.kb_index, kbe.kb_value); + did_something = 1; + break; + case 'h': + setkey_usage(argv); + return 1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + return 1; + } + } while (1); + + if(optind != argc || !did_something) { + setkey_usage(argv); + return 1; + } + + return 0; +} diff --git a/toolbox/setprop.c b/toolbox/setprop.c new file mode 100644 index 0000000..63ad2b4 --- /dev/null +++ b/toolbox/setprop.c @@ -0,0 +1,18 @@ +#include <stdio.h> + +#include <cutils/properties.h> + +int setprop_main(int argc, char *argv[]) +{ + if(argc != 3) { + fprintf(stderr,"usage: setprop <key> <value>\n"); + return 1; + } + + if(property_set(argv[1], argv[2])){ + fprintf(stderr,"could not set property\n"); + return 1; + } + + return 0; +} diff --git a/toolbox/sleep.c b/toolbox/sleep.c new file mode 100644 index 0000000..c09ae03 --- /dev/null +++ b/toolbox/sleep.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +static void +usage(const char *s) +{ + fprintf(stderr, "USAGE: %s SECONDS\n", s); + exit(-1); +} + +int sleep_main(int argc, char *argv[]) +{ + unsigned long seconds; + char *endptr; + + if (argc != 2) { + usage(argv[0]); + } + + seconds = strtoul(argv[1], &endptr, 10); + + if (endptr == argv[1]) { + usage(argv[0]); + } + + + sleep((unsigned int)seconds); + + return 0; +} + + diff --git a/toolbox/smd.c b/toolbox/smd.c new file mode 100644 index 0000000..65ff994 --- /dev/null +++ b/toolbox/smd.c @@ -0,0 +1,40 @@ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +int smd_main(int argc, char **argv) +{ + int fd, len, r, port = 0; + char devname[32]; + argc--; + argv++; + + if((argc > 0) && (argv[0][0] == '-')) { + port = atoi(argv[0] + 1); + argc--; + argv++; + } + + sprintf(devname,"/dev/smd%d",port); + fd = open(devname, O_WRONLY); + if(fd < 0) { + fprintf(stderr,"failed to open smd0 - %s\n", + strerror(errno)); + return -1; + } + while(argc > 0) { + len = strlen(argv[0]); + r = write(fd, argv[0], len); + if(r != len) { + fprintf(stderr,"failed to write smd0 (%d) %s\n", + r, strerror(errno)); + return -1; + } + argc--; + argv++; + write(fd, argc ? " " : "\r", 1); + } + close(fd); + return 0; +} diff --git a/toolbox/start.c b/toolbox/start.c new file mode 100644 index 0000000..3bd9fbb --- /dev/null +++ b/toolbox/start.c @@ -0,0 +1,20 @@ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <cutils/properties.h> + +int start_main(int argc, char *argv[]) +{ + char buf[1024]; + if(argc > 1) { + property_set("ctl.start", argv[1]); + } else { + /* default to "start zygote" "start runtime" */ + property_set("ctl.start", "zygote"); + property_set("ctl.start", "runtime"); + } + + return 0; +} diff --git a/toolbox/stop.c b/toolbox/stop.c new file mode 100644 index 0000000..05baffd --- /dev/null +++ b/toolbox/stop.c @@ -0,0 +1,20 @@ +#include <stdio.h> +#include <string.h> + +#include <cutils/properties.h> + +int stop_main(int argc, char *argv[]) +{ + char buf[1024]; + + if(argc > 1) { + property_set("ctl.stop", argv[1]); + } else{ + /* default to "stop runtime" "stop zygote" */ + property_set("ctl.stop", "runtime"); + property_set("ctl.stop", "zygote"); + } + + return 0; +} + diff --git a/toolbox/sync.c b/toolbox/sync.c new file mode 100644 index 0000000..8284276 --- /dev/null +++ b/toolbox/sync.c @@ -0,0 +1,7 @@ +#include <unistd.h> + +int sync_main(int argc, char **argv) +{ + sync(); + return 0; +} diff --git a/toolbox/syren.c b/toolbox/syren.c new file mode 100644 index 0000000..06e329e --- /dev/null +++ b/toolbox/syren.c @@ -0,0 +1,154 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <malloc.h> + +/* ioctl crap */ +#define SYREN_RD 101 +#define SYREN_WR 102 +#define SYREN_OLD_RD 108 +#define SYREN_OLD_WR 109 + +struct syren_io_args { + unsigned long page; + unsigned long addr; + unsigned long value; +}; + +typedef struct { + u_char page; + u_char addr; + const char *name; +} syren_reg; + +static syren_reg registers[] = { + { 0, 0x04, "TOGBR1" }, + { 0, 0x05, "TOGBR2" }, + { 0, 0x06, "VBDCTRL" }, + { 1, 0x07, "VBUCTRL" }, + { 1, 0x08, "VBCTRL" }, + { 1, 0x09, "PWDNRG" }, + { 1, 0x0a, "VBPOP" }, + { 1, 0x0b, "VBCTRL2" }, + { 1, 0x0f, "VAUDCTRL" }, + { 1, 0x10, "VAUSCTRL" }, + { 1, 0x11, "VAUOCTRL" }, + { 1, 0x12, "VAUDPLL" }, + { 1, 0x17, "VRPCSIMR" }, + { 0, 0, 0 } +}; + +static syren_reg *find_reg(const char *name) +{ + int i; + + for (i = 0; registers[i].name != 0; i++) { + if (!strcasecmp(registers[i].name, name)) + return ®isters[i]; + } + + return NULL; +} + +static int usage(void) +{ + fprintf(stderr, "usage: syren [r/w] [REGNAME | page:addr] (value)\n"); + return 1; +} + +int +syren_main(int argc, char **argv) +{ + int cmd = -1; + syren_reg *r; + struct syren_io_args sio; + char name[32]; + int fd; + + if (argc < 3) { + return usage(); + } + + switch(argv[1][0]) { + case 'r': + cmd = SYREN_RD; + break; + case 'w': + cmd = SYREN_WR; + break; + case 'R': + cmd = SYREN_OLD_RD; + break; + case 'W': + cmd = SYREN_OLD_WR; + break; + default: + return usage(); + } + + if (cmd == SYREN_WR || cmd == SYREN_OLD_WR) { + if (argc < 4) + return usage(); + sio.value = strtoul(argv[3], 0, 0); + } + + fd = open("/dev/eac", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "can't open /dev/eac\n"); + return 1; + } + + if (strcasecmp(argv[2], "all") == 0) { + int i; + if (cmd != SYREN_RD && cmd != SYREN_OLD_RD) { + fprintf(stderr, "can only read all registers\n"); + return 1; + } + + for (i = 0; registers[i].name; i++) { + sio.page = registers[i].page; + sio.addr = registers[i].addr; + if (ioctl(fd, cmd, &sio) < 0) { + fprintf(stderr, "%s: error\n", registers[i].name); + } else { + fprintf(stderr, "%s: %04x\n", registers[i].name, sio.value); + } + } + + close(fd); + return 0; + } + + r = find_reg(argv[2]); + if (r == NULL) { + strcpy(name, argv[2]); + char *addr_str = strchr(argv[2], ':'); + if (addr_str == NULL) + return usage(); + *addr_str++ = 0; + sio.page = strtoul(argv[2], 0, 0); + sio.addr = strtoul(addr_str, 0, 0); + } else { + strcpy(name, r->name); + sio.page = r->page; + sio.addr = r->addr; + } + + if (ioctl(fd, cmd, &sio) < 0) { + fprintf(stderr, "ioctl(%d) failed\n", cmd); + return 1; + } + + if (cmd == SYREN_RD || cmd == SYREN_OLD_RD) { + printf("%s: %04x\n", name, sio.value); + } else { + printf("wrote %04x to %s\n", sio.value, name); + } + + close(fd); + + return 0; +} + diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c new file mode 100644 index 0000000..0eac390 --- /dev/null +++ b/toolbox/toolbox.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int, char **); + +static int toolbox_main(int argc, char **argv) +{ + // "toolbox foo ..." is equivalent to "foo ..." + if (argc > 1) { + return main(argc - 1, argv + 1); + } else { + printf("Toolbox!\n"); + return 0; + } +} + +#define TOOL(name) int name##_main(int, char**); +#include "tools.h" +#undef TOOL + +static struct +{ + const char *name; + int (*func)(int, char**); +} tools[] = { + { "toolbox", toolbox_main }, +#define TOOL(name) { #name, name##_main }, +#include "tools.h" +#undef TOOL + { 0, 0 }, +}; + +int main(int argc, char **argv) +{ + int i; + char *name = argv[0]; + + if((argc > 1) && (argv[1][0] == '@')) { + name = argv[1] + 1; + argc--; + argv++; + } else { + char *cmd = strrchr(argv[0], '/'); + if (cmd) + name = cmd + 1; + } + + for(i = 0; tools[i].name; i++){ + if(!strcmp(tools[i].name, name)){ + return tools[i].func(argc, argv); + } + } + + printf("%s: no such tool\n", argv[0]); + return -1; +} diff --git a/toolbox/top.c b/toolbox/top.c new file mode 100644 index 0000000..dcc0843 --- /dev/null +++ b/toolbox/top.c @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <ctype.h> +#include <dirent.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +struct cpu_info { + long unsigned utime, ntime, stime, itime; + long unsigned iowtime, irqtime, sirqtime; +}; + +#define PROC_NAME_LEN 64 +#define THREAD_NAME_LEN 32 + +struct proc_info { + struct proc_info *next; + pid_t pid; + pid_t tid; + uid_t uid; + gid_t gid; + char name[PROC_NAME_LEN]; + char tname[THREAD_NAME_LEN]; + char state; + long unsigned utime; + long unsigned stime; + long unsigned delta_utime; + long unsigned delta_stime; + long unsigned delta_time; + long vss; + long rss; + int num_threads; +}; + +struct proc_list { + struct proc_info **array; + int size; +}; + +#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); } + +#define INIT_PROCS 50 +#define THREAD_MULT 8 +static struct proc_info **old_procs, **new_procs; +static int num_old_procs, num_new_procs; +static struct proc_info *free_procs; +static int num_used_procs, num_free_procs; + +static int max_procs, delay, iterations, threads; + +static struct cpu_info old_cpu, new_cpu; + +static struct proc_info *alloc_proc(void); +static void free_proc(struct proc_info *proc); +static void read_procs(void); +static int read_stat(char *filename, struct proc_info *proc); +static void add_proc(int proc_num, struct proc_info *proc); +static int read_cmdline(char *filename, struct proc_info *proc); +static int read_status(char *filename, struct proc_info *proc); +static void print_procs(void); +static struct proc_info *find_old_proc(pid_t pid, pid_t tid); +static void free_old_procs(void); +static int (*proc_cmp)(const void *a, const void *b); +static int proc_cpu_cmp(const void *a, const void *b); +static int proc_vss_cmp(const void *a, const void *b); +static int proc_rss_cmp(const void *a, const void *b); +static int proc_thr_cmp(const void *a, const void *b); +static int numcmp(long long a, long long b); +static void usage(char *cmd); + +int top_main(int argc, char *argv[]) { + int i; + + num_used_procs = num_free_procs = 0; + + max_procs = 0; + delay = 3; + iterations = -1; + proc_cmp = &proc_cpu_cmp; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-m")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -m expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + max_procs = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-n")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -n expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + iterations = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-d")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -d expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + delay = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-s")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -s expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + ++i; + if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; } + if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; } + if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; } + if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; } + fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]); + exit(EXIT_FAILURE); + } + if (!strcmp(argv[i], "-t")) { threads = 1; continue; } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (threads && proc_cmp == &proc_thr_cmp) { + fprintf(stderr, "Sorting by threads per thread makes no sense!\n"); + exit(EXIT_FAILURE); + } + + free_procs = NULL; + + num_new_procs = num_old_procs = 0; + new_procs = old_procs = NULL; + + read_procs(); + while ((iterations == -1) || (iterations-- > 0)) { + old_procs = new_procs; + num_old_procs = num_new_procs; + memcpy(&old_cpu, &new_cpu, sizeof(old_cpu)); + sleep(delay); + read_procs(); + print_procs(); + free_old_procs(); + } + + return 0; +} + +static struct proc_info *alloc_proc(void) { + struct proc_info *proc; + + if (free_procs) { + proc = free_procs; + free_procs = free_procs->next; + num_free_procs--; + } else { + proc = malloc(sizeof(*proc)); + if (!proc) die("Could not allocate struct process_info.\n"); + } + + num_used_procs++; + + return proc; +} + +static void free_proc(struct proc_info *proc) { + proc->next = free_procs; + free_procs = proc; + + num_used_procs--; + num_free_procs++; +} + +#define MAX_LINE 256 + +static void read_procs(void) { + DIR *proc_dir, *task_dir; + struct dirent *pid_dir, *tid_dir; + char filename[64]; + FILE *file; + int proc_num; + struct proc_info *proc; + pid_t pid, tid; + + int i; + + proc_dir = opendir("/proc"); + if (!proc_dir) die("Could not open /proc.\n"); + + new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *)); + num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1); + + file = fopen("/proc/stat", "r"); + if (!file) die("Could not open /proc/stat.\n"); + fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime, + &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime); + fclose(file); + + proc_num = 0; + while ((pid_dir = readdir(proc_dir))) { + if (!isdigit(pid_dir->d_name[0])) + continue; + + pid = atoi(pid_dir->d_name); + + struct proc_info cur_proc; + + if (!threads) { + proc = alloc_proc(); + + proc->pid = proc->tid = pid; + + sprintf(filename, "/proc/%d/stat", pid); + read_stat(filename, proc); + + sprintf(filename, "/proc/%d/cmdline", pid); + read_cmdline(filename, proc); + + sprintf(filename, "/proc/%d/status", pid); + read_status(filename, proc); + + proc->num_threads = 0; + } else { + sprintf(filename, "/proc/%d/cmdline", pid); + read_cmdline(filename, &cur_proc); + + sprintf(filename, "/proc/%d/status", pid); + read_status(filename, &cur_proc); + + proc = NULL; + } + + sprintf(filename, "/proc/%d/task", pid); + task_dir = opendir(filename); + if (!task_dir) continue; + + while ((tid_dir = readdir(task_dir))) { + if (!isdigit(tid_dir->d_name[0])) + continue; + + if (threads) { + tid = atoi(tid_dir->d_name); + + proc = alloc_proc(); + + proc->pid = pid; proc->tid = tid; + + sprintf(filename, "/proc/%d/task/%d/stat", pid, tid); + read_stat(filename, proc); + + strcpy(proc->name, cur_proc.name); + proc->uid = cur_proc.uid; + proc->gid = cur_proc.gid; + + add_proc(proc_num++, proc); + } else { + proc->num_threads++; + } + } + + closedir(task_dir); + + if (!threads) + add_proc(proc_num++, proc); + } + + for (i = proc_num; i < num_new_procs; i++) + new_procs[i] = NULL; + + closedir(proc_dir); +} + +static int read_stat(char *filename, struct proc_info *proc) { + FILE *file; + char buf[MAX_LINE], *open_paren, *close_paren; + int res, idx; + + file = fopen(filename, "r"); + if (!file) return 1; + fgets(buf, MAX_LINE, file); + fclose(file); + + /* Split at first '(' and last ')' to get process name. */ + open_paren = strchr(buf, '('); + close_paren = strrchr(buf, ')'); + if (!open_paren || !close_paren) return 1; + + *open_paren = *close_paren = '\0'; + strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN); + proc->tname[THREAD_NAME_LEN-1] = 0; + + /* Scan rest of string. */ + sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld", + &proc->state, &proc->utime, &proc->stime, &proc->vss, &proc->rss); + + return 0; +} + +static void add_proc(int proc_num, struct proc_info *proc) { + int i; + + if (proc_num >= num_new_procs) { + new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *)); + if (!new_procs) die("Could not expand procs array.\n"); + for (i = num_new_procs; i < 2 * num_new_procs; i++) + new_procs[i] = NULL; + num_new_procs = 2 * num_new_procs; + } + new_procs[proc_num] = proc; +} + +static int read_cmdline(char *filename, struct proc_info *proc) { + FILE *file; + char line[MAX_LINE]; + + line[0] = '\0'; + file = fopen(filename, "r"); + if (!file) return 1; + fgets(line, MAX_LINE, file); + fclose(file); + if (strlen(line) > 0) { + strncpy(proc->name, line, PROC_NAME_LEN); + proc->name[PROC_NAME_LEN-1] = 0; + } else + proc->name[0] = 0; + return 0; +} + +static int read_status(char *filename, struct proc_info *proc) { + FILE *file; + char line[MAX_LINE]; + unsigned int uid, gid; + + file = fopen(filename, "r"); + if (!file) return 1; + while (fgets(line, MAX_LINE, file)) { + sscanf(line, "Uid: %u", &uid); + sscanf(line, "Gid: %u", &gid); + } + fclose(file); + proc->uid = uid; proc->gid = gid; + return 0; +} + +static void print_procs(void) { + int i; + struct proc_info *old_proc, *proc; + long unsigned total_delta_time; + struct passwd *user; + struct group *group; + char *user_str, user_buf[20]; + char *group_str, group_buf[20]; + + for (i = 0; i < num_new_procs; i++) { + if (new_procs[i]) { + old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid); + if (old_proc) { + new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime; + new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime; + } else { + new_procs[i]->delta_utime = 0; + new_procs[i]->delta_stime = 0; + } + new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime; + } + } + + total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime + + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime) + - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime + + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime); + + qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp); + + printf("\n\n\n"); + printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n", + ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time, + ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time, + ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time, + ((new_cpu.irqtime + new_cpu.sirqtime) + - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time); + printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n", + new_cpu.utime - old_cpu.utime, + new_cpu.ntime - old_cpu.ntime, + new_cpu.stime - old_cpu.stime, + new_cpu.itime - old_cpu.itime, + new_cpu.iowtime - old_cpu.iowtime, + new_cpu.irqtime - old_cpu.irqtime, + new_cpu.sirqtime - old_cpu.sirqtime, + total_delta_time); + printf("\n"); + if (!threads) + printf("%5s %4s %1s %5s %7s %7s %-8s %s\n", "PID", "CPU%", "S", "#THR", "VSS", "RSS", "UID", "Name"); + else + printf("%5s %5s %4s %1s %7s %7s %-8s %-15s %s\n", "PID", "TID", "CPU%", "S", "VSS", "RSS", "UID", "Thread", "Proc"); + + for (i = 0; i < num_new_procs; i++) { + proc = new_procs[i]; + + if (!proc || (max_procs && (i >= max_procs))) + break; + user = getpwuid(proc->uid); + group = getgrgid(proc->gid); + if (user && user->pw_name) { + user_str = user->pw_name; + } else { + snprintf(user_buf, 20, "%d", proc->uid); + user_str = user_buf; + } + if (group && group->gr_name) { + group_str = group->gr_name; + } else { + snprintf(group_buf, 20, "%d", proc->gid); + group_str = group_buf; + } + if (!threads) + printf("%5d %3ld%% %c %5d %6ldK %6ldK %-8.8s %s\n", proc->pid, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads, + proc->vss / 1024, proc->rss * getpagesize() / 1024, user_str, proc->name[0] != 0 ? proc->name : proc->tname); + else + printf("%5d %5d %3ld%% %c %6ldK %6ldK %-8.8s %-15s %s\n", proc->pid, proc->tid, proc->delta_time * 100 / total_delta_time, proc->state, + proc->vss / 1024, proc->rss * getpagesize() / 1024, user_str, proc->tname, proc->name); + } +} + +static struct proc_info *find_old_proc(pid_t pid, pid_t tid) { + int i; + + for (i = 0; i < num_old_procs; i++) + if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid)) + return old_procs[i]; + + return NULL; +} + +static void free_old_procs(void) { + int i; + + for (i = 0; i < num_old_procs; i++) + if (old_procs[i]) + free_proc(old_procs[i]); + + free(old_procs); +} + +static int proc_cpu_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->delta_time, pb->delta_time); +} + +static int proc_vss_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->vss, pb->vss); +} + +static int proc_rss_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->rss, pb->rss); +} + +static int proc_thr_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->num_threads, pb->num_threads); +} + +static int numcmp(long long a, long long b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static void usage(char *cmd) { + fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n" + " -m num Maximum number of processes to display.\n" + " -n num Updates to show before exiting.\n" + " -d num Seconds to wait between updates.\n" + " -s col Column to sort by (cpu,vss,rss,thr).\n" + " -t Show threads instead of processes.\n" + " -h Display this help screen.\n", + cmd); +} diff --git a/toolbox/umount.c b/toolbox/umount.c new file mode 100644 index 0000000..92c6076 --- /dev/null +++ b/toolbox/umount.c @@ -0,0 +1,74 @@ + +#include <sys/mount.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <linux/loop.h> + +// FIXME - only one loop mount is supported at a time +#define LOOP_DEVICE "/dev/block/loop0" + +static int is_loop_mount(const char* path) +{ + FILE* f; + int count; + char device[256]; + char mount_path[256]; + char rest[256]; + int result = 0; + int path_length = strlen(path); + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stdout, "could not open /proc/mounts\n"); + return -1; + } + + do { + count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest); + if (count == 3) { + if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0) { + result = 1; + break; + } + } + } while (count == 3); + + fclose(f); + return result; +} + +int umount_main(int argc, char *argv[]) +{ + int loop, loop_fd; + + if(argc != 2) { + fprintf(stderr,"umount <path>\n"); + return 1; + } + + loop = is_loop_mount(argv[1]); + if(umount(argv[1])){ + fprintf(stderr,"failed.\n"); + return 1; + } + + if (loop) { + // free the loop device + loop_fd = open(LOOP_DEVICE, O_RDONLY); + if (loop_fd < -1) { + perror("open loop device failed"); + return 1; + } + if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { + perror("ioctl LOOP_CLR_FD failed"); + return 1; + } + + close(loop_fd); + } + + return 0; +} diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c new file mode 100644 index 0000000..600f136 --- /dev/null +++ b/toolbox/vmstat.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> +#include <unistd.h> + +struct state { + long procs_r; + long procs_b; + + long mem_free; + long mem_mapped; + long mem_anon; + long mem_slab; + + long sys_in; + long sys_cs; + long sys_flt; + + long cpu_us; + long cpu_ni; + long cpu_sy; + long cpu_id; + long cpu_wa; + long cpu_ir; + long cpu_si; +}; + +#define MAX_LINE 256 + +char line[MAX_LINE]; + +static void read_state(struct state *s); +static int read_meminfo(struct state *s); +static int read_stat(struct state *s); +static int read_vmstat(struct state *s); +static void print_header(void); +static void print_line(struct state *old, struct state *new); +static void usage(char *cmd); + +int vmstat_main(int argc, char *argv[]) { + struct state s[2]; + int iterations, delay, header_interval; + int toggle, count; + int i; + + iterations = 0; + delay = 1; + header_interval = 20; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-n")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -n requires an argument.\n"); + exit(EXIT_FAILURE); + } + iterations = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-d")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -d requires an argument.\n"); + exit(EXIT_FAILURE); + } + delay = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-r")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -r requires an argument.\n"); + exit(EXIT_FAILURE); + } + header_interval = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + toggle = 0; + count = 0; + + if (!header_interval) + print_header(); + read_state(&s[1 - toggle]); + while ((iterations == 0) || (iterations-- > 0)) { + sleep(delay); + read_state(&s[toggle]); + if (header_interval) { + if (count == 0) + print_header(); + count = (count + 1) % header_interval; + } + print_line(&s[1 - toggle], &s[toggle]); + toggle = 1 - toggle; + } + + return 0; +} + +static void read_state(struct state *s) { + int error; + + error = read_meminfo(s); + if (error) { + fprintf(stderr, "vmstat: could not read /proc/meminfo: %s\n", strerror(error)); + exit(EXIT_FAILURE); + } + + error = read_stat(s); + if (error) { + fprintf(stderr, "vmstat: could not read /proc/stat: %s\n", strerror(error)); + exit(EXIT_FAILURE); + } + + error = read_vmstat(s); + if (error) { + fprintf(stderr, "vmstat: could not read /proc/vmstat: %s\n", strerror(error)); + exit(EXIT_FAILURE); + } +} + +static int read_meminfo(struct state *s) { + FILE *f; + + f = fopen("/proc/meminfo", "r"); + if (!f) return errno; + + while (fgets(line, MAX_LINE, f)) { + sscanf(line, "MemFree: %ld kB", &s->mem_free); + sscanf(line, "AnonPages: %ld kB", &s->mem_anon); + sscanf(line, "Mapped: %ld kB", &s->mem_mapped); + sscanf(line, "Slab: %ld kB", &s->mem_slab); + } + + fclose(f); + + return 0; +} + +static int read_stat(struct state *s) { + FILE *f; + + f = fopen("/proc/stat", "r"); + if (!f) return errno; + + while (fgets(line, MAX_LINE, f)) { + if (!strncmp(line, "cpu ", 4)) { + sscanf(line, "cpu %ld %ld %ld %ld %ld %ld %ld", + &s->cpu_us, &s->cpu_ni, &s->cpu_sy, &s->cpu_id, &s->cpu_wa, + &s->cpu_ir, &s->cpu_si); + } + sscanf(line, "intr %ld", &s->sys_in); + sscanf(line, "ctxt %ld", &s->sys_cs); + sscanf(line, "procs_running %ld", &s->procs_r); + sscanf(line, "procs_blocked %ld", &s->procs_b); + } + + fclose(f); + + return 0; +} + +static int read_vmstat(struct state *s) { + FILE *f; + + f = fopen("/proc/vmstat", "r"); + if (!f) return errno; + + while (fgets(line, MAX_LINE, f)) { + sscanf(line, "pgmajfault %ld", &s->sys_flt); + } + + fclose(f); + + return 0; +} + +static void print_header(void) { + printf("%-5s %-27s %-14s %-17s\n", "procs", "memory", "system", "cpu"); + printf("%2s %2s %6s %6s %6s %6s %4s %4s %4s %2s %2s %2s %2s %2s %2s\n", "r", "b", "free", "mapped", "anon", "slab", "in", "cs", "flt", "us", "ni", "sy", "id", "wa", "ir"); +} + +/* Jiffies to percent conversion */ +#define JP(jif) ((jif) * 100 / (HZ)) +#define NORM(var) ((var) = (((var) > 99) ? (99) : (var))) + +static void print_line(struct state *old, struct state *new) { + int us, ni, sy, id, wa, ir; + us = JP(new->cpu_us - old->cpu_us); NORM(us); + ni = JP(new->cpu_ni - old->cpu_ni); NORM(ni); + sy = JP(new->cpu_sy - old->cpu_sy); NORM(sy); + id = JP(new->cpu_id - old->cpu_id); NORM(id); + wa = JP(new->cpu_wa - old->cpu_wa); NORM(wa); + ir = JP(new->cpu_ir - old->cpu_ir); NORM(ir); + printf("%2ld %2ld %6ld %6ld %6ld %6ld %4ld %4ld %4ld %2d %2d %2d %2d %2d %2d\n", + new->procs_r ? (new->procs_r - 1) : 0, new->procs_b, + new->mem_free, new->mem_mapped, new->mem_anon, new->mem_slab, + new->sys_in - old->sys_in, new->sys_cs - old->sys_cs, new->sys_flt - old->sys_flt, + us, ni, sy, id, wa, ir); +} + +static void usage(char *cmd) { + fprintf(stderr, "Usage: %s [ -h ] [ -n iterations ] [ -d delay ] [ -r header_repeat ]\n" + " -n iterations How many rows of data to print.\n" + " -d delay How long to sleep between rows.\n" + " -r header_repeat How many rows to print before repeating\n" + " the header. Zero means never repeat.\n" + " -h Displays this help screen.\n", + cmd); +} diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c new file mode 100644 index 0000000..d311992 --- /dev/null +++ b/toolbox/watchprops.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include <cutils/properties.h> + +#include <sys/atomics.h> + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + + +extern prop_area *__system_property_area__; + +typedef struct pwatch pwatch; + +struct pwatch +{ + const prop_info *pi; + unsigned serial; +}; + +static pwatch watchlist[1024]; + +static void announce(const prop_info *pi) +{ + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; + char *x; + + __system_property_read(pi, name, value); + + for(x = value; *x; x++) { + if((*x < 32) || (*x > 127)) *x = '.'; + } + + fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value); +} + +int watchprops_main(int argc, char *argv[]) +{ + prop_area *pa = __system_property_area__; + unsigned serial = pa->serial; + unsigned count = pa->count; + unsigned n; + + if(count >= 1024) exit(1); + + for(n = 0; n < count; n++) { + watchlist[n].pi = __system_property_find_nth(n); + watchlist[n].serial = watchlist[n].pi->serial; + } + + for(;;) { + do { + __futex_wait(&pa->serial, serial, 0); + } while(pa->serial == serial); + + while(count < pa->count){ + watchlist[count].pi = __system_property_find_nth(count); + watchlist[count].serial = watchlist[n].pi->serial; + announce(watchlist[count].pi); + count++; + if(count == 1024) exit(1); + } + + for(n = 0; n < count; n++){ + unsigned tmp = watchlist[n].pi->serial; + if(watchlist[n].serial != tmp) { + announce(watchlist[n].pi); + watchlist[n].serial = tmp; + } + } + } + return 0; +} diff --git a/toolbox/wipe.c b/toolbox/wipe.c new file mode 100644 index 0000000..7e263fd --- /dev/null +++ b/toolbox/wipe.c @@ -0,0 +1,176 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/reboot.h> +#include <sys/stat.h> + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + + +/* Directories created by init defined in system/rootdir/init.rc */ +static char *INIT_DIRS[] = { + "/system/etc/ppp", + "/data/misc", + "/data/local", + "/data/local/tmp", + "/data/data", + "/data/app_private", + "/data/app", + NULL +}; + +static void wipe (const char *path); + +static int usage() +{ + fprintf(stderr, "wipe <system|data|all>\n\n" + "system means '/system'\n" + "data means '/data'\n"); + + return -1; +} + +int wipe_main (int argc, char *argv[]) +{ + char *whatToWipe; + + if (argc != 2) return usage(); + + whatToWipe = argv[1]; + + if (0 == strcmp (whatToWipe, "system")) { + fprintf(stdout, "Wiping /system\n"); + wipe ("/system"); + fprintf(stdout, "Done wiping /android\n"); + } else if (0 == strcmp (whatToWipe, "data")) { + fprintf(stdout, "Wiping /data\n"); + wipe ("/data"); + fprintf(stdout, "Done wiping /data\n"); + } else if (0 == strcmp (whatToWipe, "all")) { + fprintf(stdout, "Wiping /system and /data\n"); + wipe ("/system"); + wipe ("/data"); + fprintf(stdout, "Done wiping /system and /data\n"); + } else if (0 == strcmp(whatToWipe, "nuke")) { + int ret; + fprintf(stdout, "Nuking the device...\n"); + wipe ("/system"); + wipe ("/data"); + fprintf(stdout, "Device nuked! Rebooting...\n"); + ret = reboot(RB_AUTOBOOT); + if (ret < 0) { + fprintf(stderr, "Reboot failed, %s\n", strerror(errno)); + return 1; + } + } else { + return usage(); + } + + return 0; +} + +static char nameBuffer[PATH_MAX]; +static struct stat statBuffer; + +static void wipe (const char *path) +{ + DIR *dir; + struct dirent *de; + int ret; + + dir = opendir(path); + + if (dir == NULL) { + fprintf (stderr, "Error opendir'ing %s '%s'\n", + path, strerror(errno)); + return; + } + + char *filenameOffset; + + strcpy(nameBuffer, path); + strcat(nameBuffer, "/"); + + filenameOffset = nameBuffer + strlen(nameBuffer); + + for (;;) { + de = readdir(dir); + + if (de == NULL) { + break; + } + + if (0 == strcmp(de->d_name, ".") + || 0 == strcmp(de->d_name, "..") + || 0 == strcmp(de->d_name, "lost+found") + ) { + continue; + } + + strcpy(filenameOffset, de->d_name); + + ret = lstat (nameBuffer, &statBuffer); + + if (ret != 0) { + fprintf(stderr, "stat() error on '%s' '%s'\n", + nameBuffer, strerror(errno)); + } + + if(S_ISDIR(statBuffer.st_mode)) { + int i; + char *newpath; + +#if 0 + closedir(dir); +#endif + + newpath = strdup(nameBuffer); + wipe(newpath); + + /* Leave directories created by init, they have special permissions. */ + for (i = 0; INIT_DIRS[i]; i++) { + if (strcmp(INIT_DIRS[i], newpath) == 0) { + break; + } + } + if (INIT_DIRS[i] == NULL) { + ret = rmdir(newpath); + if (ret != 0) { + fprintf(stderr, "rmdir() error on '%s' '%s'\n", + newpath, strerror(errno)); + } + } + + free(newpath); + +#if 0 + dir = opendir(path); + if (dir == NULL) { + fprintf (stderr, "Error opendir'ing %s '%s'\n", + path, strerror(errno)); + return; + } +#endif + + strcpy(nameBuffer, path); + strcat(nameBuffer, "/"); + + } else { + ret = unlink(nameBuffer); + + if (ret != 0) { + fprintf(stderr, "unlink() error on '%s' '%s'\n", + nameBuffer, strerror(errno)); + } + } + } + + closedir(dir); + +} diff --git a/vold/Android.mk b/vold/Android.mk new file mode 100644 index 0000000..3dd9f87 --- /dev/null +++ b/vold/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + vold.c \ + cmd_dispatch.c \ + uevent.c \ + mmc.c \ + misc.c \ + blkdev.c \ + ums.c \ + geom_mbr_enc.c \ + volmgr.c \ + media.c \ + volmgr_vfat.c \ + volmgr_ext3.c \ + logwrapper.c \ + ProcessKiller.c\ + switch.c \ + format.c \ + devmapper.c + +LOCAL_MODULE:= vold + +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) + +LOCAL_CFLAGS := + +LOCAL_SHARED_LIBRARIES := libcutils + +include $(BUILD_EXECUTABLE) diff --git a/vold/ProcessKiller.c b/vold/ProcessKiller.c new file mode 100644 index 0000000..fc3eb37 --- /dev/null +++ b/vold/ProcessKiller.c @@ -0,0 +1,222 @@ +/* + * 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. + */ + +/* +** mountd process killer +*/ + +#include "vold.h" + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <poll.h> +#include <sys/stat.h> + + +static boolean ReadSymLink(const char* path, char* link) +{ + struct stat s; + int length; + + if (lstat(path, &s) < 0) + return false; + if ((s.st_mode & S_IFMT) != S_IFLNK) + return false; + + // we have a symlink + length = readlink(path, link, PATH_MAX - 1); + if (length <= 0) + return false; + link[length] = 0; + return true; +} + +static boolean PathMatchesMountPoint(const char* path, const char* mountPoint) +{ + int length = strlen(mountPoint); + if (length > 1 && strncmp(path, mountPoint, length) == 0) + { + // we need to do extra checking if mountPoint does not end in a '/' + if (mountPoint[length - 1] == '/') + return true; + // if mountPoint does not have a trailing slash, we need to make sure + // there is one in the path to avoid partial matches. + return (path[length] == 0 || path[length] == '/'); + } + + return false; +} + +static void GetProcessName(int pid, char buffer[PATH_MAX]) +{ + int fd; + sprintf(buffer, "/proc/%d/cmdline", pid); + fd = open(buffer, O_RDONLY); + if (fd < 0) { + strcpy(buffer, "???"); + } else { + int length = read(fd, buffer, PATH_MAX - 1); + buffer[length] = 0; + close(fd); + } +} + +static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint) +{ + DIR* dir; + struct dirent* de; + boolean fileOpen = false; + char path[PATH_MAX]; + char link[PATH_MAX]; + int parent_length; + + // compute path to process's directory of open files + sprintf(path, "/proc/%d/fd", pid); + dir = opendir(path); + if (!dir) + return false; + + // remember length of the path + parent_length = strlen(path); + // append a trailing '/' + path[parent_length++] = '/'; + + while ((de = readdir(dir)) != 0 && !fileOpen) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + // append the file name, after truncating to parent directory + path[parent_length] = 0; + strcat(path, de->d_name); + + if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has open file %s", name, pid, link); + fileOpen = true; + } + } + + closedir(dir); + return fileOpen; +} + +static boolean CheckFileMaps(int pid, const char* mountPoint) +{ + FILE* file; + char buffer[PATH_MAX + 100]; + boolean mapOpen = false; + + sprintf(buffer, "/proc/%d/maps", pid); + file = fopen(buffer, "r"); + if (!file) + return false; + + while (!mapOpen && fgets(buffer, sizeof(buffer), file)) + { + // skip to the path + const char* path = strchr(buffer, '/'); + if (path && PathMatchesMountPoint(path, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has open file map for %s", name, pid, path); + mapOpen = true; + } + } + + fclose(file); + return mapOpen; +} + +static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message) +{ + char path[PATH_MAX]; + char link[PATH_MAX]; + + sprintf(path, "/proc/%d/%s", pid, name); + if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) + { + char name[PATH_MAX]; + GetProcessName(pid, name); + LOG_ERROR("process %s (%d) has %s in %s", name, pid, message, mountPoint); + return true; + } + else + return false; +} + +static int get_pid(const char* s) +{ + int result = 0; + while (*s) { + if (!isdigit(*s)) return -1; + result = 10 * result + (*s++ - '0'); + } + return result; +} + +// hunt down and kill processes that have files open on the given mount point +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded) +{ + DIR* dir; + struct dirent* de; + + LOG_ERROR("KillProcessesWithOpenFiles %s", mountPoint); + dir = opendir("/proc"); + if (!dir) return; + + while ((de = readdir(dir)) != 0) + { + boolean killed = false; + // does the name look like a process ID? + int pid = get_pid(de->d_name); + if (pid == -1) continue; + + if (CheckFileDescriptorSymLinks(pid, mountPoint) // check for open files + || CheckFileMaps(pid, mountPoint) // check for mmap() + || CheckSymLink(pid, mountPoint, "cwd", "working directory") // check working directory + || CheckSymLink(pid, mountPoint, "root", "chroot") // check for chroot() + || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path + ) + { + int i; + boolean hit = false; + + for (i = 0; i < num_excluded; i++) { + if (pid == excluded[i]) { + LOG_ERROR("I just need a little more TIME captain!"); + hit = true; + break; + } + } + + if (!hit) { + LOG_ERROR("Killing process %d", pid); + kill(pid, (sigkill ? SIGKILL : SIGTERM)); + } + } + } + + closedir(dir); +} diff --git a/vold/blkdev.c b/vold/blkdev.c new file mode 100644 index 0000000..533bc35 --- /dev/null +++ b/vold/blkdev.c @@ -0,0 +1,319 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <linux/fs.h> +#include <linux/msdos_fs.h> + +#include "vold.h" +#include "blkdev.h" +#include "diskmbr.h" + +#define DEBUG_BLKDEV 0 + +static blkdev_list_t *list_root = NULL; + +static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major, + int minor, char *type, struct media *media); + +static int fat_valid_media(unsigned char media) +{ + return 0xf8 <= media || media == 0xf0; +} + +char *blkdev_get_devpath(blkdev_t *blk) +{ + char *dp = malloc(256); + sprintf(dp, "%s/vold/%d:%d", DEVPATH, blk->major, blk->minor); + return dp; +} + +int blkdev_refresh(blkdev_t *blk) +{ + int fd = 0; + char *devpath = NULL; + unsigned char *block = NULL; + int i, rc; + + if (!(block = malloc(512))) + goto out; + + /* + * Get the device size + */ + devpath = blkdev_get_devpath(blk); + + if ((fd = open(devpath, O_RDONLY)) < 0) { + LOGE("Unable to open device '%s' (%s)", devpath, strerror(errno)); + return -errno; + } + + if (ioctl(fd, BLKGETSIZE, &blk->nr_sec)) { + LOGE("Unable to get device size (%m)"); + return -errno; + } + close(fd); + free(devpath); + + /* + * Open the disk partition table + */ + devpath = blkdev_get_devpath(blk->disk); + if ((fd = open(devpath, O_RDONLY)) < 0) { + LOGE("Unable to open device '%s' (%s)", devpath, + strerror(errno)); + free(devpath); + return -errno; + } + + free(devpath); + + if ((rc = read(fd, block, 512)) != 512) { + LOGE("Unable to read device partition table (%d, %s)", + rc, strerror(errno)); + goto out; + } + + /* + * If we're a disk, then process the partition table. Otherwise we're + * a partition so get the partition type + */ + + if (blk->type == blkdev_disk) { + blk->nr_parts = 0; + + if ((block[0x1fe] != 0x55) || (block[0x1ff] != 0xAA)) { + LOG_VOL("Disk %d:%d does not contain a partition table", + blk->major, blk->minor); + goto out; + } + + for (i = 0; i < 4; i++) { + struct dos_partition part; + + dos_partition_dec(block + DOSPARTOFF + i * sizeof(struct dos_partition), &part); + if (part.dp_flag != 0 && part.dp_flag != 0x80) { + struct fat_boot_sector *fb = (struct fat_boot_sector *) &block[0]; + + if (!i && fb->reserved && fb->fats && fat_valid_media(fb->media)) { + LOG_VOL("Detected FAT filesystem in partition table"); + break; + } else { + LOG_VOL("Partition table looks corrupt"); + break; + } + } + if (part.dp_size != 0 && part.dp_typ != 0) + blk->nr_parts++; + } + } else if (blk->type == blkdev_partition) { + struct dos_partition part; + int part_no = blk->minor -1; + + dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part); + blk->part_type = part.dp_typ; + } + + out: + + if (block) + free(block); + + char tmp[255]; + char tmp2[32]; + sprintf(tmp, "%s (blkdev %d:%d), %u secs (%u MB)", + (blk->type == blkdev_disk ? "Disk" : "Partition"), + blk->major, blk->minor, + blk->nr_sec, + (uint32_t) (((uint64_t) blk->nr_sec * 512) / 1024) / 1024); + + if (blk->type == blkdev_disk) + sprintf(tmp2, " %d partitions", blk->nr_parts); + else + sprintf(tmp2, " type 0x%x", blk->part_type); + + strcat(tmp, tmp2); + LOG_VOL(tmp); + + close(fd); + + return 0; +} + +blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type) +{ + return _blkdev_create(disk, devpath, major, minor, type, media); +} + +static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major, + int minor, char *type, struct media *media) +{ + blkdev_t *new; + struct blkdev_list *list_entry; + + if (disk && disk->type != blkdev_disk) { + LOGE("Non disk parent specified for blkdev!"); + return NULL; + } + + if (!(new = malloc(sizeof(blkdev_t)))) + return NULL; + + memset(new, 0, sizeof(blkdev_t)); + + if (!(list_entry = malloc(sizeof(struct blkdev_list)))) { + free (new); + return NULL; + } + list_entry->dev = new; + list_entry->next = NULL; + + if (!list_root) + list_root = list_entry; + else { + struct blkdev_list *list_scan = list_root; + while (list_scan->next) + list_scan = list_scan->next; + list_scan->next = list_entry; + } + + if (devpath) + new->devpath = strdup(devpath); + new->major = major; + new->minor = minor; + new->media = media; + new->nr_sec = 0xffffffff; + + if (disk) + new->disk = disk; + else + new->disk = new; // Note the self disk pointer + + /* Create device nodes */ + char nodepath[255]; + mode_t mode = 0666 | S_IFBLK; + dev_t dev = (major << 8) | minor; + + sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, major, minor); + if (mknod(nodepath, mode, dev) < 0) { + LOGE("Error making device nodes for '%s' (%s)", + nodepath, strerror(errno)); + } + + if (!strcmp(type, "disk")) + new->type = blkdev_disk; + else if (!strcmp(type, "partition")) + new->type = blkdev_partition; + else { + LOGE("Unknown block device type '%s'", type); + new->type = blkdev_unknown; + } + + return new; +} + +void blkdev_destroy(blkdev_t *blkdev) +{ + struct blkdev_list *list_next; + + if (list_root->dev == blkdev) { + list_next = list_root->next; + free (list_root); + list_root = list_next; + } else { + struct blkdev_list *list_scan = list_root; + while (list_scan->next->dev != blkdev) + list_scan = list_scan -> next; + list_next = list_scan->next->next; + free(list_scan->next); + list_scan->next = list_next; + } + + if (blkdev->devpath) + free(blkdev->devpath); + + char nodepath[255]; + sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, blkdev->major, blkdev->minor); + unlink(nodepath); + + free(blkdev); +} + +blkdev_t *blkdev_lookup_by_path(char *devpath) +{ + struct blkdev_list *list_scan = list_root; + + while (list_scan) { + if (!strcmp(list_scan->dev->devpath, devpath)) + return list_scan->dev; + list_scan = list_scan->next; + } + return NULL; +} + +blkdev_t *blkdev_lookup_by_devno(int maj, int min) +{ + struct blkdev_list *list_scan = list_root; + + while (list_scan) { + if ((list_scan->dev->major == maj) && + (list_scan->dev->minor == min)) + return list_scan->dev; + list_scan = list_scan->next; + } + return NULL; +} + +/* + * Given a disk device, return the number of partitions which + * have yet to be processed. + */ +int blkdev_get_num_pending_partitions(blkdev_t *blk) +{ + struct blkdev_list *list_scan = list_root; + int num = blk->nr_parts; + + if (blk->type != blkdev_disk) + return -EINVAL; + + while (list_scan) { + if (list_scan->dev->type != blkdev_partition) + goto next; + + if (list_scan->dev->major != blk->major) + goto next; + + if (list_scan->dev->nr_sec != 0xffffffff && + list_scan->dev->devpath) { + num--; + } + next: + list_scan = list_scan->next; + } + return num; +} + diff --git a/vold/blkdev.h b/vold/blkdev.h new file mode 100644 index 0000000..e789739 --- /dev/null +++ b/vold/blkdev.h @@ -0,0 +1,64 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BLKDEV_H +#define _BLKDEV_H + +#include <sys/types.h> + +struct media; + +enum blk_type { blkdev_unknown, blkdev_disk, blkdev_partition }; + +struct blkdev { + char *devpath; + enum blk_type type; + struct media *media; + + // If type == blkdev_disk then nr_parts = number of partitions + int nr_parts; + + // If type == blkdev_partition then part_type = partition type + uint8_t part_type; + // If type == blkdev_partition + struct blkdev *disk; + + unsigned int nr_sec; + + int major; + int minor; +}; + +struct blkdev_list { + struct blkdev *dev; + struct blkdev_list *next; +}; + +typedef struct blkdev blkdev_t; +typedef struct blkdev_list blkdev_list_t; + +blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type); +blkdev_t *blkdev_create_pending_partition(blkdev_t *disk, char *dev_fspath, int major, int minor, struct media *media); +blkdev_t *blkdev_lookup_by_path(char *devpath); +blkdev_t *blkdev_lookup_by_devno(int maj, int min); +char *blkdev_get_devpath(blkdev_t *blk); + +void blkdev_destroy(blkdev_t *blk); + +int blkdev_get_num_pending_partitions(blkdev_t *blk); +int blkdev_refresh(blkdev_t *blk); +#endif diff --git a/vold/cmd_dispatch.c b/vold/cmd_dispatch.c new file mode 100644 index 0000000..ab65fd9 --- /dev/null +++ b/vold/cmd_dispatch.c @@ -0,0 +1,115 @@ + +/* + * 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 <unistd.h> +#include <errno.h> + +#include "vold.h" +#include "cmd_dispatch.h" +#include "ums.h" +#include "volmgr.h" + +struct cmd_dispatch { + char *cmd; + int (* dispatch) (char *); +}; + +static void dispatch_cmd(char *cmd); +static int do_send_ums_status(char *cmd); +static int do_set_ums_enable(char *cmd); +static int do_mount_volume(char *cmd); +static int do_eject_media(char *cmd); +static int do_format_media(char *cmd); + +static struct cmd_dispatch dispatch_table[] = { + { VOLD_CMD_ENABLE_UMS, do_set_ums_enable }, + { VOLD_CMD_DISABLE_UMS, do_set_ums_enable }, + { VOLD_CMD_SEND_UMS_STATUS, do_send_ums_status }, + { VOLD_CMD_MOUNT_VOLUME, do_mount_volume }, + { VOLD_CMD_EJECT_MEDIA, do_eject_media }, + { VOLD_CMD_FORMAT_MEDIA, do_format_media }, + { NULL, NULL } +}; + +int process_framework_command(int socket) +{ + int rc; + char buffer[101]; + + if ((rc = read(socket, buffer, sizeof(buffer) -1)) < 0) { + LOGE("Unable to read framework command (%s)", strerror(errno)); + return -errno; + } else if (!rc) + return -ECONNRESET; + + int start = 0; + int i; + + buffer[rc] = 0; + + for (i = 0; i < rc; i++) { + if (buffer[i] == 0) { + dispatch_cmd(buffer + start); + start = i + 1; + } + } + return 0; +} + +static void dispatch_cmd(char *cmd) +{ + struct cmd_dispatch *c; + + LOG_VOL("dispatch_cmd(%s):", cmd); + + for (c = dispatch_table; c->cmd != NULL; c++) { + if (!strncmp(c->cmd, cmd, strlen(c->cmd))) { + c->dispatch(cmd); + return; + } + } + + LOGE("No cmd handlers defined for '%s'", cmd); +} + +static int do_send_ums_status(char *cmd) +{ + return ums_send_status(); +} + +static int do_set_ums_enable(char *cmd) +{ + if (!strcmp(cmd, VOLD_CMD_ENABLE_UMS)) + return volmgr_enable_ums(true); + + return volmgr_enable_ums(false); +} + +static int do_mount_volume(char *cmd) +{ + return volmgr_start_volume_by_mountpoint(&cmd[strlen("mount_volume:")]); +} + +static int do_format_media(char *cmd) +{ + return volmgr_format_volume(&cmd[strlen("format_media:")]); +} + +static int do_eject_media(char *cmd) +{ + return volmgr_stop_volume_by_mountpoint(&cmd[strlen("eject_media:")]); +} diff --git a/vold/cmd_dispatch.h b/vold/cmd_dispatch.h new file mode 100644 index 0000000..f8ba6b3 --- /dev/null +++ b/vold/cmd_dispatch.h @@ -0,0 +1,31 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CMD_DISPATCH_H +#define _CMD_DISPATCH_H + +// These must match the strings in java/android/android/os/UsbListener.java +#define VOLD_CMD_ENABLE_UMS "enable_ums" +#define VOLD_CMD_DISABLE_UMS "disable_ums" +#define VOLD_CMD_SEND_UMS_STATUS "send_ums_status" + +// these commands should contain a volume mount point after the colon +#define VOLD_CMD_MOUNT_VOLUME "mount_volume:" +#define VOLD_CMD_EJECT_MEDIA "eject_media:" +#define VOLD_CMD_FORMAT_MEDIA "format_media:" + +#endif diff --git a/vold/devmapper.c b/vold/devmapper.c new file mode 100644 index 0000000..ef44c90 --- /dev/null +++ b/vold/devmapper.c @@ -0,0 +1,463 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <sched.h> +#include <fcntl.h> + +#include <sys/mount.h> + +#include <linux/loop.h> +#include <linux/dm-ioctl.h> + +#include <cutils/config_utils.h> +#include <cutils/properties.h> + +#include "vold.h" +#include "devmapper.h" + +#define DEBUG_DEVMAPPER 1 + +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +static struct dm_ioctl *_dm_ioctl_setup(struct devmapping *dm, int flags) +{ + void *buffer; + void *p; + const size_t min_size = 16 * 1024; + size_t len = sizeof(struct dm_ioctl); + struct dm_ioctl *io; + struct dm_target_spec *tgt; + int i; + char params[1024]; + char key[80]; + + key[0] = '\0'; + for (i = 0; i < (int) sizeof(dm->key); i++) { + char tmp[8]; + + sprintf(tmp, "%02x", dm->key[i]); + strcat(key, tmp); + } + + char srcdev[128]; + + // XXX: Handle non crypt targets and non twofish (use param) + if (dm->src_type == dmsrc_loopback) + strcpy(srcdev, dm->type_data.loop.loop_dev); + else if (dm->src_type == dmsrc_partition) + strcpy(srcdev, dm->type_data.part.part_dev); + + sprintf(params, "twofish %s 0 %s 0", key, srcdev); + +LOG_VOL("Params = '%s'", params); + + if (len < min_size) + len = min_size; + + if (!(buffer = malloc(len))) { + LOGE("out of memory"); + return NULL; + } + + memset(buffer, 0, len); + io = buffer; + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + io->data_size = len; + io->data_start = sizeof(struct dm_ioctl); + + io->flags = flags; + io->dev = 0; + + io->target_count = 1; + io->event_nr = 1; + strncpy(io->name, dm->target, sizeof(io->name)); + + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = (dm->size_mb * (1024 * 1024)) / 512; + strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type)); + + p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + strcpy((char *) p, params); + p+= strlen(params) + 1; + + p = _align(p, 8); + tgt->next = p - buffer; + + return io; +} + +static int get_next_available_dm() +{ + int i; + + for (i = 0; i < 8; i++) { + char path[255]; + sprintf(path, "/dev/block/dm-%d", i); + if ((access(path, F_OK) < 0) && (errno == ENOENT)) + return i; + } + + LOGE("Out of device mapper numbers"); + return -1; +} + +static int create_devmapping(struct devmapping *dm) +{ + struct dm_ioctl *io; + int rc, fd; + +#if DEBUG_DEVMAPPER + LOG_VOL("create_devmapping():"); +#endif + + if (dm->dm_no < 0) { + LOGE("Invalid dm_no set"); + return -EINVAL; + } + + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening device mapper (%d)", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(dm, 0))) { + LOGE("Unable to setup ioctl (out of memory)"); + close(fd); + return -ENOMEM; + } + + if ((rc = ioctl(fd, DM_DEV_CREATE, io)) < 0) { + LOGE("device-mapper create ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(dm, DM_STATUS_TABLE_FLAG))) { + LOGE("Unable to setup ioctl (out of memory)"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(fd, DM_TABLE_LOAD, io)) < 0) { + LOGE("device-mapper load ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(dm, 0))) { + LOGE("Unable to setup ioctl (out of memory)"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(fd, DM_DEV_SUSPEND, io)) < 0) { + LOGE("device-mapper resume ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (fd); + return rc; +} + +static int loopback_start(struct devmapping *dm) +{ + int i; + int fd; + char filename[255]; + int rc; + +#if DEBUG_DEVMAPPER + LOG_VOL("loopback_start(%s):", dm->type_data.loop.loop_src); +#endif + + for (i = 0; i < MAX_LOOP; i++) { + struct loop_info info; + + sprintf(filename, "/dev/block/loop%d", i); + + if ((fd = open(filename, O_RDWR)) < 0) { + LOGE("Unable to open %s (%s)", filename, strerror(errno)); + return -errno; + } + + rc = ioctl(fd, LOOP_GET_STATUS, &info); + if (rc < 0 && errno == ENXIO) + break; + + close(fd); + + if (rc < 0) { + LOGE("Unable to get loop status for %s (%s)", filename, + strerror(errno)); + return -errno; + } + } + + if (i == MAX_LOOP) { + LOGE("Out of loop devices"); + return -ENOSPC; + } + + int file_fd; + + if ((file_fd = open(dm->type_data.loop.loop_src, O_RDWR)) < 0) { + LOGE("Unable to open %s (%s)", dm->type_data.loop.loop_src, + strerror(errno)); + return -errno; + } + + if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) { + LOGE("Error setting up loopback interface (%s)", strerror(errno)); + return -errno; + } + + dm->type_data.loop.loop_dev = strdup(filename); + dm->type_data.loop.loop_no = i; + + close(fd); + close(file_fd); + +#if DEBUG_DEVMAPPER + LOG_VOL("Loop setup on %s for %s", dm->type_data.loop.loop_dev, + dm->type_data.loop.loop_src); +#endif + + return 0; +} + +int devmapper_start(struct devmapping *dm) +{ + int rc; + char src_blkdev_path[255]; + +#if DEBUG_DEVMAPPER + LOG_VOL("devmapper_start()"); +#endif + + if (dm->src_type == dmsrc_loopback) { + if ((rc = loopback_start(dm)) < 0) + return rc; + } else if (dm->src_type == dmsrc_partition) { + LOGE("partition maps not yet supported"); + return -ENOSYS; + } else { + LOGE("devmapper_start(): Unsupported source type '%d'", dm->src_type); + return -ENOENT; + } + + /* + * Configure the device mapper + */ + if ((rc = create_devmapping(dm)) < 0) { + LOGE("Failed to create devmapping (%d)", rc); + // XXX: if loopback then tear down + return rc; + } + + return 0; +} + +struct devmapping *devmapper_init(char *src, char *src_type, uint32_t size_mb, + char *target, char *params, char *tgt_fs, char *mediapath) +{ + struct devmapping *dm; + + if (!(dm = malloc(sizeof(struct devmapping)))) { + LOGE("devmapper_init(): out of memory"); + return NULL; + } + + memset(dm, 0, sizeof(struct devmapping)); + + if (!strcmp(src_type, "loopback_file")) { + dm->src_type = dmsrc_loopback; + dm->type_data.loop.loop_src = strdup(src); + } else if (!strncmp(src_type, "partition ", strlen("partition "))) { + dm->src_type = dmsrc_partition; + char *p = strtok(src_type, " "); + if (!p) { + LOGE("Invalid partition specifier"); + goto out_free; + } + dm->type_data.part.part_type = strtoul(p, NULL, 0); + } else { + LOGE("Invalid src_type defined (%s)", src_type); + goto out_free; + } + + // XXX: Validate these + dm->size_mb = size_mb; + dm->target = strdup(target); + dm->params = strdup(params); + dm->tgt_fs = strdup(tgt_fs); + + if ((dm->dm_no = get_next_available_dm()) < 0) + goto out_free; + + sprintf(mediapath, "/devices/virtual/block/dm-%d", dm->dm_no); + + if (!(dm->media = media_create(mediapath, + "unknown", + "unknown", + media_devmapper))) { + LOGE("Unable to create media"); + goto out_free; + } + + return dm; + out_free: + if (dm->target) + free(dm->target); + if (dm->params) + free(dm->params); + if (dm->tgt_fs) + free(dm->tgt_fs); + + free(dm); + return NULL; +} + +int devmapper_genesis(struct devmapping *dm) +{ + + if (dm->src_type == dmsrc_loopback) { + int fd; + + LOG_VOL("devmapper_genesis(): Working on %s", + dm->type_data.loop.loop_src); + + unlink(dm->type_data.loop.loop_src); + + LOG_VOL("devmapper_genesis(): Creating imagefile (%u MB)", + dm->size_mb); + + if ((fd = creat(dm->type_data.loop.loop_src, 0600)) < 0) { + LOGE("Error creating imagefile (%s)", strerror(errno)); + return -errno; + } + + if (ftruncate(fd, (dm->size_mb * (1024 * 1024))) < 0) { + LOGE("Error truncating imagefile (%s)", strerror(errno)); + close(fd); + return -errno; + } + close(fd); + } else if (dm->src_type == dmsrc_partition) { + LOGE("partition maps not yet supported"); + return -ENOSYS; + } + + return devmapper_start(dm); +} + +static int destroy_devmapping(struct devmapping *dm) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + LOG_VOL("destroy_devmapping():"); + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening device mapper (%d)", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(dm, DM_PERSISTENT_DEV_FLAG))) { + LOGE("Unable to setup ioctl (out of memory)"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) { + LOGE("device-mapper remove ioctl failed (%d)", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int loopback_stop(struct devmapping *dm) +{ + char devname[255]; + int device_fd; + int rc = 0; + + LOG_VOL("loopback_stop():"); + + device_fd = open(dm->type_data.loop.loop_dev, O_RDONLY); + if (device_fd < 0) { + LOG_ERROR("Failed to open loop (%d)", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("Failed to destroy loop (%d)", errno); + rc = -errno; + } + + close(device_fd); + return rc; +} + +int devmapper_stop(struct devmapping *dm) +{ + int rc; + + LOG_VOL("devmapper_stop():"); + + if ((rc = destroy_devmapping(dm))) + return rc; + + if (dm->src_type == dmsrc_loopback) { + if ((rc = loopback_stop(dm))) + return rc; + } else if (dm->src_type == dmsrc_partition) { + LOGE("partition maps not yet supported"); + return -ENOSYS; + } + return 0; +} diff --git a/vold/devmapper.h b/vold/devmapper.h new file mode 100644 index 0000000..3d8cab3 --- /dev/null +++ b/vold/devmapper.h @@ -0,0 +1,70 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEVMAPPER_H +#define _DEVMAPPER_H + +#include <pthread.h> + +#include "vold.h" +#include "blkdev.h" +#include "media.h" + +#define MAX_LOOP 8 + +enum dm_src_type { + dmsrc_unknown, + dmsrc_loopback, + dmsrc_partition, +}; + +struct loop_data { + char *loop_src; + + char *loop_dev; + int loop_no; +}; + +struct part_data { + char part_type; + + char *part_dev; +}; + +struct devmapping { + enum dm_src_type src_type; + union { + struct loop_data loop; + struct part_data part; + } type_data; + + uint32_t size_mb; + char *target; + char *params; + char *tgt_fs; + + unsigned char key[16]; + int dm_no; + + media_t *media; +}; + +struct devmapping *devmapper_init(char *, char *, unsigned int, char *, char *, char *, char *); +int devmapper_start(struct devmapping *); +int devmapper_stop(struct devmapping *); +int devmapper_genesis(struct devmapping *); +#endif diff --git a/vold/diskmbr.h b/vold/diskmbr.h new file mode 100644 index 0000000..417e7ca --- /dev/null +++ b/vold/diskmbr.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)disklabel.h 8.2 (Berkeley) 7/10/94 + * $FreeBSD: src/sys/sys/diskmbr.h,v 1.99.2.1 2005/01/31 23:26:56 imp Exp $ + */ + +#ifndef _SYS_DISKMBR_H_ +#define _SYS_DISKMBR_H_ + +#define DOSBBSECTOR 0 /* DOS boot block relative sector number */ +#define DOSPARTOFF 446 +#define DOSPARTSIZE 16 +#define NDOSPART 4 +#define NEXTDOSPART 32 +#define DOSMAGICOFFSET 510 +#define DOSMAGIC 0xAA55 + +#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */ +#define DOSPTYP_LINSWP 0x82 /* Linux swap partition */ +#define DOSPTYP_LINUX 0x83 /* Linux partition */ +#define DOSPTYP_PMBR 0xee /* GPT Protective MBR */ +#define DOSPTYP_EXT 5 /* DOS extended partition */ +#define DOSPTYP_EXTLBA 15 /* DOS extended partition */ + +struct dos_partition { + unsigned char dp_flag; /* bootstrap flags */ + unsigned char dp_shd; /* starting head */ + unsigned char dp_ssect; /* starting sector */ + unsigned char dp_scyl; /* starting cylinder */ + unsigned char dp_typ; /* partition type */ + unsigned char dp_ehd; /* end head */ + unsigned char dp_esect; /* end sector */ + unsigned char dp_ecyl; /* end cylinder */ + u_int32_t dp_start; /* absolute starting sector number */ + u_int32_t dp_size; /* partition size in sectors */ +}; +#ifdef CTASSERT +CTASSERT(sizeof (struct dos_partition) == DOSPARTSIZE); +#endif + +void dos_partition_dec(void const *pp, struct dos_partition *d); +void dos_partition_enc(void *pp, struct dos_partition *d); + +#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */ +#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */ + +#define DIOCSMBR _IOW('M', 129, u_char[512]) + +#endif /* !_SYS_DISKMBR_H_ */ diff --git a/vold/format.c b/vold/format.c new file mode 100755 index 0000000..dd0515c --- /dev/null +++ b/vold/format.c @@ -0,0 +1,113 @@ + +/* + * 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 <fcntl.h> +#include <errno.h> + +#include <linux/fs.h> + +#include "vold.h" +#include "blkdev.h" +#include "format.h" +#include "diskmbr.h" +#include "logwrapper.h" + +static char MKDOSFS_PATH[] = "/system/bin/mkdosfs"; +static char MKE2FS_PATH[] = "/system/bin/mke2fs"; + +int format_partition(blkdev_t *part, char *type) +{ + char *devpath; + int rc = -EINVAL; + + devpath = blkdev_get_devpath(part); + + if (!strcmp(type, FORMAT_TYPE_FAT32)) { + char *args[7]; + args[0] = MKDOSFS_PATH; + args[1] = "-F 32"; + args[2] = "-c 32"; + args[3] = "-n 2"; + args[4] = "-O android"; + args[5] = devpath; + args[6] = NULL; + rc = logwrap(6, args); + } else { + char *args[7]; + args[0] = MKE2FS_PATH; + args[1] = "-b 4096"; + args[2] = "-m 1"; + args[3] = "-L android"; + args[4] = "-v"; + args[5] = devpath; + args[6] = NULL; + rc = logwrap(6, args); + } + + free(devpath); + + if (rc == 0) { + LOG_VOL("Filesystem formatted OK"); + return 0; + } else { + LOGE("Format failed (unknokwn exit code %d)", rc); + return -EIO; + } + return 0; +} + +int initialize_mbr(blkdev_t *disk) +{ + int fd, rc; + unsigned char block[512]; + struct dos_partition part; + char *devpath; + + devpath = blkdev_get_devpath(disk); + + memset(&part, 0, sizeof(part)); + part.dp_flag = 0x80; + part.dp_typ = 0xc; + part.dp_start = ((1024 * 64) / 512) + 1; + part.dp_size = disk->nr_sec - part.dp_start; + + memset(block, 0, sizeof(block)); + block[0x1fe] = 0x55; + block[0x1ff] = 0xaa; + + dos_partition_enc(block + DOSPARTOFF, &part); + + if ((fd = open(devpath, O_RDWR)) < 0) { + LOGE("Error opening disk file (%s)", strerror(errno)); + return -errno; + } + free(devpath); + + if (write(fd, block, sizeof(block)) < 0) { + LOGE("Error writing MBR (%s)", strerror(errno)); + close(fd); + return -errno; + } + + if (ioctl(fd, BLKRRPART, NULL) < 0) { + LOGE("Error re-reading partition table (%s)", strerror(errno)); + close(fd); + return -errno; + } + close(fd); + return 0; +} diff --git a/vold/format.h b/vold/format.h new file mode 100644 index 0000000..73cc012 --- /dev/null +++ b/vold/format.h @@ -0,0 +1,26 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _FORMAT_H +#define _FORMAT_H + +#define FORMAT_TYPE_EXT2 "ext2" +#define FORMAT_TYPE_FAT32 "fat32" + +int format_partition(blkdev_t *part, char *type); +int initialize_mbr(blkdev_t *disk); +#endif diff --git a/vold/geom_mbr_enc.c b/vold/geom_mbr_enc.c new file mode 100644 index 0000000..f1f8339 --- /dev/null +++ b/vold/geom_mbr_enc.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2003 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Functions to encode or decode struct dos_partition into a bytestream + * of correct endianess and packing. These functions do no validation + * or sanity checking, they only pack/unpack the fields correctly. + * + * NB! This file must be usable both in kernel and userland. + */ + +#include <sys/types.h> +#include <sys/endian.h> + +#include "diskmbr.h" + +static __inline uint32_t __unused +le32dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +static __inline void +le32enc(void *pp, uint32_t u) +{ + unsigned char *p = (unsigned char *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +void +dos_partition_dec(void const *pp, struct dos_partition *d) +{ + unsigned char const *p = pp; + + d->dp_flag = p[0]; + d->dp_shd = p[1]; + d->dp_ssect = p[2]; + d->dp_scyl = p[3]; + d->dp_typ = p[4]; + d->dp_ehd = p[5]; + d->dp_esect = p[6]; + d->dp_ecyl = p[7]; + d->dp_start = le32dec(p + 8); + d->dp_size = le32dec(p + 12); +} + +void +dos_partition_enc(void *pp, struct dos_partition *d) +{ + unsigned char *p = pp; + + p[0] = d->dp_flag; + p[1] = d->dp_shd; + p[2] = d->dp_ssect; + p[3] = d->dp_scyl; + p[4] = d->dp_typ; + p[5] = d->dp_ehd; + p[6] = d->dp_esect; + p[7] = d->dp_ecyl; + le32enc(p + 8, d->dp_start); + le32enc(p + 12, d->dp_size); +} diff --git a/vold/logwrapper.c b/vold/logwrapper.c new file mode 100644 index 0000000..25d2281 --- /dev/null +++ b/vold/logwrapper.c @@ -0,0 +1,147 @@ +/* + * 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 <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include "private/android_filesystem_config.h" +#include "cutils/log.h" + +int parent(const char *tag, int parent_read) { + int status; + char buffer[4096]; + + int a = 0; // start index of unprocessed data + int b = 0; // end index of unprocessed data + int sz; + while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { + + sz += b; + // Log one line at a time + for (b = 0; b < sz; b++) { + if (buffer[b] == '\r') { + buffer[b] = '\0'; + } else if (buffer[b] == '\n') { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + a = b + 1; + } + } + + if (a == 0 && b == sizeof(buffer) - 1) { + // buffer is full, flush + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + b = 0; + } else if (a != b) { + // Keep left-overs + b -= a; + memmove(buffer, &buffer[a], b); + a = 0; + } else { + a = 0; + b = 0; + } + + } + // Flush remaining data + if (a != b) { + buffer[b] = '\0'; + LOG(LOG_INFO, tag, &buffer[a]); + } + status = 0xAAAA; + if (wait(&status) != -1) { // Wait for child + if (WIFEXITED(status)) { + LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, + WEXITSTATUS(status)); + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag, + WTERMSIG(status)); + else if (WIFSTOPPED(status)) + LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag, + WSTOPSIG(status)); + } else + LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag, + strerror(errno), errno); + return -EAGAIN; +} + +void child(int argc, char* argv[]) { + // create null terminated argv_child array + char* argv_child[argc + 1]; + memcpy(argv_child, argv, argc * sizeof(char *)); + argv_child[argc] = NULL; + + // XXX: PROTECT FROM VIKING KILLER + if (execvp(argv_child[0], argv_child)) { + LOG(LOG_ERROR, "logwrapper", + "executing %s failed: %s", argv_child[0], strerror(errno)); + exit(-1); + } +} + +int logwrap(int argc, char* argv[], pid_t *childPid) +{ + pid_t pid; + + int parent_ptty; + int child_ptty; + char *child_devname = NULL; + + /* Use ptty instead of socketpair so that STDOUT is not buffered */ + parent_ptty = open("/dev/ptmx", O_RDWR); + if (parent_ptty < 0) { + LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty"); + return -errno; + } + + if (grantpt(parent_ptty) || unlockpt(parent_ptty) || + ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { + LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx"); + return -1; + } + + pid = fork(); + if (pid < 0) { + LOG(LOG_ERROR, "logwrapper", "Failed to fork"); + return -errno; + } else if (pid == 0) { + child_ptty = open(child_devname, O_RDWR); + if (child_ptty < 0) { + LOG(LOG_ERROR, "logwrapper", "Problem with child ptty"); + return -errno; + } + + // redirect stdout and stderr + close(parent_ptty); + dup2(child_ptty, 1); + dup2(child_ptty, 2); + close(child_ptty); + + child(argc, argv); + } else { + return parent(argv[0], parent_ptty); + } + + return 0; +} diff --git a/vold/logwrapper.h b/vold/logwrapper.h new file mode 100644 index 0000000..602e24c --- /dev/null +++ b/vold/logwrapper.h @@ -0,0 +1,23 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOGWRAPPER_H +#define _LOGWRAPPER_H + +#include <stdlib.h> +int logwrap(int argc, char* argv[]); +#endif diff --git a/vold/media.c b/vold/media.c new file mode 100644 index 0000000..40637ff --- /dev/null +++ b/vold/media.c @@ -0,0 +1,165 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> + +#include <sys/types.h> + +#include "vold.h" +#include "media.h" + +static media_list_t *list_root = NULL; + +media_t *media_create(char *devpath, char *name, char *serial, media_type_t type) +{ + media_list_t *list_entry; + media_t *new; + + if (!(new = malloc(sizeof(media_t)))) + return NULL; + + memset(new, 0, sizeof(media_t)); + + if (!(list_entry = malloc(sizeof(media_list_t)))) { + free(new); + return NULL; + } + list_entry->media = new; + list_entry->next = NULL; + + if (!list_root) + list_root = list_entry; + else { + media_list_t *list_scan = list_root; + while(list_scan->next) + list_scan = list_scan->next; + list_scan->next = list_entry; + } + + new->devpath = strdup(devpath); + new->name = strdup(name); + if (!serial) + new->serial = 0; + else + new->serial = strtoul(serial, NULL, 0); + + new->media_type = type; + + return new; +} + +void media_destroy(media_t *media) +{ + media_list_t *list_next; + + if (list_root->media == media) { + list_next = list_root->next; + free(list_root); + list_root = list_next; + } else { + media_list_t *list_scan = list_root; + while (list_scan->next->media != media) + list_scan = list_scan -> next; + list_next = list_scan->next->next; + free(list_scan->next); + list_scan->next = list_next; + } + + free(media->devpath); + free(media->name); + + while(media->devs) + media_remove_blkdev(media, media->devs->dev); + free(media); +} + +media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match) +{ + media_list_t *list_scan = list_root; + + while (list_scan) { + if (fuzzy_match) { + if (!strncmp(list_scan->media->devpath, devpath, strlen(devpath))) + return list_scan->media; + } else { + if (!strcmp(list_scan->media->devpath, devpath)) + return list_scan->media; + } + list_scan = list_scan->next; + } +#if DEBUG_MEDIA + LOG_VOL("media_lookup_by_path(): No media found @ %s", devpath); +#endif + return NULL; +} + +int media_add_blkdev(media_t *card, blkdev_t *dev) +{ + blkdev_list_t *list_entry; + + if (!(list_entry = malloc(sizeof(blkdev_list_t)))) { + LOGE("Out of memory"); + return -ENOMEM; + } + + list_entry->next = NULL; + list_entry->dev = dev; + if (!card->devs) + card->devs = list_entry; + else { + blkdev_list_t *scan = card->devs; + + while(scan->next) + scan = scan->next; + + scan->next = list_entry; + } + return 0; +} + +void media_remove_blkdev(media_t *card, blkdev_t *dev) +{ + if (card->devs->dev == dev) + card->devs = card->devs->next; + else { + blkdev_list_t *scan = card->devs; + while (scan->next->dev != dev) + scan = scan -> next; + blkdev_list_t *next = scan->next->next; + free(scan->next); + scan->next = next; + } +} + +media_t *media_lookup_by_dev(blkdev_t *dev) +{ + media_list_t *media_scan = list_root; + + while (media_scan) { + blkdev_list_t *blk_scan = media_scan->media->devs; + while (blk_scan) { + if (blk_scan->dev == dev) + return media_scan->media; + blk_scan = blk_scan->next; + } + media_scan = media_scan->next; + } + return NULL; +} diff --git a/vold/media.h b/vold/media.h new file mode 100644 index 0000000..567ce04 --- /dev/null +++ b/vold/media.h @@ -0,0 +1,51 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MEDIA_H +#define _MEDIA_H + +#include <sys/types.h> + +#include "blkdev.h" + +typedef enum media_type { + media_unknown, + media_mmc, + media_devmapper, +} media_type_t; + +typedef struct media { + char *devpath; + char *name; + uint32_t serial; + media_type_t media_type; + + blkdev_list_t *devs; +} media_t; + +typedef struct media_list { + media_t *media; + struct media_list *next; +} media_list_t; + +media_t *media_create(char *devpath, char *name, char *serial, enum media_type); +media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match); +media_t *media_lookup_by_dev(blkdev_t *dev); +void media_destroy(media_t *media); +int media_add_blkdev(media_t *media, blkdev_t *dev); +void media_remove_blkdev(media_t *media, blkdev_t *dev); +#endif diff --git a/vold/misc.c b/vold/misc.c new file mode 100644 index 0000000..951414c --- /dev/null +++ b/vold/misc.c @@ -0,0 +1,91 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +void *read_file(char *filename, ssize_t *_size) +{ + int ret, fd; + struct stat sb; + ssize_t size; + void *buffer = NULL; + + /* open the file */ + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + /* find out how big it is */ + if (fstat(fd, &sb) < 0) + goto bail; + size = sb.st_size; + + /* allocate memory for it to be read into */ + buffer = malloc(size); + if (!buffer) + goto bail; + + /* slurp it into our buffer */ + ret = read(fd, buffer, size); + if (ret != size) + goto bail; + + /* let the caller know how big it is */ + *_size = size; + +bail: + close(fd); + return buffer; +} +char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer) +{ + int i; + + strcpy(buffer, path); + + for (i = 0; i < num_elements_to_remove; i++) { + char *p = &buffer[strlen(buffer)-1]; + + for (p = &buffer[strlen(buffer) -1]; *p != '/'; p--); + *p = '\0'; + } + + return buffer; +} + +char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var) +{ + char filename[255]; + char *p; + ssize_t sz; + + sprintf(filename, "/sys%s/%s", devpath, var); + p = read_file(filename, &sz); + p[(strlen(p) - 1)] = '\0'; + strncpy(buffer, p, maxlen); + free(p); + return buffer; +} + diff --git a/vold/mmc.c b/vold/mmc.c new file mode 100644 index 0000000..0f08964 --- /dev/null +++ b/vold/mmc.c @@ -0,0 +1,293 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> + +#include <sys/types.h> + +#include "vold.h" +#include "mmc.h" +#include "media.h" + +#define DEBUG_BOOTSTRAP 0 + +static int mmc_bootstrap_controller(char *sysfs_path); +static int mmc_bootstrap_card(char *sysfs_path); +static int mmc_bootstrap_block(char *devpath); +static int mmc_bootstrap_mmcblk(char *devpath); +static int mmc_bootstrap_mmcblk_partition(char *devpath); + +/* + * Bootstrap our mmc information. + */ +int mmc_bootstrap() +{ + DIR *d; + struct dirent *de; + + if (!(d = opendir(SYSFS_CLASS_MMC_PATH))) { + LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_MMC_PATH); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + + sprintf(tmp, "%s/%s", SYSFS_CLASS_MMC_PATH, de->d_name); + if (mmc_bootstrap_controller(tmp)) + LOG_ERROR("Error bootstrapping controller '%s' (%m)", tmp); + } + + closedir(d); + + return 0; +} + +static int mmc_bootstrap_controller(char *sysfs_path) +{ + DIR *d; + struct dirent *de; + +#if DEBUG_BOOTSTRAP + LOG_VOL("bootstrap_controller(%s):", sysfs_path); +#endif + if (!(d = opendir(sysfs_path))) { + LOG_ERROR("Unable to open '%s' (%m)", sysfs_path); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + + if ((!strcmp(de->d_name, "uevent")) || + (!strcmp(de->d_name, "subsystem")) || + (!strcmp(de->d_name, "device")) || + (!strcmp(de->d_name, "power"))) { + continue; + } + + sprintf(tmp, "%s/%s", sysfs_path, de->d_name); + + if (mmc_bootstrap_card(tmp) < 0) + LOG_ERROR("Error bootstrapping card '%s' (%m)", tmp); + } // while + + closedir(d); + return 0; +} + +static int mmc_bootstrap_card(char *sysfs_path) +{ + char saved_cwd[255]; + char new_cwd[255]; + char *devpath; + char *uevent_params[4]; + char *p; + char filename[255]; + char tmp[255]; + ssize_t sz; + +#if DEBUG_BOOTSTRAP + LOG_VOL("bootstrap_card(%s):", sysfs_path); +#endif + + /* + * sysfs_path is based on /sys/class, but we want the actual device class + */ + if (!getcwd(saved_cwd, sizeof(saved_cwd))) { + LOGE("Error getting working dir path"); + return -errno; + } + + if (chdir(sysfs_path) < 0) { + LOGE("Unable to chdir to %s (%m)", sysfs_path); + return -errno; + } + + if (!getcwd(new_cwd, sizeof(new_cwd))) { + LOGE("Buffer too small for device path"); + return -errno; + } + + if (chdir(saved_cwd) < 0) { + LOGE("Unable to restore working dir"); + return -errno; + } + + devpath = &new_cwd[4]; // Skip over '/sys' + + /* + * Collect parameters so we can simulate a UEVENT + */ + sprintf(tmp, "DEVPATH=%s", devpath); + uevent_params[0] = (char *) strdup(tmp); + + sprintf(filename, "/sys%s/type", devpath); + p = read_file(filename, &sz); + p[strlen(p) - 1] = '\0'; + sprintf(tmp, "MMC_TYPE=%s", p); + free(p); + uevent_params[1] = (char *) strdup(tmp); + + sprintf(filename, "/sys%s/name", devpath); + p = read_file(filename, &sz); + p[strlen(p) - 1] = '\0'; + sprintf(tmp, "MMC_NAME=%s", p); + free(p); + uevent_params[2] = (char *) strdup(tmp); + + uevent_params[3] = (char *) NULL; + + if (simulate_uevent("mmc", devpath, "add", uevent_params) < 0) { + LOGE("Error simulating uevent (%m)"); + return -errno; + } + + /* + * Check for block drivers + */ + char block_devpath[255]; + sprintf(tmp, "%s/block", devpath); + sprintf(filename, "/sys%s/block", devpath); + if (!access(filename, F_OK)) { + if (mmc_bootstrap_block(tmp)) { + LOGE("Error bootstrapping block @ %s", tmp); + } + } + + return 0; +} + +static int mmc_bootstrap_block(char *devpath) +{ + char blockdir_path[255]; + DIR *d; + struct dirent *de; + +#if DEBUG_BOOTSTRAP + LOG_VOL("mmc_bootstrap_block(%s):", devpath); +#endif + + sprintf(blockdir_path, "/sys%s", devpath); + + if (!(d = opendir(blockdir_path))) { + LOGE("Failed to opendir %s", devpath); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + sprintf(tmp, "%s/%s", devpath, de->d_name); + if (mmc_bootstrap_mmcblk(tmp)) + LOGE("Error bootstraping mmcblk @ %s", tmp); + } + closedir(d); + return 0; +} + +static int mmc_bootstrap_mmcblk(char *devpath) +{ + char *mmcblk_devname; + int part_no; + int rc; + +#if DEBUG_BOOTSTRAP + LOG_VOL("mmc_bootstrap_mmcblk(%s):", devpath); +#endif + + if ((rc = mmc_bootstrap_mmcblk_partition(devpath))) { + LOGE("Error bootstrapping mmcblk partition '%s'", devpath); + return rc; + } + + for (mmcblk_devname = &devpath[strlen(devpath)]; + *mmcblk_devname != '/'; mmcblk_devname--); + mmcblk_devname++; + + for (part_no = 0; part_no < 4; part_no++) { + char part_file[255]; + sprintf(part_file, "/sys%s/%sp%d", devpath, mmcblk_devname, part_no); + if (!access(part_file, F_OK)) { + char part_devpath[255]; + + sprintf(part_devpath, "%s/%sp%d", devpath, mmcblk_devname, part_no); + if (mmc_bootstrap_mmcblk_partition(part_devpath)) + LOGE("Error bootstrapping mmcblk partition '%s'", part_devpath); + } + } + + return 0; +} + +static int mmc_bootstrap_mmcblk_partition(char *devpath) +{ + char filename[255]; + char *uevent_buffer; + ssize_t sz; + char *uevent_params[4]; + char tmp[255]; + FILE *fp; + char line[255]; + +#if DEBUG_BOOTSTRAP + LOG_VOL("mmc_bootstrap_mmcblk_partition(%s):", devpath); +#endif + + sprintf(tmp, "DEVPATH=%s", devpath); + uevent_params[0] = strdup(tmp); + + sprintf(filename, "/sys%s/uevent", devpath); + if (!(fp = fopen(filename, "r"))) { + LOGE("Unable to open '%s' (%m)", filename); + return -errno; + } + + while (fgets(line, sizeof(line), fp)) { + line[strlen(line)-1] = 0; + if (!strncmp(line, "DEVTYPE=", 8)) + uevent_params[1] = strdup(line); + else if (!strncmp(line, "MAJOR=",6)) + uevent_params[2] = strdup(line); + else if (!strncmp(line, "MINOR=",6)) + uevent_params[3] = strdup(line); + } + fclose(fp); + + if (!uevent_params[1] || !uevent_params[2] || !uevent_params[3]) { + LOGE("mmcblk uevent missing required params"); + return -1; + } + uevent_params[4] = '\0'; + + if (simulate_uevent("block", devpath, "add", uevent_params) < 0) { + LOGE("Error simulating uevent (%m)"); + return -errno; + } + return 0; +} diff --git a/vold/mmc.h b/vold/mmc.h new file mode 100644 index 0000000..5a5d184 --- /dev/null +++ b/vold/mmc.h @@ -0,0 +1,23 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MMC_H +#define _MMC_H + +#define SYSFS_CLASS_MMC_PATH "/sys/class/mmc_host" + +#endif diff --git a/vold/switch.c b/vold/switch.c new file mode 100644 index 0000000..ba9ddb3 --- /dev/null +++ b/vold/switch.c @@ -0,0 +1,121 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> + +#include <sys/types.h> + +#include "vold.h" +#include "switch.h" + +#define DEBUG_BOOTSTRAP 0 + +static int mmc_bootstrap_switch(char *sysfs_path); + +int switch_bootstrap() +{ + DIR *d; + struct dirent *de; + + if (!(d = opendir(SYSFS_CLASS_SWITCH_PATH))) { + LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_SWITCH_PATH); + return -errno; + } + + while ((de = readdir(d))) { + char tmp[255]; + + if (de->d_name[0] == '.') + continue; + + sprintf(tmp, "%s/%s", SYSFS_CLASS_SWITCH_PATH, de->d_name); + if (mmc_bootstrap_switch(tmp)) + LOG_ERROR("Error bootstrapping switch '%s' (%m)", tmp); + } + + closedir(d); + + return 0; +} + +static int mmc_bootstrap_switch(char *sysfs_path) +{ +#if DEBUG_BOOTSTRAP + LOG_VOL("bootstrap_switch(%s):", sysfs_path); +#endif + + char filename[255]; + char name[255]; + char state[255]; + char tmp[255]; + char *uevent_params[3]; + char devpath[255]; + FILE *fp; + + /* + * Read switch name + */ + sprintf(filename, "%s/name", sysfs_path); + if (!(fp = fopen(filename, "r"))) { + LOGE("Error opening switch name path '%s' (%s)", + sysfs_path, strerror(errno)); + return -errno; + } + if (!fgets(name, sizeof(name), fp)) { + LOGE("Unable to read switch name"); + fclose(fp); + return -EIO; + } + fclose(fp); + + name[strlen(name) -1] = '\0'; + sprintf(devpath, "/devices/virtual/switch/%s", name); + sprintf(tmp, "SWITCH_NAME=%s", name); + uevent_params[0] = (char *) strdup(tmp); + + /* + * Read switch state + */ + sprintf(filename, "%s/state", sysfs_path); + if (!(fp = fopen(filename, "r"))) { + LOGE("Error opening switch state path '%s' (%s)", + sysfs_path, strerror(errno)); + return -errno; + } + if (!fgets(state, sizeof(state), fp)) { + LOGE("Unable to read switch state"); + fclose(fp); + return -EIO; + } + fclose(fp); + + state[strlen(state) -1] = '\0'; + sprintf(tmp, "SWITCH_STATE=%s", state); + uevent_params[1] = (char *) strdup(tmp); + + uevent_params[2] = (char *) NULL; + + if (simulate_uevent("switch", devpath, "add", uevent_params) < 0) { + LOGE("Error simulating uevent (%s)", strerror(errno)); + return -errno; + } + + return 0; +} diff --git a/vold/switch.h b/vold/switch.h new file mode 100644 index 0000000..6729f2d --- /dev/null +++ b/vold/switch.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SWITCH_H +#define _SWITCH_H + +#include "vold.h" + +#define SYSFS_CLASS_SWITCH_PATH "/sys/class/switch" + +#endif diff --git a/vold/uevent.c b/vold/uevent.c new file mode 100644 index 0000000..b1a6944 --- /dev/null +++ b/vold/uevent.c @@ -0,0 +1,443 @@ + +/* + * 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 <stdlib.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include "vold.h" +#include "uevent.h" +#include "mmc.h" +#include "blkdev.h" +#include "volmgr.h" +#include "media.h" + +#define DEBUG_UEVENT 0 + +#define UEVENT_PARAMS_MAX 32 + +enum uevent_action { action_add, action_remove, action_change }; + +struct uevent { + char *path; + enum uevent_action action; + char *subsystem; + char *param[UEVENT_PARAMS_MAX]; + unsigned int seqnum; +}; + +struct uevent_dispatch { + char *subsystem; + int (* dispatch) (struct uevent *); +}; + +static void dump_uevent(struct uevent *); +static int dispatch_uevent(struct uevent *event); +static void free_uevent(struct uevent *event); +static char *get_uevent_param(struct uevent *event, char *param_name); + +static int handle_powersupply_event(struct uevent *event); +static int handle_switch_event(struct uevent *); +static int handle_battery_event(struct uevent *); +static int handle_mmc_event(struct uevent *); +static int handle_block_event(struct uevent *); +static int handle_bdi_event(struct uevent *); +static void _cb_blkdev_ok_to_destroy(blkdev_t *dev); + +static struct uevent_dispatch dispatch_table[] = { + { "switch", handle_switch_event }, + { "battery", handle_battery_event }, + { "mmc", handle_mmc_event }, + { "block", handle_block_event }, + { "bdi", handle_bdi_event }, + { "power_supply", handle_powersupply_event }, + { NULL, NULL } +}; + +static boolean low_batt = false; +static boolean door_open = true; + +int process_uevent_message(int socket) +{ + char buffer[64 * 1024]; // Thank god we're not in the kernel :) + int count; + char *s = buffer; + char *end; + struct uevent *event; + int param_idx = 0; + int i; + int first = 1; + int rc = 0; + + if ((count = recv(socket, buffer, sizeof(buffer), 0)) < 0) { + LOGE("Error receiving uevent (%s)", strerror(errno)); + return -errno; + } + + if (!(event = malloc(sizeof(struct uevent)))) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -errno; + } + + memset(event, 0, sizeof(struct uevent)); + + end = s + count; + while (s < end) { + if (first) { + char *p; + for (p = s; *p != '@'; p++); + p++; + event->path = strdup(p); + first = 0; + } else { + if (!strncmp(s, "ACTION=", strlen("ACTION="))) { + char *a = s + strlen("ACTION="); + + if (!strcmp(a, "add")) + event->action = action_add; + else if (!strcmp(a, "change")) + event->action = action_change; + else if (!strcmp(a, "remove")) + event->action = action_remove; + } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM="))) + event->seqnum = atoi(s + strlen("SEQNUM=")); + else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM="))) + event->subsystem = strdup(s + strlen("SUBSYSTEM=")); + else + event->param[param_idx++] = strdup(s); + } + s+= strlen(s) + 1; + } + + rc = dispatch_uevent(event); + + free_uevent(event); + return rc; +} + +int simulate_uevent(char *subsys, char *path, char *action, char **params) +{ + struct uevent *event; + char tmp[255]; + int i, rc; + + if (!(event = malloc(sizeof(struct uevent)))) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -errno; + } + + memset(event, 0, sizeof(struct uevent)); + + event->subsystem = strdup(subsys); + + if (!strcmp(action, "add")) + event->action = action_add; + else if (!strcmp(action, "change")) + event->action = action_change; + else if (!strcmp(action, "remove")) + event->action = action_remove; + else { + LOGE("Invalid action '%s'", action); + return -1; + } + + event->path = strdup(path); + + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!params[i]) + break; + event->param[i] = strdup(params[i]); + } + + rc = dispatch_uevent(event); + free_uevent(event); + return rc; +} + +static int dispatch_uevent(struct uevent *event) +{ + int i; + +#if DEBUG_UEVENT + dump_uevent(event); +#endif + for (i = 0; dispatch_table[i].subsystem != NULL; i++) { + if (!strcmp(dispatch_table[i].subsystem, event->subsystem)) + return dispatch_table[i].dispatch(event); + } + +#if DEBUG_UEVENT + LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem); +#endif + return 0; +} + +static void dump_uevent(struct uevent *event) +{ + int i; + + LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s", + event->seqnum, event->subsystem, event->action, event->path); + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!event->param[i]) + break; + LOG_VOL("%s", event->param[i]); + } +} + +static void free_uevent(struct uevent *event) +{ + int i; + free(event->path); + free(event->subsystem); + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!event->param[i]) + break; + free(event->param[i]); + } + free(event); +} + +static char *get_uevent_param(struct uevent *event, char *param_name) +{ + int i; + + for (i = 0; i < UEVENT_PARAMS_MAX; i++) { + if (!event->param[i]) + break; + if (!strncmp(event->param[i], param_name, strlen(param_name))) + return &event->param[i][strlen(param_name) + 1]; + } + + LOGE("get_uevent_param(): No parameter '%s' found", param_name); + return NULL; +} + +/* + * --------------- + * Uevent Handlers + * --------------- + */ + +static int handle_powersupply_event(struct uevent *event) +{ + char *ps_type = get_uevent_param(event, "POWER_SUPPLY_TYPE"); + char *ps_cap = get_uevent_param(event, "POWER_SUPPLY_CAPACITY"); + + if (!strcasecmp(ps_type, "battery")) { + int capacity = atoi(ps_cap); + + if (capacity < 5) + low_batt = true; + else + low_batt = false; +LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open); + volmgr_safe_mode(low_batt || door_open); + } + return 0; +} + +static int handle_switch_event(struct uevent *event) +{ + char *name = get_uevent_param(event, "SWITCH_NAME"); + char *state = get_uevent_param(event, "SWITCH_STATE"); + + + if (!strcmp(name, "usb_mass_storage")) { + if (!strcmp(state, "online")) { + ums_hostconnected_set(true); + } else { + ums_hostconnected_set(false); + volmgr_enable_ums(false); + } + } else if (!strcmp(name, "sd-door")) { + if (!strcmp(state, "open")) + door_open = true; + else + door_open = false; +LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open); + volmgr_safe_mode(low_batt || door_open); + } else + LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name); + + return 0; +} + +static int handle_battery_event(struct uevent *event) +{ + return 0; +} + +static int handle_block_event(struct uevent *event) +{ + char mediapath[255]; + media_t *media; + int n; + int maj, min; + blkdev_t *blkdev; + + /* + * Look for backing media for this block device + */ + if (!strncmp(get_uevent_param(event, "DEVPATH"), + "/devices/virtual/", + strlen("/devices/virtual/"))) { + n = 0; + } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk")) + n = 2; + else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition")) + n = 3; + else { + LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE")); + return -EINVAL; + } + + truncate_sysfs_path(event->path, n, mediapath); + + if (!(media = media_lookup_by_path(mediapath, false))) { +#if DEBUG_UEVENT + LOG_VOL("No backend media found @ device path '%s'", mediapath); +#endif + return 0; + } + + maj = atoi(get_uevent_param(event, "MAJOR")); + min = atoi(get_uevent_param(event, "MINOR")); + + if (event->action == action_add) { + blkdev_t *disk; + + /* + * If there isn't a disk already its because *we* + * are the disk + */ + disk = blkdev_lookup_by_devno(maj, 0); + + if (!(blkdev = blkdev_create(disk, + event->path, + maj, + min, + media, + get_uevent_param(event, "DEVTYPE")))) { + LOGE("Unable to allocate new blkdev (%m)"); + return -1; + } + + blkdev_refresh(blkdev); + + /* + * Add the blkdev to media + */ + int rc; + if ((rc = media_add_blkdev(media, blkdev)) < 0) { + LOGE("Unable to add blkdev to card (%d)", rc); + return rc; + } + + LOG_VOL("New blkdev %d.%d on media %s, media path %s, Dpp %d", + blkdev->major, blkdev->minor, media->name, mediapath, + blkdev_get_num_pending_partitions(blkdev->disk)); + + if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) { + if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) { + LOGE("Volmgr failed to handle device (%d)", rc); + return rc; + } + } + } else if (event->action == action_remove) { + if (!(blkdev = blkdev_lookup_by_devno(maj, min))) + return 0; + + LOG_VOL("Destroying blkdev %d.%d @ %s on media %s", blkdev->major, + blkdev->minor, blkdev->devpath, media->name); + volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy); + + } else if (event->action == action_change) { + if (!(blkdev = blkdev_lookup_by_devno(maj, min))) + return 0; + + LOG_VOL("Modified blkdev %d.%d @ %s on media %s", blkdev->major, + blkdev->minor, blkdev->devpath, media->name); + + blkdev_refresh(blkdev); + } else { +#if DEBUG_UEVENT + LOG_VOL("No handler implemented for action %d", event->action); +#endif + } + return 0; +} + +static void _cb_blkdev_ok_to_destroy(blkdev_t *dev) +{ + media_t *media = media_lookup_by_dev(dev); + if (media) + media_remove_blkdev(media, dev); + blkdev_destroy(dev); +} + +static int handle_bdi_event(struct uevent *event) +{ + return 0; +} + +static int handle_mmc_event(struct uevent *event) +{ + if (event->action == action_add) { + media_t *media; + char serial[80]; + char *type; + + /* + * Pull card information from sysfs + */ + type = get_uevent_param(event, "MMC_TYPE"); + if (strcmp(type, "SD") && strcmp(type, "MMC")) + return 0; + + read_sysfs_var(serial, sizeof(serial), event->path, "serial"); + if (!(media = media_create(event->path, + get_uevent_param(event, "MMC_NAME"), + serial, + media_mmc))) { + LOGE("Unable to allocate new media (%m)"); + return -1; + } + LOG_VOL("New MMC card '%s' (serial %u) added @ %s", media->name, + media->serial, media->devpath); + } else if (event->action == action_remove) { + media_t *media; + + if (!(media = media_lookup_by_path(event->path, false))) { + LOGE("Unable to lookup media '%s'", event->path); + return -1; + } + + LOG_VOL("MMC card '%s' (serial %u) @ %s removed", media->name, + media->serial, media->devpath); + media_destroy(media); + } else { +#if DEBUG_UEVENT + LOG_VOL("No handler implemented for action %d", event->action); +#endif + } + + return 0; +} diff --git a/vold/uevent.h b/vold/uevent.h new file mode 100644 index 0000000..0e9e671 --- /dev/null +++ b/vold/uevent.h @@ -0,0 +1,21 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UEVENT_MSG_H +#define _UEVENT_MSG_H + +#endif diff --git a/vold/ums.c b/vold/ums.c new file mode 100644 index 0000000..4d0fc25 --- /dev/null +++ b/vold/ums.c @@ -0,0 +1,129 @@ + +/* + * 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 <fcntl.h> +#include <errno.h> + +#include "vold.h" +#include "ums.h" + +#define DEBUG_UMS 0 + +static boolean host_connected = false; +static boolean ums_enabled = false; + +int ums_bootstrap(void) +{ + return 0; +} + +void ums_enabled_set(boolean enabled) +{ + ums_enabled = enabled; + send_msg(enabled ? VOLD_EVT_UMS_ENABLED : VOLD_EVT_UMS_DISABLED); +} + +boolean ums_enabled_get() +{ + return ums_enabled; +} + +void ums_hostconnected_set(boolean connected) +{ +#if DEBUG_UMS + LOG_VOL("ums_hostconnected_set(%d):", connected); +#endif + host_connected = connected; + + if (!connected) + ums_enabled_set(false); + send_msg(connected ? VOLD_EVT_UMS_CONNECTED : VOLD_EVT_UMS_DISCONNECTED); +} + +int ums_enable(char *dev_fspath, char *lun_syspath) +{ + LOG_VOL("ums_enable(%s, %s):", dev_fspath, lun_syspath); + + int fd; + char filename[255]; + + sprintf(filename, "/sys/%s/file", lun_syspath); + if ((fd = open(filename, O_WRONLY)) < 0) { + LOGE("Unable to open '%s' (%s)", filename, strerror(errno)); + return -errno; + } + + if (write(fd, dev_fspath, strlen(dev_fspath)) < 0) { + LOGE("Unable to write to ums lunfile (%s)", strerror(errno)); + close(fd); + return -errno; + } + + close(fd); + return 0; +} + +int ums_disable(char *lun_syspath) +{ +#if DEBUG_UMS + LOG_VOL("ums_disable(%s):", lun_syspath); +#endif + + int fd; + char filename[255]; + + sprintf(filename, "/sys/%s/file", lun_syspath); + if ((fd = open(filename, O_WRONLY)) < 0) { + LOGE("Unable to open '%s' (%s)", filename, strerror(errno)); + return -errno; + } + + char ch = 0; + + if (write(fd, &ch, 1) < 0) { + LOGE("Unable to write to ums lunfile (%s)", strerror(errno)); + close(fd); + return -errno; + } + + close(fd); + return 0; +} + +boolean ums_hostconnected_get(void) +{ + return host_connected; +} + +int ums_send_status(void) +{ + int rc; + +#if DEBUG_UMS + LOG_VOL("ums_send_status():"); +#endif + + rc = send_msg(ums_enabled_get() ? VOLD_EVT_UMS_ENABLED : + VOLD_EVT_UMS_DISABLED); + if (rc < 0) + return rc; + + rc = send_msg(ums_hostconnected_get() ? VOLD_EVT_UMS_CONNECTED : + VOLD_EVT_UMS_DISCONNECTED); + + return rc; +} diff --git a/vold/ums.h b/vold/ums.h new file mode 100644 index 0000000..02cdec3 --- /dev/null +++ b/vold/ums.h @@ -0,0 +1,31 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UMS_H +#define _UMS_H + +// these must match the corresponding strings in java/android/android/os/UsbListener.java +#define VOLD_EVT_UMS_ENABLED "ums_enabled" +#define VOLD_EVT_UMS_DISABLED "ums_disabled" +#define VOLD_EVT_UMS_CONNECTED "ums_connected" +#define VOLD_EVT_UMS_DISCONNECTED "ums_disconnected" + + +int ums_send_status(void); +int ums_enable(char *device_file, char *lun_syspath); +int ums_disable(char *lun_syspath); +#endif diff --git a/vold/vold.c b/vold/vold.c new file mode 100644 index 0000000..17331ac --- /dev/null +++ b/vold/vold.c @@ -0,0 +1,234 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <pthread.h> + +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/un.h> + +#include <cutils/config_utils.h> +#include <cutils/cpu_info.h> +#include <cutils/properties.h> +#include <cutils/sockets.h> + +#include <linux/netlink.h> + +#include <private/android_filesystem_config.h> + +#include "vold.h" +#include "volmgr.h" + + +#define VOLD_SOCKET "vold" + +/* + * Globals + */ + +static int ver_major = 2; +static int ver_minor = 0; +static pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER; +static int fw_sock = -1; + +int main(int argc, char **argv) +{ + int door_sock = -1; + int uevent_sock = -1; + struct sockaddr_nl nladdr; + int uevent_sz = 64 * 1024; + + LOG_VOL("Android Volume Daemon version %d.%d", ver_major, ver_minor); + + /* + * Create all the various sockets we'll need + */ + + // Socket to listen on for incomming framework connections + if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) { + LOGE("Obtaining file descriptor socket '%s' failed: %s", + VOLD_SOCKET, strerror(errno)); + exit(1); + } + + if (listen(door_sock, 4) < 0) { + LOGE("Unable to listen on fd '%d' for socket '%s': %s", + door_sock, VOLD_SOCKET, strerror(errno)); + exit(1); + } + + mkdir("/dev/block/vold", 0755); + + // Socket to listen on for uevent changes + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = getpid(); + nladdr.nl_groups = 0xffffffff; + + if ((uevent_sock = socket(PF_NETLINK, + SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { + LOGE("Unable to create uevent socket: %s", strerror(errno)); + exit(1); + } + + if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz, + sizeof(uevent_sz)) < 0) { + LOGE("Unable to set uevent socket options: %s", strerror(errno)); + exit(1); + } + + if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { + LOGE("Unable to bind uevent socket: %s", strerror(errno)); + exit(1); + } + + /* + * Bootstrap + */ + + // Volume Manager + volmgr_bootstrap(); + + // SD Card system + mmc_bootstrap(); + + // USB Mass Storage + ums_bootstrap(); + + // Switch + switch_bootstrap(); + + /* + * Main loop + */ + LOG_VOL("Bootstrapping complete"); + while(1) { + fd_set read_fds; + struct timeval to; + int max = 0; + int rc = 0; + + to.tv_sec = (60 * 60); + to.tv_usec = 0; + + FD_ZERO(&read_fds); + FD_SET(door_sock, &read_fds); + if (door_sock > max) + max = door_sock; + FD_SET(uevent_sock, &read_fds); + if (uevent_sock > max) + max = uevent_sock; + + if (fw_sock != -1) { + FD_SET(fw_sock, &read_fds); + if (fw_sock > max) + max = fw_sock; + } + + if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) { + LOGE("select() failed (%s)", strerror(errno)); + sleep(1); + continue; + } + + if (!rc) { + continue; + } + + if (FD_ISSET(door_sock, &read_fds)) { + struct sockaddr addr; + socklen_t alen; + + alen = sizeof(addr); + + if (fw_sock != -1) { + LOGE("Dropping duplicate framework connection"); + int tmp = accept(door_sock, &addr, &alen); + close(tmp); + continue; + } + + if ((fw_sock = accept(door_sock, &addr, &alen)) < 0) { + LOGE("Unable to accept framework connection (%s)", + strerror(errno)); + } + LOG_VOL("Accepted connection from framework"); + if ((rc = volmgr_send_states()) < 0) { + LOGE("Unable to send volmgr status to framework (%d)", rc); + } + } + + if (FD_ISSET(fw_sock, &read_fds)) { + if ((rc = process_framework_command(fw_sock)) < 0) { + if (rc == -ECONNRESET) { + LOGE("Framework disconnected"); + close(fw_sock); + fw_sock = -1; + } else { + LOGE("Error processing framework command (%s)", + strerror(errno)); + } + } + } + + if (FD_ISSET(uevent_sock, &read_fds)) { + if ((rc = process_uevent_message(uevent_sock)) < 0) { + LOGE("Error processing uevent msg (%s)", strerror(errno)); + } + } + } // while + +} + +int send_msg(char* message) +{ + int result = -1; + + pthread_mutex_lock(&write_mutex); + + LOG_VOL("send_msg(%s):", message); + + if (fw_sock >= 0) + result = write(fw_sock, message, strlen(message) + 1); + + pthread_mutex_unlock(&write_mutex); + + return result; +} + +int send_msg_with_data(char *message, char *data) +{ + int result = -1; + + char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1); + if (!buffer) { + LOGE("alloca failed in send_msg_with_data"); + return -1; + } + + strcpy(buffer, message); + strcat(buffer, data); + return send_msg(buffer); +} diff --git a/vold/vold.h b/vold/vold.h new file mode 100644 index 0000000..0876bec --- /dev/null +++ b/vold/vold.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VOLD_H__ +#define VOLD_H__ + +#define LOG_TAG "vold" +#include "cutils/log.h" + +typedef int boolean; +enum { + false = 0, + true = 1 +}; + +#define DEVPATH "/dev/block/" +#define DEVPATHLENGTH 11 + +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) + +// Set this for logging error messages +#define ENABLE_LOG_ERROR + +// set this to log vold events +#define ENABLE_LOG_VOL + +#ifdef ENABLE_LOG_ERROR +#define LOG_ERROR(fmt, args...) \ + { LOGE(fmt , ## args); } +#else +#define LOG_ERROR(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ERROR */ + +#ifdef ENABLE_LOG_VOL +#define LOG_VOL(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_VOL(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_VOL */ + +#ifdef ENABLE_LOG_SERVER +#define LOG_SERVER(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_SERVER(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_SERVER */ + +#ifdef ENABLE_LOG_ASEC +#define LOG_ASEC(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_ASEC(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ASEC */ + +/* + * Prototypes + */ + +int process_framework_command(int socket); + +int process_inotify_event(int fd); +int inotify_bootstrap(void); + +int process_uevent_message(int socket); +int simulate_uevent(char *subsystem, char *path, char *action, char **params); + +int mmc_bootstrap(void); +int ums_bootstrap(void); + +int volmgr_bootstrap(void); + +int switch_bootstrap(void); + +void *read_file(char *filename, ssize_t *_size); +char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer); +char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var); + +void ums_hostconnected_set(boolean connected); +boolean ums_hostconnected_get(void); + +int send_msg(char *msg); +int send_msg_with_data(char *msg, char *data); +#endif diff --git a/vold/volmgr.c b/vold/volmgr.c new file mode 100644 index 0000000..c3ce8d1 --- /dev/null +++ b/vold/volmgr.c @@ -0,0 +1,1229 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <sched.h> + +#include <sys/mount.h> + +#include <cutils/config_utils.h> +#include <cutils/properties.h> + +#include "vold.h" +#include "volmgr.h" +#include "blkdev.h" +#include "ums.h" +#include "format.h" +#include "devmapper.h" + +#include "volmgr_ext3.h" +#include "volmgr_vfat.h" + +#define DEBUG_VOLMGR 0 + +static volume_t *vol_root = NULL; +static boolean safe_mode = true; + +static struct volmgr_fstable_entry fs_table[] = { + { "ext3", ext_identify, ext_check, ext_mount , true }, + { "vfat", vfat_identify, vfat_check, vfat_mount , false }, + { NULL, NULL, NULL, NULL , false} +}; + +struct _volume_state_event_map { + volume_state_t state; + char *event; + char *property_val; +}; + +static struct _volume_state_event_map volume_state_strings[] = { + { volstate_unknown, "volstate_unknown:", "unknown" }, + { volstate_nomedia, VOLD_EVT_NOMEDIA, VOLD_ES_PVAL_NOMEDIA }, + { volstate_unmounted, VOLD_EVT_UNMOUNTED, VOLD_ES_PVAL_UNMOUNTED }, + { volstate_checking, VOLD_EVT_CHECKING, VOLD_ES_PVAL_CHECKING }, + { volstate_mounted, VOLD_EVT_MOUNTED, VOLD_ES_PVAL_MOUNTED }, + { volstate_mounted_ro, VOLD_EVT_MOUNTED_RO, VOLD_ES_PVAL_MOUNTED_RO }, + { volstate_badremoval, VOLD_EVT_BADREMOVAL, VOLD_ES_PVAL_BADREMOVAL }, + { volstate_damaged, VOLD_EVT_DAMAGED, VOLD_ES_PVAL_DAMAGED }, + { volstate_nofs, VOLD_EVT_NOFS, VOLD_ES_PVAL_NOFS }, + { volstate_ums, VOLD_EVT_UMS, VOLD_ES_PVAL_UMS }, + { 0, NULL, NULL } +}; + + +static int volmgr_readconfig(char *cfg_path); +static int volmgr_config_volume(cnode *node); +static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy); +static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev); +static int _volmgr_start(volume_t *vol, blkdev_t *dev); +static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev); +static void *volmgr_start_fs_thread(void *arg); +static void volmgr_start_fs_thread_sighandler(int signo); +static void volume_setstate(volume_t *vol, volume_state_t state); +static char *conv_volstate_to_eventstr(volume_state_t state); +static char *conv_volstate_to_propstr(volume_state_t state); +static int volume_send_state(volume_t *vol); +static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg); +static int _volmgr_enable_ums(volume_t *); +static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *arg), boolean emit_statechange); +static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange); +static void _cb_volume_stopped_for_eject(volume_t *v, void *arg); +static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg); +static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev); +static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg); +static void volmgr_reaper_thread_sighandler(int signo); +static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path); +static int volmgr_send_eject_request(volume_t *v); +static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked); + +static boolean _mountpoint_mounted(char *mp) +{ + char device[256]; + char mount_path[256]; + char rest[256]; + FILE *fp; + char line[1024]; + + if (!(fp = fopen("/proc/mounts", "r"))) { + LOGE("Error opening /proc/mounts (%s)", strerror(errno)); + return false; + } + + while(fgets(line, sizeof(line), fp)) { + line[strlen(line)-1] = '\0'; + sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); + if (!strcmp(mount_path, mp)) { + fclose(fp); + return true; + } + + } + + fclose(fp); + return false; +} + +/* + * Public functions + */ + +int volmgr_set_volume_key(char *mount_point, unsigned char *key) +{ + volume_t *v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + + if (!v) + return -ENOENT; + + if (v->media_type != media_devmapper) { + LOGE("Cannot set key on a non devmapper volume"); + pthread_mutex_unlock(&v->lock); + return -EINVAL; + } + + memcpy(v->dm->key, key, sizeof(v->dm->key)); + pthread_mutex_unlock(&v->lock); + return 0; +} + +int volmgr_format_volume(char *mount_point) +{ + int rc; + volume_t *v; + + LOG_VOL("volmgr_format_volume(%s):", mount_point); + + v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + + if (!v) + return -ENOENT; + + if (v->state == volstate_mounted || + v->state == volstate_mounted_ro || + v->state == volstate_ums || + v->state == volstate_checking) { + LOGE("Can't format '%s', currently in state %d", mount_point, v->state); + pthread_mutex_unlock(&v->lock); + return -EBUSY; + } else if (v->state == volstate_nomedia && + v->media_type != media_devmapper) { + LOGE("Can't format '%s', (no media)", mount_point); + pthread_mutex_unlock(&v->lock); + return -ENOMEDIUM; + } + + // XXX:Reject if the underlying source media is not present + + if (v->media_type == media_devmapper) { + if ((rc = devmapper_genesis(v->dm)) < 0) { + LOGE("devmapper genesis failed for %s (%d)", mount_point, rc); + pthread_mutex_unlock(&v->lock); + return rc; + } + } else { + if ((rc = initialize_mbr(v->dev->disk)) < 0) { + LOGE("MBR init failed for %s (%d)", mount_point, rc); + pthread_mutex_unlock(&v->lock); + return rc; + } + } + + volume_setstate(v, volstate_formatting); + pthread_mutex_unlock(&v->lock); + return rc; +} + +int volmgr_bootstrap(void) +{ + int rc; + + if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) { + LOGE("Unable to process config"); + return rc; + } + + /* + * Check to see if any of our volumes is mounted + */ + volume_t *v = vol_root; + while (v) { + if (_mountpoint_mounted(v->mount_point)) { + LOG_VOL("Volume '%s' already mounted at startup", v->mount_point); + v->state = volstate_mounted; + } + v = v->next; + } + + return 0; +} + +int volmgr_safe_mode(boolean enable) +{ + if (enable == safe_mode) + return 0; + + safe_mode = enable; + + volume_t *v = vol_root; + int rc; + + while (v) { + pthread_mutex_lock(&v->lock); + if (v->state == volstate_mounted && v->fs) { + rc = v->fs->mount_fn(v->dev, v, safe_mode); + if (!rc) { + LOG_VOL("Safe mode %s on %s", (enable ? "enabled" : "disabled"), v->mount_point); + } else { + LOGE("Failed to %s safe-mode on %s (%s)", + (enable ? "enable" : "disable" ), v->mount_point, strerror(-rc)); + } + } + + pthread_mutex_unlock(&v->lock); + v = v->next; + } + + return 0; +} + +int volmgr_send_states(void) +{ + volume_t *vol_scan = vol_root; + int rc; + + while (vol_scan) { + pthread_mutex_lock(&vol_scan->lock); + if ((rc = volume_send_state(vol_scan)) < 0) { + LOGE("Error sending state to framework (%d)", rc); + } + pthread_mutex_unlock(&vol_scan->lock); + vol_scan = vol_scan->next; + break; // XXX: + } + + return 0; +} + +/* + * Called when a block device is ready to be + * evaluated by the volume manager. + */ +int volmgr_consider_disk(blkdev_t *dev) +{ + volume_t *vol; + + if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true))) + return 0; + + pthread_mutex_lock(&vol->lock); + + if (vol->state == volstate_mounted) { + LOGE("Volume %s already mounted (did we just crash?)", vol->mount_point); + pthread_mutex_unlock(&vol->lock); + return 0; + } + + int rc = _volmgr_consider_disk_and_vol(vol, dev); + pthread_mutex_unlock(&vol->lock); + return rc; +} + +int volmgr_start_volume_by_mountpoint(char *mount_point) +{ + volume_t *v; + + v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + if (!v) + return -ENOENT; + + if (v->media_type == media_devmapper) { + if (devmapper_start(v->dm) < 0) { + LOGE("volmgr failed to start devmapper volume '%s'", + v->mount_point); + } + } else if (v->media_type == media_mmc) { + if (!v->dev) { + LOGE("Cannot start volume '%s' (volume is not bound)", mount_point); + pthread_mutex_unlock(&v->lock); + return -ENOENT; + } + + if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) { + LOGE("volmgr failed to start volume '%s'", v->mount_point); + } + } + + pthread_mutex_unlock(&v->lock); + return 0; +} + +static void _cb_volstopped_for_devmapper_teardown(volume_t *v, void *arg) +{ + devmapper_stop(v->dm); + volume_setstate(v, volstate_nomedia); + pthread_mutex_unlock(&v->lock); +} + +int volmgr_stop_volume_by_mountpoint(char *mount_point) +{ + int rc; + volume_t *v; + + v = volmgr_lookup_volume_by_mountpoint(mount_point, true); + if (!v) + return -ENOENT; + + if (v->state == volstate_mounted) + volmgr_send_eject_request(v); + + if (v->media_type == media_devmapper) + rc = volmgr_shutdown_volume(v, _cb_volstopped_for_devmapper_teardown, false); + else + rc = volmgr_shutdown_volume(v, NULL, true); + + /* + * If shutdown returns -EINPROGRESS, + * do *not* release the lock as + * it is now owned by the reaper thread + */ + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + } + return 0; +} + +int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *)) +{ + LOG_VOL("Volmgr notified of %d:%d eject", dev->major, dev->minor); + + volume_t *v; + int rc; + + // XXX: Partitioning support is going to need us to stop *all* + // devices in this volume + if (!(v = volmgr_lookup_volume_by_dev(dev))) { + if (cb) + cb(dev); + return 0; + } + + pthread_mutex_lock(&v->lock); + + volume_state_t old_state = v->state; + + if (v->state == volstate_mounted || + v->state == volstate_ums || + v->state == volstate_checking) { + + volume_setstate(v, volstate_badremoval); + + /* + * Stop any devmapper volumes which + * are using us as a source + * XXX: We may need to enforce stricter + * order here + */ + volume_t *dmvol = vol_root; + while (dmvol) { + if ((dmvol->media_type == media_devmapper) && + (dmvol->dm->src_type == dmsrc_loopback) && + (!strncmp(dmvol->dm->type_data.loop.loop_src, + v->mount_point, strlen(v->mount_point)))) { + + pthread_mutex_lock(&dmvol->lock); + if (dmvol->state != volstate_nomedia) { + rc = volmgr_shutdown_volume(dmvol, _cb_volstopped_for_devmapper_teardown, false); + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&dmvol->lock); + } + } else + pthread_mutex_unlock(&dmvol->lock); + } + dmvol = dmvol->next; + } + + } else if (v->state == volstate_formatting) { + /* + * The device is being ejected due to + * kernel disk revalidation. + */ + LOG_VOL("Volmgr ignoring eject of %d:%d (volume formatting)", + dev->major, dev->minor); + if (cb) + cb(dev); + pthread_mutex_unlock(&v->lock); + return 0; + } else + volume_setstate(v, volstate_nomedia); + + if (old_state == volstate_ums) { + ums_disable(v->ums_path); + pthread_mutex_unlock(&v->lock); + } else { + int rc = volmgr_stop_volume(v, _cb_volume_stopped_for_eject, cb, false); + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + } + } + return 0; +} + +static void _cb_volume_stopped_for_eject(volume_t *v, void *arg) +{ + void (* eject_cb) (blkdev_t *) = arg; + +#if DEBUG_VOLMGR + LOG_VOL("Volume %s has been stopped for eject", v->mount_point); +#endif + + if (eject_cb) + eject_cb(v->dev); + v->dev = NULL; // Clear dev because its being ejected +} + +/* + * Instructs the volume manager to enable or disable USB mass storage + * on any volumes configured to use it. + */ +int volmgr_enable_ums(boolean enable) +{ + volume_t *v = vol_root; + + while(v) { + if (v->ums_path) { + int rc; + + if (enable) { + pthread_mutex_lock(&v->lock); + if (v->state == volstate_mounted) + volmgr_send_eject_request(v); + else if (v->state == volstate_ums) { + pthread_mutex_unlock(&v->lock); + goto next_vol; + } + + // Stop the volume, and enable UMS in the callback + rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable, false); + if (rc != -EINPROGRESS) { + if (rc) + LOGE("unable to shutdown volume '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + } + } else { + // Disable UMS + pthread_mutex_lock(&v->lock); + if (v->state != volstate_ums) { + pthread_mutex_unlock(&v->lock); + goto next_vol; + } + + if ((rc = ums_disable(v->ums_path)) < 0) { + LOGE("unable to disable ums on '%s'", v->mount_point); + pthread_mutex_unlock(&v->lock); + continue; + } + + LOG_VOL("Kick-starting volume %d:%d after UMS disable", + v->dev->disk->major, v->dev->disk->minor); + // Start volume + if ((rc = _volmgr_consider_disk_and_vol(v, v->dev->disk)) < 0) { + LOGE("volmgr failed to consider disk %d:%d", + v->dev->disk->major, v->dev->disk->minor); + } + pthread_mutex_unlock(&v->lock); + } + } + next_vol: + v = v->next; + } + return 0; +} + +/* + * Static functions + */ + +static int volmgr_send_eject_request(volume_t *v) +{ + return send_msg_with_data(VOLD_EVT_EJECTING, v->mount_point); +} + +// vol->lock must be held! +static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev) +{ + int rc = 0; + +#if DEBUG_VOLMGR + LOG_VOL("volmgr_consider_disk_and_vol(%s, %d:%d):", vol->mount_point, + dev->major, dev->minor); +#endif + + if (vol->state == volstate_unknown || + vol->state == volstate_mounted || + vol->state == volstate_mounted_ro || + vol->state == volstate_damaged) { + LOGE("Cannot consider volume '%s' because it is in state '%d", + vol->mount_point, vol->state); + return -EADDRINUSE; + } + + if (vol->state == volstate_formatting) { + LOG_VOL("Evaluating dev '%s' for formattable filesystems for '%s'", + dev->devpath, vol->mount_point); + /* + * Since we only support creating 1 partition (right now), + * we can just lookup the target by devno + */ + blkdev_t *part = blkdev_lookup_by_devno(dev->major, 1); + if (!part) { + part = blkdev_lookup_by_devno(dev->major, 0); + if (!part) { + LOGE("Unable to find device to format"); + return -ENODEV; + } + } + + if ((rc = format_partition(part, + vol->media_type == media_devmapper ? + FORMAT_TYPE_EXT2 : FORMAT_TYPE_FAT32)) < 0) { + LOGE("format failed (%d)", rc); + return rc; + } + + } + + LOG_VOL("Evaluating dev '%s' for mountable filesystems for '%s'", + dev->devpath, vol->mount_point); + + if (dev->nr_parts == 0) { + rc = _volmgr_start(vol, dev); +#if DEBUG_VOLMGR + LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", vol->mount_point, + dev->major, dev->minor, rc); +#endif + } else { + /* + * Device has multiple partitions + * This is where interesting partition policies could be implemented. + * For now just try them in sequence until one succeeds + */ + + rc = -ENODEV; + int i; + for (i = 0; i < dev->nr_parts; i++) { + blkdev_t *part = blkdev_lookup_by_devno(dev->major, (i+1)); + if (!part) { + LOGE("Error - unable to lookup partition for blkdev %d:%d", dev->major, (i+1)); + continue; + } + rc = _volmgr_start(vol, part); +#if DEBUG_VOLMGR + LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", + vol->mount_point, part->major, part->minor, rc); +#endif + if (!rc) + break; + } + + if (rc == -ENODEV) { + // Assert to make sure each partition had a backing blkdev + LOGE("Internal consistency error"); + return 0; + } + } + + if (rc == -ENODATA) { + LOGE("Device %d:%d contains no usable filesystems", + dev->major, dev->minor); + rc = 0; + } + + return rc; +} + +static void volmgr_reaper_thread_sighandler(int signo) +{ + LOGE("Volume reaper thread got signal %d", signo); +} + +static void __reaper_cleanup(void *arg) +{ + volume_t *vol = (volume_t *) arg; + + if (vol->worker_args.reaper_args.cb) + vol->worker_args.reaper_args.cb(vol, vol->worker_args.reaper_args.cb_arg); + + vol->worker_running = false; + + // Wake up anyone that was waiting on this thread + pthread_mutex_unlock(&vol->worker_sem); + + // Unlock the volume + pthread_mutex_unlock(&vol->lock); +} + +static void *volmgr_reaper_thread(void *arg) +{ + volume_t *vol = (volume_t *) arg; + + pthread_cleanup_push(__reaper_cleanup, arg); + + vol->worker_running = true; + vol->worker_pid = getpid(); + + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = volmgr_reaper_thread_sighandler; + sigaction(SIGUSR1, &actions, NULL); + + LOG_VOL("Reaper here - working on %s", vol->mount_point); + + boolean send_sig_kill = false; + int i, rc; + + for (i = 0; i < 10; i++) { + errno = 0; + rc = umount(vol->mount_point); + LOG_VOL("volmngr reaper umount(%s) attempt %d (%s)", + vol->mount_point, i + 1, strerror(errno)); + if (!rc) + break; + if (rc && (errno == EINVAL || errno == ENOENT)) { + rc = 0; + break; + } + sleep(1); + if (i >= 4) { + KillProcessesWithOpenFiles(vol->mount_point, send_sig_kill, NULL, 0); + if (!send_sig_kill) + send_sig_kill = true; + } + } + + if (!rc) { + LOG_VOL("Reaper sucessfully unmounted %s", vol->mount_point); + vol->fs = NULL; + volume_setstate(vol, volstate_unmounted); + } else { + LOGE("Unable to unmount!! (%d)", rc); + } + + out: + pthread_cleanup_pop(1); + pthread_exit(NULL); + return NULL; +} + +// vol->lock must be held! +static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg) +{ + + if (vol->worker_running) { + LOGE("Worker thread is currently running.. waiting.."); + pthread_mutex_lock(&vol->worker_sem); + LOG_VOL("Worker thread now available"); + } + + vol->worker_args.reaper_args.cb = cb; + vol->worker_args.reaper_args.cb_arg = arg; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&vol->worker_thread, &attr, volmgr_reaper_thread, vol); +} + +static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange) +{ + int i, rc; + + if (v->state == volstate_mounted || v->state == volstate_badremoval) { + // Try to unmount right away (5 retries) + for (i = 0; i < 5; i++) { + rc = umount(v->mount_point); + if (!rc) + break; + + if (rc && (errno == EINVAL || errno == ENOENT)) { + rc = 0; + break; + } + + LOG_VOL("volmngr quick stop umount(%s) attempt %d (%s)", + v->mount_point, i + 1, strerror(errno)); + + if (i == 0) + usleep(1000 * 250); // First failure, sleep for 250 ms + else + sched_yield(); + } + + if (!rc) { + LOG_VOL("volmgr_stop_volume(%s): Volume unmounted sucessfully", + v->mount_point); + if (emit_statechange) + volume_setstate(v, volstate_unmounted); + v->fs = NULL; + goto out_cb_immed; + } + + /* + * Since the volume is still in use, dispatch the stopping to + * a thread + */ + LOG_VOL("Volume %s is busy (%d) - uncaging the reaper", v->mount_point, rc); + volmgr_uncage_reaper(v, cb, arg); + return -EINPROGRESS; + } else if (v->state == volstate_checking) { + volume_setstate(v, volstate_unmounted); + if (v->worker_running) { + LOG_VOL("Cancelling worker thread"); + pthread_kill(v->worker_thread, SIGUSR1); + } else + LOGE("Strange... we were in checking state but worker thread wasn't running.."); + goto out_cb_immed; + } + + out_cb_immed: + if (cb) + cb(v, arg); + return 0; +} + + +/* + * Gracefully stop a volume + * v->lock must be held! + * if we return -EINPROGRESS, do NOT release the lock as the reaper + * is using the volume + */ +static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *), boolean emit_statechange) +{ + return volmgr_stop_volume(v, cb, NULL, emit_statechange); +} + +static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg) +{ + void (* shutdown_cb) (volume_t *) = arg; + +#if DEBUG_VOLMGR + LOG_VOL("Volume %s has been stopped for shutdown", v->mount_point); +#endif + shutdown_cb(v); +} + + +/* + * Called when a volume is sucessfully unmounted for UMS enable + */ +static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg) +{ + int rc; + char *devdir_path; + +#if DEBUG_VOLMGR + LOG_VOL("_cb_volstopped_for_ums_enable(%s):", v->mount_point); +#endif + devdir_path = blkdev_get_devpath(v->dev->disk); + + if ((rc = ums_enable(devdir_path, v->ums_path)) < 0) { + free(devdir_path); + LOGE("Error enabling ums (%d)", rc); + return; + } + free(devdir_path); + volume_setstate(v, volstate_ums); + pthread_mutex_unlock(&v->lock); +} + +static int volmgr_readconfig(char *cfg_path) +{ + cnode *root = config_node("", ""); + cnode *node; + + config_load_file(root, cfg_path); + node = root->first_child; + + while (node) { + if (!strncmp(node->name, "volume_", 7)) + volmgr_config_volume(node); + else + LOGE("Skipping unknown configuration node '%s'", node->name); + node = node->next; + } + return 0; +} + +static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path) +{ + int i; + +#if DEBUG_VOLMGR + LOG_VOL("volmgr_add_mediapath_to_volume(%p, %s):", v, media_path); +#endif + for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) { + if (!v->media_paths[i]) { + v->media_paths[i] = strdup(media_path); + return; + } + } + LOGE("Unable to add media path '%s' to volume (out of media slots)", media_path); +} + +static int volmgr_config_volume(cnode *node) +{ + volume_t *new; + int rc = 0, i; + + char *dm_src, *dm_src_type, *dm_tgt, *dm_param, *dm_tgtfs; + uint32_t dm_size_mb = 0; + + dm_src = dm_src_type = dm_tgt = dm_param = dm_tgtfs = NULL; +#if DEBUG_VOLMGR + LOG_VOL("volmgr_configure_volume(%s):", node->name); +#endif + if (!(new = malloc(sizeof(volume_t)))) + return -ENOMEM; + memset(new, 0, sizeof(volume_t)); + + new->state = volstate_nomedia; + pthread_mutex_init(&new->lock, NULL); + pthread_mutex_init(&new->worker_sem, NULL); + + cnode *child = node->first_child; + + while (child) { + if (!strcmp(child->name, "media_path")) + volmgr_add_mediapath_to_volume(new, child->value); + else if (!strcmp(child->name, "emu_media_path")) + volmgr_add_mediapath_to_volume(new, child->value); + else if (!strcmp(child->name, "media_type")) { + if (!strcmp(child->value, "mmc")) + new->media_type = media_mmc; + else if (!strcmp(child->value, "devmapper")) + new->media_type = media_devmapper; + else { + LOGE("Invalid media type '%s'", child->value); + rc = -EINVAL; + goto out_free; + } + } else if (!strcmp(child->name, "mount_point")) + new->mount_point = strdup(child->value); + else if (!strcmp(child->name, "ums_path")) + new->ums_path = strdup(child->value); + else if (!strcmp(child->name, "dm_src")) + dm_src = strdup(child->value); + else if (!strcmp(child->name, "dm_src_type")) + dm_src_type = strdup(child->value); + else if (!strcmp(child->name, "dm_src_size_mb")) + dm_size_mb = atoi(child->value); + else if (!strcmp(child->name, "dm_target")) + dm_tgt = strdup(child->value); + else if (!strcmp(child->name, "dm_target_params")) + dm_param = strdup(child->value); + else if (!strcmp(child->name, "dm_target_fs")) + dm_tgtfs = strdup(child->value); + else + LOGE("Ignoring unknown config entry '%s'", child->name); + child = child->next; + } + + if (new->media_type == media_mmc) { + if (!new->media_paths[0] || !new->mount_point || new->media_type == media_unknown) { + LOGE("Required configuration parameter missing for mmc volume"); + rc = -EINVAL; + goto out_free; + } + } else if (new->media_type == media_devmapper) { + if (!dm_src || !dm_src_type || !dm_tgt || + !dm_param || !dm_tgtfs || !dm_size_mb) { + LOGE("Required configuration parameter missing for devmapper volume"); + rc = -EINVAL; + goto out_free; + } + + char dm_mediapath[255]; + if (!(new->dm = devmapper_init(dm_src, dm_src_type, dm_size_mb, + dm_tgt, dm_param, dm_tgtfs, dm_mediapath))) { + LOGE("Unable to initialize devmapping"); + goto out_free; + } + LOG_VOL("media path for devmapper volume = '%s'", dm_mediapath); + volmgr_add_mediapath_to_volume(new, dm_mediapath); + } + + if (!vol_root) + vol_root = new; + else { + volume_t *scan = vol_root; + while (scan->next) + scan = scan->next; + scan->next = new; + } + + if (dm_src) + free(dm_src); + if (dm_src_type) + free(dm_src_type); + if (dm_tgt) + free(dm_tgt); + if (dm_param) + free(dm_param); + if (dm_tgtfs) + free(dm_tgtfs); + + return rc; + + out_free: + + if (dm_src) + free(dm_src); + if (dm_src_type) + free(dm_src_type); + if (dm_tgt) + free(dm_tgt); + if (dm_param) + free(dm_param); + if (dm_tgtfs) + free(dm_tgtfs); + + + for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) { + if (new->media_paths[i]) + free(new->media_paths[i]); + } + if (new->mount_point) + free(new->mount_point); + if (new->ums_path) + free(new->ums_path); + return rc; +} + +static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev) +{ + volume_t *scan = vol_root; + while(scan) { + if (scan->dev == dev) + return scan; + scan = scan->next; + } + return NULL; +} + +static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked) +{ + volume_t *v = vol_root; + + while(v) { + pthread_mutex_lock(&v->lock); + if (!strcmp(v->mount_point, mount_point)) { + if (!leave_locked) + pthread_mutex_unlock(&v->lock); + return v; + } + pthread_mutex_unlock(&v->lock); + v = v->next; + } + return NULL; +} + +static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy) +{ + volume_t *scan = vol_root; + int i; + + while (scan) { + + for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) { + if (!scan->media_paths[i]) + continue; + + if (fuzzy && !strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i]))) + return scan; + else if (!fuzzy && !strcmp(media_path, scan->media_paths[i])) + return scan; + } + + scan = scan->next; + } + return NULL; +} + +/* + * Attempt to bring a volume online + * Returns: 0 on success, errno on failure, with the following exceptions: + * - ENODATA - Unsupported filesystem type / blank + * vol->lock MUST be held! + */ +static int _volmgr_start(volume_t *vol, blkdev_t *dev) +{ + struct volmgr_fstable_entry *fs; + int rc = ENODATA; + +#if DEBUG_VOLMGR + LOG_VOL("_volmgr_start(%s, %d:%d):", vol->mount_point, + dev->major, dev->minor); +#endif + + if (vol->state == volstate_mounted) { + LOGE("Unable to start volume '%s' (already mounted)", vol->mount_point); + return -EBUSY; + } + + for (fs = fs_table; fs->name; fs++) { + if (!fs->identify_fn(dev)) + break; + } + + if (!fs) { + LOGE("No supported filesystems on %d:%d", dev->major, dev->minor); + volume_setstate(vol, volstate_nofs); + return -ENODATA; + } + + return volmgr_start_fs(fs, vol, dev); +} + +// vol->lock MUST be held! +static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev) +{ + /* + * Spawn a thread to do the actual checking / mounting in + */ + + if (vol->worker_running) { + LOGE("Worker thread is currently running.. waiting.."); + pthread_mutex_lock(&vol->worker_sem); + LOG_VOL("Worker thread now available"); + } + + vol->dev = dev; + + vol->worker_args.start_args.fs = fs; + vol->worker_args.start_args.dev = dev; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol); + + return 0; +} + +static void __start_fs_thread_lock_cleanup(void *arg) +{ + volume_t *vol = (volume_t *) arg; + +#if DEBUG_VOLMGR + LOG_VOL("__start_fs_thread_lock_cleanup(%s):", vol->mount_point); +#endif + + vol->worker_running = false; + + // Wake up anyone that was waiting on this thread + pthread_mutex_unlock(&vol->worker_sem); + + // Unlock the volume + pthread_mutex_unlock(&vol->lock); +} + +static void *volmgr_start_fs_thread(void *arg) +{ + volume_t *vol = (volume_t *) arg; + + pthread_cleanup_push(__start_fs_thread_lock_cleanup, arg); + pthread_mutex_lock(&vol->lock); + + vol->worker_running = true; + vol->worker_pid = getpid(); + + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = volmgr_start_fs_thread_sighandler; + sigaction(SIGUSR1, &actions, NULL); + + struct volmgr_fstable_entry *fs = vol->worker_args.start_args.fs; + blkdev_t *dev = vol->worker_args.start_args.dev; + int rc; + +#if DEBUG_VOLMGR + LOG_VOL("Worker thread pid %d starting %s fs %d:%d on %s", getpid(), + fs->name, dev->major, dev->minor, vol->mount_point); +#endif + + if (fs->check_fn) { +#if DEBUG_VOLMGR + LOG_VOL("Starting %s filesystem check on %d:%d", fs->name, + dev->major, dev->minor); +#endif + volume_setstate(vol, volstate_checking); + pthread_mutex_unlock(&vol->lock); + rc = fs->check_fn(dev); + pthread_mutex_lock(&vol->lock); + if (vol->state != volstate_checking) { + LOG_VOL("filesystem check aborted"); + goto out; + } + + if (rc < 0) { + LOGE("%s filesystem check failed on %d:%d (%s)", fs->name, + dev->major, dev->minor, strerror(-rc)); + if (rc == -ENODATA) { + volume_setstate(vol, volstate_nofs); + goto out; + } + goto out_unmountable; + } +#if DEBUG_VOLMGR + LOG_VOL("%s filesystem check of %d:%d OK", fs->name, + dev->major, dev->minor); +#endif + } + + rc = fs->mount_fn(dev, vol, safe_mode); + if (!rc) { + LOG_VOL("Sucessfully mounted %s filesystem %d:%d on %s (safe-mode %s)", + fs->name, dev->major, dev->minor, vol->mount_point, + (safe_mode ? "on" : "off")); + vol->fs = fs; + volume_setstate(vol, volstate_mounted); + goto out; + } + + LOGE("%s filesystem mount of %d:%d failed (%d)", fs->name, dev->major, + dev->minor, rc); + + out_unmountable: + volume_setstate(vol, volstate_damaged); + out: + pthread_cleanup_pop(1); + pthread_exit(NULL); + return NULL; +} + +static void volmgr_start_fs_thread_sighandler(int signo) +{ + LOGE("Volume startup thread got signal %d", signo); +} + +static void volume_setstate(volume_t *vol, volume_state_t state) +{ + if (state == vol->state) + return; + +#if DEBUG_VOLMGR + LOG_VOL("Volume %s state change from %d -> %d", vol->mount_point, vol->state, state); +#endif + + vol->state = state; + + char *prop_val = conv_volstate_to_propstr(vol->state); + + if (prop_val) { + property_set(PROP_EXTERNAL_STORAGE_STATE, prop_val); + volume_send_state(vol); + } +} + +static int volume_send_state(volume_t *vol) +{ + char *event = conv_volstate_to_eventstr(vol->state); + + return send_msg_with_data(event, vol->mount_point); +} + +static char *conv_volstate_to_eventstr(volume_state_t state) +{ + int i; + + for (i = 0; volume_state_strings[i].event != NULL; i++) { + if (volume_state_strings[i].state == state) + break; + } + + return volume_state_strings[i].event; +} + +static char *conv_volstate_to_propstr(volume_state_t state) +{ + int i; + + for (i = 0; volume_state_strings[i].event != NULL; i++) { + if (volume_state_strings[i].state == state) + break; + } + + return volume_state_strings[i].property_val; +} + diff --git a/vold/volmgr.h b/vold/volmgr.h new file mode 100644 index 0000000..2c7ec50 --- /dev/null +++ b/vold/volmgr.h @@ -0,0 +1,135 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _VOLMGR_H +#define _VOLMGR_H + +#include <pthread.h> + +#include "vold.h" +#include "blkdev.h" +#include "media.h" +#include "devmapper.h" + +#define PROP_EXTERNAL_STORAGE_STATE "EXTERNAL_STORAGE_STATE" + +// these must match the corresponding states in the MediaState enum. +// A path to the volume mount point follows the colon +typedef enum volume_state { + volstate_unknown, + + volstate_nomedia, +#define VOLD_EVT_NOMEDIA "volume_nomedia:" +#define VOLD_ES_PVAL_NOMEDIA "removed" + + volstate_unmounted, +#define VOLD_EVT_UNMOUNTED "volume_unmounted:" +#define VOLD_ES_PVAL_UNMOUNTED "unmounted" + + volstate_checking, +#define VOLD_EVT_CHECKING "volume_checking:" +#define VOLD_ES_PVAL_CHECKING "checking" + + volstate_mounted, +#define VOLD_EVT_MOUNTED "volume_mounted:" +#define VOLD_ES_PVAL_MOUNTED "mounted" + + volstate_mounted_ro, +#define VOLD_EVT_MOUNTED_RO "volume_mounted_ro:" +#define VOLD_ES_PVAL_MOUNTED_RO "mounted_ro" + + volstate_badremoval, +#define VOLD_EVT_BADREMOVAL "volume_badremoval:" +#define VOLD_ES_PVAL_BADREMOVAL "bad_removal" + + volstate_damaged, +#define VOLD_EVT_DAMAGED "volume_damaged:" +#define VOLD_ES_PVAL_DAMAGED "unmountable" + + volstate_nofs, +#define VOLD_EVT_NOFS "volume_nofs:" +#define VOLD_ES_PVAL_NOFS "nofs" + + volstate_ums, +#define VOLD_EVT_UMS "volume_ums:" +#define VOLD_ES_PVAL_UMS "shared" + + volstate_ejecting, +#define VOLD_EVT_EJECTING "volume_ejecting:" +#define VOLD_ES_PVAL_EJECTING "ejecting" + + volstate_formatting, +} volume_state_t; + +struct volume; + +struct volmgr_fstable_entry { + char *name; + int (*identify_fn) (blkdev_t *dev); + int (*check_fn) (blkdev_t *dev); + int (*mount_fn) (blkdev_t *dev, struct volume *vol, boolean safe_mode); + boolean case_sensitive_paths; +}; + +struct volmgr_start_args { + struct volmgr_fstable_entry *fs; + blkdev_t *dev; +}; + +struct volmgr_reaper_args { + void (*cb) (struct volume *, void *); + void *cb_arg; +}; + +#define VOLMGR_MAX_MEDIAPATHS_PER_VOLUME 8 + +typedef struct volume { + char *media_paths[VOLMGR_MAX_MEDIAPATHS_PER_VOLUME]; + + media_type_t media_type; + char *mount_point; + char *ums_path; + struct devmapping *dm; + + pthread_mutex_t lock; + volume_state_t state; + blkdev_t *dev; + pid_t worker_pid; + pthread_t worker_thread; + union { + struct volmgr_start_args start_args; + struct volmgr_reaper_args reaper_args; + } worker_args; + boolean worker_running; + pthread_mutex_t worker_sem; + + struct volmgr_fstable_entry *fs; + + struct volume *next; +} volume_t; + +int volmgr_consider_disk(blkdev_t *dev); +int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *)); +int volmgr_send_states(void); +int volmgr_enable_ums(boolean enable); +int volmgr_stop_volume_by_mountpoint(char *mount_point); +int volmgr_start_volume_by_mountpoint(char *mount_point); +int volmgr_safe_mode(boolean enable); +int volmgr_format_volume(char *mount_point); +int volmgr_set_volume_key(char *mount_point, unsigned char *key); +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded); +#endif diff --git a/vold/volmgr_ext3.c b/vold/volmgr_ext3.c new file mode 100644 index 0000000..680be21 --- /dev/null +++ b/vold/volmgr_ext3.c @@ -0,0 +1,184 @@ + +/* + * 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 <fcntl.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mount.h> + +#include <linux/ext2_fs.h> +#include <linux/ext3_fs.h> + +#include "vold.h" +#include "volmgr.h" +#include "volmgr_ext3.h" +#include "logwrapper.h" + + +#define EXT_DEBUG 0 + +static char E2FSCK_PATH[] = "/system/bin/e2fsck"; + +int ext_identify(blkdev_t *dev) +{ + int rc = -1; + int fd; + struct ext3_super_block sb; + char *devpath; + +#if EXT_DEBUG + LOG_VOL("ext_identify(%d:%d):", dev-major, dev->minor); +#endif + + devpath = blkdev_get_devpath(dev); + + if ((fd = open(devpath, O_RDWR)) < 0) { + LOGE("Unable to open device '%s' (%s)", devpath, + strerror(errno)); + free(devpath); + return -errno; + } + + if (lseek(fd, 1024, SEEK_SET) < 0) { + LOGE("Unable to lseek to get superblock (%s)", strerror(errno)); + rc = -errno; + goto out; + } + + if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) { + LOGE("Unable to read superblock (%s)", strerror(errno)); + rc = -errno; + goto out; + } + + if (sb.s_magic == EXT2_SUPER_MAGIC || + sb.s_magic == EXT3_SUPER_MAGIC) + rc = 0; + else + rc = -ENODATA; + + out: +#if EXT_DEBUG + LOG_VOL("ext_identify(%s): rc = %d", devpath, rc); +#endif + free(devpath); + close(fd); + return rc; +} + +int ext_check(blkdev_t *dev) +{ + char *devpath; + +#if EXT_DEBUG + LOG_VOL("ext_check(%s):", dev->dev_fspath); +#endif + + devpath = blkdev_get_devpath(dev); + + if (access(E2FSCK_PATH, X_OK)) { + LOGE("ext_check(%s): %s not found (skipping checks)", + devpath, E2FSCK_PATH); + free(devpath); + return 0; + } + + char *args[5]; + + args[0] = E2FSCK_PATH; + args[1] = "-v"; + args[2] = "-p"; + args[3] = devpath; + args[4] = NULL; + + int rc = logwrap(4, args); + + if (rc == 0) { + LOG_VOL("filesystem '%s' had no errors", devpath); + } else if (rc == 1) { + LOG_VOL("filesystem '%s' had corrected errors", devpath); + rc = 0; + } else if (rc == 2) { + LOGE("VOL volume '%s' had corrected errors (system should be rebooted)", devpath); + rc = -EIO; + } else if (rc == 4) { + LOGE("VOL volume '%s' had uncorrectable errors", devpath); + rc = -EIO; + } else if (rc == 8) { + LOGE("Operational error while checking volume '%s'", devpath); + rc = -EIO; + } else { + LOGE("Unknown e2fsck exit code (%d)", rc); + rc = -EIO; + } + free(devpath); + return rc; +} + +int ext_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode) +{ +#if EXT_DEBUG + LOG_VOL("ext_mount(%s, %s, %d):", dev->dev_fspath, vol->mount_point, safe_mode); +#endif + + char *fs[] = { "ext3", "ext2", NULL }; + char *devpath; + + devpath = blkdev_get_devpath(dev); + + int flags, rc = 0; + + flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME; + + if (safe_mode) + flags |= MS_SYNCHRONOUS; + + if (vol->state == volstate_mounted) { + LOG_VOL("Remounting %s on %s, safe mode %d", devpath, + vol->mount_point, safe_mode); + flags |= MS_REMOUNT; + } + + char **f; + for (f = fs; *f != NULL; f++) { + rc = mount(devpath, vol->mount_point, *f, flags, NULL); + if (rc && errno == EROFS) { + LOGE("ext_mount(%s, %s): Read only filesystem - retrying mount RO", + devpath, vol->mount_point); + flags |= MS_RDONLY; + rc = mount(devpath, vol->mount_point, *f, flags, NULL); + } +#if EXT_DEBUG + LOG_VOL("ext_mount(%s, %s): %s mount rc = %d", devpath, *f, + vol->mount_point, rc); +#endif + if (!rc) + break; + } + free(devpath); + + // Chmod the mount point so that its a free-for-all. + // (required for consistency with VFAT.. sigh) + if (chmod(vol->mount_point, 0777) < 0) { + LOGE("Failed to chmod %s (%s)", vol->mount_point, strerror(errno)); + return -errno; + } + + return rc; +} diff --git a/vold/volmgr_ext3.h b/vold/volmgr_ext3.h new file mode 100644 index 0000000..bfe882a --- /dev/null +++ b/vold/volmgr_ext3.h @@ -0,0 +1,27 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _VOLMGR_EXT3_H +#define _VOLMGR_EXT3_H + +#include "volmgr.h" +#include "blkdev.h" + +int ext_identify(blkdev_t *blkdev); +int ext_check(blkdev_t *blkdev); +int ext_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode); +#endif diff --git a/vold/volmgr_vfat.c b/vold/volmgr_vfat.c new file mode 100644 index 0000000..1dc4c8d --- /dev/null +++ b/vold/volmgr_vfat.c @@ -0,0 +1,135 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> + +#include <sys/mount.h> + +#include "vold.h" +#include "volmgr.h" +#include "volmgr_vfat.h" +#include "logwrapper.h" + +#define VFAT_DEBUG 0 + +static char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck"; + +int vfat_identify(blkdev_t *dev) +{ +#if VFAT_DEBUG + LOG_VOL("vfat_identify(%d:%d):", dev->major, dev->minor); +#endif + return 0; // XXX: Implement +} + +int vfat_check(blkdev_t *dev) +{ + int rc; + +#if VFAT_DEBUG + LOG_VOL("vfat_check(%d:%d):", dev->major, dev->minor); +#endif + + if (access(FSCK_MSDOS_PATH, X_OK)) { + LOGE("vfat_check(%d:%d): %s not found (skipping checks)", + dev->major, dev->minor, FSCK_MSDOS_PATH); + return 0; + } + +#ifdef VERIFY_PASS + char *args[7]; + args[0] = FSCK_MSDOS_PATH; + args[1] = "-v"; + args[2] = "-V"; + args[3] = "-w"; + args[4] = "-p"; + args[5] = blkdev_get_devpath(dev); + args[6] = NULL; + rc = logwrap(6, args); + free(args[5]); +#else + char *args[6]; + args[0] = FSCK_MSDOS_PATH; + args[1] = "-v"; + args[2] = "-w"; + args[3] = "-p"; + args[4] = blkdev_get_devpath(dev); + args[5] = NULL; + rc = logwrap(5, args); + free(args[4]); +#endif + + if (rc == 0) { + LOG_VOL("Filesystem check completed OK"); + return 0; + } else if (rc == 1) { + LOG_VOL("Filesystem check failed (general failure)"); + return -EINVAL; + } else if (rc == 2) { + LOG_VOL("Filesystem check failed (invalid usage)"); + return -EIO; + } else if (rc == 4) { + LOG_VOL("Filesystem check completed (errors fixed)"); + } else if (rc == 8) { + LOG_VOL("Filesystem check failed (not a FAT filesystem)"); + return -ENODATA; + } else { + LOG_VOL("Filesystem check failed (unknown exit code %d)", rc); + return -EIO; + } + return 0; +} + +int vfat_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode) +{ + int flags, rc; + char *devpath; + + devpath = blkdev_get_devpath(dev); + +#if VFAT_DEBUG + LOG_VOL("vfat_mount(%d:%d, %s, %d):", dev->major, dev->minor, vol->mount_point, safe_mode); +#endif + + flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC; + + if (safe_mode) + flags |= MS_SYNCHRONOUS; + if (vol->state == volstate_mounted) { + LOG_VOL("Remounting %d:%d on %s, safe mode %d", dev->major, + dev->minor, vol->mount_point, safe_mode); + flags |= MS_REMOUNT; + } + + rc = mount(devpath, vol->mount_point, "vfat", flags, + "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); + + if (rc && errno == EROFS) { + LOGE("vfat_mount(%d:%d, %s): Read only filesystem - retrying mount RO", + dev->major, dev->minor, vol->mount_point); + flags |= MS_RDONLY; + rc = mount(devpath, vol->mount_point, "vfat", flags, + "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); + } + +#if VFAT_DEBUG + LOG_VOL("vfat_mount(%s, %d:%d): mount rc = %d", dev->major,k dev->minor, + vol->mount_point, rc); +#endif + free (devpath); + return rc; +} diff --git a/vold/volmgr_vfat.h b/vold/volmgr_vfat.h new file mode 100644 index 0000000..d9cf04d --- /dev/null +++ b/vold/volmgr_vfat.h @@ -0,0 +1,29 @@ + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _VOLMGR_VFAT_H +#define _VOLMGR_VFAT_H + +#include "volmgr.h" +#include "blkdev.h" + + + +int vfat_identify(blkdev_t *blkdev); +int vfat_check(blkdev_t *blkdev); +int vfat_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode); +#endif |