Merge "Revert "Remove netcfg's unused options.""
diff --git a/init/Android.mk b/init/Android.mk
index 4df4489..606c199 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -17,7 +17,11 @@
 	ueventd_parser.c \
 	watchdogd.c
 
-LOCAL_CFLAGS    += -Wno-unused-parameter
+LOCAL_CFLAGS += \
+    -std=gnu11 \
+    -Wall \
+    -Werror -Wno-error=deprecated-declarations \
+    -Wno-unused-parameter \
 
 ifeq ($(strip $(INIT_BOOTCHART)),true)
 LOCAL_SRC_FILES += bootchart.c
diff --git a/init/bootchart.c b/init/bootchart.c
index a514261..27c7f65 100644
--- a/init/bootchart.c
+++ b/init/bootchart.c
@@ -66,7 +66,7 @@
 proc_read(const char*  filename, char* buff, size_t  buffsize)
 {
     int  len = 0;
-    int  fd  = open(filename, O_RDONLY);
+    int  fd  = open(filename, O_RDONLY | O_CLOEXEC);
     if (fd >= 0) {
         len = unix_read(fd, buff, buffsize-1);
         close(fd);
@@ -144,7 +144,7 @@
     struct tm  now = *localtime(&now_t);
     strftime(date, sizeof(date), "%x %X", &now);
 
-    out = fopen( LOG_HEADER, "w" );
+    out = fopen( LOG_HEADER, "we" );
     if (out == NULL)
         return;
 
@@ -170,12 +170,6 @@
 }
 
 static void
-close_on_exec(int  fd)
-{
-    fcntl(fd, F_SETFD, FD_CLOEXEC);
-}
-
-static void
 open_log_file(int*  plogfd, const char*  logfile)
 {
     int    logfd = *plogfd;
@@ -183,12 +177,11 @@
     /* create log file if needed */
     if (logfd < 0) 
     {
-        logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
+        logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0755);
         if (logfd < 0) {
             *plogfd = -2;
             return;
         }
-        close_on_exec(logfd);
         *plogfd = logfd;
     }
 }
@@ -220,9 +213,8 @@
     do_log_uptime(log);
 
     /* append file content */
-    fd = open(procfile,O_RDONLY);
+    fd = open(procfile,O_RDONLY|O_CLOEXEC);
     if (fd >= 0) {
-        close_on_exec(fd);
         for (;;) {
             int  ret;
             ret = unix_read(fd, buff, sizeof(buff));
@@ -264,7 +256,7 @@
 
             /* read process stat line */
             snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
-            fd = open(filename,O_RDONLY);
+            fd = open(filename,O_RDONLY|O_CLOEXEC);
             if (fd >= 0) {
                len = unix_read(fd, buff, sizeof(buff)-1);
                close(fd);
@@ -340,7 +332,7 @@
 
     /* create kernel process accounting file */
     {
-        int  fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
+        int  fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
         if (fd >= 0) {
             close(fd);
             acct( LOG_ACCT );
diff --git a/init/builtins.c b/init/builtins.c
index 76c0a18..2521daf 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -59,7 +59,7 @@
 {
     int fd, ret, len;
 
-    fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600);
+    fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600);
 
     if (fd < 0)
         return -errno;
@@ -99,7 +99,7 @@
 {
     int fd, ret;
 
-    fd = open("/dev/tty0", O_RDWR | O_SYNC);
+    fd = open("/dev/tty0", O_RDWR | O_SYNC | O_CLOEXEC);
     if (fd < 0)
         return -1;
 
@@ -205,6 +205,67 @@
     return -1;
 }
 
+int do_execonce(int nargs, char **args)
+{
+    pid_t child;
+    int child_status = 0;
+    static int already_done;
+
+    if (already_done) {
+      return -1;
+    }
+    already_done = 1;
+    if (!(child = fork())) {
+        /*
+         * Child process.
+         */
+        zap_stdio();
+        char *exec_args[100];
+        size_t num_process_args = nargs;
+
+        memset(exec_args, 0, sizeof(exec_args));
+        if (num_process_args > ARRAY_SIZE(exec_args) - 1) {
+            ERROR("exec called with %zu args, limit is %zu", num_process_args,
+                  ARRAY_SIZE(exec_args) - 1);
+            _exit(1);
+        }
+        for (size_t i = 1; i < num_process_args; i++)
+            exec_args[i - 1] = args[i];
+
+        if (execv(exec_args[0], exec_args) == -1) {
+            ERROR("Failed to execv '%s' (%s)", exec_args[0], strerror(errno));
+            _exit(1);
+        }
+        ERROR("Returned from execv()!");
+        _exit(1);
+    }
+
+    /*
+     * Parent process.
+     */
+    if (child == -1) {
+        ERROR("Fork failed\n");
+        return -1;
+    }
+
+    if (TEMP_FAILURE_RETRY(waitpid(child, &child_status, 0)) == -1) {
+        ERROR("waitpid(): failed (%s)\n", strerror(errno));
+        return -1;
+    }
+
+    if (WIFSIGNALED(child_status)) {
+        INFO("Child exited due to signal %d\n", WTERMSIG(child_status));
+        return -1;
+    } else if (WIFEXITED(child_status)) {
+        INFO("Child exited normally (exit code %d)\n", WEXITSTATUS(child_status));
+        return WEXITSTATUS(child_status);
+    }
+
+    ERROR("Abnormal child process exit\n");
+
+    return -1;
+}
+
 int do_export(int nargs, char **args)
 {
     return add_environment(args[1], args[2]);
@@ -370,14 +431,14 @@
         struct loop_info info;
 
         mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        fd = open(source + 5, mode);
+        fd = open(source + 5, mode | O_CLOEXEC);
         if (fd < 0) {
             return -1;
         }
 
         for (n = 0; ; n++) {
             sprintf(tmp, "/dev/block/loop%d", n);
-            loop = open(tmp, mode);
+            loop = open(tmp, mode | O_CLOEXEC);
             if (loop < 0) {
                 close(fd);
                 return -1;
@@ -423,7 +484,7 @@
 static int wipe_data_via_recovery()
 {
     mkdir("/cache/recovery", 0700);
-    int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
+    int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
     if (fd >= 0) {
         write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
         write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
@@ -709,10 +770,10 @@
     if (stat(args[1], &info) < 0) 
         return -1;
 
-    if ((fd1 = open(args[1], O_RDONLY)) < 0) 
+    if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
         goto out_err;
 
-    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
+    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
         goto out_err;
 
     if (!(buffer = malloc(info.st_size)))
diff --git a/init/devices.c b/init/devices.c
index dde43df..0de92f5 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -865,20 +865,20 @@
     if (l == -1)
         goto data_free_out;
 
-    loading_fd = open(loading, O_WRONLY);
+    loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
     if(loading_fd < 0)
         goto file_free_out;
 
-    data_fd = open(data, O_WRONLY);
+    data_fd = open(data, O_WRONLY|O_CLOEXEC);
     if(data_fd < 0)
         goto loading_close_out;
 
 try_loading_again:
-    fw_fd = open(file1, O_RDONLY);
+    fw_fd = open(file1, O_RDONLY|O_CLOEXEC);
     if(fw_fd < 0) {
-        fw_fd = open(file2, O_RDONLY);
+        fw_fd = open(file2, O_RDONLY|O_CLOEXEC);
         if (fw_fd < 0) {
-            fw_fd = open(file3, O_RDONLY);
+            fw_fd = open(file3, O_RDONLY|O_CLOEXEC);
             if (fw_fd < 0) {
                 if (booting) {
                         /* If we're not fully booted, we may be missing
@@ -1044,7 +1044,7 @@
         coldboot("/sys/block");
         coldboot("/sys/devices");
         t1 = get_usecs();
-        fd = open(COLDBOOT_DONE, O_WRONLY|O_CREAT, 0000);
+        fd = open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000);
         close(fd);
         log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
         // t0 & t1 are unused if the log isn't doing anything.
diff --git a/init/init.c b/init/init.c
index 7ddab80..8bb6da0 100644
--- a/init/init.c
+++ b/init/init.c
@@ -126,7 +126,7 @@
     return -1;
 }
 
-static void zap_stdio(void)
+void zap_stdio(void)
 {
     int fd;
     fd = open("/dev/null", O_RDWR);
@@ -349,7 +349,7 @@
                 if (arg_idx == INIT_PARSER_MAXARGS)
                     break;
             }
-            arg_ptrs[arg_idx] = '\0';
+            arg_ptrs[arg_idx] = NULL;
             execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
         }
         _exit(127);
@@ -540,17 +540,35 @@
     return (list_tail(&act->commands) == &cmd->clist);
 }
 
+
+void build_triggers_string(char *name_str, int length, struct action *cur_action) {
+    struct listnode *node;
+    struct trigger *cur_trigger;
+
+    list_for_each(node, &cur_action->triggers) {
+        cur_trigger = node_to_item(node, struct trigger, nlist);
+        if (node != cur_action->triggers.next) {
+            strlcat(name_str, " " , length);
+        }
+        strlcat(name_str, cur_trigger->name , length);
+    }
+}
+
 void execute_one_command(void)
 {
     int ret, i;
     char cmd_str[256] = "";
+    char name_str[256] = "";
 
     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);
+
+        build_triggers_string(name_str, sizeof(name_str), cur_action);
+
+        INFO("processing action %p (%s)\n", cur_action, name_str);
         cur_command = get_first_command(cur_action);
     } else {
         cur_command = get_next_command(cur_action, cur_command);
@@ -568,7 +586,7 @@
             }
         }
         INFO("command '%s' action=%s status=%d (%s:%d)\n",
-             cmd_str, cur_action ? cur_action->name : "", ret, cur_command->filename,
+             cmd_str, cur_action ? name_str : "", ret, cur_command->filename,
              cur_command->line);
     }
 }
@@ -608,7 +626,7 @@
     size_t total_bytes_written = 0;
 
     hwrandom_fd = TEMP_FAILURE_RETRY(
-            open("/dev/hw_random", O_RDONLY | O_NOFOLLOW));
+            open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
     if (hwrandom_fd == -1) {
         if (errno == ENOENT) {
           ERROR("/dev/hw_random not found\n");
@@ -621,7 +639,7 @@
     }
 
     urandom_fd = TEMP_FAILURE_RETRY(
-            open("/dev/urandom", O_WRONLY | O_NOFOLLOW));
+            open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
     if (urandom_fd == -1) {
         ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
         goto ret;
@@ -675,12 +693,12 @@
         snprintf(console_name, sizeof(console_name), "/dev/%s", console);
     }
 
-    fd = open(console_name, O_RDWR);
+    fd = open(console_name, O_RDWR | O_CLOEXEC);
     if (fd >= 0)
         have_console = 1;
     close(fd);
 
-    fd = open("/dev/tty0", O_WRONLY);
+    fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
     if (fd >= 0) {
         const char *msg;
             msg = "\n"
@@ -1011,7 +1029,7 @@
     mount("sysfs", "/sys", "sysfs", 0, NULL);
 
         /* indicate that booting is in progress to background fw loaders, etc */
-    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
+    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
         /* We must have some place other than / to create the
          * device nodes for kmsg and null, otherwise we won't
diff --git a/init/init.h b/init/init.h
index a7615a3..654a80b 100644
--- a/init/init.h
+++ b/init/init.h
@@ -37,6 +37,11 @@
     char *args[1];
 };
 
+struct trigger {
+    struct listnode nlist;
+    const char *name;
+};
+
 struct action {
         /* node in list of all actions */
     struct listnode alist;
@@ -46,12 +51,15 @@
     struct listnode tlist;
 
     unsigned hash;
-    const char *name;
 
+        /* list of actions which triggers the commands*/
+    struct listnode triggers;
     struct listnode commands;
     struct command *current;
 };
 
+void build_triggers_string(char *name_str, int length, struct action *cur_action);
+
 struct socketinfo {
     struct socketinfo *next;
     const char *name;
@@ -141,5 +149,6 @@
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 extern int selinux_reload_policy(void);
+void zap_stdio(void);
 
 #endif	/* _INIT_INIT_H */
diff --git a/init/init_parser.c b/init/init_parser.c
index 2b4db8e..02cbf3d 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -97,6 +97,7 @@
     case 'e':
         if (!strcmp(s, "nable")) return K_enable;
         if (!strcmp(s, "xec")) return K_exec;
+        if (!strcmp(s, "xeconce")) return K_execonce;
         if (!strcmp(s, "xport")) return K_export;
         break;
     case 'g':
@@ -499,77 +500,92 @@
 void action_for_each_trigger(const char *trigger,
                              void (*func)(struct action *act))
 {
-    struct listnode *node;
+    struct listnode *node, *node2;
     struct action *act;
+    struct trigger *cur_trigger;
+
     list_for_each(node, &action_list) {
         act = node_to_item(node, struct action, alist);
-        if (!strcmp(act->name, trigger)) {
-            func(act);
+        list_for_each(node2, &act->triggers) {
+            cur_trigger = node_to_item(node2, struct trigger, nlist);
+            if (!strcmp(cur_trigger->name, trigger)) {
+                func(act);
+            }
         }
     }
 }
 
+
 void queue_property_triggers(const char *name, const char *value)
 {
-    struct listnode *node;
+    struct listnode *node, *node2;
     struct action *act;
+    struct trigger *cur_trigger;
+    bool match;
+    int name_length;
+
     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);
+            match = !name;
+        list_for_each(node2, &act->triggers) {
+            cur_trigger = node_to_item(node2, struct trigger, nlist);
+            if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
+                const char *test = cur_trigger->name + strlen("property:");
+                if (!match) {
+                    name_length = strlen(name);
+                    if (!strncmp(name, test, name_length) &&
+                        test[name_length] == '=' &&
+                        (!strcmp(test + name_length + 1, value) ||
+                        !strcmp(test + name_length + 1, "*"))) {
+                        match = true;
+                        continue;
+                    }
+                } else {
+                     const char* equals = strchr(test, '=');
+                     if (equals) {
+                         char prop_name[PROP_NAME_MAX + 1];
+                         char value[PROP_VALUE_MAX];
+                         int length = equals - test;
+                         if (length <= PROP_NAME_MAX) {
+                             int ret;
+                             memcpy(prop_name, test, length);
+                             prop_name[length] = 0;
 
-            if (!strncmp(name, test, name_length) &&
-                    test[name_length] == '=' &&
-                    (!strcmp(test + name_length + 1, value) ||
-                     !strcmp(test + name_length + 1, "*"))) {
-                action_add_queue_tail(act);
-            }
+                             /* does the property exist, and match the trigger value? */
+                             ret = property_get(prop_name, value);
+                             if (ret > 0 && (!strcmp(equals + 1, value) ||
+                                !strcmp(equals + 1, "*"))) {
+                                 continue;
+                             }
+                         }
+                     }
+                 }
+             }
+             match = false;
+             break;
+        }
+        if (match) {
+            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];
-                char value[PROP_VALUE_MAX];
-                int length = equals - name;
-                if (length > PROP_NAME_MAX) {
-                    ERROR("property name too long in trigger %s", act->name);
-                } else {
-                    int ret;
-                    memcpy(prop_name, name, length);
-                    prop_name[length] = 0;
-
-                    /* does the property exist, and match the trigger value? */
-                    ret = property_get(prop_name, value);
-                    if (ret > 0 && (!strcmp(equals + 1, value) ||
-                                    !strcmp(equals + 1, "*"))) {
-                        action_add_queue_tail(act);
-                    }
-                }
-            }
-        }
-    }
+    queue_property_triggers(NULL, NULL);
 }
 
 void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
 {
     struct action *act;
     struct command *cmd;
+    struct trigger *cur_trigger;
 
     act = calloc(1, sizeof(*act));
-    act->name = name;
+    cur_trigger = calloc(1, sizeof(*cur_trigger));
+    cur_trigger->name = name;
+    list_init(&act->triggers);
+    list_add_tail(&act->triggers, &cur_trigger->nlist);
     list_init(&act->commands);
     list_init(&act->qlist);
 
@@ -611,6 +627,7 @@
 static void *parse_service(struct parse_state *state, int nargs, char **args)
 {
     struct service *svc;
+    struct trigger *cur_trigger;
     if (nargs < 3) {
         parse_error(state, "services must have a name and a program\n");
         return 0;
@@ -635,9 +652,12 @@
     svc->name = args[1];
     svc->classname = "default";
     memcpy(svc->args, args + 2, sizeof(char*) * nargs);
+    cur_trigger = calloc(1, sizeof(*cur_trigger));
     svc->args[nargs] = 0;
     svc->nargs = nargs;
-    svc->onrestart.name = "onrestart";
+    list_init(&svc->onrestart.triggers);
+    cur_trigger->name = "onrestart";
+    list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
     list_init(&svc->onrestart.commands);
     list_add_tail(&service_list, &svc->slist);
     return svc;
@@ -821,16 +841,29 @@
 static void *parse_action(struct parse_state *state, int nargs, char **args)
 {
     struct action *act;
+    struct trigger *cur_trigger;
+    int i;
     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->triggers);
+
+    for (i = 1; i < nargs; i++) {
+        if (!(i % 2)) {
+            if (strcmp(args[i], "&&")) {
+                parse_error(state, "& is the only symbol allowed to concatenate actions\n");
+                return 0;
+            } else
+                continue;
+        }
+        cur_trigger = calloc(1, sizeof(*cur_trigger));
+        cur_trigger->name = args[i];
+        list_add_tail(&act->triggers, &cur_trigger->nlist);
+    }
+
     list_init(&act->commands);
     list_init(&act->qlist);
     list_add_tail(&action_list, &act->alist);
diff --git a/init/keychords.c b/init/keychords.c
index 4a64042..5a9e45f 100644
--- a/init/keychords.c
+++ b/init/keychords.c
@@ -72,12 +72,11 @@
     if (!keychords)
         return;
 
-    fd = open("/dev/keychord", O_RDWR);
+    fd = open("/dev/keychord", O_RDWR | O_CLOEXEC);
     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) {
diff --git a/init/keywords.h b/init/keywords.h
index 2d97e5b..7473586 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -8,6 +8,7 @@
 int do_domainname(int nargs, char **args);
 int do_enable(int nargs, char **args);
 int do_exec(int nargs, char **args);
+int do_execonce(int nargs, char **args);
 int do_export(int nargs, char **args);
 int do_hostname(int nargs, char **args);
 int do_ifup(int nargs, char **args);
@@ -59,6 +60,7 @@
     KEYWORD(domainname,  COMMAND, 1, do_domainname)
     KEYWORD(enable,      COMMAND, 1, do_enable)
     KEYWORD(exec,        COMMAND, 1, do_exec)
+    KEYWORD(execonce,    COMMAND, 1, do_execonce)
     KEYWORD(export,      COMMAND, 2, do_export)
     KEYWORD(group,       OPTION,  0, 0)
     KEYWORD(hostname,    COMMAND, 1, do_hostname)
diff --git a/init/parser.c b/init/parser.c
index 48e7aec..80bfb09 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -15,9 +15,10 @@
     struct command *cmd;
     struct listnode *node;
     struct listnode *node2;
+    char name_str[256] = "";
     struct socketinfo *si;
     int n;
-    
+
     list_for_each(node, &service_list) {
         svc = node_to_item(node, struct service, slist);
         RAW("service %s\n", svc->name);
@@ -34,7 +35,11 @@
 
     list_for_each(node, &action_list) {
         act = node_to_item(node, struct action, alist);
-        RAW("on %s\n", act->name);
+        RAW("on ");
+        build_triggers_string(name_str, sizeof(name_str), act);
+        RAW("%s", name_str);
+        RAW("\n");
+
         list_for_each(node2, &act->commands) {
             cmd = node_to_item(node2, struct command, clist);
             RAW("  %p", cmd->func);
diff --git a/init/readme.txt b/init/readme.txt
index 750d953..e5406ed 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -123,14 +123,27 @@
    Triggers of this form occur when the property <name> is set
    to the specific value <value>.
 
+   One can also test multiple properties to execute a group
+   of commands. For example:
+
+   on property:test.a=1 && property:test.b=1
+       setprop test.c 1
+
+   The above stub sets test.c to 1 only when
+   both test.a=1 and test.b=1
+
 Commands
 --------
 
 exec <path> [ <argument> ]*
+   This command is not implemented.
+
+execonce <path> [ <argument> ]*
    Fork and execute a program (<path>).  This will block until
-   the program completes execution.  It is best to avoid exec
-   as unlike the builtin commands, it runs the risk of getting
-   init "stuck". (??? maybe there should be a timeout?)
+   the program completes execution.  This command can be run at most
+   once during init's lifetime. Subsequent invocations are ignored.
+   It is best to avoid exec as unlike the builtin commands, it runs
+   the risk of getting init "stuck".
 
 export <name> <value>
    Set the environment variable <name> equal to <value> in the
diff --git a/init/signal_handler.c b/init/signal_handler.c
index 7e8e1a7..952f970 100644
--- a/init/signal_handler.c
+++ b/init/signal_handler.c
@@ -147,13 +147,9 @@
     sigaction(SIGCHLD, &act, 0);
 
     /* create a signalling mechanism for the sigchld handler */
-    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
+    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 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();
diff --git a/init/util.c b/init/util.c
index e1a3ee3..8f27ee9 100644
--- a/init/util.c
+++ b/init/util.c
@@ -155,7 +155,7 @@
     struct stat sb;
 
     data = 0;
-    fd = open(fn, O_RDONLY);
+    fd = open(fn, O_RDONLY|O_CLOEXEC);
     if(fd < 0) return 0;
 
     // for security reasons, disallow world-writable
@@ -207,7 +207,7 @@
     ssize_t pmtdsize;
     int r;
 
-    fd = open("/proc/mtd", O_RDONLY);
+    fd = open("/proc/mtd", O_RDONLY|O_CLOEXEC);
     if (fd < 0)
         return;
 
@@ -416,7 +416,7 @@
     if (hardware[0])
         return;
 
-    fd = open(cpuinfo, O_RDONLY);
+    fd = open(cpuinfo, O_RDONLY | O_CLOEXEC);
     if (fd < 0) return;
 
     for (;;) {
@@ -479,7 +479,7 @@
     char *ptr;
     int fd;
 
-    fd = open("/proc/cmdline", O_RDONLY);
+    fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
     if (fd >= 0) {
         int n = read(fd, cmdline, sizeof(cmdline) - 1);
         if (n < 0) n = 0;
diff --git a/init/watchdogd.c b/init/watchdogd.c
index 7f64fc4..0790811 100644
--- a/init/watchdogd.c
+++ b/init/watchdogd.c
@@ -48,7 +48,7 @@
 
     timeout = interval + margin;
 
-    fd = open(DEV_NAME, O_RDWR);
+    fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
     if (fd < 0) {
         ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
         return 1;