am 2ad6067c: (-s ours) do not merge: cherry-picked 44db990d3a4ce0edbdd16fa7ac20693ef601b723 from master branch
Merge commit '2ad6067ce491446ab22f59a363d36575a942f5c7' into gingerbread
* commit '2ad6067ce491446ab22f59a363d36575a942f5c7':
do not merge: cherry-picked 44db990d3a4ce0edbdd16fa7ac20693ef601b723 from master branch
diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop
new file mode 100644
index 0000000..18b0594
--- /dev/null
+++ b/ThirdPartyProject.prop
@@ -0,0 +1,10 @@
+# Copyright 2010 Google Inc. All Rights Reserved.
+#Fri Jul 16 10:03:09 PDT 2010
+currentVersion=2.6.32
+version=2.6.32
+isNative=true
+feedurl=http\://kernel.org/pub/linux/kernel/v2.6/
+name=linux
+keywords=linux
+onDevice=true
+homepage=http\://kernel.org
diff --git a/adb/Android.mk b/adb/Android.mk
index 0c93c1e..7faca9b 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -62,7 +62,6 @@
file_sync_client.c \
$(EXTRA_SRCS) \
$(USB_SRCS) \
- shlist.c \
utils.c \
usb_vendors.c
@@ -74,7 +73,7 @@
endif
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter
-LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE := adb
LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS)
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index 6a5191a..c40695a 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -35,7 +35,7 @@
(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
+ The ADB server considers that a device is ONLINE when it has successfully
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.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index b0124a4..be4d50b 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -74,7 +74,7 @@
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.
+ running on the host. This will fail if there is none or more than one.
host:<request>
When asking for information related to a device, 'host:' can also be
@@ -146,7 +146,7 @@
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
+ privileges and thus may not run on all devices. <path> is a full
path from the root of the filesystem.
tcp:<port>
@@ -173,7 +173,7 @@
framebuffer:
This service is used to send snapshots of the framebuffer to a client.
- It requires sufficient priviledges but works as follow:
+ It requires sufficient privileges but works as follow:
After the OKAY, the service sends 16-byte binary structure
containing the following fields (little-endian format):
@@ -190,14 +190,14 @@
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
+ If the adbd daemon doesn't have sufficient privileges 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).
+ network tethering).
It is used to perform a gethostbyname(<address>) on the host and return
the corresponding IP address as a 4-byte string.
@@ -209,7 +209,7 @@
- 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
+ - when everything is read successfully, 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.
diff --git a/adb/adb.c b/adb/adb.c
index ca51bf0..d9f96df 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -144,9 +144,6 @@
void handle_online(void)
{
D("adb: online\n");
-#if !ADB_HOST
- property_set("adb.connected","1");
-#endif
}
void handle_offline(atransport *t)
@@ -154,9 +151,6 @@
D("adb: offline\n");
//Close the associated usb
run_transport_disconnects(t);
-#if !ADB_HOST
- property_set("adb.connected","");
-#endif
}
#if TRACE_PACKETS
@@ -693,7 +687,7 @@
#endif
#if ADB_HOST
-int launch_server()
+int launch_server(int server_port)
{
#ifdef HAVE_WIN32_PROC
/* we need to start the server in the background */
@@ -828,7 +822,17 @@
}
#endif
-int adb_main(int is_daemon)
+/* Constructs a local name of form tcp:port.
+ * target_str points to the target string, it's content will be overwritten.
+ * target_size is the capacity of the target string.
+ * server_port is the port number to use for the local name.
+ */
+void build_local_name(char* target_str, size_t target_size, int server_port)
+{
+ snprintf(target_str, target_size, "tcp:%d", server_port);
+}
+
+int adb_main(int is_daemon, int server_port)
{
#if !ADB_HOST
int secure = 0;
@@ -851,9 +855,11 @@
HOST = 1;
usb_vendors_init();
usb_init();
- local_init(ADB_LOCAL_TRANSPORT_PORT);
+ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
- if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
+ char local_name[30];
+ build_local_name(local_name, sizeof(local_name), server_port);
+ if(install_listener(local_name, "*smartsocket*", NULL)) {
exit(1);
}
#else
@@ -879,7 +885,7 @@
}
}
- /* don't listen on port 5037 if we are running in secure mode */
+ /* don't listen on a port (default 5037) if running in secure mode */
/* don't run as root if we are running in secure mode */
if (secure) {
struct __user_cap_header_struct header;
@@ -920,18 +926,23 @@
cap.inheritable = 0;
capset(&header, &cap);
- D("Local port 5037 disabled\n");
+ D("Local port disabled\n");
} else {
- if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
+ char local_name[30];
+ build_local_name(local_name, sizeof(local_name), server_port);
+ if(install_listener(local_name, "*smartsocket*", NULL)) {
exit(1);
}
}
/* for the device, start the usb transport if the
- ** android usb device exists and "service.adb.tcp"
- ** is not set, otherwise start the network transport.
+ ** android usb device exists and the "service.adb.tcp.port" and
+ ** "persist.adb.tcp.port" properties are not set.
+ ** Otherwise start the network transport.
*/
- property_get("service.adb.tcp.port", value, "0");
+ property_get("service.adb.tcp.port", value, "");
+ if (!value[0])
+ property_get("persist.adb.tcp.port", value, "");
if (sscanf(value, "%d", &port) == 1 && port > 0) {
// listen on TCP port specified by service.adb.tcp.port property
local_init(port);
@@ -940,7 +951,7 @@
usb_init();
} else {
// listen on default port
- local_init(ADB_LOCAL_TRANSPORT_PORT);
+ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
}
init_jdwp();
#endif
@@ -964,6 +975,105 @@
return 0;
}
+#if ADB_HOST
+void connect_device(char* host, char* buffer, int buffer_size)
+{
+ int port, fd;
+ char* portstr = strchr(host, ':');
+ char hostbuf[100];
+ char serial[100];
+
+ strncpy(hostbuf, host, sizeof(hostbuf) - 1);
+ if (portstr) {
+ if (portstr - host >= sizeof(hostbuf)) {
+ snprintf(buffer, buffer_size, "bad host name %s", host);
+ return;
+ }
+ // zero terminate the host at the point we found the colon
+ hostbuf[portstr - host] = 0;
+ if (sscanf(portstr + 1, "%d", &port) == 0) {
+ snprintf(buffer, buffer_size, "bad port number %s", portstr);
+ return;
+ }
+ } else {
+ port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ }
+
+ snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port);
+ if (find_transport(serial)) {
+ snprintf(buffer, buffer_size, "already connected to %s", serial);
+ return;
+ }
+
+ fd = socket_network_client(hostbuf, port, SOCK_STREAM);
+ if (fd < 0) {
+ snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port);
+ return;
+ }
+
+ D("client: connected on remote on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ register_socket_transport(fd, serial, port, 0);
+ snprintf(buffer, buffer_size, "connected to %s", serial);
+}
+
+void connect_emulator(char* port_spec, char* buffer, int buffer_size)
+{
+ char* port_separator = strchr(port_spec, ',');
+ if (!port_separator) {
+ snprintf(buffer, buffer_size,
+ "unable to parse '%s' as <console port>,<adb port>",
+ port_spec);
+ return;
+ }
+
+ // Zero-terminate console port and make port_separator point to 2nd port.
+ *port_separator++ = 0;
+ int console_port = strtol(port_spec, NULL, 0);
+ int adb_port = strtol(port_separator, NULL, 0);
+ if (!(console_port > 0 && adb_port > 0)) {
+ *(port_separator - 1) = ',';
+ snprintf(buffer, buffer_size,
+ "Invalid port numbers: Expected positive numbers, got '%s'",
+ port_spec);
+ return;
+ }
+
+ /* Check if the emulator is already known.
+ * Note: There's a small but harmless race condition here: An emulator not
+ * present just yet could be registered by another invocation right
+ * after doing this check here. However, local_connect protects
+ * against double-registration too. From here, a better error message
+ * can be produced. In the case of the race condition, the very specific
+ * error message won't be shown, but the data doesn't get corrupted. */
+ atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
+ if (known_emulator != NULL) {
+ snprintf(buffer, buffer_size,
+ "Emulator on port %d already registered.", adb_port);
+ return;
+ }
+
+ /* Check if more emulators can be registered. Similar unproblematic
+ * race condition as above. */
+ int candidate_slot = get_available_local_transport_index();
+ if (candidate_slot < 0) {
+ snprintf(buffer, buffer_size, "Cannot accept more emulators.");
+ return;
+ }
+
+ /* Preconditions met, try to connect to the emulator. */
+ if (!local_connect_arbitrary_ports(console_port, adb_port)) {
+ snprintf(buffer, buffer_size,
+ "Connected to emulator on ports %d,%d", console_port, adb_port);
+ } else {
+ snprintf(buffer, buffer_size,
+ "Could not connect to emulator on ports %d,%d",
+ console_port, adb_port);
+ }
+}
+#endif
+
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
{
atransport *transport = NULL;
@@ -1021,43 +1131,16 @@
return 0;
}
- // add a new TCP transport
+ // add a new TCP transport, device or emulator
if (!strncmp(service, "connect:", 8)) {
char buffer[4096];
- int port, fd;
char* host = service + 8;
- char* portstr = strchr(host, ':');
-
- if (!portstr) {
- snprintf(buffer, sizeof(buffer), "unable to parse %s as <host>:<port>", host);
- goto done;
+ if (!strncmp(host, "emu:", 4)) {
+ connect_emulator(host + 4, buffer, sizeof(buffer));
+ } else {
+ connect_device(host, buffer, sizeof(buffer));
}
- if (find_transport(host)) {
- snprintf(buffer, sizeof(buffer), "Already connected to %s", host);
- goto done;
- }
-
- // zero terminate host by overwriting the ':'
- *portstr++ = 0;
- if (sscanf(portstr, "%d", &port) == 0) {
- snprintf(buffer, sizeof(buffer), "bad port number %s", portstr);
- goto done;
- }
-
- fd = socket_network_client(host, port, SOCK_STREAM);
- if (fd < 0) {
- snprintf(buffer, sizeof(buffer), "unable to connect to %s:%d", host, port);
- goto done;
- }
-
- D("client: connected on remote on fd %d\n", fd);
- close_on_exec(fd);
- disable_tcp_nagle(fd);
- snprintf(buf, sizeof buf, "%s:%d", host, port);
- register_socket_transport(fd, buf, port, 0);
- snprintf(buffer, sizeof(buffer), "connected to %s:%d", host, port);
-
-done:
+ // Send response for emulator and device
snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
writex(reply_fd, buf, strlen(buf));
return 0;
@@ -1068,12 +1151,23 @@
char buffer[4096];
memset(buffer, 0, sizeof(buffer));
char* serial = service + 11;
- atransport *t = find_transport(serial);
-
- if (t) {
- unregister_transport(t);
+ if (serial[0] == 0) {
+ // disconnect from all TCP devices
+ unregister_all_tcp_transports();
} else {
- snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+ char hostbuf[100];
+ // assume port 5555 if no port is specified
+ if (!strchr(serial, ':')) {
+ snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
+ serial = hostbuf;
+ }
+ atransport *t = find_transport(serial);
+
+ if (t) {
+ unregister_transport(t);
+ } else {
+ snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+ }
}
snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
@@ -1181,6 +1275,6 @@
}
start_device_log();
- return adb_main(0);
+ return adb_main(0, DEFAULT_ADB_PORT);
#endif
}
diff --git a/adb/adb.h b/adb/adb.h
index aaf8a28..3d2a77b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -183,6 +183,7 @@
/* used to identify transports for clients */
char *serial;
char *product;
+ int adb_port; // Use for emulators (local transport)
/* a list of adisconnect callbacks called when the transport is kicked */
int kicked;
@@ -237,8 +238,8 @@
void send_packet(apacket *p, atransport *t);
void get_my_path(char *s, size_t maxLen);
-int launch_server();
-int adb_main(int is_daemon);
+int launch_server(int server_port);
+int adb_main(int is_daemon, int server_port);
/* transports are ref-counted
@@ -262,6 +263,9 @@
void kick_transport( atransport* t );
/* initialize a transport object's func pointers and state */
+#if ADB_HOST
+int get_available_local_transport_index();
+#endif
int init_socket_transport(atransport *t, int s, int port, int local);
void init_usb_transport(atransport *t, usb_handle *usb, int state);
@@ -271,8 +275,9 @@
/* cause new transports to be init'd and added to the list */
void register_socket_transport(int s, const char *serial, int port, int local);
-/* this should only be used for the "adb disconnect" command */
+/* these should only be used for the "adb disconnect" command */
void unregister_transport(atransport *t);
+void unregister_all_tcp_transports();
void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
@@ -280,6 +285,9 @@
void unregister_usb_transport(usb_handle *usb);
atransport *find_transport(const char *serial);
+#if ADB_HOST
+atransport* find_emulator_transport_by_adb_port(int adb_port);
+#endif
int service_to_fd(const char *name);
#if ADB_HOST
@@ -358,8 +366,8 @@
#define print_packet(tag,p) do {} while (0)
#endif
-#define ADB_PORT 5037
-#define ADB_LOCAL_TRANSPORT_PORT 5555
+#define DEFAULT_ADB_PORT 5037
+#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
#define ADB_CLASS 0xff
#define ADB_SUBCLASS 0x42
@@ -368,6 +376,7 @@
void local_init(int port);
int local_connect(int port);
+int local_connect_arbitrary_ports(int console_port, int adb_port);
/* usb host/client interface */
void usb_init();
diff --git a/adb/adb_client.c b/adb/adb_client.c
index 243f0fa..882810a 100644
--- a/adb/adb_client.c
+++ b/adb/adb_client.c
@@ -16,12 +16,19 @@
static transport_type __adb_transport = kTransportAny;
static const char* __adb_serial = NULL;
+static int __adb_server_port = DEFAULT_ADB_PORT;
+
void adb_set_transport(transport_type type, const char* serial)
{
__adb_transport = type;
__adb_serial = serial;
}
+void adb_set_tcp_specifics(int server_port)
+{
+ __adb_server_port = server_port;
+}
+
int adb_get_emulator_console_port(void)
{
const char* serial = __adb_serial;
@@ -174,7 +181,7 @@
}
snprintf(tmp, sizeof tmp, "%04x", len);
- fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
+ fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
if(fd < 0) {
strcpy(__adb_error, "cannot connect to daemon");
return -2;
@@ -204,9 +211,10 @@
int fd = _adb_connect("host:version");
if(fd == -2) {
- fprintf(stdout,"* daemon not running. starting it now *\n");
+ fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
+ __adb_server_port);
start_server:
- if(launch_server(0)) {
+ if(launch_server(__adb_server_port)) {
fprintf(stderr,"* failed to start daemon *\n");
return -1;
} else {
@@ -314,5 +322,3 @@
adb_close(fd);
return 0;
}
-
-
diff --git a/adb/adb_client.h b/adb/adb_client.h
index 8061579..40ab189 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -25,6 +25,10 @@
*/
void adb_set_transport(transport_type type, const char* serial);
+/* Set TCP specifics of the transport to use
+*/
+void adb_set_tcp_specifics(int server_port);
+
/* Return the console port of the currently connected emulator (if any)
* of -1 if there is no emulator, and -2 if there is more than one.
* assumes adb_set_transport() was alled previously...
diff --git a/adb/commandline.c b/adb/commandline.c
index 857cee3..dcba83b 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -37,11 +37,6 @@
#include "adb_client.h"
#include "file_sync_service.h"
-#ifdef SH_HISTORY
-#include "shlist.h"
-#include "history.h"
-#endif
-
enum {
IGNORE_DATA,
WIPE_DATA,
@@ -105,13 +100,18 @@
" environment variable is used, which must\n"
" be an absolute path.\n"
" devices - list all connected devices\n"
- " connect <host>:<port> - connect to a device via TCP/IP\n"
- " disconnect <host>:<port> - disconnect from a TCP/IP device\n"
+ " connect <host>[:<port>] - connect to a device via TCP/IP\n"
+ " Port 5555 is used by default if no port number is specified.\n"
+ " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n"
+ " Port 5555 is used by default if no port number is specified.\n"
+ " Using this ocmmand with no additional arguments\n"
+ " will disconnect from all connected TCP/IP 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"
+ " (-l means list but don't copy)\n"
" (see 'adb help all')\n"
" adb shell - run remote shell interactively\n"
" adb shell <command> - run remote shell command\n"
@@ -170,6 +170,12 @@
"\n"
" - If it is \"system\" or \"data\", only the corresponding partition\n"
" is updated.\n"
+ "\n"
+ "environmental variables:\n"
+ " ADB_TRACE - Print debug information. A comma separated list of the following values\n"
+ " 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
+ " ANDROID_SERIAL - The serial number to connect to. -s takes priority over this if given.\n"
+ " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n"
);
}
@@ -226,23 +232,10 @@
}
}
-#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;
@@ -251,9 +244,6 @@
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);
@@ -262,97 +252,34 @@
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 {
+ for(n = 0; n < r; n++){
+ switch(buf[n]) {
+ case '\n':
+ 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
- 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;
+ exit(0);
}
+ default:
+ 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;
}
@@ -761,6 +688,7 @@
int quote;
transport_type ttype = kTransportAny;
char* serial = NULL;
+ char* server_port_str = NULL;
/* If defined, this should be an absolute path to
* the directory containing all of the various system images
@@ -776,7 +704,20 @@
serial = getenv("ANDROID_SERIAL");
- /* modifiers and flags */
+ /* Validate and assign the server port */
+ server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
+ int server_port = DEFAULT_ADB_PORT;
+ if (server_port_str && strlen(server_port_str) > 0) {
+ server_port = (int) strtol(server_port_str, NULL, 0);
+ if (server_port <= 0) {
+ fprintf(stderr,
+ "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number. Got \"%s\"\n",
+ server_port_str);
+ return usage();
+ }
+ }
+
+ /* modifiers and flags */
while(argc > 0) {
if(!strcmp(argv[0],"nodaemon")) {
no_daemon = 1;
@@ -805,7 +746,7 @@
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
- if(argc < 2) return usage();
+ if(argc < 2 || argv[0][2] != '\0') return usage();
serial = argv[1];
argc--;
argv++;
@@ -823,12 +764,13 @@
}
adb_set_transport(ttype, serial);
+ adb_set_tcp_specifics(server_port);
if ((argc > 0) && (!strcmp(argv[0],"server"))) {
if (no_daemon || is_daemon) {
- r = adb_main(is_daemon);
+ r = adb_main(is_daemon, server_port);
} else {
- r = launch_server();
+ r = launch_server(server_port);
}
if(r) {
fprintf(stderr,"* could not start server *\n");
@@ -856,13 +798,33 @@
}
}
- if(!strcmp(argv[0], "connect") || !strcmp(argv[0], "disconnect")) {
+ if(!strcmp(argv[0], "connect")) {
char *tmp;
if (argc != 2) {
- fprintf(stderr, "Usage: adb %s <host>:<port>\n", argv[0]);
+ fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
return 1;
}
- snprintf(buf, sizeof buf, "host:%s:%s", argv[0], argv[1]);
+ snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
+ tmp = adb_query(buf);
+ if(tmp) {
+ printf("%s\n", tmp);
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if(!strcmp(argv[0], "disconnect")) {
+ char *tmp;
+ if (argc > 2) {
+ fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
+ return 1;
+ }
+ if (argc == 2) {
+ snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]);
+ } else {
+ snprintf(buf, sizeof buf, "host:disconnect:");
+ }
tmp = adb_query(buf);
if(tmp) {
printf("%s\n", tmp);
@@ -893,10 +855,10 @@
/* quote empty strings and strings with spaces */
quote = (**argv == 0 || strchr(*argv, ' '));
if (quote)
- strcat(buf, "\"");
+ strcat(buf, "\"");
strcat(buf, *argv++);
if (quote)
- strcat(buf, "\"");
+ strcat(buf, "\"");
}
for(;;) {
@@ -1042,10 +1004,19 @@
if(!strcmp(argv[0], "sync")) {
char *srcarg, *android_srcpath, *data_srcpath;
+ int listonly = 0;
+
int ret;
if(argc < 2) {
/* No local path was specified. */
srcarg = NULL;
+ } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
+ listonly = 1;
+ if (argc == 3) {
+ srcarg = argv[2];
+ } else {
+ srcarg = NULL;
+ }
} else if(argc == 2) {
/* A local path or "android"/"data" arg was specified. */
srcarg = argv[1];
@@ -1056,9 +1027,9 @@
if(ret != 0) return usage();
if(android_srcpath != NULL)
- ret = do_sync_sync(android_srcpath, "/system");
+ ret = do_sync_sync(android_srcpath, "/system", listonly);
if(ret == 0 && data_srcpath != NULL)
- ret = do_sync_sync(data_srcpath, "/data");
+ ret = do_sync_sync(data_srcpath, "/data", listonly);
free(android_srcpath);
free(data_srcpath);
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index 0ebfe73..da25ae8 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -670,7 +670,7 @@
}
-static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps)
+static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
{
copyinfo *filelist = 0;
copyinfo *ci, *next;
@@ -718,8 +718,9 @@
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 */)){
+ fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
+ if(!listonly &&
+ sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
return 1;
}
pushed++;
@@ -757,7 +758,7 @@
if(S_ISDIR(st.st_mode)) {
BEGIN();
- if(copy_local_dir_remote(fd, lpath, rpath, 0)) {
+ if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
return 1;
} else {
END();
@@ -959,7 +960,7 @@
return 1;
}
- if(S_ISREG(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+ if(S_ISREG(mode) || S_ISLNK(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,
@@ -1001,7 +1002,7 @@
}
}
-int do_sync_sync(const char *lpath, const char *rpath)
+int do_sync_sync(const char *lpath, const char *rpath, int listonly)
{
fprintf(stderr,"syncing %s...\n",rpath);
@@ -1012,7 +1013,7 @@
}
BEGIN();
- if(copy_local_dir_remote(fd, lpath, rpath, 1)){
+ if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){
return 1;
} else {
END();
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 4ee40ba..11ea06b 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -79,7 +79,7 @@
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_sync(const char *lpath, const char *rpath, int listonly);
int do_sync_pull(const char *rpath, const char *lpath);
#define SYNC_DATA_MAX (64*1024)
diff --git a/adb/history.h b/adb/history.h
deleted file mode 100755
index ef86ad9..0000000
--- a/adb/history.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#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/remount_service.c b/adb/remount_service.c
index 26bc841..4cb41e7 100644
--- a/adb/remount_service.c
+++ b/adb/remount_service.c
@@ -30,19 +30,19 @@
static int system_ro = 1;
-/* Returns the mount number of the requested partition from /proc/mtd */
-static int find_mount(const char *findme)
+/* Returns the device used to mount a directory in /proc/mounts */
+static char *find_mount(const char *dir)
{
int fd;
int res;
int size;
char *token = NULL;
const char delims[] = "\n";
- char buf[1024];
+ char buf[4096];
- fd = unix_open("/proc/mtd", O_RDONLY);
+ fd = unix_open("/proc/mounts", O_RDONLY);
if (fd < 0)
- return -errno;
+ return NULL;
buf[sizeof(buf) - 1] = '\0';
size = adb_read(fd, buf, sizeof(buf) - 1);
@@ -51,33 +51,41 @@
token = strtok(buf, delims);
while (token) {
- char mtdname[16];
- int mtdnum, mtdsize, mtderasesize;
+ char mount_dev[256];
+ char mount_dir[256];
+ int mount_freq;
+ int mount_passno;
- res = sscanf(token, "mtd%d: %x %x %15s",
- &mtdnum, &mtdsize, &mtderasesize, mtdname);
-
- if (res == 4 && !strcmp(mtdname, findme))
- return mtdnum;
+ res = sscanf(token, "%255s %255s %*s %*s %d %d\n",
+ mount_dev, mount_dir, &mount_freq, &mount_passno);
+ mount_dev[255] = 0;
+ mount_dir[255] = 0;
+ if (res == 4 && (strcmp(dir, mount_dir) == 0))
+ return strdup(mount_dev);
token = strtok(NULL, delims);
}
- return -1;
+ return NULL;
}
/* Init mounts /system as read only, remount to enable writes. */
static int remount_system()
{
- int num;
- char source[64];
+ char *dev;
+
if (system_ro == 0) {
return 0;
}
- if ((num = find_mount("\"system\"")) < 0)
+
+ dev = find_mount("/system");
+
+ if (!dev)
return -1;
- snprintf(source, sizeof source, "/dev/block/mtdblock%d", num);
- system_ro = mount(source, "/system", "yaffs2", MS_REMOUNT, NULL);
+ system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL);
+
+ free(dev);
+
return system_ro;
}
diff --git a/adb/shlist.c b/adb/shlist.c
deleted file mode 100755
index 44919ef..0000000
--- a/adb/shlist.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*-------------------------------------------------------------------*/
-/* 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
deleted file mode 100755
index 0a9b07b..0000000
--- a/adb/shlist.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-------------------------------------------------------------------*/
-/* 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/transport.c b/adb/transport.c
index c2877d2..62bdfdb 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -671,21 +671,26 @@
}
+static void transport_unref_locked(atransport *t)
+{
+ 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);
+ }
+}
+
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);
- }
+ transport_unref_locked(t);
adb_mutex_unlock(&transport_lock);
}
}
@@ -894,6 +899,29 @@
transport_unref(t);
}
+// unregisters all non-emulator TCP transports
+void unregister_all_tcp_transports()
+{
+ atransport *t, *next;
+ adb_mutex_lock(&transport_lock);
+ for (t = transport_list.next; t != &transport_list; t = next) {
+ next = t->next;
+ if (t->type == kTransportLocal && t->adb_port == 0) {
+ t->next->prev = t->prev;
+ t->prev->next = next;
+ // we cannot call kick_transport when holding transport_lock
+ if (!t->kicked)
+ {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ transport_unref_locked(t);
+ }
+ }
+
+ adb_mutex_unlock(&transport_lock);
+}
+
#endif
void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
diff --git a/adb/transport_local.c b/adb/transport_local.c
index 81d120e..8dfc98d 100644
--- a/adb/transport_local.c
+++ b/adb/transport_local.c
@@ -41,9 +41,9 @@
#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
+/* we keep a list of opened transports. The atransport struct knows to which
+ * local transport it is connected. The list is used to detect when we're
+ * trying to connect twice to a given local transport.
*/
#define ADB_LOCAL_TRANSPORT_MAX 16
@@ -102,7 +102,11 @@
}
-int local_connect(int port)
+int local_connect(int port) {
+ return local_connect_arbitrary_ports(port-1, port);
+}
+
+int local_connect_arbitrary_ports(int console_port, int adb_port)
{
char buf[64];
int fd = -1;
@@ -110,19 +114,19 @@
#if ADB_HOST
const char *host = getenv("ADBHOST");
if (host) {
- fd = socket_network_client(host, port, SOCK_STREAM);
+ fd = socket_network_client(host, adb_port, SOCK_STREAM);
}
#endif
if (fd < 0) {
- fd = socket_loopback_client(port, SOCK_STREAM);
+ fd = socket_loopback_client(adb_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, 1);
+ snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port);
+ register_socket_transport(fd, buf, adb_port, 1);
return 0;
}
return -1;
@@ -132,7 +136,7 @@
static void *client_socket_thread(void *x)
{
#if ADB_HOST
- int port = ADB_LOCAL_TRANSPORT_PORT;
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
int count = ADB_LOCAL_TRANSPORT_MAX;
D("transport: client_socket_thread() starting\n");
@@ -227,7 +231,50 @@
adb_close(t->fd);
}
-int init_socket_transport(atransport *t, int s, int port, int local)
+
+#if ADB_HOST
+/* Only call this function if you already hold local_transports_lock. */
+atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
+{
+ int i;
+ for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] && local_transports[i]->adb_port == adb_port) {
+ return local_transports[i];
+ }
+ }
+ return NULL;
+}
+
+atransport* find_emulator_transport_by_adb_port(int adb_port)
+{
+ adb_mutex_lock( &local_transports_lock );
+ atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
+ adb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+
+/* Only call this function if you already hold local_transports_lock. */
+int get_available_local_transport_index_locked()
+{
+ int i;
+ for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int get_available_local_transport_index()
+{
+ adb_mutex_lock( &local_transports_lock );
+ int result = get_available_local_transport_index_locked();
+ adb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+#endif
+
+int init_socket_transport(atransport *t, int s, int adb_port, int local)
{
int fail = 0;
@@ -239,26 +286,30 @@
t->sync_token = 1;
t->connection_state = CS_OFFLINE;
t->type = kTransportLocal;
+ t->adb_port = 0;
#if ADB_HOST
if (HOST && local) {
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) {
+ t->adb_port = adb_port;
+ atransport* existing_transport =
+ find_emulator_transport_by_adb_port_locked(adb_port);
+ int index = get_available_local_transport_index_locked();
+ if (existing_transport != NULL) {
D("local transport for port %d already registered (%p)?\n",
- port, local_transports[index]);
+ adb_port, existing_transport);
fail = -1;
- }
- else
+ } else if (index < 0) {
+ // Too many emulators.
+ D("cannot register more emulators. Maximum is %d\n",
+ ADB_LOCAL_TRANSPORT_MAX);
+ fail = -1;
+ } else {
local_transports[index] = t;
- }
- adb_mutex_unlock( &local_transports_lock );
+ }
+ }
+ adb_mutex_unlock( &local_transports_lock );
}
#endif
return fail;
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index 7f3cb54..7cbd741 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -67,6 +67,14 @@
#define VENDOR_ID_KYOCERA 0x0482
// Pantech's USB Vendor ID
#define VENDOR_ID_PANTECH 0x10A9
+// Qualcomm's USB Vendor ID
+#define VENDOR_ID_QUALCOMM 0x05c6
+// NEC's USB Vendor ID
+#define VENDOR_ID_NEC 0x0409
+// Panasonic Mobile Communication's USB Vendor ID
+#define VENDOR_ID_PMC 0x04DA
+// Toshiba's USB Vendor ID
+#define VENDOR_ID_TOSHIBA 0x0930
/** built-in vendor list */
@@ -87,6 +95,10 @@
VENDOR_ID_ZTE,
VENDOR_ID_KYOCERA,
VENDOR_ID_PANTECH,
+ VENDOR_ID_QUALCOMM,
+ VENDOR_ID_NEC,
+ VENDOR_ID_PMC,
+ VENDOR_ID_TOSHIBA,
};
#define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index e850a2e..7c36367 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -374,7 +374,7 @@
ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff);
for (i = 0; i < ehdr.e_phnum; i++) {
/* Parse the program header */
- get_remote_struct(pid, (char *) ptr+i, &phdr,
+ get_remote_struct(pid, (char *) (ptr+i), &phdr,
sizeof(Elf32_Phdr));
/* Found a EXIDX segment? */
if (phdr.p_type == PT_ARM_EXIDX) {
diff --git a/fastboot/engine.c b/fastboot/engine.c
index 6d62c6e..8ba202c 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.c
@@ -285,7 +285,8 @@
a->start = now();
if (start < 0) start = a->start;
if (a->msg) {
- fprintf(stderr,"%30s... ",a->msg);
+ // fprintf(stderr,"%30s... ",a->msg);
+ fprintf(stderr,"%s...\n",a->msg);
}
if (a->op == OP_DOWNLOAD) {
status = fb_download_data(usb, a->data, a->size);
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index bed30b2..f3bfbeba 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -150,6 +150,8 @@
(info->dev_vendor != 0x18d1) && // Google
(info->dev_vendor != 0x0451) &&
(info->dev_vendor != 0x0502) &&
+ (info->dev_vendor != 0x0fce) && // Sony Ericsson
+ (info->dev_vendor != 0x05c6) && // Qualcomm
(info->dev_vendor != 0x22b8) && // Motorola
(info->dev_vendor != 0x0955) && // Nvidia
(info->dev_vendor != 0x413c) && // DELL
diff --git a/fastboot/protocol.c b/fastboot/protocol.c
index c788a12..3948363 100644
--- a/fastboot/protocol.c
+++ b/fastboot/protocol.c
@@ -62,7 +62,7 @@
}
if(!memcmp(status, "INFO", 4)) {
- fprintf(stderr,"%s\n", status);
+ fprintf(stderr,"(bootloader) %s\n", status + 4);
continue;
}
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index 2ce53eb..78b7b98 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.c
@@ -137,6 +137,7 @@
ctrl.wIndex = 0;
ctrl.wLength = sizeof(buffer);
ctrl.data = buffer;
+ ctrl.timeout = 50;
result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
if (result > 0) {
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c
index 0b0512d..9488687 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.c
@@ -64,7 +64,7 @@
/** 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) {
+static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
IOReturn kr;
IOUSBFindInterfaceRequest request;
io_iterator_t iterator;
@@ -515,8 +515,29 @@
return -1;
}
+#if 0
result = (*h->interface)->WritePipe(
h->interface, h->bulkOut, (void *)data, len);
+#else
+ /* Attempt to work around crashes in the USB driver that may be caused
+ * by trying to write too much data at once. The kernel IOCopyMapper
+ * panics if a single iovmAlloc needs more than half of its mapper pages.
+ */
+ const int maxLenToSend = 1048576; // 1 MiB
+ int lenRemaining = len;
+ result = 0;
+ while (lenRemaining > 0) {
+ int lenToSend = lenRemaining > maxLenToSend
+ ? maxLenToSend : lenRemaining;
+
+ result = (*h->interface)->WritePipe(
+ h->interface, h->bulkOut, (void *)data, lenToSend);
+ if (result != 0) break;
+
+ lenRemaining -= lenToSend;
+ data = (const char*)data + lenToSend;
+ }
+#endif
#if 0
if ((result == 0) && (h->zero_mask)) {
diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h
index f51ddb1..32b7026 100644
--- a/include/arch/linux-arm/AndroidConfig.h
+++ b/include/arch/linux-arm/AndroidConfig.h
@@ -42,9 +42,16 @@
#define HAVE_PTHREADS
/*
+ * Do we have pthread_setname_np()?
+ *
+ * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with
+ * the same name but different parameters, so we can't use that here.)
+ */
+#define HAVE_ANDROID_PTHREAD_SETNAME_NP
+
+/*
* Do we have the futex syscall?
*/
-
#define HAVE_FUTEX
/*
diff --git a/include/arch/linux-sh/AndroidConfig.h b/include/arch/linux-sh/AndroidConfig.h
index 5e93990..76ae7d7 100644
--- a/include/arch/linux-sh/AndroidConfig.h
+++ b/include/arch/linux-sh/AndroidConfig.h
@@ -42,9 +42,16 @@
#define HAVE_PTHREADS
/*
+ * Do we have pthread_setname_np()?
+ *
+ * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with
+ * the same name but different parameters, so we can't use that here.)
+ */
+#define HAVE_ANDROID_PTHREAD_SETNAME_NP
+
+/*
* Do we have the futex syscall?
*/
-
#define HAVE_FUTEX
/*
diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h
index 4db3e72..b9800dd 100644
--- a/include/arch/target_linux-x86/AndroidConfig.h
+++ b/include/arch/target_linux-x86/AndroidConfig.h
@@ -28,9 +28,16 @@
#define HAVE_PTHREADS
/*
+ * Do we have pthread_setname_np()?
+ *
+ * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with
+ * the same name but different parameters, so we can't use that here.)
+ */
+#define HAVE_ANDROID_PTHREAD_SETNAME_NP
+
+/*
* Do we have the futex syscall?
*/
-
#define HAVE_FUTEX
/*
diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h
index fd56dbe..25b233e 100644
--- a/include/cutils/ashmem.h
+++ b/include/cutils/ashmem.h
@@ -10,7 +10,7 @@
#ifndef _CUTILS_ASHMEM_H
#define _CUTILS_ASHMEM_H
-#include <stdint.h>
+#include <stddef.h>
#ifdef __cplusplus
extern "C" {
diff --git a/include/cutils/atomic-arm.h b/include/cutils/atomic-arm.h
new file mode 100644
index 0000000..0dd629d
--- /dev/null
+++ b/include/cutils/atomic-arm.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_ARM_H
+#define ANDROID_CUTILS_ATOMIC_ARM_H
+
+#include <stdint.h>
+#include <machine/cpu-features.h>
+
+extern inline void android_compiler_barrier(void)
+{
+ __asm__ __volatile__ ("" : : : "memory");
+}
+
+#if ANDROID_SMP == 0
+extern inline void android_memory_barrier(void)
+{
+ android_compiler_barrier();
+}
+#elif defined(__ARM_HAVE_DMB)
+extern inline void android_memory_barrier(void)
+{
+ __asm__ __volatile__ ("dmb" : : : "memory");
+}
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline void android_memory_barrier(void)
+{
+ __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5"
+ : : "r" (0) : "memory");
+}
+#else
+extern inline void android_memory_barrier(void)
+{
+ typedef void (kuser_memory_barrier)(void);
+ (*(kuser_memory_barrier *)0xffff0fa0)();
+}
+#endif
+
+extern inline int32_t android_atomic_acquire_load(volatile int32_t *ptr)
+{
+ int32_t value = *ptr;
+ android_memory_barrier();
+ return value;
+}
+
+extern inline int32_t android_atomic_release_load(volatile int32_t *ptr)
+{
+ android_memory_barrier();
+ return *ptr;
+}
+
+extern inline void android_atomic_acquire_store(int32_t value,
+ volatile int32_t *ptr)
+{
+ *ptr = value;
+ android_memory_barrier();
+}
+
+extern inline void android_atomic_release_store(int32_t value,
+ volatile int32_t *ptr)
+{
+ android_memory_barrier();
+ *ptr = value;
+}
+
+#if defined(__thumb__)
+extern int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%3]\n"
+ "mov %1, #0\n"
+ "teq %0, %4\n"
+ "strexeq %1, %5, [%3]"
+ : "=&r" (prev), "=&r" (status), "+m"(*ptr)
+ : "r" (ptr), "Ir" (old_value), "r" (new_value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev != old_value;
+}
+#else
+extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
+{
+ typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *);
+ int32_t prev, status;
+ prev = *ptr;
+ do {
+ status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr);
+ if (__builtin_expect(status == 0, 1))
+ return 0;
+ prev = *ptr;
+ } while (prev == old_value);
+ return 1;
+}
+#endif
+
+extern inline int android_atomic_acquire_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int status = android_atomic_cas(old_value, new_value, ptr);
+ android_memory_barrier();
+ return status;
+}
+
+extern inline int android_atomic_release_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ android_memory_barrier();
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+
+#if defined(__thumb__)
+extern int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%3]\n"
+ "strex %1, %4, [%3]"
+ : "=&r" (prev), "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "r" (new_value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ android_memory_barrier();
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev;
+ __asm__ __volatile__ ("swp %0, %2, [%3]"
+ : "=&r" (prev), "+m" (*ptr)
+ : "r" (new_value), "r" (ptr)
+ : "cc");
+ android_memory_barrier();
+ return prev;
+}
+#endif
+
+#if defined(__thumb__)
+extern int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr)
+{
+ int32_t prev, tmp, status;
+ android_memory_barrier();
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%4]\n"
+ "add %1, %0, %5\n"
+ "strex %2, %1, [%4]"
+ : "=&r" (prev), "=&r" (tmp),
+ "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "Ir" (increment)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ android_memory_barrier();
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev + increment, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#endif
+
+extern inline int32_t android_atomic_inc(volatile int32_t *addr) {
+ return android_atomic_add(1, addr);
+}
+
+extern inline int32_t android_atomic_dec(volatile int32_t *addr) {
+ return android_atomic_add(-1, addr);
+}
+
+#if defined(__thumb__)
+extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, tmp, status;
+ android_memory_barrier();
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%4]\n"
+ "and %1, %0, %5\n"
+ "strex %2, %1, [%4]"
+ : "=&r" (prev), "=&r" (tmp),
+ "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "Ir" (value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ android_memory_barrier();
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev & value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#endif
+
+#if defined(__thumb__)
+extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, tmp, status;
+ android_memory_barrier();
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%4]\n"
+ "orr %1, %0, %5\n"
+ "strex %2, %1, [%4]"
+ : "=&r" (prev), "=&r" (tmp),
+ "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "Ir" (value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ android_memory_barrier();
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev | value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#endif
+
+#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */
diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h
new file mode 100644
index 0000000..715e0aa
--- /dev/null
+++ b/include/cutils/atomic-inline.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_INLINE_H
+#define ANDROID_CUTILS_ATOMIC_INLINE_H
+
+/*
+ * Inline declarations and macros for some special-purpose atomic
+ * operations. These are intended for rare circumstances where a
+ * memory barrier needs to be issued inline rather than as a function
+ * call.
+ *
+ * Most code should not use these.
+ *
+ * Anything that does include this file must set ANDROID_SMP to either
+ * 0 or 1, indicating compilation for UP or SMP, respectively.
+ *
+ * Macros defined in this header:
+ *
+ * void ANDROID_MEMBAR_FULL(void)
+ * Full memory barrier. Provides a compiler reordering barrier, and
+ * on SMP systems emits an appropriate instruction.
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP before including atomic-inline.h"
+#endif
+
+#if defined(__arm__)
+#include <cutils/atomic-arm.h>
+#elif defined(__i386__) || defined(__x86_64__)
+#include <cutils/atomic-x86.h>
+#elif defined(__sh__)
+/* implementation is in atomic-android-sh.c */
+#else
+#error atomic operations are unsupported
+#endif
+
+#if ANDROID_SMP == 0
+#define ANDROID_MEMBAR_FULL android_compiler_barrier
+#else
+#define ANDROID_MEMBAR_FULL android_memory_barrier
+#endif
+
+#endif /* ANDROID_CUTILS_ATOMIC_INLINE_H */
diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h
new file mode 100644
index 0000000..06b643f
--- /dev/null
+++ b/include/cutils/atomic-x86.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_X86_H
+#define ANDROID_CUTILS_ATOMIC_X86_H
+
+#include <stdint.h>
+
+extern inline void android_compiler_barrier(void)
+{
+ __asm__ __volatile__ ("" : : : "memory");
+}
+
+#if ANDROID_SMP == 0
+extern inline void android_memory_barrier(void)
+{
+ android_compiler_barrier();
+}
+#else
+extern inline void android_memory_barrier(void)
+{
+ __asm__ __volatile__ ("mfence" : : : "memory");
+}
+#endif
+
+extern inline int32_t android_atomic_acquire_load(volatile int32_t *ptr) {
+ int32_t value = *ptr;
+ android_compiler_barrier();
+ return value;
+}
+
+extern inline int32_t android_atomic_release_load(volatile int32_t *ptr) {
+ android_memory_barrier();
+ return *ptr;
+}
+
+extern inline void android_atomic_acquire_store(int32_t value,
+ volatile int32_t *ptr) {
+ *ptr = value;
+ android_memory_barrier();
+}
+
+extern inline void android_atomic_release_store(int32_t value,
+ volatile int32_t *ptr) {
+ android_compiler_barrier();
+ *ptr = value;
+}
+
+extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev;
+ __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
+ : "=a" (prev)
+ : "q" (new_value), "m" (*ptr), "0" (old_value)
+ : "memory");
+ return prev != old_value;
+}
+
+extern inline int android_atomic_acquire_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ /* Loads are not reordered with other loads. */
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+extern inline int android_atomic_release_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ /* Stores are not reordered with other stores. */
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+extern inline int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr)
+{
+ __asm__ __volatile__ ("xchgl %1, %0"
+ : "=r" (new_value)
+ : "m" (*ptr), "0" (new_value)
+ : "memory");
+ /* new_value now holds the old value of *ptr */
+ return new_value;
+}
+
+extern inline int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr)
+{
+ __asm__ __volatile__ ("lock; xaddl %0, %1"
+ : "+r" (increment), "+m" (*ptr)
+ : : "memory");
+ /* increment now holds the old value of *ptr */
+ return increment;
+}
+
+extern inline int32_t android_atomic_inc(volatile int32_t *addr) {
+ return android_atomic_add(1, addr);
+}
+
+extern inline int32_t android_atomic_dec(volatile int32_t *addr) {
+ return android_atomic_add(-1, addr);
+}
+
+extern inline int32_t android_atomic_and(int32_t value,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev & value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev | value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+#endif /* ANDROID_CUTILS_ATOMIC_X86_H */
diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h
index 5694d66..3866848 100644
--- a/include/cutils/atomic.h
+++ b/include/cutils/atomic.h
@@ -25,53 +25,102 @@
#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.
+ * A handful of basic atomic operations. The appropriate pthread
+ * functions should be used instead of these whenever possible.
+ *
+ * The "acquire" and "release" terms can be defined intuitively in terms
+ * of the placement of memory barriers in a simple lock implementation:
+ * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds
+ * - barrier
+ * - [do work]
+ * - barrier
+ * - store(lock-is-free)
+ * In very crude terms, the initial (acquire) barrier prevents any of the
+ * "work" from happening before the lock is held, and the later (release)
+ * barrier ensures that all of the work happens before the lock is released.
+ * (Think of cached writes, cache read-ahead, and instruction reordering
+ * around the CAS and store instructions.)
+ *
+ * The barriers must apply to both the compiler and the CPU. Note it is
+ * legal for instructions that occur before an "acquire" barrier to be
+ * moved down below it, and for instructions that occur after a "release"
+ * barrier to be moved up above it.
+ *
+ * The ARM-driven implementation we use here is short on subtlety,
+ * and actually requests a full barrier from the compiler and the CPU.
+ * The only difference between acquire and release is in whether they
+ * are issued before or after the atomic operation with which they
+ * are associated. To ease the transition to C/C++ atomic intrinsics,
+ * you should not rely on this, and instead assume that only the minimal
+ * acquire/release protection is provided.
+ *
+ * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries.
+ * If they are not, atomicity is not guaranteed.
*/
-void android_atomic_write(int32_t value, volatile int32_t* addr);
-
/*
- * all these atomic operations return the previous value
+ * Basic arithmetic and bitwise operations. These all provide a
+ * barrier with "release" ordering, and return the previous value.
+ *
+ * These have the same characteristics (e.g. what happens on overflow)
+ * as the equivalent non-atomic C operations.
*/
-
-
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);
+/*
+ * Perform an atomic load with "acquire" or "release" ordering.
+ *
+ * This is only necessary if you need the memory barrier. A 32-bit read
+ * from a 32-bit aligned address is atomic on all supported platforms.
+ */
+int32_t android_atomic_acquire_load(volatile int32_t* addr);
+int32_t android_atomic_release_load(volatile int32_t* addr);
+
+/*
+ * Perform an atomic store with "acquire" or "release" ordering.
+ *
+ * This is only necessary if you need the memory barrier. A 32-bit write
+ * to a 32-bit aligned address is atomic on all supported platforms.
+ */
+void android_atomic_acquire_store(int32_t value, volatile int32_t* addr);
+void android_atomic_release_store(int32_t value, volatile int32_t* addr);
+
+/*
+ * Unconditional swap operation with release ordering.
+ *
+ * Stores the new value at *addr, and returns the previous value.
+ */
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.
+ * Compare-and-set operation with "acquire" or "release" ordering.
+ *
+ * This returns zero if the new value was successfully stored, which will
+ * only happen when *addr == oldvalue.
+ *
+ * (The return value is inverted from implementations on other platforms,
+ * but matches the ARM ldrex/strex result.)
+ *
+ * Implementations that use the release CAS in a loop may be less efficient
+ * than possible, because we re-issue the memory barrier on each iteration.
*/
-
-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,
+int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr);
+int android_atomic_release_cas(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);
+/*
+ * Aliases for code using an older version of this header. These are now
+ * deprecated and should not be used. The definitions will be removed
+ * in a future release.
+ */
+#define android_atomic_write android_atomic_release_store
+#define android_atomic_cmpxchg android_atomic_release_cas
-
-
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/cutils/mspace.h b/include/cutils/mspace.h
index e6e4047..93fe48e 100644
--- a/include/cutils/mspace.h
+++ b/include/cutils/mspace.h
@@ -87,6 +87,11 @@
size_t max_capacity, int locked, void *base);
size_t destroy_contiguous_mspace(mspace msp);
+
+/*
+ Returns the position of the "break" within the given mspace.
+*/
+void *contiguous_mspace_sbrk0(mspace msp);
#endif
/*
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 474cfac..1dbe171 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -51,6 +51,7 @@
#define AID_SDCARD_RW 1015 /* external storage write access */
#define AID_VPN 1016 /* vpn system */
#define AID_KEYSTORE 1017 /* keystore subsystem */
+#define AID_USB 1018 /* USB devices */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
@@ -100,6 +101,7 @@
{ "sdcard_rw", AID_SDCARD_RW, },
{ "vpn", AID_VPN, },
{ "keystore", AID_KEYSTORE, },
+ { "usb", AID_USB, },
{ "inet", AID_INET, },
{ "net_raw", AID_NET_RAW, },
{ "net_admin", AID_NET_ADMIN, },
diff --git a/init/Android.mk b/init/Android.mk
index d3766d4..162c226 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -10,7 +10,12 @@
property_service.c \
util.c \
parser.c \
- logo.c
+ logo.c \
+ keychords.c \
+ signal_handler.c \
+ init_parser.c \
+ ueventd.c \
+ ueventd_parser.c
ifeq ($(strip $(INIT_BOOTCHART)),true)
LOCAL_SRC_FILES += bootchart.c
@@ -25,9 +30,20 @@
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)
+# Make a symlink from /sbin/ueventd to /init
+SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd
+$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+ @echo "Symlink: $@ -> ../$(INIT_BINARY)"
+ @mkdir -p $(dir $@)
+ @rm -rf $@
+ $(hide) ln -sf ../$(INIT_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/init/builtins.c b/init/builtins.c
index b4af700..e0ccf9f 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -35,6 +35,9 @@
#include "keywords.h"
#include "property_service.h"
#include "devices.h"
+#include "init_parser.h"
+#include "util.h"
+#include "log.h"
#include <private/android_filesystem_config.h>
@@ -218,7 +221,7 @@
int do_import(int nargs, char **args)
{
- return parse_config_file(args[1]);
+ return init_parse_config_file(args[1]);
}
int do_mkdir(int nargs, char **args)
@@ -255,6 +258,7 @@
const char *name;
unsigned flag;
} mount_flags[] = {
+ { "move", MS_MOVE },
{ "noatime", MS_NOATIME },
{ "nosuid", MS_NOSUID },
{ "nodev", MS_NODEV },
@@ -274,6 +278,7 @@
char *options = NULL;
unsigned flags = 0;
int n, i;
+ int wait = 0;
for (n = 4; n < nargs; n++) {
for (i = 0; mount_flags[i].name; i++) {
@@ -283,9 +288,13 @@
}
}
- /* 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];
+ if (!mount_flags[i].name) {
+ if (!strcmp(args[n], "wait"))
+ wait = 1;
+ /* if our last argument isn't a flag, wolf it up as an option string */
+ else if (n + 1 == nargs)
+ options = args[n];
+ }
}
system = args[1];
@@ -300,6 +309,8 @@
sprintf(tmp, "/dev/block/mtdblock%d", n);
+ if (wait)
+ wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
if (mount(tmp, target, system, flags, options) < 0) {
return -1;
}
@@ -346,6 +357,8 @@
ERROR("out of loopback devices");
return -1;
} else {
+ if (wait)
+ wait_for_file(source, COMMAND_RETRY_TIMEOUT);
if (mount(source, target, system, flags, options) < 0) {
return -1;
}
@@ -413,7 +426,6 @@
int do_trigger(int nargs, char **args)
{
action_for_each_trigger(args[1], action_add_queue_tail);
- drain_action_queue();
return 0;
}
@@ -546,29 +558,10 @@
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;
+int do_wait(int nargs, char **args)
+{
+ if (nargs == 2) {
+ return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
}
- /* 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;
+ return -1;
}
diff --git a/init/devices.c b/init/devices.c
index 530d6d7..8e912de 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -31,20 +31,25 @@
#include <private/android_filesystem_config.h>
#include <sys/time.h>
#include <asm/page.h>
+#include <sys/wait.h>
-#include "init.h"
#include "devices.h"
+#include "util.h"
+#include "log.h"
+#include "list.h"
-#define CMDLINE_PREFIX "/dev"
#define SYSFS_PREFIX "/sys"
#define FIRMWARE_DIR "/etc/firmware"
-#define MAX_QEMU_PERM 6
+
+static int device_fd = -1;
struct uevent {
const char *action;
const char *path;
const char *subsystem;
const char *firmware;
+ const char *partition_name;
+ int partition_num;
int major;
int minor;
};
@@ -83,102 +88,21 @@
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 },
-
- /* the msm hw3d client device node is world writable/readable. */
- { "/dev/msm_hw3dc", 0666, AID_ROOT, AID_ROOT, 0 },
-
- /* gpu driver for adreno200 is globally accessible */
- { "/dev/kgsl", 0666, AID_ROOT, AID_ROOT, 0 },
-
- /* these should not be world writable */
- { "/dev/diag", 0660, AID_RADIO, AID_RADIO, 0 },
- { "/dev/diag_arm9", 0660, AID_RADIO, AID_RADIO, 0 },
- { "/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", 0660, AID_SYSTEM, 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/msm_hw3dm", 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_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/snd/", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 },
- { "/dev/msm_camera/", 0660, AID_SYSTEM, AID_SYSTEM, 1 },
- { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8973_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8973_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/bma150", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/cm3602", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/lightsensor", 0640, AID_SYSTEM, 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/audience_a1026", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/tpa2018d1", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/msm_audio_ctl", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/vdec", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/q6venc", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/snd/dsp", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/snd/dsp1", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/snd/mixer", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 },
- { "/dev/qemu_trace", 0666, AID_SYSTEM, AID_SYSTEM, 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 },
- /* CDMA radio interface MUX */
- { "/dev/ts0710mux", 0640, AID_RADIO, AID_RADIO, 1 },
- { "/dev/ppp", 0660, AID_RADIO, AID_VPN, 0 },
- { "/dev/tun", 0640, AID_VPN, AID_VPN, 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);
+static list_declare(dev_perms);
/*
* 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 add_dev_perms(const char *name, mode_t perm, unsigned int uid,
+ unsigned int gid, unsigned short prefix) {
int size;
+ char *tmp = 0;
struct perm_node *node = malloc(sizeof (struct perm_node));
if (!node)
return -ENOMEM;
@@ -193,51 +117,10 @@
node->dp.gid = gid;
node->dp.prefix = prefix;
- list_add_tail(&devperms_partners, &node->plist);
+ list_add_tail(&dev_perms, &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)
{
@@ -263,38 +146,32 @@
static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
{
mode_t perm;
+ struct listnode *node;
+ struct perm_node *perm_node;
+ struct perms_ *dp;
- 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;
+ /* search the perms list in reverse so that ueventd.$hardware can
+ * override ueventd.rc
+ */
+ list_for_each_reverse(node, &dev_perms) {
+ perm_node = node_to_item(node, struct perm_node, plist);
+ dp = &perm_node->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;
+ if (dp->prefix) {
+ if (strncmp(path, dp->name, strlen(dp->name)))
+ continue;
+ } else {
+ if (strcmp(path, dp->name))
+ continue;
}
- /* Default if nothing found. */
- *uid = 0;
- *gid = 0;
- return 0600;
+ *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)
@@ -346,6 +223,8 @@
uevent->firmware = "";
uevent->major = -1;
uevent->minor = -1;
+ uevent->partition_name = NULL;
+ uevent->partition_num = -1;
/* currently ignoring SEQNUM */
while(*msg) {
@@ -367,6 +246,12 @@
} else if(!strncmp(msg, "MINOR=", 6)) {
msg += 6;
uevent->minor = atoi(msg);
+ } else if(!strncmp(msg, "PARTN=", 6)) {
+ msg += 6;
+ uevent->partition_num = atoi(msg);
+ } else if(!strncmp(msg, "PARTNAME=", 9)) {
+ msg += 9;
+ uevent->partition_name = msg;
}
/* advance to after the next \0 */
@@ -379,11 +264,77 @@
uevent->firmware, uevent->major, uevent->minor);
}
+static char **parse_platform_block_device(struct uevent *uevent)
+{
+ const char *driver;
+ const char *path;
+ char *slash;
+ int width;
+ char buf[256];
+ char link_path[256];
+ int fd;
+ int link_num = 0;
+ int ret;
+ char *p;
+ unsigned int size;
+ struct stat info;
+
+ char **links = malloc(sizeof(char *) * 4);
+ if (!links)
+ return NULL;
+ memset(links, 0, sizeof(char *) * 4);
+
+ /* Drop "/devices/platform/" */
+ path = uevent->path;
+ driver = path + 18;
+ slash = strchr(driver, '/');
+ if (!slash)
+ goto err;
+ width = slash - driver;
+ if (width <= 0)
+ goto err;
+
+ snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s",
+ width, driver);
+
+ if (uevent->partition_name) {
+ p = strdup(uevent->partition_name);
+ sanitize(p);
+ if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ free(p);
+ }
+
+ if (uevent->partition_num >= 0) {
+ if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ }
+
+ slash = strrchr(path, '/');
+ if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+
+ return links;
+
+err:
+ free(links);
+ return NULL;
+}
+
static void handle_device_event(struct uevent *uevent)
{
char devpath[96];
+ int devpath_ready = 0;
char *base, *name;
+ char **links = NULL;
int block;
+ int i;
/* if it's not a /dev device, nothing to do */
if((uevent->major < 0) || (uevent->minor < 0))
@@ -404,10 +355,31 @@
block = 1;
base = "/dev/block/";
mkdir(base, 0755);
+ if (!strncmp(uevent->path, "/devices/platform/", 18))
+ links = parse_platform_block_device(uevent);
} else {
block = 0;
/* this should probably be configurable somehow */
- if(!strncmp(uevent->subsystem, "graphics", 8)) {
+ if (!strncmp(uevent->subsystem, "usb", 3)) {
+ if (!strcmp(uevent->subsystem, "usb")) {
+ /* This imitates the file system that would be created
+ * if we were using devfs instead.
+ * Minors are broken up into groups of 128, starting at "001"
+ */
+ int bus_id = uevent->minor / 128 + 1;
+ int device_id = uevent->minor % 128 + 1;
+ /* build directories */
+ mkdir("/dev/bus", 0755);
+ mkdir("/dev/bus/usb", 0755);
+ snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
+ mkdir(devpath, 0755);
+ snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
+ devpath_ready = 1;
+ } else {
+ /* ignore other USB events */
+ return;
+ }
+ } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
base = "/dev/graphics/";
mkdir(base, 0755);
} else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
@@ -437,16 +409,29 @@
base = "/dev/";
}
- snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+ if (!devpath_ready)
+ snprintf(devpath, sizeof(devpath), "%s%s", base, name);
if(!strcmp(uevent->action, "add")) {
make_device(devpath, block, uevent->major, uevent->minor);
- return;
+ if (links) {
+ for (i = 0; links[i]; i++)
+ make_link(devpath, links[i]);
+ }
}
if(!strcmp(uevent->action, "remove")) {
+ if (links) {
+ for (i = 0; links[i]; i++)
+ remove_link(devpath, links[i]);
+ }
unlink(devpath);
- return;
+ }
+
+ if (links) {
+ for (i = 0; links[i]; i++)
+ free(links[i]);
+ free(links);
}
}
@@ -555,6 +540,8 @@
static void handle_firmware_event(struct uevent *uevent)
{
pid_t pid;
+ int status;
+ int ret;
if(strcmp(uevent->subsystem, "firmware"))
return;
@@ -567,11 +554,15 @@
if (!pid) {
process_firmware_event(uevent);
exit(EXIT_SUCCESS);
+ } else {
+ do {
+ ret = waitpid(pid, &status, 0);
+ } while (ret == -1 && errno == EINTR);
}
}
#define UEVENT_MSG_LEN 1024
-void handle_device_fd(int fd)
+void handle_device_fd()
{
for(;;) {
char msg[UEVENT_MSG_LEN+2];
@@ -580,7 +571,7 @@
struct sockaddr_nl snl;
struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};
- ssize_t n = recvmsg(fd, &hdr, 0);
+ ssize_t n = recvmsg(device_fd, &hdr, 0);
if (n <= 0) {
break;
}
@@ -625,7 +616,7 @@
** socket's buffer.
*/
-static void do_coldboot(int event_fd, DIR *d)
+static void do_coldboot(DIR *d)
{
struct dirent *de;
int dfd, fd;
@@ -636,7 +627,7 @@
if(fd >= 0) {
write(fd, "add\n", 4);
close(fd);
- handle_device_fd(event_fd);
+ handle_device_fd();
}
while((de = readdir(d))) {
@@ -653,40 +644,49 @@
if(d2 == 0)
close(fd);
else {
- do_coldboot(event_fd, d2);
+ do_coldboot(d2);
closedir(d2);
}
}
}
-static void coldboot(int event_fd, const char *path)
+static void coldboot(const char *path)
{
DIR *d = opendir(path);
if(d) {
- do_coldboot(event_fd, d);
+ do_coldboot(d);
closedir(d);
}
}
-int device_init(void)
+void device_init(void)
{
suseconds_t t0, t1;
+ struct stat info;
int fd;
- fd = open_uevent_socket();
- if(fd < 0)
- return -1;
+ device_fd = open_uevent_socket();
+ if(device_fd < 0)
+ return;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
+ fcntl(device_fd, F_SETFD, FD_CLOEXEC);
+ fcntl(device_fd, F_SETFL, O_NONBLOCK);
- t0 = get_usecs();
- coldboot(fd, "/sys/class");
- coldboot(fd, "/sys/block");
- coldboot(fd, "/sys/devices");
- t1 = get_usecs();
+ if (stat(coldboot_done, &info) < 0) {
+ t0 = get_usecs();
+ coldboot("/sys/class");
+ coldboot("/sys/block");
+ coldboot("/sys/devices");
+ t1 = get_usecs();
+ fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
+ close(fd);
+ log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+ } else {
+ log_event_print("skipping coldboot, already done\n");
+ }
+}
- log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
-
- return fd;
+int get_device_fd()
+{
+ return device_fd;
}
diff --git a/init/devices.h b/init/devices.h
index b484da4..8593a1b 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,11 +17,11 @@
#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);
+#include <sys/stat.h>
+extern void handle_device_fd();
+extern void device_init(void);
+extern int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
+ unsigned int gid, unsigned short prefix);
+int get_device_fd();
#endif /* _INIT_DEVICES_H */
diff --git a/init/init.c b/init/init.c
index 4d98cc2..8f95da7 100755
--- a/init/init.c
+++ b/init/init.c
@@ -25,27 +25,32 @@
#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 <libgen.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
+#include <private/android_filesystem_config.h>
#include <termios.h>
-#include <linux/kd.h>
-#include <linux/keychord.h>
#include <sys/system_properties.h>
#include "devices.h"
#include "init.h"
+#include "list.h"
+#include "log.h"
#include "property_service.h"
#include "bootchart.h"
+#include "signal_handler.h"
+#include "keychords.h"
+#include "init_parser.h"
+#include "util.h"
+#include "ueventd.h"
static int property_triggers_enabled = 0;
@@ -62,11 +67,12 @@
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 notify_service_state(const char *name, const char *state)
+static struct action *cur_action = NULL;
+static struct command *cur_command = NULL;
+static struct listnode *command_queue = NULL;
+
+void notify_service_state(const char *name, const char *state)
{
char pname[PROP_NAME_MAX];
int len = strlen(name);
@@ -122,24 +128,6 @@
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;
@@ -208,9 +196,11 @@
char tmp[32];
int fd, sz;
- get_property_workspace(&fd, &sz);
- sprintf(tmp, "%d,%d", dup(fd), sz);
- add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
+ if (properties_inited()) {
+ 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);
@@ -266,7 +256,7 @@
ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
}
} else {
- char *arg_ptrs[SVC_MAXARGS+1];
+ char *arg_ptrs[INIT_PARSER_MAXARGS+1];
int arg_idx = svc->nargs;
char *tmp = strdup(dynamic_args);
char *next = tmp;
@@ -277,7 +267,7 @@
while((bword = strsep(&next, " "))) {
arg_ptrs[arg_idx++] = bword;
- if (arg_idx == SVC_MAXARGS)
+ if (arg_idx == INIT_PARSER_MAXARGS)
break;
}
arg_ptrs[arg_idx] = '\0';
@@ -296,7 +286,8 @@
svc->pid = pid;
svc->flags |= SVC_RUNNING;
- notify_service_state(svc->name, "running");
+ if (properties_inited())
+ notify_service_state(svc->name, "running");
}
void service_stop(struct service *svc)
@@ -322,90 +313,8 @@
void property_changed(const char *name, const char *value)
{
- if (property_triggers_enabled) {
+ 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;
- }
- }
-
- svc->flags |= SVC_RESTARTING;
-
- /* 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);
- }
- notify_service_state(svc->name, "restarting");
- return 0;
}
static void restart_service_if_needed(struct service *svc)
@@ -431,13 +340,6 @@
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;
@@ -486,78 +388,6 @@
}
}
-#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, '=');
@@ -585,8 +415,6 @@
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
@@ -631,198 +459,208 @@
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);
- }
- }
-}
-
-void drain_action_queue(void)
+static struct command *get_first_command(struct action *act)
{
struct listnode *node;
- struct command *cmd;
- struct action *act;
- int ret;
+ node = list_head(&act->commands);
+ if (!node)
+ return NULL;
- 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);
- }
- }
+ return node_to_item(node, struct command, clist);
}
-void open_devnull_stdio(void)
+static struct command *get_next_command(struct action *act, struct command *cmd)
+{
+ struct listnode *node;
+ node = cmd->clist.next;
+ if (!node)
+ return NULL;
+ if (node == &act->commands)
+ return NULL;
+
+ return node_to_item(node, struct command, clist);
+}
+
+static int is_last_command(struct action *act, struct command *cmd)
+{
+ return (list_tail(&act->commands) == &cmd->clist);
+}
+
+void execute_one_command(void)
+{
+ int ret;
+
+ if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
+ cur_action = action_remove_queue_head();
+ cur_command = NULL;
+ if (!cur_action)
+ return;
+ INFO("processing action %p (%s)\n", cur_action, cur_action->name);
+ cur_command = get_first_command(cur_action);
+ } else {
+ cur_command = get_next_command(cur_action, cur_command);
+ }
+
+ if (!cur_command)
+ return;
+
+ ret = cur_command->func(cur_command->nargs, cur_command->args);
+ INFO("command '%s' r=%d\n", cur_command->args[0], ret);
+}
+
+static int wait_for_coldboot_done_action(int nargs, char **args)
+{
+ int ret;
+ INFO("wait for %s\n", coldboot_done);
+ ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
+ if (ret)
+ ERROR("Timed out waiting for %s\n", coldboot_done);
+ return ret;
+}
+
+static int property_init_action(int nargs, char **args)
+{
+ INFO("property init\n");
+ property_init();
+ return 0;
+}
+
+static int keychord_init_action(int nargs, char **args)
+{
+ keychord_init();
+ return 0;
+}
+
+static int console_init_action(int nargs, char **args)
{
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);
+ char tmp[PROP_VALUE_MAX];
+
+ 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) {
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
+ 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);
}
}
-
- exit(1);
+ return 0;
}
-void add_service_keycodes(struct service *svc)
+static int set_init_properties_action(int nargs, char **args)
{
- struct input_keychord *keychord;
- int i, size;
+ char tmp[PROP_VALUE_MAX];
- 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;
- }
+ if (qemu[0])
+ import_kernel_cmdline(1);
- 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;
+ 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");
- for (i = 0; i < svc->nkeycodes; i++) {
- keychord->keycodes[i] = svc->keycodes[i];
- }
- keychords_count++;
- keychords_length += size;
- }
+ 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);
+ return 0;
}
-int open_keychord()
+static int property_service_init_action(int nargs, char **args)
{
- 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;
+ /* 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.
+ */
+ start_property_service();
+ return 0;
}
-void handle_keychord(int fd)
+static int signal_init_action(int nargs, char **args)
{
- struct service *svc;
- char* debuggable;
- char* adb_enabled;
- int ret;
- __u16 id;
+ signal_init();
+ return 0;
+}
- // only handle keychords if ro.debuggable is set or adb is enabled.
- // the logic here is that bugreports should be enabled in userdebug or eng builds
- // and on user builds for users that are developers.
- debuggable = property_get("ro.debuggable");
- adb_enabled = property_get("init.svc.adbd");
- if ((debuggable && !strcmp(debuggable, "1")) ||
- (adb_enabled && !strcmp(adb_enabled, "running"))) {
- ret = read(fd, &id, sizeof(id));
- if (ret != sizeof(id)) {
- ERROR("could not read keychord id\n");
- return;
- }
+static int check_startup_action(int nargs, char **args)
+{
+ /* make sure we actually have all the pieces we need */
+ if ((get_property_set_fd() < 0) ||
+ (get_signal_fd() < 0)) {
+ ERROR("init startup failure\n");
+ exit(1);
+ }
+ return 0;
+}
- svc = service_find_by_keychord(id);
- if (svc) {
- INFO("starting service %s from keychord\n", svc->name);
- service_start(svc, NULL);
- } else {
- ERROR("service for keychord %d not found\n", id);
- }
+static int queue_property_triggers_action(int nargs, char **args)
+{
+ queue_all_property_triggers();
+ /* enable property triggers */
+ property_triggers_enabled = 1;
+ return 0;
+}
+
+#if BOOTCHART
+static int bootchart_init_action(int nargs, char **args)
+{
+ 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
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];
+ int fd_count = 0;
struct pollfd ufds[4];
char *tmpdev;
char* debuggable;
+ char tmp[32];
+ int property_set_fd_init = 0;
+ int signal_fd_init = 0;
+ int keychord_fd_init = 0;
- act.sa_handler = sigchld_handler;
- act.sa_flags = SA_NOCLDSTOP;
- act.sa_mask = 0;
- act.sa_restorer = NULL;
- sigaction(SIGCHLD, &act, 0);
+ if (!strcmp(basename(argv[0]), "ueventd"))
+ return ueventd_main(argc, argv);
/* clear the umask */
umask(0);
@@ -852,165 +690,82 @@
log_init();
INFO("reading config file\n");
- parse_config_file("/init.rc");
+ init_parse_config_file("/init.rc");
/* pull the kernel commandline and ramdisk properties file in */
- qemu_init();
import_kernel_cmdline(0);
- get_hardware_name();
+ get_hardware_name(hardware, &revision);
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
- parse_config_file(tmp);
+ init_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
- 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);
+ queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ queue_builtin_action(property_init_action, "property_init");
+ queue_builtin_action(keychord_init_action, "keychord_init");
+ queue_builtin_action(console_init_action, "console_init");
+ queue_builtin_action(set_init_properties_action, "set_init_properties");
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
- drain_action_queue();
+ action_for_each_trigger("early-fs", action_add_queue_tail);
+ action_for_each_trigger("fs", action_add_queue_tail);
+ action_for_each_trigger("post-fs", action_add_queue_tail);
- /* 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;
- }
+ queue_builtin_action(property_service_init_action, "property_service_init");
+ queue_builtin_action(signal_init_action, "signal_init");
+ queue_builtin_action(check_startup_action, "check_startup");
/* 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();
+ queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers");
- /* 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");
- }
+ queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
for(;;) {
int nr, i, timeout = -1;
- for (i = 0; i < fd_count; i++)
- ufds[i].revents = 0;
-
- drain_action_queue();
+ execute_one_command();
restart_processes();
+ if (!property_set_fd_init && get_property_set_fd() > 0) {
+ ufds[fd_count].fd = get_property_set_fd();
+ ufds[fd_count].events = POLLIN;
+ ufds[fd_count].revents = 0;
+ fd_count++;
+ property_set_fd_init = 1;
+ }
+ if (!signal_fd_init && get_signal_fd() > 0) {
+ ufds[fd_count].fd = get_signal_fd();
+ ufds[fd_count].events = POLLIN;
+ ufds[fd_count].revents = 0;
+ fd_count++;
+ signal_fd_init = 1;
+ }
+ if (!keychord_fd_init && get_keychord_fd() > 0) {
+ ufds[fd_count].fd = get_keychord_fd();
+ ufds[fd_count].events = POLLIN;
+ ufds[fd_count].revents = 0;
+ fd_count++;
+ keychord_fd_init = 1;
+ }
+
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
+ if (!action_queue_empty() || cur_action)
+ timeout = 0;
+
#if BOOTCHART
if (bootchart_count > 0) {
if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
@@ -1021,25 +776,21 @@
}
}
#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;
+ for (i = 0; i < fd_count; i++) {
+ if (ufds[i].revents == POLLIN) {
+ if (ufds[i].fd == get_property_set_fd())
+ handle_property_set_fd();
+ else if (ufds[i].fd == get_keychord_fd())
+ handle_keychord();
+ else if (ufds[i].fd == get_signal_fd())
+ handle_signal();
+ }
}
-
- 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
index f92a4d7..8691335 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,56 +17,12 @@
#ifndef _INIT_INIT_H
#define _INIT_INIT_H
-int mtd_name_to_number(const char *name);
+#include "list.h"
+
+#include <sys/stat.h>
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, ...)
- __attribute__ ((format(printf, 2, 3)));
-
-#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 */
@@ -116,7 +72,7 @@
#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */
-#define SVC_MAXARGS 64
+#define COMMAND_RETRY_TIMEOUT 5
struct service {
/* list of all services */
@@ -154,7 +110,7 @@
char *args[1];
}; /* ^-------'args' MUST be at the end of this struct! */
-int parse_config_file(const char *fn);
+void notify_service_state(const char *name, const char *state);
struct service *service_find_by_name(const char *name);
struct service *service_find_by_pid(pid_t pid);
@@ -168,14 +124,6 @@
void service_start(struct service *svc, const char *dynamic_args);
void property_changed(const char *name, const char *value);
-void drain_action_queue(void);
-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 );
diff --git a/init/init_parser.c b/init/init_parser.c
new file mode 100644
index 0000000..585a5b5
--- /dev/null
+++ b/init/init_parser.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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 "parser.h"
+#include "init_parser.h"
+#include "log.h"
+#include "list.h"
+#include "property_service.h"
+#include "util.h"
+
+#include <cutils/iosched_policy.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);
+
+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);
+
+#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, "opy")) return K_copy;
+ if (!strcmp(s, "apability")) return K_capability;
+ if (!strcmp(s, "hdir")) return K_chdir;
+ if (!strcmp(s, "hroot")) return K_chroot;
+ 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;
+ 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, "oprio")) return K_ioprio;
+ 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;
+ if (!strcmp(s, "ait")) return K_wait;
+ break;
+ }
+ return K_UNKNOWN;
+}
+
+void parse_line_no_op(struct parse_state *state, int nargs, char **args)
+{
+}
+
+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[INIT_PARSER_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 < INIT_PARSER_MAXARGS) {
+ args[nargs++] = state.text;
+ }
+ break;
+ }
+ }
+}
+
+int init_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(prop_name);
+ if (value && !strcmp(equals + 1, value)) {
+ action_add_queue_tail(act);
+ }
+ }
+ }
+ }
+ }
+}
+
+void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
+{
+ struct action *act;
+ struct command *cmd;
+
+ act = calloc(1, sizeof(*act));
+ act->name = name;
+ list_init(&act->commands);
+
+ cmd = calloc(1, sizeof(*cmd));
+ cmd->func = func;
+ cmd->args[0] = name;
+ list_add_tail(&act->commands, &cmd->clist);
+
+ list_add_tail(&action_list, &act->alist);
+ 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;
+ }
+}
+
+int action_queue_empty()
+{
+ return list_empty(&action_queue);
+}
+
+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;
+ }
+
+ svc->ioprio_class = IoSchedClass_NONE;
+
+ 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_ioprio:
+ if (nargs != 3) {
+ parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
+ } else {
+ svc->ioprio_pri = strtoul(args[2], 0, 8);
+
+ if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
+ parse_error(state, "priority value must be range 0 - 7\n");
+ break;
+ }
+
+ if (!strcmp(args[1], "rt")) {
+ svc->ioprio_class = IoSchedClass_RT;
+ } else if (!strcmp(args[1], "be")) {
+ svc->ioprio_class = IoSchedClass_BE;
+ } else if (!strcmp(args[1], "idle")) {
+ svc->ioprio_class = IoSchedClass_IDLE;
+ } else {
+ parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
+ }
+ }
+ 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/init_parser.h b/init/init_parser.h
new file mode 100644
index 0000000..ff13b04
--- /dev/null
+++ b/init/init_parser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_INIT_PARSER_H_
+#define _INIT_INIT_PARSER_H_
+
+#define INIT_PARSER_MAXARGS 64
+
+struct action;
+
+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));
+int action_queue_empty(void);
+void queue_property_triggers(const char *name, const char *value);
+void queue_all_property_triggers();
+void queue_builtin_action(int (*func)(int nargs, char **args), char *name);
+
+int init_parse_config_file(const char *fn);
+
+#endif
diff --git a/init/keychords.c b/init/keychords.c
new file mode 100644
index 0000000..53ab391
--- /dev/null
+++ b/init/keychords.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/keychord.h>
+
+#include "init.h"
+#include "log.h"
+#include "property_service.h"
+
+static struct input_keychord *keychords = 0;
+static int keychords_count = 0;
+static int keychords_length = 0;
+static int keychord_fd = -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;
+ }
+}
+
+void keychord_init()
+{
+ int fd, ret;
+
+ service_for_each(add_service_keycodes);
+
+ /* nothing to do if no services require keychords */
+ if (!keychords)
+ return;
+
+ fd = open("/dev/keychord", O_RDWR);
+ if (fd < 0) {
+ ERROR("could not open /dev/keychord\n");
+ return;
+ }
+ 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;
+
+ keychord_fd = fd;
+}
+
+void handle_keychord()
+{
+ struct service *svc;
+ const char* debuggable;
+ const char* adb_enabled;
+ int ret;
+ __u16 id;
+
+ // only handle keychords if ro.debuggable is set or adb is enabled.
+ // the logic here is that bugreports should be enabled in userdebug or eng builds
+ // and on user builds for users that are developers.
+ debuggable = property_get("ro.debuggable");
+ adb_enabled = property_get("init.svc.adbd");
+ if ((debuggable && !strcmp(debuggable, "1")) ||
+ (adb_enabled && !strcmp(adb_enabled, "running"))) {
+ ret = read(keychord_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, NULL);
+ } else {
+ ERROR("service for keychord %d not found\n", id);
+ }
+ }
+}
+
+int get_keychord_fd()
+{
+ return keychord_fd;
+}
diff --git a/init/keychords.h b/init/keychords.h
new file mode 100644
index 0000000..070b858
--- /dev/null
+++ b/init/keychords.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_KEYCHORDS_H_
+#define _INIT_KEYCHORDS_H_
+
+struct service;
+
+void add_service_keycodes(struct service *svc);
+void keychord_init(void);
+void handle_keychord(void);
+int get_keychord_fd(void);
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
index 254c785..25315d8 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -27,7 +27,7 @@
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);
+int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
@@ -69,12 +69,12 @@
KEYWORD(symlink, COMMAND, 1, do_symlink)
KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
KEYWORD(user, OPTION, 0, 0)
+ KEYWORD(wait, COMMAND, 1, do_wait)
KEYWORD(write, COMMAND, 2, do_write)
KEYWORD(copy, COMMAND, 2, do_copy)
KEYWORD(chown, COMMAND, 2, do_chown)
KEYWORD(chmod, COMMAND, 2, do_chmod)
KEYWORD(loglevel, COMMAND, 1, do_loglevel)
- KEYWORD(device, COMMAND, 4, do_device)
KEYWORD(ioprio, OPTION, 0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
KEYWORD_COUNT,
diff --git a/init/list.h b/init/list.h
new file mode 100644
index 0000000..0a7b28c
--- /dev/null
+++ b/init/list.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_LIST_H_
+#define _INIT_LIST_H_
+
+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)
+
+#define list_for_each_reverse(node, list) \
+ for (node = (list)->prev; node != (list); node = node->prev)
+
+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)
+
+#endif
diff --git a/init/log.h b/init/log.h
new file mode 100644
index 0000000..3d93965
--- /dev/null
+++ b/init/log.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_LOG_H_
+#define _INIT_LOG_H_
+
+void log_init(void);
+void log_set_level(int level);
+void log_close(void);
+void log_write(int level, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+#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 */
+
+#endif
diff --git a/init/logo.c b/init/logo.c
index 6a740bf..614224c 100644
--- a/init/logo.c
+++ b/init/logo.c
@@ -25,7 +25,7 @@
#include <linux/fb.h>
#include <linux/kd.h>
-#include "init.h"
+#include "log.h"
#ifdef ANDROID
#include <cutils/memory.h>
diff --git a/init/parser.c b/init/parser.c
index 7da0d19..2f36ac7 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -1,23 +1,10 @@
#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"
-
-#include <cutils/iosched_policy.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);
+#include "parser.h"
+#include "list.h"
+#include "log.h"
#define RAW(x...) log_write(6, x)
@@ -62,27 +49,6 @@
#endif
}
-#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;
@@ -100,115 +66,6 @@
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, "opy")) return K_copy;
- if (!strcmp(s, "apability")) return K_capability;
- if (!strcmp(s, "hdir")) return K_chdir;
- if (!strcmp(s, "hroot")) return K_chroot;
- 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, "oprio")) return K_ioprio;
- 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;
@@ -322,504 +179,3 @@
}
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[SVC_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 < SVC_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(prop_name);
- 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;
- }
-
- svc->ioprio_class = IoSchedClass_NONE;
-
- 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_ioprio:
- if (nargs != 3) {
- parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
- } else {
- svc->ioprio_pri = strtoul(args[2], 0, 8);
-
- if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
- parse_error(state, "priority value must be range 0 - 7\n");
- break;
- }
-
- if (!strcmp(args[1], "rt")) {
- svc->ioprio_class = IoSchedClass_RT;
- } else if (!strcmp(args[1], "be")) {
- svc->ioprio_class = IoSchedClass_BE;
- } else if (!strcmp(args[1], "idle")) {
- svc->ioprio_class = IoSchedClass_IDLE;
- } else {
- parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
- }
- }
- 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/parser.h b/init/parser.h
new file mode 100644
index 0000000..be93758
--- /dev/null
+++ b/init/parser.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PARSER_H_
+#define PARSER_H_
+
+#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;
+};
+
+int lookup_keyword(const char *s);
+void DUMP(void);
+int next_token(struct parse_state *state);
+void parse_error(struct parse_state *state, const char *fmt, ...);
+
+#endif /* PARSER_H_ */
diff --git a/init/property_service.c b/init/property_service.c
index d2505dd..d8fea56 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -27,7 +27,6 @@
#include <cutils/misc.h>
#include <cutils/sockets.h>
-#include <cutils/ashmem.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
@@ -43,10 +42,15 @@
#include "property_service.h"
#include "init.h"
+#include "util.h"
+#include "log.h"
#define PERSISTENT_PROPERTY_DIR "/data/property"
static int persistent_properties_loaded = 0;
+static int property_area_inited = 0;
+
+static int property_set_fd = -1;
/* White list of permissions for setting property services. */
struct {
@@ -105,21 +109,31 @@
void *data;
int fd;
- fd = ashmem_create_region("system_properties", size);
- if(fd < 0)
+ /* dev is a tmpfs that we can use to carve a shared workspace
+ * out of, so let's do that...
+ */
+ fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600);
+ if (fd < 0)
return -1;
+ if (ftruncate(fd, size) < 0)
+ goto out;
+
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);
+ close(fd);
+
+ fd = open("/dev/__properties__", O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ unlink("/dev/__properties__");
w->data = data;
w->size = size;
w->fd = fd;
-
return 0;
out:
@@ -160,7 +174,7 @@
/* plug into the lib property services */
__system_property_area__ = pa;
-
+ property_area_inited = 1;
return 0;
}
@@ -187,7 +201,7 @@
*
* Returns 1 if uid allowed, 0 otherwise.
*/
-static int check_control_perms(const char *name, int uid, int gid) {
+static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) {
int i;
if (uid == AID_SYSTEM || uid == AID_ROOT)
return 1;
@@ -208,7 +222,7 @@
* Checks permissions for setting system properties.
* Returns 1 if uid allowed, 0 otherwise.
*/
-static int check_perms(const char *name, unsigned int uid, int gid)
+static int check_perms(const char *name, unsigned int uid, unsigned int gid)
{
int i;
if (uid == 0)
@@ -344,7 +358,7 @@
return 0;
}
-void handle_property_set_fd(int fd)
+void handle_property_set_fd()
{
prop_msg msg;
int s;
@@ -355,7 +369,7 @@
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
- if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
+ if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
return;
}
@@ -493,7 +507,12 @@
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
-int start_property_service(void)
+int properties_inited(void)
+{
+ return property_area_inited;
+}
+
+void start_property_service(void)
{
int fd;
@@ -504,10 +523,15 @@
load_persistent_properties();
fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
- if(fd < 0) return -1;
+ if(fd < 0) return;
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl(fd, F_SETFL, O_NONBLOCK);
listen(fd, 8);
- return fd;
+ property_set_fd = fd;
+}
+
+int get_property_set_fd()
+{
+ return property_set_fd;
}
diff --git a/init/property_service.h b/init/property_service.h
index d12f1f3..045d20a 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,12 +17,13 @@
#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 handle_property_set_fd(void);
extern void property_init(void);
-extern int start_property_service(void);
+extern void start_property_service(void);
void get_property_workspace(int *fd, int *sz);
extern const char* property_get(const char *name);
extern int property_set(const char *name, const char *value);
+extern int properties_inited();
+int get_property_set_fd(void);
#endif /* _INIT_PROPERTY_H */
diff --git a/init/signal_handler.c b/init/signal_handler.c
new file mode 100644
index 0000000..3e5d136
--- /dev/null
+++ b/init/signal_handler.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <cutils/sockets.h>
+#include <sys/reboot.h>
+
+#include "init.h"
+#include "list.h"
+#include "util.h"
+#include "log.h"
+
+static int signal_fd = -1;
+static int signal_recv_fd = -1;
+
+static void sigchld_handler(int s)
+{
+ write(signal_fd, &s, 1);
+}
+
+#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;
+ }
+ }
+
+ svc->flags |= SVC_RESTARTING;
+
+ /* 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);
+ }
+ notify_service_state(svc->name, "restarting");
+ return 0;
+}
+
+void handle_signal(void)
+{
+ char tmp[32];
+
+ /* we got a SIGCHLD - reap and restart as needed */
+ read(signal_recv_fd, tmp, sizeof(tmp));
+ while (!wait_for_one_process(0))
+ ;
+}
+
+void signal_init(void)
+{
+ int s[2];
+
+ struct sigaction act;
+
+ act.sa_handler = sigchld_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ act.sa_mask = 0;
+ act.sa_restorer = NULL;
+ sigaction(SIGCHLD, &act, 0);
+
+ /* create a signalling mechanism for the sigchld handler */
+ 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);
+ }
+
+ handle_signal();
+}
+
+int get_signal_fd()
+{
+ return signal_recv_fd;
+}
diff --git a/init/signal_handler.h b/init/signal_handler.h
new file mode 100644
index 0000000..b092ccb
--- /dev/null
+++ b/init/signal_handler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_SIGNAL_HANDLER_H_
+#define _INIT_SIGNAL_HANDLER_H_
+
+void signal_init(void);
+void handle_signal(void);
+int get_signal_fd(void);
+
+#endif
diff --git a/init/ueventd.c b/init/ueventd.c
new file mode 100644
index 0000000..d51ffde
--- /dev/null
+++ b/init/ueventd.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <poll.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <private/android_filesystem_config.h>
+
+#include "ueventd.h"
+#include "log.h"
+#include "util.h"
+#include "devices.h"
+#include "ueventd_parser.h"
+
+static char hardware[32];
+static unsigned revision = 0;
+
+int ueventd_main(int argc, char **argv)
+{
+ struct pollfd ufd;
+ int nr;
+ char tmp[32];
+
+ open_devnull_stdio();
+ log_init();
+
+ INFO("starting ueventd\n");
+
+ get_hardware_name(hardware, &revision);
+
+ ueventd_parse_config_file("/ueventd.rc");
+
+ snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
+ ueventd_parse_config_file(tmp);
+
+ device_init();
+
+ ufd.events = POLLIN;
+ ufd.fd = get_device_fd();
+
+ while(1) {
+ ufd.revents = 0;
+ nr = poll(&ufd, 1, -1);
+ if (nr <= 0)
+ continue;
+ if (ufd.revents == POLLIN)
+ handle_device_fd();
+ }
+}
+
+static int get_android_id(const char *id)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(android_ids); i++)
+ if (!strcmp(id, android_ids[i].name))
+ return android_ids[i].aid;
+ return 0;
+}
+
+void set_device_permission(int nargs, char **args)
+{
+ char *name;
+ mode_t perm;
+ uid_t uid;
+ gid_t gid;
+ int prefix = 0;
+ char *endptr;
+ int ret;
+ char *tmp = 0;
+
+ if (nargs == 0)
+ return;
+
+ if (args[0][0] == '#')
+ return;
+
+ if (nargs != 4) {
+ ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
+ return;
+ }
+
+ name = args[0];
+ /* If path starts with mtd@ lookup the mount number. */
+ if (!strncmp(name, "mtd@", 4)) {
+ int n = mtd_name_to_number(name + 4);
+ if (n >= 0)
+ asprintf(&tmp, "/dev/mtd/mtd%d", n);
+ name = tmp;
+ } else {
+ int len = strlen(name);
+ if (name[len - 1] == '*') {
+ prefix = 1;
+ name[len - 1] = '\0';
+ }
+ }
+
+ perm = strtol(args[1], &endptr, 8);
+ if (!endptr || *endptr != '\0') {
+ ERROR("invalid mode '%s'\n", args[1]);
+ free(tmp);
+ return;
+ }
+
+ ret = get_android_id(args[2]);
+ if (ret < 0) {
+ ERROR("invalid uid '%s'\n", args[2]);
+ free(tmp);
+ return;
+ }
+ uid = ret;
+
+ ret = get_android_id(args[3]);
+ if (ret < 0) {
+ ERROR("invalid gid '%s'\n", args[3]);
+ free(tmp);
+ return;
+ }
+ gid = ret;
+
+ add_dev_perms(name, perm, uid, gid, prefix);
+ free(tmp);
+}
diff --git a/init/ueventd.h b/init/ueventd.h
new file mode 100644
index 0000000..9066e47
--- /dev/null
+++ b/init/ueventd.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_H_
+#define _INIT_UEVENTD_H_
+
+int ueventd_main(int argc, char **argv);
+
+#endif
diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c
new file mode 100644
index 0000000..0dd8b4d
--- /dev/null
+++ b/init/ueventd_parser.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "ueventd_parser.h"
+#include "parser.h"
+#include "log.h"
+#include "list.h"
+#include "util.h"
+
+static void parse_line_device(struct parse_state *state, int nargs, char **args);
+
+static void parse_config(const char *fn, char *s)
+{
+ struct parse_state state;
+ char *args[UEVENTD_PARSER_MAXARGS];
+ int nargs;
+ nargs = 0;
+ state.filename = fn;
+ state.line = 1;
+ state.ptr = s;
+ state.nexttoken = 0;
+ state.parse_line = parse_line_device;
+ for (;;) {
+ int token = next_token(&state);
+ switch (token) {
+ case T_EOF:
+ state.parse_line(&state, 0, 0);
+ return;
+ case T_NEWLINE:
+ if (nargs) {
+ state.parse_line(&state, nargs, args);
+ nargs = 0;
+ }
+ break;
+ case T_TEXT:
+ if (nargs < UEVENTD_PARSER_MAXARGS) {
+ args[nargs++] = state.text;
+ }
+ break;
+ }
+ }
+}
+
+int ueventd_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 void parse_line_device(struct parse_state* state, int nargs, char **args)
+{
+ set_device_permission(nargs, args);
+}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
new file mode 100644
index 0000000..48f9bb8
--- /dev/null
+++ b/init/ueventd_parser.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_PARSER_H_
+#define _INIT_UEVENTD_PARSER_H_
+
+#define UEVENTD_PARSER_MAXARGS 4
+
+int ueventd_parse_config_file(const char *fn);
+void set_device_permission(int nargs, char **args);
+
+#endif
diff --git a/init/util.c b/init/util.c
index 0b7667d..377754b 100644
--- a/init/util.c
+++ b/init/util.c
@@ -21,6 +21,7 @@
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
+#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -32,7 +33,9 @@
#include <private/android_filesystem_config.h>
-#include "init.h"
+#include "log.h"
+#include "list.h"
+#include "util.h"
static int log_fd = -1;
/* Inital log level before init.rc is parsed and this this is reset. */
@@ -209,3 +212,246 @@
item->prev->next = item->next;
}
+#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;
+}
+
+/*
+ * gettime() - returns the time in seconds of the system's monotonic clock or
+ * zero on error.
+ */
+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;
+}
+
+int mkdir_recursive(const char *pathname, mode_t mode)
+{
+ char buf[128];
+ const char *slash;
+ const char *p = pathname;
+ int width;
+ int ret;
+ struct stat info;
+
+ while ((slash = strchr(p, '/')) != NULL) {
+ width = slash - pathname;
+ p = slash + 1;
+ if (width < 0)
+ break;
+ if (width == 0)
+ continue;
+ if ((unsigned int)width > sizeof(buf) - 1) {
+ ERROR("path too long for mkdir_recursive\n");
+ return -1;
+ }
+ memcpy(buf, pathname, width);
+ buf[width] = 0;
+ if (stat(buf, &info) != 0) {
+ ret = mkdir(buf, mode);
+ if (ret && errno != EEXIST)
+ return ret;
+ }
+ }
+ ret = mkdir(pathname, mode);
+ if (ret && errno != EEXIST)
+ return ret;
+ return 0;
+}
+
+void sanitize(char *s)
+{
+ if (!s)
+ return;
+ while (isalnum(*s))
+ s++;
+ *s = 0;
+}
+void make_link(const char *oldpath, const char *newpath)
+{
+ int ret;
+ char buf[256];
+ char *slash;
+ int width;
+
+ slash = strrchr(newpath, '/');
+ if (!slash)
+ return;
+ width = slash - newpath;
+ if (width <= 0 || width > (int)sizeof(buf) - 1)
+ return;
+ memcpy(buf, newpath, width);
+ buf[width] = 0;
+ ret = mkdir_recursive(buf, 0755);
+ if (ret)
+ ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno);
+
+ ret = symlink(oldpath, newpath);
+ if (ret && errno != EEXIST)
+ ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
+}
+
+void remove_link(const char *oldpath, const char *newpath)
+{
+ char path[256];
+ ssize_t ret;
+ ret = readlink(newpath, path, sizeof(path) - 1);
+ if (ret <= 0)
+ return;
+ path[ret] = 0;
+ if (!strcmp(path, oldpath))
+ unlink(newpath);
+}
+
+int wait_for_file(const char *filename, int timeout)
+{
+ struct stat info;
+ time_t timeout_time = gettime() + timeout;
+ int ret = -1;
+
+ while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
+ usleep(10000);
+
+ return ret;
+}
+
+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 get_hardware_name(char *hardware, unsigned int *revision)
+{
+ 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);
+ }
+ }
+}
diff --git a/init/util.h b/init/util.h
new file mode 100644
index 0000000..2e47369
--- /dev/null
+++ b/init/util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UTIL_H_
+#define _INIT_UTIL_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+static const char *coldboot_done = "/dev/.coldboot_done";
+
+int mtd_name_to_number(const char *name);
+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);
+time_t gettime(void);
+unsigned int decode_uid(const char *s);
+
+int mkdir_recursive(const char *pathname, mode_t mode);
+void sanitize(char *p);
+void make_link(const char *oldpath, const char *newpath);
+void remove_link(const char *oldpath, const char *newpath);
+int wait_for_file(const char *filename, int timeout);
+void open_devnull_stdio(void);
+void get_hardware_name(char *hardware, unsigned int *revision);
+#endif
diff --git a/libacc/acc.cpp b/libacc/acc.cpp
index 8f33b0b..6e13f9a 100644
--- a/libacc/acc.cpp
+++ b/libacc/acc.cpp
@@ -23,10 +23,11 @@
#include <cutils/hashmap.h>
-#if defined(__i386__)
#include <sys/mman.h>
-#endif
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
#if defined(__arm__)
#define DEFAULT_ARM_CODEGEN
@@ -230,7 +231,7 @@
void release() {
if (pProgramBase != 0) {
- free(pProgramBase);
+ munmap(pProgramBase, mSize);
pProgramBase = 0;
}
}
@@ -263,7 +264,9 @@
virtual void init(int size) {
release();
mSize = size;
- pProgramBase = (char*) calloc(1, size);
+ pProgramBase = (char*) mmap(NULL, size,
+ PROT_EXEC | PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ind = pProgramBase;
}
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 4c45cc9..e8c7775 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -16,10 +16,17 @@
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
+ifeq ($(TARGET_CPU_SMP),true)
+ targetSmpFlag := -DANDROID_SMP=1
+else
+ targetSmpFlag := -DANDROID_SMP=0
+endif
+hostSmpFlag := -DANDROID_SMP=0
+
commonSources := \
array.c \
hashmap.c \
- atomic.c \
+ atomic.c.arm \
native_handle.c \
buffer.c \
socket_inaddr_any_server.c \
@@ -77,9 +84,10 @@
# Static library for host
# ========================================================
LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources)
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
LOCAL_LDLIBS := -lpthread
LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += $(hostSmpFlag)
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -92,6 +100,7 @@
LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c
LOCAL_LDLIBS := -lpthread
LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CFLAGS += $(targetSmpFlag)
include $(BUILD_SHARED_LIBRARY)
else #!sim
@@ -103,7 +112,7 @@
LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c
ifeq ($(TARGET_ARCH),arm)
-LOCAL_SRC_FILES += memset32.S atomic-android-arm.S
+LOCAL_SRC_FILES += memset32.S
else # !arm
ifeq ($(TARGET_ARCH),sh)
LOCAL_SRC_FILES += memory.c atomic-android-sh.c
@@ -114,12 +123,14 @@
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += $(targetSmpFlag)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libcutils
LOCAL_WHOLE_STATIC_LIBRARIES := libcutils
LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CFLAGS += $(targetSmpFlag)
include $(BUILD_SHARED_LIBRARY)
endif #!sim
diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S
deleted file mode 100644
index 1dd2363..0000000
--- a/libcutils/atomic-android-arm.S
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <machine/cpu-features.h>
-
-/*
- * NOTE: these atomic operations are SMP safe on all architectures.
- */
-
- .text
- .align
-
- .global android_atomic_write
- .type android_atomic_write, %function
-
- .global android_atomic_inc
- .type android_atomic_inc, %function
- .global android_atomic_dec
- .type android_atomic_dec, %function
-
- .global android_atomic_add
- .type android_atomic_add, %function
- .global android_atomic_and
- .type android_atomic_and, %function
- .global android_atomic_or
- .type android_atomic_or, %function
-
- .global android_atomic_swap
- .type android_atomic_swap, %function
-
- .global android_atomic_cmpxchg
- .type android_atomic_cmpxchg, %function
-
-/*
- * ----------------------------------------------------------------------------
- * 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:
- str r0, [r1]
- bx lr;
-
-/*
- * ----------------------------------------------------------------------------
- * android_atomic_inc
- * input: r0 = address
- * output: r0 = old value
- */
-
-android_atomic_inc:
- .fnstart
- .save {r4, lr}
- 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
- .fnend
-
-/*
- * ----------------------------------------------------------------------------
- * android_atomic_dec
- * input: r0=address
- * output: r0 = old value
- */
-
-android_atomic_dec:
- .fnstart
- .save {r4, lr}
- 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
- .fnend
-
-/*
- * ----------------------------------------------------------------------------
- * android_atomic_add
- * input: r0=value, r1=address
- * output: r0 = old value
- */
-
-android_atomic_add:
- .fnstart
- .save {r4, lr}
- 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
- .fnend
-
-
-/*
- * ----------------------------------------------------------------------------
- * android_atomic_and
- * input: r0=value, r1=address
- * output: r0 = old value
- */
-
-android_atomic_and:
- .fnstart
- .save {r4, r5, lr}
- 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
- .fnend
-
-/*
- * ----------------------------------------------------------------------------
- * android_atomic_or
- * input: r0=value, r1=address
- * output: r0 = old value
- */
-
-android_atomic_or:
- .fnstart
- .save {r4, r5, lr}
- 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
- .fnend
-
-/*
- * ----------------------------------------------------------------------------
- * android_atomic_swap
- * input: r0=value, r1=address
- * output: r0 = old value
- */
-
-/* replaced swp instruction with ldrex/strex for ARMv6 & ARMv7 */
-android_atomic_swap:
-#if defined (_ARM_HAVE_LDREX_STREX)
-1: ldrex r2, [r1]
- strex r3, r0, [r1]
- teq r3, #0
- bne 1b
- mov r0, r2
- mcr p15, 0, r0, c7, c10, 5 /* or, use dmb */
-#else
- swp r0, r0, [r1]
-#endif
- 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:
- .fnstart
- .save {r4, lr}
- 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
- .fnend
-
-/*
- * ----------------------------------------------------------------------------
- * 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
deleted file mode 100644
index 1574c9c..0000000
--- a/libcutils/atomic-android-armv6.S
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
- .text
- .align
-
- .global android_atomic_write
- .type android_atomic_write, %function
-
- .global android_atomic_inc
- .type android_atomic_inc, %function
- .global android_atomic_dec
- .type android_atomic_dec, %function
-
- .global android_atomic_add
- .type android_atomic_add, %function
- .global android_atomic_and
- .type android_atomic_and, %function
- .global android_atomic_or
- .type android_atomic_or, %function
-
- .global android_atomic_swap
- .type android_atomic_swap, %function
-
- .global android_atomic_cmpxchg
- .type android_atomic_cmpxchg, %function
-
-
-
-/* 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:
- str r0, [r1]
- 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-android-sh.c b/libcutils/atomic-android-sh.c
index acbea97..abe7d25 100644
--- a/libcutils/atomic-android-sh.c
+++ b/libcutils/atomic-android-sh.c
@@ -35,6 +35,9 @@
* ARM implementation, in this file above.
* We follow the fact that the initializer for mutex is a simple zero
* value.
+ *
+ * (3) These operations are NOT safe for SMP, as there is no currently
+ * no definition for a memory barrier operation.
*/
#include <pthread.h>
@@ -46,18 +49,35 @@
&_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
-void android_atomic_write(int32_t value, volatile int32_t* addr) {
+int32_t android_atomic_acquire_load(volatile int32_t* addr)
+{
+ return *addr;
+}
+
+int32_t android_atomic_release_load(volatile int32_t* addr)
+{
+ return *addr;
+}
+
+void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) {
int32_t oldValue;
do {
oldValue = *addr;
- } while (android_atomic_cmpxchg(oldValue, value, addr));
+ } while (android_atomic_release_cas(oldValue, value, addr));
+}
+
+void android_atomic_release_store(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, value, addr));
}
int32_t android_atomic_inc(volatile int32_t* addr) {
int32_t oldValue;
do {
oldValue = *addr;
- } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr));
+ } while (android_atomic_release_cas(oldValue, oldValue+1, addr));
return oldValue;
}
@@ -65,7 +85,7 @@
int32_t oldValue;
do {
oldValue = *addr;
- } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr));
+ } while (android_atomic_release_cas(oldValue, oldValue-1, addr));
return oldValue;
}
@@ -73,7 +93,7 @@
int32_t oldValue;
do {
oldValue = *addr;
- } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr));
+ } while (android_atomic_release_cas(oldValue, oldValue+value, addr));
return oldValue;
}
@@ -81,7 +101,7 @@
int32_t oldValue;
do {
oldValue = *addr;
- } while (android_atomic_cmpxchg(oldValue, oldValue&value, addr));
+ } while (android_atomic_release_cas(oldValue, oldValue&value, addr));
return oldValue;
}
@@ -89,11 +109,15 @@
int32_t oldValue;
do {
oldValue = *addr;
- } while (android_atomic_cmpxchg(oldValue, oldValue|value, addr));
+ } while (android_atomic_release_cas(oldValue, oldValue|value, addr));
return oldValue;
}
-int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) {
+int32_t android_atomic_acquire_swap(int32_t value, volatile int32_t* addr) {
+ return android_atomic_release_swap(value, addr);
+}
+
+int32_t android_atomic_release_swap(int32_t value, volatile int32_t* addr) {
int32_t oldValue;
do {
oldValue = *addr;
@@ -101,7 +125,12 @@
return oldValue;
}
-int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue,
+int android_atomic_acquire_cmpxchg(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr) {
+ return android_atomic_release_cmpxchg(oldValue, newValue, addr);
+}
+
+int android_atomic_release_cmpxchg(int32_t oldvalue, int32_t newvalue,
volatile int32_t* addr) {
int result;
pthread_mutex_t* lock = SWAP_LOCK(addr);
@@ -118,42 +147,3 @@
return result;
}
-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;
-}
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
index 41faaa2..f6cd8b0 100644
--- a/libcutils/atomic.c
+++ b/libcutils/atomic.c
@@ -14,326 +14,6 @@
* limitations under the License.
*/
-#include <cutils/atomic.h>
-#ifdef HAVE_WIN32_THREADS
-#include <windows.h>
-#else
-#include <sched.h>
-#endif
+#define inline
-/*****************************************************************************/
-#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;
-}
-
-/*****************************************************************************/
-#elif __sh__
-// implementation for SuperH is in atomic-android-sh.c.
-
-#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
+#include <cutils/atomic-inline.h>
diff --git a/libcutils/mspace.c b/libcutils/mspace.c
index 63b199d..6d3b35c 100644
--- a/libcutils/mspace.c
+++ b/libcutils/mspace.c
@@ -271,4 +271,16 @@
}
return 0;
}
+
+void *contiguous_mspace_sbrk0(mspace msp) {
+ struct mspace_contig_state *cs;
+ mstate ms;
+ const unsigned int pagesize = PAGESIZE;
+
+ ms = (mstate)msp;
+ cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
+ assert(cs->magic == CONTIG_STATE_MAGIC);
+ assert(cs->m == ms);
+ return cs->brk;
+}
#endif
diff --git a/liblinenoise/Android.mk b/liblinenoise/Android.mk
new file mode 100644
index 0000000..b32a5f1
--- /dev/null
+++ b/liblinenoise/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Static library
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= liblinenoise
+LOCAL_SRC_FILES := linenoise.c
+
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/liblinenoise/MODULE_LICENSE_BSD_LIKE b/liblinenoise/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/liblinenoise/MODULE_LICENSE_BSD_LIKE
diff --git a/liblinenoise/NOTICE b/liblinenoise/NOTICE
new file mode 100644
index 0000000..f61419e
--- /dev/null
+++ b/liblinenoise/NOTICE
@@ -0,0 +1,28 @@
+Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * 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 Redis 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.
\ No newline at end of file
diff --git a/liblinenoise/linenoise.c b/liblinenoise/linenoise.c
new file mode 100644
index 0000000..4f6775c
--- /dev/null
+++ b/liblinenoise/linenoise.c
@@ -0,0 +1,449 @@
+/* linenoise.c -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ *
+ * http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * 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 Redis 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.
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Switch to gets() if $TERM is something we can't support.
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - Completion?
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * CHA (Cursor Horizontal Absolute)
+ * Sequence: ESC [ n G
+ * Effect: moves cursor to column n
+ *
+ * EL (Erase Line)
+ * Sequence: ESC [ n K
+ * Effect: if n is 0 or missing, clear from cursor to end of line
+ * Effect: if n is 1, clear from beginning of line to cursor
+ * Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ * Sequence: ESC [ n C
+ * Effect: moves cursor forward of n chars
+ *
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define LINENOISE_MAX_LINE 4096
+static char *unsupported_term[] = {"dumb","cons25",NULL};
+
+static struct termios orig_termios; /* in order to restore at exit */
+static int rawmode = 0; /* for atexit() function to check if restore is needed*/
+static int atexit_registered = 0; /* register atexit just 1 time */
+static int history_max_len = 100;
+static int history_len = 0;
+char **history = NULL;
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+
+static int isUnsupportedTerm(void) {
+ char *term = getenv("TERM");
+ int j;
+
+ if (term == NULL) return 0;
+ for (j = 0; unsupported_term[j]; j++)
+ if (!strcasecmp(term,unsupported_term[j])) return 1;
+ return 0;
+}
+
+static void freeHistory(void) {
+ if (history) {
+ int j;
+
+ for (j = 0; j < history_len; j++)
+ free(history[j]);
+ free(history);
+ }
+}
+
+static int enableRawMode(int fd) {
+ struct termios raw;
+
+ if (!isatty(STDIN_FILENO)) goto fatal;
+ if (!atexit_registered) {
+ atexit(linenoiseAtExit);
+ atexit_registered = 1;
+ }
+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+ raw = orig_termios; /* modify the original mode */
+ /* input modes: no break, no CR to NL, no parity check, no strip char,
+ * no start/stop output control. */
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ /* output modes - disable post processing */
+ raw.c_oflag &= ~(OPOST);
+ /* control modes - set 8 bit chars */
+ raw.c_cflag |= (CS8);
+ /* local modes - choing off, canonical off, no extended functions,
+ * no signal chars (^Z,^C) */
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ /* control chars - set return condition: min number of bytes and timer.
+ * We want read to return every single byte, without timeout. */
+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+ /* put terminal in raw mode */
+ if (tcsetattr(fd,TCSADRAIN,&raw) < 0) goto fatal;
+ rawmode = 1;
+ return 0;
+
+fatal:
+ errno = ENOTTY;
+ return -1;
+}
+
+static void disableRawMode(int fd) {
+ /* Don't even check the return value as it's too late. */
+ if (rawmode && tcsetattr(fd,TCSADRAIN,&orig_termios) != -1)
+ rawmode = 0;
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+ disableRawMode(STDIN_FILENO);
+ freeHistory();
+}
+
+static int getColumns(void) {
+ struct winsize ws;
+
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 4096;
+ if (ws.ws_col == 0) {
+ return 4096;
+ }
+ return ws.ws_col;
+}
+
+static int effectiveLen(const char* prompt) {
+ int col = 0;
+ char c;
+ // TODO: Handle escape sequences.
+ while ( (c = *prompt++) != 0 ) {
+ if (c == '\n') {
+ col = 0;
+ } else {
+ col++;
+ }
+ }
+ return col;
+}
+
+static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
+ char seq[64];
+ size_t plen = effectiveLen(prompt);
+
+ while((plen+pos) >= cols) {
+ buf++;
+ len--;
+ pos--;
+ }
+ while (plen+len > cols) {
+ len--;
+ }
+
+ /* Cursor to left edge */
+ snprintf(seq,64,"\x1b[0G");
+ if (write(fd,seq,strlen(seq)) == -1) return;
+ /* Write the prompt and the current buffer content */
+ if (write(fd,prompt,strlen(prompt)) == -1) return;
+ if (write(fd,buf,len) == -1) return;
+ /* Erase to right */
+ snprintf(seq,64,"\x1b[0K");
+ if (write(fd,seq,strlen(seq)) == -1) return;
+ /* Move cursor to original position. */
+ snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
+ if (write(fd,seq,strlen(seq)) == -1) return;
+}
+
+static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
+ size_t plen = strlen(prompt);
+ size_t pos = 0;
+ size_t len = 0;
+ size_t cols = getColumns();
+ int history_index = 0;
+
+ buf[0] = '\0';
+ buflen--; /* Make sure there is always space for the nulterm */
+
+ /* The latest history entry is always our current buffer, that
+ * initially is just an empty string. */
+ linenoiseHistoryAdd("");
+
+ if (write(fd,prompt,plen) == -1) return -1;
+ while(1) {
+ char c;
+ int nread;
+ char seq[2];
+
+ nread = read(fd,&c,1);
+ if (nread <= 0) return len;
+ switch(c) {
+ case 10: /* line feed. */
+ case 13: /* enter */
+ history_len--;
+ return len;
+ case 4: /* ctrl-d */
+ history_len--;
+ return (len == 0) ? -1 : (int)len;
+ case 3: /* ctrl-c */
+ errno = EAGAIN;
+ return -1;
+ case 127: /* backspace */
+ case 8: /* ctrl-h */
+ if (pos > 0 && len > 0) {
+ memmove(buf+pos-1,buf+pos,len-pos);
+ pos--;
+ len--;
+ buf[len] = '\0';
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ break;
+ case 20: /* ctrl-t */
+ if (pos > 0 && pos < len) {
+ int aux = buf[pos-1];
+ buf[pos-1] = buf[pos];
+ buf[pos] = aux;
+ if (pos != len-1) pos++;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ break;
+ case 2: /* ctrl-b */
+ goto left_arrow;
+ case 6: /* ctrl-f */
+ goto right_arrow;
+ case 16: /* ctrl-p */
+ seq[1] = 65;
+ goto up_down_arrow;
+ case 14: /* ctrl-n */
+ seq[1] = 66;
+ goto up_down_arrow;
+ break;
+ case 27: /* escape sequence */
+ if (read(fd,seq,2) == -1) break;
+ if (seq[0] == 91 && seq[1] == 68) {
+left_arrow:
+ /* left arrow */
+ if (pos > 0) {
+ pos--;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else if (seq[0] == 91 && seq[1] == 67) {
+right_arrow:
+ /* right arrow */
+ if (pos != len) {
+ pos++;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) {
+up_down_arrow:
+ /* up and down arrow: history */
+ if (history_len > 1) {
+ /* Update the current history entry before to
+ * overwrite it with tne next one. */
+ free(history[history_len-1-history_index]);
+ history[history_len-1-history_index] = strdup(buf);
+ /* Show the new entry */
+ history_index += (seq[1] == 65) ? 1 : -1;
+ if (history_index < 0) {
+ history_index = 0;
+ break;
+ } else if (history_index >= history_len) {
+ history_index = history_len-1;
+ break;
+ }
+ strncpy(buf,history[history_len-1-history_index],buflen);
+ buf[buflen] = '\0';
+ len = pos = strlen(buf);
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ }
+ break;
+ default:
+ if (len < buflen) {
+ if (len == pos) {
+ buf[pos] = c;
+ pos++;
+ len++;
+ buf[len] = '\0';
+ if (plen+len < cols) {
+ /* Avoid a full update of the line in the
+ * trivial case. */
+ if (write(fd,&c,1) == -1) return -1;
+ } else {
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else {
+ memmove(buf+pos+1,buf+pos,len-pos);
+ buf[pos] = c;
+ len++;
+ pos++;
+ buf[len] = '\0';
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ }
+ break;
+ case 21: /* Ctrl+u, delete the whole line. */
+ buf[0] = '\0';
+ pos = len = 0;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 11: /* Ctrl+k, delete from current to end of line. */
+ buf[pos] = '\0';
+ len = pos;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 1: /* Ctrl+a, go to the start of the line */
+ pos = 0;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 5: /* ctrl+e, go to the end of the line */
+ pos = len;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ }
+ }
+ return len;
+}
+
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+ int fd = STDIN_FILENO;
+ int count;
+
+ if (buflen == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!isatty(STDIN_FILENO)) {
+ if (fgets(buf, buflen, stdin) == NULL) return -1;
+ count = strlen(buf);
+ if (count && buf[count-1] == '\n') {
+ count--;
+ buf[count] = '\0';
+ }
+ } else {
+ if (enableRawMode(fd) == -1) return -1;
+ count = linenoisePrompt(fd, buf, buflen, prompt);
+ disableRawMode(fd);
+ }
+ return count;
+}
+
+char *linenoise(const char *prompt) {
+ char buf[LINENOISE_MAX_LINE];
+ int count;
+
+ if (isUnsupportedTerm()) {
+ size_t len;
+
+ printf("%s",prompt);
+ fflush(stdout);
+ if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+ len = strlen(buf);
+ while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+ len--;
+ buf[len] = '\0';
+ }
+ return strdup(buf);
+ } else {
+ count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+ if (count == -1) return NULL;
+ return strdup(buf);
+ }
+}
+
+/* Using a circular buffer is smarter, but a bit more complex to handle. */
+int linenoiseHistoryAdd(const char *line) {
+ char *linecopy;
+
+ if (history_max_len == 0) return 0;
+ if (history == 0) {
+ history = malloc(sizeof(char*)*history_max_len);
+ if (history == NULL) return 0;
+ memset(history,0,(sizeof(char*)*history_max_len));
+ }
+ linecopy = strdup(line);
+ if (!linecopy) return 0;
+ if (history_len == history_max_len) {
+ memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+ history_len--;
+ }
+ history[history_len] = linecopy;
+ history_len++;
+ return 1;
+}
+
+int linenoiseHistorySetMaxLen(int len) {
+ char **new;
+
+ if (len < 1) return 0;
+ if (history) {
+ int tocopy = history_len;
+
+ new = malloc(sizeof(char*)*len);
+ if (new == NULL) return 0;
+ if (len < tocopy) tocopy = len;
+ memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy);
+ free(history);
+ history = new;
+ }
+ history_max_len = len;
+ if (history_len > history_max_len)
+ history_len = history_max_len;
+ return 1;
+}
diff --git a/liblinenoise/linenoise.h b/liblinenoise/linenoise.h
new file mode 100644
index 0000000..57bf9d1
--- /dev/null
+++ b/liblinenoise/linenoise.h
@@ -0,0 +1,41 @@
+/* linenoise.h -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * See linenoise.c for more information.
+ *
+ * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * 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 Redis 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.
+ */
+
+#ifndef __LINENOISE_H
+#define __LINENOISE_H
+
+char *linenoise(const char *prompt);
+int linenoiseHistoryAdd(const char *line);
+int linenoiseHistorySetMaxLen(int len);
+
+#endif /* __LINENOISE_H */
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index ed9d699..f8b7254 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -454,7 +454,7 @@
numVecs = numLines*3; // 3 iovecs per line.
if (numVecs > INLINE_VECS) {
- vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines);
+ vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
if (vec == NULL) {
msg = "LOG: write failed, no memory";
numVecs = 3;
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 45e392a..6755ba1 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -70,7 +70,7 @@
vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
va_end(ap);
- LOGD(errmsg);
+ LOGD("%s", errmsg);
}
const char *dhcp_lasterror()
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 0cc85d9..6491d24 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -40,7 +40,13 @@
buffer.cpp
ifeq ($(TARGET_ARCH),arm)
+ifeq ($(TARGET_ARCH_VERSION),armv7-a)
+PIXELFLINGER_SRC_FILES += col32cb16blend_neon.S
+PIXELFLINGER_SRC_FILES += col32cb16blend.S
+else
PIXELFLINGER_SRC_FILES += t32cb16blend.S
+PIXELFLINGER_SRC_FILES += col32cb16blend.S
+endif
endif
ifeq ($(TARGET_ARCH),arm)
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index ff7b0b3..d3720c3 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -424,5 +424,15 @@
*mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
}
+#if 0
+#pragma mark -
+#pragma mark Byte/half word extract and extend (ARMv6+ only)...
+#endif
+
+void ARMAssembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+ *mPC++ = (cc<<28) | 0x6CF0070 | (Rd<<12) | ((rotate >> 3) << 10) | Rm;
+}
+
}; // namespace android
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index ef3b66a..a667cb5 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -123,6 +123,7 @@
int RdHi, int RdLo, int Rs, int Rm);
virtual void SMLAW(int cc, int y,
int Rd, int Rm, int Rs, int Rn);
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
private:
ARMAssembler(const ARMAssembler& rhs);
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
index 465b3bd..ff6af2a 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -203,6 +203,9 @@
virtual void SMLAW(int cc, int y,
int Rd, int Rm, int Rs, int Rn) = 0;
+ // byte/half word extract...
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate) = 0;
+
// -----------------------------------------------------------------------
// convenience...
// -----------------------------------------------------------------------
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
index 18c4618..7c422db 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
@@ -195,6 +195,9 @@
mTarget->SMLAW(cc, y, Rd, Rm, Rs, Rn);
}
+void ARMAssemblerProxy::UXTB16(int cc, int Rd, int Rm, int rotate) {
+ mTarget->UXTB16(cc, Rd, Rm, rotate);
+}
}; // namespace android
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
index 4bdca9c..9134cce 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
@@ -114,6 +114,8 @@
virtual void SMLAW(int cc, int y,
int Rd, int Rm, int Rs, int Rn);
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+
private:
ARMAssemblerInterface* mTarget;
};
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 29410c8..5877ff4 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -19,6 +19,8 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
@@ -39,15 +41,14 @@
Assembly::Assembly(size_t size)
: mCount(1), mSize(0)
{
- mBase = (uint32_t*)malloc(size);
- if (mBase) {
- mSize = size;
- }
+ mBase = (uint32_t*)mspace_malloc(getMspace(), size);
+ mSize = size;
+ ensureMbaseExecutable();
}
Assembly::~Assembly()
{
- free(mBase);
+ mspace_free(getMspace(), mBase);
}
void Assembly::incStrong(const void*) const
@@ -75,11 +76,32 @@
ssize_t Assembly::resize(size_t newSize)
{
- mBase = (uint32_t*)realloc(mBase, newSize);
+ mBase = (uint32_t*)mspace_realloc(getMspace(), mBase, newSize);
mSize = newSize;
+ ensureMbaseExecutable();
return size();
}
+mspace Assembly::getMspace()
+{
+ static mspace msp = create_contiguous_mspace(2 * 1024, 1024 * 1024, /*locked=*/ false);
+ return msp;
+}
+
+void Assembly::ensureMbaseExecutable()
+{
+ long pagesize = sysconf(_SC_PAGESIZE);
+ long pagemask = ~(pagesize - 1); // assumes pagesize is a power of 2
+
+ uint32_t* pageStart = (uint32_t*) (((uintptr_t) mBase) & pagemask);
+ size_t adjustedLength = mBase - pageStart + mSize;
+
+ if (mBase && mprotect(pageStart, adjustedLength, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
+ mspace_free(getMspace(), mBase);
+ mBase = NULL;
+ }
+}
+
// ----------------------------------------------------------------------------
CodeCache::CodeCache(size_t size)
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
index 8ff1366..aaafd26 100644
--- a/libpixelflinger/codeflinger/CodeCache.h
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <pthread.h>
#include <sys/types.h>
+#include <cutils/mspace.h>
#include "tinyutils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
@@ -67,9 +68,12 @@
typedef void weakref_type;
private:
+ static mspace getMspace();
+ void ensureMbaseExecutable();
+
mutable int32_t mCount;
uint32_t* mBase;
- ssize_t mSize;
+ size_t mSize;
};
// ----------------------------------------------------------------------------
diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c
index 4676da0..c17f3ec 100644
--- a/libpixelflinger/codeflinger/disassem.c
+++ b/libpixelflinger/codeflinger/disassem.c
@@ -80,6 +80,7 @@
* 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)
+ * j - xtb rotate literal (bits 10-11)
* b - branch address
* t - thumb branch address (bits 24, 0-23)
* k - breakpoint comment (bits 0-3, 8-19)
@@ -122,6 +123,7 @@
{ 0x0fe000f0, 0x00c00090, "smull", "Sdnms" },
{ 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" },
{ 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" },
+ { 0x0fff03f0, 0x06cf0070, "uxtb16", "dmj" },
{ 0x0d700000, 0x04200000, "strt", "daW" },
{ 0x0d700000, 0x04300000, "ldrt", "daW" },
{ 0x0d700000, 0x04600000, "strbt", "daW" },
@@ -276,7 +278,7 @@
#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 insn_stkblktrans(x) insn_stack_block_transfers[(3*((x >> 20)&1))^((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]
@@ -406,6 +408,10 @@
else
di->di_printf("f%d", insn & 7);
break;
+ /* j - xtb rotate literal (bits 10-11) */
+ case 'j':
+ di->di_printf("ror #%d", ((insn >> 10) & 3) << 3);
+ break;
/* b - branch address */
case 'b':
branch = ((insn << 2) & 0x03ffffff);
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index 90e6584..7f6f8da 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -25,6 +25,9 @@
#include "codeflinger/GGLAssembler.h"
+#ifdef __ARM_ARCH__
+#include <machine/cpu-features.h>
+#endif
namespace android {
@@ -567,7 +570,7 @@
RSB(GE, 0, height, height, imm(0));
MUL(AL, 0, height, stride, height);
} else {
- // u has not been CLAMPed yet
+ // v 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));
@@ -868,6 +871,106 @@
load(txPtr, texel, 0);
}
+#if __ARM_ARCH__ >= 6
+// ARMv6 version, using UXTB16, and scheduled for Cortex-A8 pipeline
+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;
+ const int prescale = 16 - adjust;
+
+ 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 offsetrt = scratches.obtain();
+ int offsetlb = scratches.obtain();
+
+ int pixellb = offsetlb;
+
+ // RB -> U * V
+ CONTEXT_LOAD(offsetrt, generated_vars.rt);
+ CONTEXT_LOAD(offsetlb, generated_vars.lb);
+ if(!round) {
+ MOV(AL, 0, U, reg_imm(U, LSL, prescale));
+ }
+ ADD(AL, 0, u, offsetrt, offsetlb);
+
+ LDR(AL, pixel, txPtr.reg, reg_scale_pre(u));
+ if (round) {
+ SMULBB(AL, u, U, V);
+ RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+ } else {
+ SMULWB(AL, u, U, V);
+ RSB(AL, 0, U, U, imm(1<<(FRAC_BITS+prescale)));
+ }
+ UXTB16(AL, temp, pixel, 0);
+ if (round) {
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ LDR(AL, pixellb, txPtr.reg, reg_scale_pre(offsetlb));
+ MUL(AL, 0, dh, temp, u);
+ UXTB16(AL, temp, pixel, 8);
+ MUL(AL, 0, dl, temp, u);
+ RSB(AL, 0, k, u, imm(0x100));
+
+ // LB -> (1-U) * V
+ if (round) {
+ SMULBB(AL, u, U, V);
+ } else {
+ SMULWB(AL, u, U, V);
+ }
+ UXTB16(AL, temp, pixellb, 0);
+ 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);
+ UXTB16(AL, temp, pixellb, 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);
+ if (round) {
+ SMULBB(AL, u, U, V);
+ } else {
+ SMULWB(AL, u, U, V);
+ }
+ UXTB16(AL, temp, pixel, 0);
+ 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);
+ UXTB16(AL, temp, pixel, 8);
+ MLA(AL, 0, dl, temp, u, dl);
+
+ // RT -> U*(1-V)
+ LDR(AL, pixel, txPtr.reg, reg_scale_pre(offsetrt));
+ SUB(AL, 0, u, k, u);
+ UXTB16(AL, temp, pixel, 0);
+ MLA(AL, 0, dh, temp, u, dh);
+ UXTB16(AL, temp, pixel, 8);
+ MLA(AL, 0, dl, temp, u, dl);
+
+ UXTB16(AL, dh, dh, 8);
+ UXTB16(AL, dl, dl, 8);
+ ORR(AL, 0, texel.reg, dh, reg_imm(dl, LSL, 8));
+}
+#else
void GGLAssembler::filter32(
const fragment_parts_t& parts,
pixel_t& texel, const texture_unit_t& tmu,
@@ -955,6 +1058,7 @@
AND(AL, 0, dl, dl, reg_imm(mask, LSL, 8));
ORR(AL, 0, texel.reg, dh, dl);
}
+#endif
void GGLAssembler::build_texture_environment(
component_t& fragment,
diff --git a/libpixelflinger/col32cb16blend.S b/libpixelflinger/col32cb16blend.S
new file mode 100644
index 0000000..1450bde
--- /dev/null
+++ b/libpixelflinger/col32cb16blend.S
@@ -0,0 +1,78 @@
+/* libs/pixelflinger/col32cb16blend.S
+**
+** (C) COPYRIGHT 2009 ARM Limited.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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_col32cb16blend_arm
+
+//
+// This function alpha blends a fixed color into a destination scanline, using
+// the formula:
+//
+// d = s + (((a + (a >> 7)) * d) >> 8)
+//
+// where d is the destination pixel,
+// s is the source color,
+// a is the alpha channel of the source color.
+//
+
+// r0 = destination buffer pointer
+// r1 = color value
+// r2 = count
+
+
+scanline_col32cb16blend_arm:
+ push {r4-r10, lr} // stack ARM regs
+
+ mov r5, r1, lsr #24 // shift down alpha
+ mov r9, #0xff // create mask
+ add r5, r5, r5, lsr #7 // add in top bit
+ rsb r5, r5, #256 // invert alpha
+ and r10, r1, #0xff // extract red
+ and r12, r9, r1, lsr #8 // extract green
+ and r4, r9, r1, lsr #16 // extract blue
+ mov r10, r10, lsl #5 // prescale red
+ mov r12, r12, lsl #6 // prescale green
+ mov r4, r4, lsl #5 // prescale blue
+ mov r9, r9, lsr #2 // create dest green mask
+
+1:
+ ldrh r8, [r0] // load dest pixel
+ subs r2, r2, #1 // decrement loop counter
+ mov r6, r8, lsr #11 // extract dest red
+ and r7, r9, r8, lsr #5 // extract dest green
+ and r8, r8, #0x1f // extract dest blue
+
+ smlabb r6, r6, r5, r10 // dest red * alpha + src red
+ smlabb r7, r7, r5, r12 // dest green * alpha + src green
+ smlabb r8, r8, r5, r4 // dest blue * alpha + src blue
+
+ mov r6, r6, lsr #8 // shift down red
+ mov r7, r7, lsr #8 // shift down green
+ mov r6, r6, lsl #11 // shift red into 565
+ orr r6, r7, lsl #5 // shift green into 565
+ orr r6, r8, lsr #8 // shift blue into 565
+
+ strh r6, [r0], #2 // store pixel to dest, update ptr
+ bne 1b // if count != 0, loop
+
+ pop {r4-r10, pc} // return
+
+
+
diff --git a/libpixelflinger/col32cb16blend_neon.S b/libpixelflinger/col32cb16blend_neon.S
new file mode 100644
index 0000000..17b0d01
--- /dev/null
+++ b/libpixelflinger/col32cb16blend_neon.S
@@ -0,0 +1,153 @@
+/* libs/pixelflinger/col32cb16blend_neon.S
+**
+** (C) COPYRIGHT 2009 ARM Limited.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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_col32cb16blend_neon
+
+//
+// This function alpha blends a fixed color into a destination scanline, using
+// the formula:
+//
+// d = s + (((a + (a >> 7)) * d) >> 8)
+//
+// where d is the destination pixel,
+// s is the source color,
+// a is the alpha channel of the source color.
+//
+// The NEON implementation processes 16 pixels per iteration. The remaining 0 - 15
+// pixels are processed in ARM code.
+//
+
+// r0 = destination buffer pointer
+// r1 = color pointer
+// r2 = count
+
+
+scanline_col32cb16blend_neon:
+ push {r4-r11, lr} // stack ARM regs
+
+ vmov.u16 q15, #256 // create alpha constant
+ movs r3, r2, lsr #4 // calc. sixteens iterations
+ vmov.u16 q14, #0x1f // create blue mask
+
+ beq 2f // if r3 == 0, branch to singles
+
+ vld4.8 {d0[], d2[], d4[], d6[]}, [r1] // load color into four registers
+ // split and duplicate them, such that
+ // d0 = 8 equal red values
+ // d2 = 8 equal green values
+ // d4 = 8 equal blue values
+ // d6 = 8 equal alpha values
+ vshll.u8 q0, d0, #5 // shift up red and widen
+ vshll.u8 q1, d2, #6 // shift up green and widen
+ vshll.u8 q2, d4, #5 // shift up blue and widen
+
+ vshr.u8 d7, d6, #7 // extract top bit of alpha
+ vaddl.u8 q3, d6, d7 // add top bit into alpha
+ vsub.u16 q3, q15, q3 // invert alpha
+
+1:
+ // This loop processes 16 pixels per iteration. In the comments, references to
+ // the first eight pixels are suffixed with "0" (red0, green0, blue0),
+ // the second eight are suffixed "1".
+ // q8 = dst red0
+ // q9 = dst green0
+ // q10 = dst blue0
+ // q13 = dst red1
+ // q12 = dst green1
+ // q11 = dst blue1
+
+ vld1.16 {d20, d21, d22, d23}, [r0] // load 16 dest pixels
+ vshr.u16 q8, q10, #11 // shift dst red0 to low 5 bits
+ pld [r0, #63] // preload next dest pixels
+ vshl.u16 q9, q10, #5 // shift dst green0 to top 6 bits
+ vand q10, q10, q14 // extract dst blue0
+ vshr.u16 q9, q9, #10 // shift dst green0 to low 6 bits
+ vmul.u16 q8, q8, q3 // multiply dst red0 by src alpha
+ vshl.u16 q12, q11, #5 // shift dst green1 to top 6 bits
+ vmul.u16 q9, q9, q3 // multiply dst green0 by src alpha
+ vshr.u16 q13, q11, #11 // shift dst red1 to low 5 bits
+ vmul.u16 q10, q10, q3 // multiply dst blue0 by src alpha
+ vshr.u16 q12, q12, #10 // shift dst green1 to low 6 bits
+ vand q11, q11, q14 // extract dst blue1
+ vadd.u16 q8, q8, q0 // add src red to dst red0
+ vmul.u16 q13, q13, q3 // multiply dst red1 by src alpha
+ vadd.u16 q9, q9, q1 // add src green to dst green0
+ vmul.u16 q12, q12, q3 // multiply dst green1 by src alpha
+ vadd.u16 q10, q10, q2 // add src blue to dst blue0
+ vmul.u16 q11, q11, q3 // multiply dst blue1 by src alpha
+ vshr.u16 q8, q8, #8 // shift down red0
+ vadd.u16 q13, q13, q0 // add src red to dst red1
+ vshr.u16 q9, q9, #8 // shift down green0
+ vadd.u16 q12, q12, q1 // add src green to dst green1
+ vshr.u16 q10, q10, #8 // shift down blue0
+ vadd.u16 q11, q11, q2 // add src blue to dst blue1
+ vsli.u16 q10, q9, #5 // shift & insert green0 into blue0
+ vshr.u16 q13, q13, #8 // shift down red1
+ vsli.u16 q10, q8, #11 // shift & insert red0 into blue0
+ vshr.u16 q12, q12, #8 // shift down green1
+ vshr.u16 q11, q11, #8 // shift down blue1
+ subs r3, r3, #1 // decrement loop counter
+ vsli.u16 q11, q12, #5 // shift & insert green1 into blue1
+ vsli.u16 q11, q13, #11 // shift & insert red1 into blue1
+
+ vst1.16 {d20, d21, d22, d23}, [r0]! // write 16 pixels back to dst
+ bne 1b // if count != 0, loop
+
+2:
+ ands r3, r2, #15 // calc. single iterations
+ beq 4f // if r3 == 0, exit
+
+ ldr r4, [r1] // load source color
+ mov r5, r4, lsr #24 // shift down alpha
+ add r5, r5, r5, lsr #7 // add in top bit
+ rsb r5, r5, #256 // invert alpha
+ and r11, r4, #0xff // extract red
+ ubfx r12, r4, #8, #8 // extract green
+ ubfx r4, r4, #16, #8 // extract blue
+ mov r11, r11, lsl #5 // prescale red
+ mov r12, r12, lsl #6 // prescale green
+ mov r4, r4, lsl #5 // prescale blue
+
+3:
+ ldrh r8, [r0] // load dest pixel
+ subs r3, r3, #1 // decrement loop counter
+ mov r6, r8, lsr #11 // extract dest red
+ ubfx r7, r8, #5, #6 // extract dest green
+ and r8, r8, #0x1f // extract dest blue
+
+ smlabb r6, r6, r5, r11 // dest red * alpha + src red
+ smlabb r7, r7, r5, r12 // dest green * alpha + src green
+ smlabb r8, r8, r5, r4 // dest blue * alpha + src blue
+
+ mov r6, r6, lsr #8 // shift down red
+ mov r7, r7, lsr #8 // shift down green
+ mov r6, r6, lsl #11 // shift red into 565
+ orr r6, r7, lsl #5 // shift green into 565
+ orr r6, r8, lsr #8 // shift blue into 565
+
+ strh r6, [r0], #2 // store pixel to dest, update ptr
+ bne 3b // if count != 0, loop
+4:
+
+ pop {r4-r11, pc} // return
+
+
+
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index f700306..a2f43eb 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -80,6 +80,7 @@
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_col32cb16blend(context_t* c);
static void scanline_memcpy(context_t* c);
static void scanline_memset8(context_t* c);
static void scanline_memset16(context_t* c);
@@ -93,6 +94,8 @@
extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t);
extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct);
+extern "C" void scanline_col32cb16blend_neon(uint16_t *dst, uint32_t *col, size_t ct);
+extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t ct);
// ----------------------------------------------------------------------------
@@ -111,6 +114,9 @@
{ { { 0x03010104, 0x00000077, { 0x00000A01, 0x00000000 } },
{ 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
"565 fb, 8888 tx", scanline_t32cb16, init_y_noop },
+ { { { 0x03515104, 0x00000077, { 0x00000000, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0xFFFFFFFF } } },
+ "565 fb, 8888 fixed color", scanline_col32cb16blend, init_y_packed },
{ { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
{ 0x00000000, 0x00000007, { 0x00000000, 0x00000000 } } },
"(nop) alpha test", scanline_noop, init_y_noop },
@@ -943,6 +949,8 @@
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->packed8888 = ggl_pack_color(c, GGL_PIXEL_FORMAT_RGBA_8888,
+ 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
@@ -1253,6 +1261,45 @@
// ----------------------------------------------------------------------------
+void scanline_col32cb16blend(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));
+
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__))
+#if defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+ scanline_col32cb16blend_neon(dst, &(c->packed8888), ct);
+#else // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+ scanline_col32cb16blend_arm(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+#else
+ uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
+ int sA = (s>>24);
+ int f = 0x100 - (sA + (sA>>7));
+ while (ct--) {
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ int sR = (s >> ( 3))&0x1F;
+ int sG = (s >> ( 8+2))&0x3F;
+ int sB = (s >> (16+3))&0x1F;
+ sR += (f*dR)>>8;
+ sG += (f*dG)>>8;
+ sB += (f*dB)>>8;
+ *dst++ = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+#endif
+
+}
+
void scanline_t32cb16(context_t* c)
{
int32_t x = c->iterators.xl;
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index f00bfbf..bdf53e8 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -27,8 +27,8 @@
#include "cutils/log.h"
void fatal(const char *msg) {
- fprintf(stderr, msg);
- LOG(LOG_ERROR, "logwrapper", msg);
+ fprintf(stderr, "%s", msg);
+ LOG(LOG_ERROR, "logwrapper", "%s", msg);
exit(-1);
}
@@ -60,7 +60,7 @@
buffer[b] = '\0';
} else if (buffer[b] == '\n') {
buffer[b] = '\0';
- LOG(LOG_INFO, tag, &buffer[a]);
+ LOG(LOG_INFO, tag, "%s", &buffer[a]);
a = b + 1;
}
}
@@ -68,7 +68,7 @@
if (a == 0 && b == sizeof(buffer) - 1) {
// buffer is full, flush
buffer[b] = '\0';
- LOG(LOG_INFO, tag, &buffer[a]);
+ LOG(LOG_INFO, tag, "%s", &buffer[a]);
b = 0;
} else if (a != b) {
// Keep left-overs
@@ -84,7 +84,7 @@
// Flush remaining data
if (a != b) {
buffer[b] = '\0';
- LOG(LOG_INFO, tag, &buffer[a]);
+ LOG(LOG_INFO, tag, "%s", &buffer[a]);
}
status = 0xAAAA;
if (wait(&status) != -1) { // Wait for child
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
index 3642647..a94cb9c 100644
--- a/mkbootimg/mkbootimg.c
+++ b/mkbootimg/mkbootimg.c
@@ -64,6 +64,7 @@
" [ --cmdline <kernel-commandline> ]\n"
" [ --board <boardname> ]\n"
" [ --base <address> ]\n"
+ " [ --pagesize <pagesize> ]\n"
" -o|--output <filename>\n"
);
return 1;
@@ -71,7 +72,7 @@
-static unsigned char padding[2048] = { 0, };
+static unsigned char padding[4096] = { 0, };
int write_padding(int fd, unsigned pagesize, unsigned itemsize)
{
@@ -120,8 +121,6 @@
hdr.second_addr = 0x10F00000;
hdr.tags_addr = 0x10000100;
- hdr.page_size = pagesize;
-
while(argc > 0){
char *arg = argv[0];
char *val = argv[1];
@@ -148,10 +147,18 @@
hdr.tags_addr = base + 0x00000100;
} else if(!strcmp(arg, "--board")) {
board = val;
+ } else if(!strcmp(arg,"--pagesize")) {
+ pagesize = strtoul(val, 0, 10);
+ if ((pagesize != 2048) && (pagesize != 4096)) {
+ fprintf(stderr,"error: unsupported page size %d\n", pagesize);
+ return -1;
+ }
} else {
return usage();
}
}
+ hdr.page_size = pagesize;
+
if(bootimg == 0) {
fprintf(stderr,"error: no output filename specified\n");
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7ac991c..329be7f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -37,8 +37,15 @@
$(file) : $(LOCAL_PATH)/init.rc | $(ACP)
$(transform-prebuilt-to-target)
ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
endif
+file := $(TARGET_ROOT_OUT)/ueventd.rc
+$(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP)
+ $(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
+
# Just like /system/etc/init.goldfish.sh, the /init.godlfish.rc is here
# to allow -user builds to properly run the dex pre-optimization pass in
# the emulator.
@@ -46,6 +53,13 @@
$(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP)
$(transform-prebuilt-to-target)
ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
+
+file := $(TARGET_ROOT_OUT)/ueventd.goldfish.rc
+$(file) : $(LOCAL_PATH)/etc/ueventd.goldfish.rc | $(ACP)
+ $(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
# create some directories (some are mount points)
DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \
diff --git a/rootdir/etc/ueventd.goldfish.rc b/rootdir/etc/ueventd.goldfish.rc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rootdir/etc/ueventd.goldfish.rc
diff --git a/rootdir/init.lowmem.rc b/rootdir/init.lowmem.rc
new file mode 100644
index 0000000..7c08054
--- /dev/null
+++ b/rootdir/init.lowmem.rc
@@ -0,0 +1,19 @@
+# Adjustments to the out-of-memory killer, for devices that are
+# tight on memory. These should not be used if not needed, as they
+# can result in more paging.
+
+on early-boot
+
+ setprop ro.FOREGROUND_APP_MEM 1536
+ setprop ro.VISIBLE_APP_MEM 2048
+ setprop ro.PERCEPTIBLE_APP_MEM 2048
+ setprop ro.HEAVY_WEIGHT_APP_MEM 2048
+ setprop ro.SECONDARY_SERVER_MEM 4096
+ setprop ro.BACKUP_APP_MEM 4096
+ setprop ro.HOME_APP_MEM 4096
+ setprop ro.HIDDEN_APP_MEM 5120
+ setprop ro.EMPTY_APP_MEM 6144
+
+on boot
+
+ write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,3072,4096,5120,6144
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1e446f7..1e3e2d2 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1,3 +1,5 @@
+on early-init
+ start ueventd
on init
@@ -14,7 +16,8 @@
export ANDROID_DATA /data
export EXTERNAL_STORAGE /mnt/sdcard
export ASEC_MOUNTPOINT /mnt/asec
- export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar
+ export LOOP_MOUNTPOINT /mnt/obb
+ export BOOTCLASSPATH /system/framework/core.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/core-junit.jar
# Backward compatibility
symlink /system/etc /etc
@@ -51,6 +54,10 @@
mkdir /mnt/asec 0700 root system
mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000
+ # Filesystem image public mount points.
+ mkdir /mnt/obb 0700 root system
+ mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
+
mount rootfs rootfs / ro remount
write /proc/sys/kernel/panic_on_oops 1
@@ -80,13 +87,16 @@
# 5.0 %
write /dev/cpuctl/bg_non_interactive/cpu.shares 52
+on fs
# 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
+ mount yaffs2 mtd@cache /cache nosuid nodev
+
+on post-fs
+ # We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
@@ -110,7 +120,6 @@
write /proc/apanic_console 1
# Same reason as /data above
- mount yaffs2 mtd@cache /cache nosuid nodev
chown system cache /cache
chmod 0770 /cache
@@ -175,32 +184,35 @@
# 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.BACKUP_APP_ADJ 2
- setprop ro.HOME_APP_ADJ 4
+ setprop ro.PERCEPTIBLE_APP_ADJ 2
+ setprop ro.HEAVY_WEIGHT_APP_ADJ 3
+ setprop ro.SECONDARY_SERVER_ADJ 4
+ setprop ro.BACKUP_APP_ADJ 5
+ setprop ro.HOME_APP_ADJ 6
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.BACKUP_APP_MEM 4096
- setprop ro.HOME_APP_MEM 4096
- setprop ro.HIDDEN_APP_MEM 5120
- setprop ro.CONTENT_PROVIDER_MEM 5632
- setprop ro.EMPTY_APP_MEM 6144
+ setprop ro.FOREGROUND_APP_MEM 2048
+ setprop ro.VISIBLE_APP_MEM 3072
+ setprop ro.PERCEPTIBLE_APP_MEM 4096
+ setprop ro.HEAVY_WEIGHT_APP_MEM 4096
+ setprop ro.SECONDARY_SERVER_MEM 6144
+ setprop ro.BACKUP_APP_MEM 6144
+ setprop ro.HOME_APP_MEM 6144
+ setprop ro.HIDDEN_APP_MEM 7168
+ setprop ro.EMPTY_APP_MEM 8192
# Write value must be consistent with the above properties.
-# Note that the driver only supports 6 slots, so we have HOME_APP at the
-# same memory level as services.
- write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15
+# Note that the driver only supports 6 slots, so we have combined some of
+# the classes into the same memory level; the associated processes of higher
+# classes will still be killed first.
+ write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15
write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
- write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144
+ write /sys/module/lowmemorykiller/parameters/minfree 2048,3072,4096,6144,7168,8192
# Set init its forked children's oom_adj.
write /proc/1/oom_adj -16
@@ -263,6 +275,9 @@
service console /system/bin/sh
console
+service ueventd /sbin/ueventd
+ critical
+
# adbd is controlled by the persist.service.adb.enable system property
service adbd /sbin/adbd
disabled
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
new file mode 100644
index 0000000..7845eb9
--- /dev/null
+++ b/rootdir/ueventd.rc
@@ -0,0 +1,77 @@
+/dev/null 0666 root root
+/dev/zero 0666 root root
+/dev/full 0666 root root
+/dev/ptmx 0666 root root
+/dev/tty 0666 root root
+/dev/random 0666 root root
+/dev/urandom 0666 root root
+/dev/ashmem 0666 root root
+/dev/binder 0666 root root
+
+# logger should be world writable (for logging) but not readable
+/dev/log/* 0662 root log
+
+# the msm hw3d client device node is world writable/readable.
+/dev/msm_hw3dc 0666 root root
+
+# gpu driver for adreno200 is globally accessible
+/dev/kgsl 0666 root root
+
+# these should not be world writable
+/dev/diag 0660 radio radio
+/dev/diag_arm9 0660 radio radio
+/dev/android_adb 0660 adb adb
+/dev/android_adb_enable 0660 adb adb
+/dev/ttyMSM0 0600 bluetooth bluetooth
+/dev/ttyHS0 0600 bluetooth bluetooth
+/dev/uinput 0660 system bluetooth
+/dev/alarm 0664 system radio
+/dev/tty0 0660 root system
+/dev/graphics/* 0660 root graphics
+/dev/msm_hw3dm 0660 system graphics
+/dev/input/* 0660 root input
+/dev/eac 0660 root audio
+/dev/cam 0660 root camera
+/dev/pmem 0660 system graphics
+/dev/pmem_adsp* 0660 system audio
+/dev/pmem_camera* 0660 system camera
+/dev/oncrpc/* 0660 root system
+/dev/adsp/* 0660 system audio
+/dev/snd/* 0660 system audio
+/dev/mt9t013 0660 system system
+/dev/msm_camera/* 0660 system system
+/dev/akm8976_daemon 0640 compass system
+/dev/akm8976_aot 0640 compass system
+/dev/akm8973_daemon 0640 compass system
+/dev/akm8973_aot 0640 compass system
+/dev/bma150 0640 compass system
+/dev/cm3602 0640 compass system
+/dev/akm8976_pffd 0640 compass system
+/dev/lightsensor 0640 system system
+/dev/msm_pcm_out* 0660 system audio
+/dev/msm_pcm_in* 0660 system audio
+/dev/msm_pcm_ctl* 0660 system audio
+/dev/msm_snd* 0660 system audio
+/dev/msm_mp3* 0660 system audio
+/dev/audience_a1026* 0660 system audio
+/dev/tpa2018d1* 0660 system audio
+/dev/msm_audpre 0660 system audio
+/dev/msm_audio_ctl 0660 system audio
+/dev/htc-acoustic 0660 system audio
+/dev/vdec 0660 system audio
+/dev/q6venc 0660 system audio
+/dev/snd/dsp 0660 system audio
+/dev/snd/dsp1 0660 system audio
+/dev/snd/mixer 0660 system audio
+/dev/smd0 0640 radio radio
+/dev/qemu_trace 0666 system system
+/dev/qmi 0640 radio radio
+/dev/qmi0 0640 radio radio
+/dev/qmi1 0640 radio radio
+/dev/qmi2 0640 radio radio
+/dev/bus/usb/* 0660 root usb
+
+# CDMA radio interface MUX
+/dev/ts0710mux* 0640 radio radio
+/dev/ppp 0660 radio vpn
+/dev/tun 0640 vpn vpn
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
new file mode 100644
index 0000000..c430ac8
--- /dev/null
+++ b/sdcard/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= sdcard.c
+LOCAL_MODULE:= sdcard
+
+LOCAL_SHARED_LIBRARIES := libc
+
+include $(BUILD_EXECUTABLE)
diff --git a/sdcard/fuse.h b/sdcard/fuse.h
new file mode 100644
index 0000000..3138da9
--- /dev/null
+++ b/sdcard/fuse.h
@@ -0,0 +1,578 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+/*
+ * from the libfuse FAQ (and consistent with the Linux Kernel license):
+ *
+ * Under what conditions may I distribute a filesystem that uses the
+ * raw kernel interface of FUSE?
+ *
+ * There are no restrictions whatsoever for using the raw kernel interface.
+ *
+ */
+
+/*
+ * This file defines the kernel interface of FUSE
+ *
+ * Protocol changelog:
+ *
+ * 7.9:
+ * - new fuse_getattr_in input argument of GETATTR
+ * - add lk_flags in fuse_lk_in
+ * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
+ * - add blksize field to fuse_attr
+ * - add file flags field to fuse_read_in and fuse_write_in
+ *
+ * 7.10
+ * - add nonseekable open flag
+ *
+ * 7.11
+ * - add IOCTL message
+ * - add unsolicited notification support
+ * - add POLL message and NOTIFY_POLL notification
+ *
+ * 7.12
+ * - add umask flag to input argument of open, mknod and mkdir
+ * - add notification messages for invalidation of inodes and
+ * directory entries
+ *
+ * 7.13
+ * - make max number of background requests and congestion threshold
+ * tunables
+ */
+
+#ifndef _LINUX_FUSE_H
+#define _LINUX_FUSE_H
+
+#include <linux/types.h>
+
+/*
+ * Version negotiation:
+ *
+ * Both the kernel and userspace send the version they support in the
+ * INIT request and reply respectively.
+ *
+ * If the major versions match then both shall use the smallest
+ * of the two minor versions for communication.
+ *
+ * If the kernel supports a larger major version, then userspace shall
+ * reply with the major version it supports, ignore the rest of the
+ * INIT message and expect a new INIT message from the kernel with a
+ * matching major version.
+ *
+ * If the library supports a larger major version, then it shall fall
+ * back to the major protocol version sent by the kernel for
+ * communication and reply with that major version (and an arbitrary
+ * supported minor version).
+ */
+
+/** Version number of this interface */
+#define FUSE_KERNEL_VERSION 7
+
+/** Minor version number of this interface */
+#define FUSE_KERNEL_MINOR_VERSION 13
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/* Make sure all structures are padded to 64bit boundary, so 32bit
+ userspace works under 64bit kernels */
+
+struct fuse_attr {
+ __u64 ino;
+ __u64 size;
+ __u64 blocks;
+ __u64 atime;
+ __u64 mtime;
+ __u64 ctime;
+ __u32 atimensec;
+ __u32 mtimensec;
+ __u32 ctimensec;
+ __u32 mode;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u32 rdev;
+ __u32 blksize;
+ __u32 padding;
+};
+
+struct fuse_kstatfs {
+ __u64 blocks;
+ __u64 bfree;
+ __u64 bavail;
+ __u64 files;
+ __u64 ffree;
+ __u32 bsize;
+ __u32 namelen;
+ __u32 frsize;
+ __u32 padding;
+ __u32 spare[6];
+};
+
+struct fuse_file_lock {
+ __u64 start;
+ __u64 end;
+ __u32 type;
+ __u32 pid; /* tgid */
+};
+
+/**
+ * Bitmasks for fuse_setattr_in.valid
+ */
+#define FATTR_MODE (1 << 0)
+#define FATTR_UID (1 << 1)
+#define FATTR_GID (1 << 2)
+#define FATTR_SIZE (1 << 3)
+#define FATTR_ATIME (1 << 4)
+#define FATTR_MTIME (1 << 5)
+#define FATTR_FH (1 << 6)
+#define FATTR_ATIME_NOW (1 << 7)
+#define FATTR_MTIME_NOW (1 << 8)
+#define FATTR_LOCKOWNER (1 << 9)
+
+/**
+ * Flags returned by the OPEN request
+ *
+ * FOPEN_DIRECT_IO: bypass page cache for this open file
+ * FOPEN_KEEP_CACHE: don't invalidate the data cache on open
+ * FOPEN_NONSEEKABLE: the file is not seekable
+ */
+#define FOPEN_DIRECT_IO (1 << 0)
+#define FOPEN_KEEP_CACHE (1 << 1)
+#define FOPEN_NONSEEKABLE (1 << 2)
+
+/**
+ * INIT request/reply flags
+ *
+ * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_DONT_MASK: don't apply umask to file mode on create operations
+ */
+#define FUSE_ASYNC_READ (1 << 0)
+#define FUSE_POSIX_LOCKS (1 << 1)
+#define FUSE_FILE_OPS (1 << 2)
+#define FUSE_ATOMIC_O_TRUNC (1 << 3)
+#define FUSE_EXPORT_SUPPORT (1 << 4)
+#define FUSE_BIG_WRITES (1 << 5)
+#define FUSE_DONT_MASK (1 << 6)
+
+/**
+ * CUSE INIT request/reply flags
+ *
+ * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl
+ */
+#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
+
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH (1 << 0)
+
+/**
+ * Getattr flags
+ */
+#define FUSE_GETATTR_FH (1 << 0)
+
+/**
+ * Lock flags
+ */
+#define FUSE_LK_FLOCK (1 << 0)
+
+/**
+ * WRITE flags
+ *
+ * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
+ * FUSE_WRITE_LOCKOWNER: lock_owner field is valid
+ */
+#define FUSE_WRITE_CACHE (1 << 0)
+#define FUSE_WRITE_LOCKOWNER (1 << 1)
+
+/**
+ * Read flags
+ */
+#define FUSE_READ_LOCKOWNER (1 << 1)
+
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT (1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
+#define FUSE_IOCTL_RETRY (1 << 2)
+
+#define FUSE_IOCTL_MAX_IOV 256
+
+/**
+ * Poll flags
+ *
+ * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify
+ */
+#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
+
+enum fuse_opcode {
+ FUSE_LOOKUP = 1,
+ FUSE_FORGET = 2, /* no reply */
+ FUSE_GETATTR = 3,
+ FUSE_SETATTR = 4,
+ FUSE_READLINK = 5,
+ FUSE_SYMLINK = 6,
+ FUSE_MKNOD = 8,
+ FUSE_MKDIR = 9,
+ FUSE_UNLINK = 10,
+ FUSE_RMDIR = 11,
+ FUSE_RENAME = 12,
+ FUSE_LINK = 13,
+ FUSE_OPEN = 14,
+ FUSE_READ = 15,
+ FUSE_WRITE = 16,
+ FUSE_STATFS = 17,
+ FUSE_RELEASE = 18,
+ FUSE_FSYNC = 20,
+ FUSE_SETXATTR = 21,
+ FUSE_GETXATTR = 22,
+ FUSE_LISTXATTR = 23,
+ FUSE_REMOVEXATTR = 24,
+ FUSE_FLUSH = 25,
+ FUSE_INIT = 26,
+ FUSE_OPENDIR = 27,
+ FUSE_READDIR = 28,
+ FUSE_RELEASEDIR = 29,
+ FUSE_FSYNCDIR = 30,
+ FUSE_GETLK = 31,
+ FUSE_SETLK = 32,
+ FUSE_SETLKW = 33,
+ FUSE_ACCESS = 34,
+ FUSE_CREATE = 35,
+ FUSE_INTERRUPT = 36,
+ FUSE_BMAP = 37,
+ FUSE_DESTROY = 38,
+ FUSE_IOCTL = 39,
+ FUSE_POLL = 40,
+
+ /* CUSE specific operations */
+ CUSE_INIT = 4096,
+};
+
+enum fuse_notify_code {
+ FUSE_NOTIFY_POLL = 1,
+ FUSE_NOTIFY_INVAL_INODE = 2,
+ FUSE_NOTIFY_INVAL_ENTRY = 3,
+ FUSE_NOTIFY_CODE_MAX,
+};
+
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
+#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
+
+struct fuse_entry_out {
+ __u64 nodeid; /* Inode ID */
+ __u64 generation; /* Inode generation: nodeid:gen must
+ be unique for the fs's lifetime */
+ __u64 entry_valid; /* Cache timeout for the name */
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 entry_valid_nsec;
+ __u32 attr_valid_nsec;
+ struct fuse_attr attr;
+};
+
+struct fuse_forget_in {
+ __u64 nlookup;
+};
+
+struct fuse_getattr_in {
+ __u32 getattr_flags;
+ __u32 dummy;
+ __u64 fh;
+};
+
+#define FUSE_COMPAT_ATTR_OUT_SIZE 96
+
+struct fuse_attr_out {
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 attr_valid_nsec;
+ __u32 dummy;
+ struct fuse_attr attr;
+};
+
+#define FUSE_COMPAT_MKNOD_IN_SIZE 8
+
+struct fuse_mknod_in {
+ __u32 mode;
+ __u32 rdev;
+ __u32 umask;
+ __u32 padding;
+};
+
+struct fuse_mkdir_in {
+ __u32 mode;
+ __u32 umask;
+};
+
+struct fuse_rename_in {
+ __u64 newdir;
+};
+
+struct fuse_link_in {
+ __u64 oldnodeid;
+};
+
+struct fuse_setattr_in {
+ __u32 valid;
+ __u32 padding;
+ __u64 fh;
+ __u64 size;
+ __u64 lock_owner;
+ __u64 atime;
+ __u64 mtime;
+ __u64 unused2;
+ __u32 atimensec;
+ __u32 mtimensec;
+ __u32 unused3;
+ __u32 mode;
+ __u32 unused4;
+ __u32 uid;
+ __u32 gid;
+ __u32 unused5;
+};
+
+struct fuse_open_in {
+ __u32 flags;
+ __u32 unused;
+};
+
+struct fuse_create_in {
+ __u32 flags;
+ __u32 mode;
+ __u32 umask;
+ __u32 padding;
+};
+
+struct fuse_open_out {
+ __u64 fh;
+ __u32 open_flags;
+ __u32 padding;
+};
+
+struct fuse_release_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 release_flags;
+ __u64 lock_owner;
+};
+
+struct fuse_flush_in {
+ __u64 fh;
+ __u32 unused;
+ __u32 padding;
+ __u64 lock_owner;
+};
+
+struct fuse_read_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 read_flags;
+ __u64 lock_owner;
+ __u32 flags;
+ __u32 padding;
+};
+
+#define FUSE_COMPAT_WRITE_IN_SIZE 24
+
+struct fuse_write_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 write_flags;
+ __u64 lock_owner;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct fuse_write_out {
+ __u32 size;
+ __u32 padding;
+};
+
+#define FUSE_COMPAT_STATFS_SIZE 48
+
+struct fuse_statfs_out {
+ struct fuse_kstatfs st;
+};
+
+struct fuse_fsync_in {
+ __u64 fh;
+ __u32 fsync_flags;
+ __u32 padding;
+};
+
+struct fuse_setxattr_in {
+ __u32 size;
+ __u32 flags;
+};
+
+struct fuse_getxattr_in {
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_getxattr_out {
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_lk_in {
+ __u64 fh;
+ __u64 owner;
+ struct fuse_file_lock lk;
+ __u32 lk_flags;
+ __u32 padding;
+};
+
+struct fuse_lk_out {
+ struct fuse_file_lock lk;
+};
+
+struct fuse_access_in {
+ __u32 mask;
+ __u32 padding;
+};
+
+struct fuse_init_in {
+ __u32 major;
+ __u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
+};
+
+struct fuse_init_out {
+ __u32 major;
+ __u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
+ __u16 max_background;
+ __u16 congestion_threshold;
+ __u32 max_write;
+};
+
+#define CUSE_INIT_INFO_MAX 4096
+
+struct cuse_init_in {
+ __u32 major;
+ __u32 minor;
+ __u32 unused;
+ __u32 flags;
+};
+
+struct cuse_init_out {
+ __u32 major;
+ __u32 minor;
+ __u32 unused;
+ __u32 flags;
+ __u32 max_read;
+ __u32 max_write;
+ __u32 dev_major; /* chardev major */
+ __u32 dev_minor; /* chardev minor */
+ __u32 spare[10];
+};
+
+struct fuse_interrupt_in {
+ __u64 unique;
+};
+
+struct fuse_bmap_in {
+ __u64 block;
+ __u32 blocksize;
+ __u32 padding;
+};
+
+struct fuse_bmap_out {
+ __u64 block;
+};
+
+struct fuse_ioctl_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 cmd;
+ __u64 arg;
+ __u32 in_size;
+ __u32 out_size;
+};
+
+struct fuse_ioctl_out {
+ __s32 result;
+ __u32 flags;
+ __u32 in_iovs;
+ __u32 out_iovs;
+};
+
+struct fuse_poll_in {
+ __u64 fh;
+ __u64 kh;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct fuse_poll_out {
+ __u32 revents;
+ __u32 padding;
+};
+
+struct fuse_notify_poll_wakeup_out {
+ __u64 kh;
+};
+
+struct fuse_in_header {
+ __u32 len;
+ __u32 opcode;
+ __u64 unique;
+ __u64 nodeid;
+ __u32 uid;
+ __u32 gid;
+ __u32 pid;
+ __u32 padding;
+};
+
+struct fuse_out_header {
+ __u32 len;
+ __s32 error;
+ __u64 unique;
+};
+
+struct fuse_dirent {
+ __u64 ino;
+ __u64 off;
+ __u32 namelen;
+ __u32 type;
+ char name[0];
+};
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
+#define FUSE_DIRENT_SIZE(d) \
+ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+
+struct fuse_notify_inval_inode_out {
+ __u64 ino;
+ __s64 off;
+ __s64 len;
+};
+
+struct fuse_notify_inval_entry_out {
+ __u64 parent;
+ __u32 namelen;
+ __u32 padding;
+};
+
+#endif /* _LINUX_FUSE_H */
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
new file mode 100644
index 0000000..82e6354
--- /dev/null
+++ b/sdcard/sdcard.c
@@ -0,0 +1,795 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/uio.h>
+#include <dirent.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "fuse.h"
+
+/* README
+ *
+ * What is this?
+ *
+ * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
+ * directory permissions (all files are given fixed owner, group, and
+ * permissions at creation, owner, group, and permissions are not
+ * changeable, symlinks and hardlinks are not createable, etc.
+ *
+ * usage: sdcard <path> <uid> <gid>
+ *
+ * It must be run as root, but will change to uid/gid as soon as it
+ * mounts a filesystem on /mnt/sdcard. It will refuse to run if uid or
+ * gid are zero.
+ *
+ *
+ * Things I believe to be true:
+ *
+ * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
+ * CREAT) must bump that node's refcount
+ * - don't forget that FORGET can forget multiple references (req->nlookup)
+ * - if an op that returns a fuse_entry fails writing the reply to the
+ * kernel, you must rollback the refcount to reflect the reference the
+ * kernel did not actually acquire
+ *
+ *
+ * Bugs:
+ *
+ * - need to move/rename node on RENAME
+ */
+
+#define FUSE_TRACE 0
+
+#if FUSE_TRACE
+#define TRACE(x...) fprintf(stderr,x)
+#else
+#define TRACE(x...) do {} while (0)
+#endif
+
+#define ERROR(x...) fprintf(stderr,x)
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+
+#define MOUNT_POINT "/mnt/sdcard"
+
+struct handle {
+ struct node *node;
+ int fd;
+};
+
+struct dirhandle {
+ struct node *node;
+ DIR *d;
+};
+
+struct node {
+ __u64 nid;
+ __u64 gen;
+
+ struct node *next;
+ struct node *child;
+ struct node *all;
+ struct node *parent;
+
+ __u32 refcount;
+ __u32 namelen;
+
+ char name[1];
+};
+
+struct fuse {
+ __u64 next_generation;
+ __u64 next_node_id;
+
+ int fd;
+
+ struct node *all;
+
+ struct node root;
+ char rootpath[1024];
+};
+
+#define PATH_BUFFER_SIZE 1024
+
+char *node_get_path(struct node *node, char *buf, const char *name)
+{
+ char *out = buf + PATH_BUFFER_SIZE - 1;
+ int len;
+ out[0] = 0;
+
+ if (name) {
+ len = strlen(name);
+ goto start;
+ }
+
+ while (node) {
+ name = node->name;
+ len = node->namelen;
+ node = node->parent;
+ start:
+ if ((len + 1) > (out - buf))
+ return 0;
+ out -= len;
+ memcpy(out, name, len);
+ out --;
+ out[0] = '/';
+ }
+
+ return out;
+}
+
+void attr_from_stat(struct fuse_attr *attr, struct stat *s)
+{
+ attr->ino = s->st_ino;
+ attr->size = s->st_size;
+ attr->blocks = s->st_blocks;
+ attr->atime = s->st_atime;
+ attr->mtime = s->st_mtime;
+ attr->ctime = s->st_ctime;
+ attr->atimensec = s->st_atime_nsec;
+ attr->mtimensec = s->st_mtime_nsec;
+ attr->ctimensec = s->st_ctime_nsec;
+ attr->mode = s->st_mode;
+ attr->nlink = s->st_nlink;
+
+ /* force permissions to something reasonable:
+ * world readable
+ * writable by the sdcard group
+ */
+ if (attr->mode & 0100) {
+ attr->mode = (attr->mode & (~0777)) | 0775;
+ } else {
+ attr->mode = (attr->mode & (~0777)) | 0664;
+ }
+
+ /* all files owned by root.sdcard */
+ attr->uid = 0;
+ attr->gid = AID_SDCARD_RW;
+}
+
+int node_get_attr(struct node *node, struct fuse_attr *attr)
+{
+ int res;
+ struct stat s;
+ char *path, buffer[PATH_BUFFER_SIZE];
+
+ path = node_get_path(node, buffer, 0);
+ res = lstat(path, &s);
+ if (res < 0) {
+ ERROR("lstat('%s') errno %d\n", path, errno);
+ return -1;
+ }
+
+ attr_from_stat(attr, &s);
+ attr->ino = node->nid;
+
+ return 0;
+}
+
+struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
+{
+ struct node *node;
+ int namelen = strlen(name);
+
+ node = calloc(1, sizeof(struct node) + namelen);
+ if (node == 0) {
+ return 0;
+ }
+
+ node->nid = nid;
+ node->gen = gen;
+ node->parent = parent;
+ node->next = parent->child;
+ parent->child = node;
+ memcpy(node->name, name, namelen + 1);
+ node->namelen = namelen;
+ parent->refcount++;
+
+ return node;
+}
+
+void fuse_init(struct fuse *fuse, int fd, const char *path)
+{
+ fuse->fd = fd;
+ fuse->next_node_id = 2;
+ fuse->next_generation = 0;
+
+ fuse->all = &fuse->root;
+
+ fuse->root.nid = FUSE_ROOT_ID; /* 1 */
+ fuse->root.next = 0;
+ fuse->root.child = 0;
+ fuse->root.parent = 0;
+
+ fuse->root.all = 0;
+ fuse->root.refcount = 2;
+
+ strcpy(fuse->root.name, path);
+ fuse->root.namelen = strlen(fuse->root.name);
+}
+
+static inline void *id_to_ptr(__u64 nid)
+{
+ return (void *) nid;
+}
+
+static inline __u64 ptr_to_id(void *ptr)
+{
+ return (__u64) ptr;
+}
+
+
+struct node *lookup_by_inode(struct fuse *fuse, __u64 nid)
+{
+ if (nid == FUSE_ROOT_ID) {
+ return &fuse->root;
+ } else {
+ return id_to_ptr(nid);
+ }
+}
+
+struct node *lookup_child_by_name(struct node *node, const char *name)
+{
+ for (node = node->child; node; node = node->next) {
+ if (!strcmp(name, node->name)) {
+ return node;
+ }
+ }
+ return 0;
+}
+
+struct node *lookup_child_by_inode(struct node *node, __u64 nid)
+{
+ for (node = node->child; node; node = node->next) {
+ if (node->nid == nid) {
+ return node;
+ }
+ }
+ return 0;
+}
+
+struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
+ struct fuse_attr *attr)
+{
+ int res;
+ struct stat s;
+ char *path, buffer[PATH_BUFFER_SIZE];
+ struct node *node;
+
+ path = node_get_path(parent, buffer, name);
+ /* XXX error? */
+
+ res = lstat(path, &s);
+ if (res < 0)
+ return 0;
+
+ node = lookup_child_by_name(parent, name);
+ if (!node) {
+ node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++);
+ if (!node)
+ return 0;
+ node->nid = ptr_to_id(node);
+ node->all = fuse->all;
+ fuse->all = node;
+ }
+
+ attr_from_stat(attr, &s);
+ attr->ino = node->nid;
+
+ return node;
+}
+
+void node_release(struct node *node)
+{
+ TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
+ node->refcount--;
+ if (node->refcount == 0) {
+ if (node->parent->child == node) {
+ node->parent->child = node->parent->child->next;
+ } else {
+ struct node *node2;
+
+ node2 = node->parent->child;
+ while (node2->next != node)
+ node2 = node2->next;
+ node2->next = node->next;
+ }
+
+ TRACE("DESTROY %p (%s)\n", node, node->name);
+
+ node_release(node->parent);
+
+ node->parent = 0;
+ node->next = 0;
+
+ /* TODO: remove debugging - poison memory */
+ memset(node, 0xef, sizeof(*node) + strlen(node->name));
+
+ free(node);
+ }
+}
+
+void fuse_status(struct fuse *fuse, __u64 unique, int err)
+{
+ struct fuse_out_header hdr;
+ hdr.len = sizeof(hdr);
+ hdr.error = err;
+ hdr.unique = unique;
+ if (err) {
+// ERROR("*** %d ***\n", err);
+ }
+ write(fuse->fd, &hdr, sizeof(hdr));
+}
+
+void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
+{
+ struct fuse_out_header hdr;
+ struct iovec vec[2];
+ int res;
+
+ hdr.len = len + sizeof(hdr);
+ hdr.error = 0;
+ hdr.unique = unique;
+
+ vec[0].iov_base = &hdr;
+ vec[0].iov_len = sizeof(hdr);
+ vec[1].iov_base = data;
+ vec[1].iov_len = len;
+
+ res = writev(fuse->fd, vec, 2);
+ if (res < 0) {
+ ERROR("*** REPLY FAILED *** %d\n", errno);
+ }
+}
+
+void lookup_entry(struct fuse *fuse, struct node *node,
+ const char *name, __u64 unique)
+{
+ struct fuse_entry_out out;
+
+ memset(&out, 0, sizeof(out));
+
+ node = node_lookup(fuse, node, name, &out.attr);
+ if (!node) {
+ fuse_status(fuse, unique, -ENOENT);
+ return;
+ }
+
+ node->refcount++;
+// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
+ out.nodeid = node->nid;
+ out.generation = node->gen;
+ out.entry_valid = 10;
+ out.attr_valid = 10;
+
+ fuse_reply(fuse, unique, &out, sizeof(out));
+}
+
+void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
+{
+ struct node *node;
+
+ if ((len < sizeof(*hdr)) || (hdr->len != len)) {
+ ERROR("malformed header\n");
+ return;
+ }
+
+ len -= hdr->len;
+
+ if (hdr->nodeid) {
+ node = lookup_by_inode(fuse, hdr->nodeid);
+ if (!node) {
+ fuse_status(fuse, hdr->unique, -ENOENT);
+ return;
+ }
+ } else {
+ node = 0;
+ }
+
+ switch (hdr->opcode) {
+ case FUSE_LOOKUP: { /* bytez[] -> entry_out */
+ TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data);
+ lookup_entry(fuse, node, (char*) data, hdr->unique);
+ return;
+ }
+ case FUSE_FORGET: {
+ struct fuse_forget_in *req = data;
+ TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup);
+ /* no reply */
+ while (req->nlookup--)
+ node_release(node);
+ return;
+ }
+ case FUSE_GETATTR: { /* getattr_in -> attr_out */
+ struct fuse_getattr_in *req = data;
+ struct fuse_attr_out out;
+
+ TRACE("GETATTR flags=%x fh=%llx\n",req->getattr_flags, req->fh);
+
+ memset(&out, 0, sizeof(out));
+ node_get_attr(node, &out.attr);
+ out.attr_valid = 10;
+
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return;
+ }
+ case FUSE_SETATTR: { /* setattr_in -> attr_out */
+ struct fuse_setattr_in *req = data;
+ struct fuse_attr_out out;
+ TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
+ req->fh, hdr->nodeid, req->valid);
+
+ /* XXX */
+
+ memset(&out, 0, sizeof(out));
+ node_get_attr(node, &out.attr);
+ out.attr_valid = 10;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return;
+ }
+// case FUSE_READLINK:
+// case FUSE_SYMLINK:
+ case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
+ struct fuse_mknod_in *req = data;
+ char *path, buffer[PATH_BUFFER_SIZE];
+ char *name = ((char*) data) + sizeof(*req);
+ int res;
+ TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
+ path = node_get_path(node, buffer, name);
+
+ req->mode = (req->mode & (~0777)) | 0664;
+ res = mknod(path, req->mode, req->rdev); /* XXX perm?*/
+ if (res < 0) {
+ fuse_status(fuse, hdr->unique, -errno);
+ } else {
+ lookup_entry(fuse, node, name, hdr->unique);
+ }
+ return;
+ }
+ case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
+ struct fuse_mkdir_in *req = data;
+ struct fuse_entry_out out;
+ char *path, buffer[PATH_BUFFER_SIZE];
+ char *name = ((char*) data) + sizeof(*req);
+ int res;
+ TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
+ path = node_get_path(node, buffer, name);
+
+ req->mode = (req->mode & (~0777)) | 0775;
+ res = mkdir(path, req->mode);
+ if (res < 0) {
+ fuse_status(fuse, hdr->unique, -errno);
+ } else {
+ lookup_entry(fuse, node, name, hdr->unique);
+ }
+ return;
+ }
+ case FUSE_UNLINK: { /* bytez[] -> */
+ char *path, buffer[PATH_BUFFER_SIZE];
+ int res;
+ TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid);
+ path = node_get_path(node, buffer, (char*) data);
+ res = unlink(path);
+ fuse_status(fuse, hdr->unique, res ? -errno : 0);
+ return;
+ }
+ case FUSE_RMDIR: { /* bytez[] -> */
+ char *path, buffer[PATH_BUFFER_SIZE];
+ int res;
+ TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid);
+ path = node_get_path(node, buffer, (char*) data);
+ res = rmdir(path);
+ fuse_status(fuse, hdr->unique, res ? -errno : 0);
+ return;
+ }
+ case FUSE_RENAME: { /* rename_in, oldname, newname -> */
+ struct fuse_rename_in *req = data;
+ char *oldname = ((char*) data) + sizeof(*req);
+ char *newname = oldname + strlen(oldname) + 1;
+ char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
+ char *newpath, newbuffer[PATH_BUFFER_SIZE];
+ struct node *newnode;
+ int res;
+
+ newnode = lookup_by_inode(fuse, req->newdir);
+ if (!newnode) {
+ fuse_status(fuse, hdr->unique, -ENOENT);
+ return;
+ }
+
+ oldpath = node_get_path(node, oldbuffer, oldname);
+ newpath = node_get_path(newnode, newbuffer, newname);
+
+ res = rename(oldpath, newpath);
+ fuse_status(fuse, hdr->unique, res ? -errno : 0);
+ return;
+ }
+// case FUSE_LINK:
+ case FUSE_OPEN: { /* open_in -> open_out */
+ struct fuse_open_in *req = data;
+ struct fuse_open_out out;
+ char *path, buffer[PATH_BUFFER_SIZE];
+ struct handle *h;
+
+ h = malloc(sizeof(*h));
+ if (!h) {
+ fuse_status(fuse, hdr->unique, -ENOMEM);
+ return;
+ }
+
+ path = node_get_path(node, buffer, 0);
+ TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
+ h->fd = open(path, req->flags);
+ if (h->fd < 0) {
+ ERROR("ERROR\n");
+ fuse_status(fuse, hdr->unique, errno);
+ free(h);
+ return;
+ }
+ out.fh = ptr_to_id(h);
+ out.open_flags = 0;
+ out.padding = 0;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return;
+ }
+ case FUSE_READ: { /* read_in -> byte[] */
+ char buffer[128 * 1024];
+ struct fuse_read_in *req = data;
+ struct handle *h = id_to_ptr(req->fh);
+ int res;
+ TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
+ if (req->size > sizeof(buffer)) {
+ fuse_status(fuse, hdr->unique, -EINVAL);
+ return;
+ }
+ res = pread(h->fd, buffer, req->size, req->offset);
+ if (res < 0) {
+ fuse_status(fuse, hdr->unique, errno);
+ return;
+ }
+ fuse_reply(fuse, hdr->unique, buffer, res);
+ return;
+ }
+ case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
+ struct fuse_write_in *req = data;
+ struct fuse_write_out out;
+ struct handle *h = id_to_ptr(req->fh);
+ int res;
+ TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
+ res = pwrite(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset);
+ if (res < 0) {
+ fuse_status(fuse, hdr->unique, errno);
+ return;
+ }
+ out.size = res;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ goto oops;
+ }
+ case FUSE_STATFS: { /* getattr_in -> attr_out */
+ struct statfs stat;
+ struct fuse_statfs_out out;
+ int res;
+
+ TRACE("STATFS\n");
+
+ if (statfs(fuse->root.name, &stat)) {
+ fuse_status(fuse, hdr->unique, -errno);
+ return;
+ }
+
+ memset(&out, 0, sizeof(out));
+ out.st.blocks = stat.f_blocks;
+ out.st.bfree = stat.f_bfree;
+ out.st.bavail = stat.f_bavail;
+ out.st.files = stat.f_files;
+ out.st.ffree = stat.f_ffree;
+ out.st.bsize = stat.f_bsize;
+ out.st.namelen = stat.f_namelen;
+ out.st.frsize = stat.f_frsize;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return;
+ }
+ case FUSE_RELEASE: { /* release_in -> */
+ struct fuse_release_in *req = data;
+ struct handle *h = id_to_ptr(req->fh);
+ TRACE("RELEASE %p(%d)\n", h, h->fd);
+ close(h->fd);
+ free(h);
+ fuse_status(fuse, hdr->unique, 0);
+ return;
+ }
+// case FUSE_FSYNC:
+// case FUSE_SETXATTR:
+// case FUSE_GETXATTR:
+// case FUSE_LISTXATTR:
+// case FUSE_REMOVEXATTR:
+ case FUSE_FLUSH:
+ fuse_status(fuse, hdr->unique, 0);
+ return;
+ case FUSE_OPENDIR: { /* open_in -> open_out */
+ struct fuse_open_in *req = data;
+ struct fuse_open_out out;
+ char *path, buffer[PATH_BUFFER_SIZE];
+ struct dirhandle *h;
+
+ h = malloc(sizeof(*h));
+ if (!h) {
+ fuse_status(fuse, hdr->unique, -ENOMEM);
+ return;
+ }
+
+ path = node_get_path(node, buffer, 0);
+ TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
+ h->d = opendir(path);
+ if (h->d == 0) {
+ ERROR("ERROR\n");
+ fuse_status(fuse, hdr->unique, -errno);
+ free(h);
+ return;
+ }
+ out.fh = ptr_to_id(h);
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return;
+ }
+ case FUSE_READDIR: {
+ struct fuse_read_in *req = data;
+ char buffer[8192];
+ struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
+ struct dirent *de;
+ struct dirhandle *h = id_to_ptr(req->fh);
+ TRACE("READDIR %p\n", h);
+ de = readdir(h->d);
+ if (!de) {
+ fuse_status(fuse, hdr->unique, 0);
+ return;
+ }
+ fde->ino = FUSE_UNKNOWN_INO;
+ fde->off = 0;
+ fde->type = de->d_type;
+ fde->namelen = strlen(de->d_name);
+ memcpy(fde->name, de->d_name, fde->namelen + 1);
+ fuse_reply(fuse, hdr->unique, fde,
+ FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
+ return;
+ }
+ case FUSE_RELEASEDIR: { /* release_in -> */
+ struct fuse_release_in *req = data;
+ struct dirhandle *h = id_to_ptr(req->fh);
+ TRACE("RELEASEDIR %p\n",h);
+ closedir(h->d);
+ free(h);
+ fuse_status(fuse, hdr->unique, 0);
+ return;
+ }
+// case FUSE_FSYNCDIR:
+ case FUSE_INIT: { /* init_in -> init_out */
+ struct fuse_init_in *req = data;
+ struct fuse_init_out out;
+
+ TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
+ req->major, req->minor, req->max_readahead, req->flags);
+
+ out.major = FUSE_KERNEL_VERSION;
+ out.minor = FUSE_KERNEL_MINOR_VERSION;
+ out.max_readahead = req->max_readahead;
+ out.flags = 0;
+ out.max_background = 32;
+ out.congestion_threshold = 32;
+ out.max_write = 256 * 1024;
+
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return;
+ }
+ default: {
+ struct fuse_out_header h;
+ ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n",
+ hdr->opcode, hdr->unique, hdr->nodeid);
+
+ oops:
+ h.len = sizeof(h);
+ h.error = -ENOSYS;
+ h.unique = hdr->unique;
+ write(fuse->fd, &h, sizeof(h));
+ break;
+ }
+ }
+}
+
+void handle_fuse_requests(struct fuse *fuse)
+{
+ unsigned char req[256 * 1024 + 128];
+ int len;
+
+ for (;;) {
+ len = read(fuse->fd, req, 8192);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ ERROR("handle_fuse_requests: errno=%d\n", errno);
+ return;
+ }
+ handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct fuse fuse;
+ char opts[256];
+ int fd;
+ int res;
+ unsigned uid;
+ unsigned gid;
+ const char *path;
+
+ if (argc != 4) {
+ ERROR("usage: sdcard <path> <uid> <gid>\n");
+ return -1;
+ }
+
+ uid = strtoul(argv[2], 0, 10);
+ gid = strtoul(argv[3], 0, 10);
+ if (!uid || !gid) {
+ ERROR("uid and gid must be nonzero\n");
+ return -1;
+ }
+
+ path = argv[1];
+
+ /* cleanup from previous instance, if necessary */
+ umount2(MOUNT_POINT, 2);
+
+ fd = open("/dev/fuse", O_RDWR);
+ if (fd < 0){
+ ERROR("cannot open fuse device (%d)\n", errno);
+ return -1;
+ }
+
+ sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other,"
+ "user_id=%d,group_id=%d", fd, uid, gid);
+
+ res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts);
+ if (res < 0) {
+ ERROR("cannot mount fuse filesystem (%d)\n", errno);
+ return -1;
+ }
+
+ if (setgid(gid) < 0) {
+ ERROR("cannot setgid!\n");
+ return -1;
+ }
+ if (setuid(uid) < 0) {
+ ERROR("cannot setuid!\n");
+ return -1;
+ }
+
+ fuse_init(&fuse, fd, path);
+
+ umask(0);
+ handle_fuse_requests(&fuse);
+
+ return 0;
+}
diff --git a/sh/Android.mk b/sh/Android.mk
index 09bb6ac..b5e5c38 100644
--- a/sh/Android.mk
+++ b/sh/Android.mk
@@ -31,7 +31,11 @@
LOCAL_MODULE:= sh
-LOCAL_CFLAGS += -DSHELL
+LOCAL_CFLAGS += -DSHELL -DWITH_LINENOISE
+
+LOCAL_STATIC_LIBRARIES := liblinenoise
+
+LOCAL_C_INCLUDES += system/core/liblinenoise
make_ash_files: PRIVATE_SRC_FILES := $(SRC_FILES)
make_ash_files: PRIVATE_CFLAGS := $(LOCAL_CFLAGS)
diff --git a/sh/ThirdPartyProject.prop b/sh/ThirdPartyProject.prop
new file mode 100644
index 0000000..eb9167e
--- /dev/null
+++ b/sh/ThirdPartyProject.prop
@@ -0,0 +1,9 @@
+# Copyright 2010 Google Inc. All Rights Reserved.
+#Fri Jul 16 10:03:08 PDT 2010
+currentVersion=Unknown
+version=1.17
+isNative=true
+name=ash
+keywords=ash
+onDevice=true
+homepage=http\://www.in-ulm.de/~mascheck/various/ash/
diff --git a/sh/input.c b/sh/input.c
index a81fd7b..056ee8b 100644
--- a/sh/input.c
+++ b/sh/input.c
@@ -64,6 +64,10 @@
#include "parser.h"
#include "myhistedit.h"
+#ifdef WITH_LINENOISE
+#include "linenoise.h"
+#endif
+
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
MKINIT
@@ -171,6 +175,9 @@
return pgetc_macro();
}
+int in_interactive_mode() {
+ return parsefile != NULL && parsefile->fd == 0;
+}
static int
preadfd(void)
@@ -203,6 +210,45 @@
} else
#endif
+#ifdef WITH_LINENOISE
+ if (parsefile->fd == 0) {
+ static char *rl_start;
+ static const char *rl_cp;
+ static int el_len;
+
+ if (rl_cp == NULL) {
+ rl_cp = rl_start = linenoise(getprompt(""));
+ if (rl_cp != NULL) {
+ el_len = strlen(rl_start);
+ if (el_len != 0) {
+ /* Add non-blank lines to history. */
+ linenoiseHistoryAdd(rl_start);
+ }
+ out2str("\n");
+ /* Client expects a newline at end of input, doesn't expect null */
+ rl_start[el_len++] = '\n';
+ }
+ }
+ 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;
+ if (rl_start != NULL) {
+ free(rl_start);
+ rl_start = NULL;
+ }
+ }
+ }
+ } else
+#endif
nr = read(parsefile->fd, buf, BUFSIZ - 8);
diff --git a/sh/input.h b/sh/input.h
index a9d3a12..99c1b77 100644
--- a/sh/input.h
+++ b/sh/input.h
@@ -46,6 +46,7 @@
extern char *parsenextc; /* next character in input buffer */
extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */
+int in_interactive_mode();
char *pfgets(char *, int);
int pgetc(void);
int preadbuffer(void);
diff --git a/sh/parser.c b/sh/parser.c
index 67de58e..faf0268 100644
--- a/sh/parser.c
+++ b/sh/parser.c
@@ -1628,6 +1628,9 @@
#ifdef WITH_HISTORY
if (!el)
#endif
+#ifdef WITH_LINENOISE
+ if (! in_interactive_mode() )
+#endif
out2str(getprompt(NULL));
}
diff --git a/sh/trap.c b/sh/trap.c
index b3b2db4..7cb5201 100644
--- a/sh/trap.c
+++ b/sh/trap.c
@@ -60,20 +60,6 @@
#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.
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index a6114ac..588dac0 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -50,9 +50,11 @@
top \
iftop \
id \
+ uptime \
vmstat \
nandread \
- ionice
+ ionice \
+ lsof
LOCAL_SRC_FILES:= \
toolbox.c \
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
index 12f28b9..895b49a 100644
--- a/toolbox/NOTICE
+++ b/toolbox/NOTICE
@@ -1,5 +1,67 @@
-Copyright (c) 2008, The Android Open Source Project
+Copyright (c) 2010, 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) 2009, 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) 2008, The Android Open Source Project.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/toolbox/ls.c b/toolbox/ls.c
index f340a83..8799514 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -173,8 +173,8 @@
date, name);
break;
case S_IFREG:
- printf("%s %-8s %-8s %8d %s %s\n",
- mode, user, group, (int) s.st_size, date, name);
+ printf("%s %-8s %-8s %8lld %s %s\n",
+ mode, user, group, s.st_size, date, name);
break;
case S_IFLNK: {
char linkto[256];
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
new file mode 100644
index 0000000..99891db
--- /dev/null
+++ b/toolbox/lsof.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2010, 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define BUF_MAX 1024
+#define CMD_DISPLAY_MAX 10
+
+struct pid_info_t {
+ pid_t pid;
+
+ char cmdline[CMD_DISPLAY_MAX];
+
+ char path[PATH_MAX];
+ ssize_t parent_length;
+};
+
+void print_header()
+{
+ printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
+ "COMMAND",
+ "PID",
+ "USER",
+ "FD",
+ "TYPE",
+ "DEVICE",
+ "SIZE/OFF",
+ "NODE",
+ "NAME");
+}
+
+void print_type(char *type, struct pid_info_t* info)
+{
+ static ssize_t link_dest_size;
+ static char link_dest[PATH_MAX];
+
+ strncat(info->path, type, sizeof(info->path));
+ if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
+ if (errno == ENOENT)
+ goto out;
+
+ snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno));
+ } else {
+ link_dest[link_dest_size] = '\0';
+ }
+
+ // Things that are just the root filesystem are uninteresting (we already know)
+ if (!strcmp(link_dest, "/"))
+ goto out;
+
+ printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", info->cmdline, info->pid, "???", type,
+ "???", "???", "???", "???", link_dest);
+
+out:
+ info->path[info->parent_length] = '\0';
+}
+
+// Prints out all file that have been memory mapped
+void print_maps(struct pid_info_t* info)
+{
+ FILE *maps;
+ char buffer[PATH_MAX + 100];
+
+ size_t offset;
+ int major, minor;
+ char device[10];
+ long int inode;
+ char file[PATH_MAX];
+
+ strncat(info->path, "maps", sizeof(info->path));
+
+ maps = fopen(info->path, "r");
+ if (!maps)
+ goto out;
+
+ while (fscanf(maps, "%*x-%*x %*s %zx %5s %ld %s\n", &offset, device, &inode,
+ file) == 4) {
+ // We don't care about non-file maps
+ if (inode == 0 || !strcmp(device, "00:00"))
+ continue;
+
+ printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", info->cmdline, info->pid, "???", "mem",
+ "???", device, offset, inode, file);
+ }
+
+ fclose(maps);
+
+out:
+ info->path[info->parent_length] = '\0';
+}
+
+// Prints out all open file descriptors
+void print_fds(struct pid_info_t* info)
+{
+ static char* fd_path = "fd/";
+ strncat(info->path, fd_path, sizeof(info->path));
+
+ int previous_length = info->parent_length;
+ info->parent_length += strlen(fd_path);
+
+ DIR *dir = opendir(info->path);
+ if (dir == NULL) {
+ char msg[BUF_MAX];
+ snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
+ printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", info->cmdline, info->pid, "???", "FDS",
+ "", "", "", "", msg);
+ goto out;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ print_type(de->d_name, info);
+ }
+ closedir(dir);
+
+out:
+ info->parent_length = previous_length;
+ info->path[info->parent_length] = '\0';
+}
+
+void lsof_dumpinfo(pid_t pid)
+{
+ int fd;
+ struct pid_info_t info;
+ info.pid = pid;
+
+ snprintf(info.path, sizeof(info.path), "/proc/%d/", pid);
+
+ info.parent_length = strlen(info.path);
+
+ // Read the command line information; each argument is terminated with NULL.
+ strncat(info.path, "cmdline", sizeof(info.path));
+ fd = open(info.path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Couldn't read %s\n", info.path);
+ return;
+ }
+ char cmdline[PATH_MAX];
+ if (read(fd, cmdline, sizeof(cmdline)) < 0) {
+ fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno));
+ close(fd);
+ return;
+ }
+ close(fd);
+ info.path[info.parent_length] = '\0';
+
+ // We only want the basename of the cmdline
+ strncpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
+ info.cmdline[sizeof(info.cmdline)-1] = '\0';
+
+ // Read each of these symlinks
+ print_type("cwd", &info);
+ print_type("exe", &info);
+ print_type("root", &info);
+
+ print_fds(&info);
+ print_maps(&info);
+}
+
+int lsof_main(int argc, char *argv[])
+{
+ DIR *dir = opendir("/proc");
+ if (dir == NULL) {
+ fprintf(stderr, "Couldn't open /proc\n");
+ return -1;
+ }
+
+ print_header();
+
+ struct dirent* de;
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ // Only inspect directories that are PID numbers
+ char* endptr;
+ long int pid = strtol(de->d_name, &endptr, 10);
+ if (*endptr != '\0')
+ continue;
+
+ lsof_dumpinfo(pid);
+ }
+ closedir(dir);
+
+ return 0;
+}
diff --git a/toolbox/route.c b/toolbox/route.c
index 4f66201..107e48a 100644
--- a/toolbox/route.c
+++ b/toolbox/route.c
@@ -61,7 +61,7 @@
if (!strcmp(argv[2], "default")) {
/* route add default dev wlan0 */
if (argc > 4 && !strcmp(argv[3], "dev")) {
- rt.rt_flags = RTF_UP | RTF_HOST;
+ rt.rt_flags = RTF_UP;
rt.rt_dev = argv[4];
errno = 0;
goto apply;
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
new file mode 100644
index 0000000..3d8061c
--- /dev/null
+++ b/toolbox/uptime.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2010, 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 <sys/time.h>
+#include <linux/ioctl.h>
+#include <linux/rtc.h>
+#include <linux/android_alarm.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+
+static void format_time(int time, char* buffer) {
+ int seconds, minutes, hours, days;
+
+ seconds = time % 60;
+ time /= 60;
+ minutes = time % 60;
+ time /= 60;
+ hours = time % 24;
+ days = time / 24;
+
+ if (days > 0)
+ sprintf(buffer, "%d days, %02d:%02d:%02d", days, hours, minutes, seconds);
+ else
+ sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
+}
+
+int64_t elapsedRealtime()
+{
+ struct timespec ts;
+ int fd, result;
+
+ fd = open("/dev/alarm", O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts);
+ close(fd);
+
+ if (result == 0)
+ return ts.tv_sec;
+ return -1;
+}
+
+int uptime_main(int argc, char *argv[])
+{
+ float up_time, idle_time;
+ char up_string[100], idle_string[100], sleep_string[100];
+ int elapsed;
+
+ FILE* file = fopen("/proc/uptime", "r");
+ if (!file) {
+ fprintf(stderr, "Could not open /proc/uptime\n");
+ return -1;
+ }
+ if (fscanf(file, "%f %f", &up_time, &idle_time) != 2) {
+ fprintf(stderr, "Could not parse /proc/uptime\n");
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ elapsed = elapsedRealtime();
+ if (elapsed < 0) {
+ fprintf(stderr, "elapsedRealtime failed\n");
+ return -1;
+ }
+
+ format_time(elapsed, up_string);
+ format_time((int)idle_time, idle_string);
+ format_time((int)(elapsed - up_time), sleep_string);
+ printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string);
+
+ return 0;
+}