init: Add the ability to start services with dynamic arguments.

    To add arguments dynamically to a service, start the service like so:

    setprop ctl.start service_to_run:arg1 arg2 arg3...

    To start a service with *no* dynamic arguments, start the service normally:

    setprop ctl.start service_to_run

    Dynamic arguments are only supported on 'oneshot' services

Signed-off-by: San Mehat <san@google.com>
diff --git a/init/builtins.c b/init/builtins.c
index 95fb223..17df0af 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -126,7 +126,7 @@
 static void service_start_if_not_disabled(struct service *svc)
 {
     if (!(svc->flags & SVC_DISABLED)) {
-        service_start(svc);
+        service_start(svc, NULL);
     }
 }
 
@@ -316,7 +316,7 @@
     struct service *svc;
     svc = service_find_by_name(args[1]);
     if (svc) {
-        service_start(svc);
+        service_start(svc, NULL);
     }
     return 0;
 }
@@ -337,7 +337,7 @@
     svc = service_find_by_name(args[1]);
     if (svc) {
         service_stop(svc);
-        service_start(svc);
+        service_start(svc, NULL);
     }
     return 0;
 }
diff --git a/init/init.c b/init/init.c
index a748ec3..b350569 100644
--- a/init/init.c
+++ b/init/init.c
@@ -156,7 +156,7 @@
     fcntl(fd, F_SETFD, 0);
 }
 
-void service_start(struct service *svc)
+void service_start(struct service *svc, const char *dynamic_args)
 {
     struct stat s;
     pid_t pid;
@@ -192,6 +192,12 @@
         return;
     }
 
+    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
+        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]);
+        svc->flags |= SVC_DISABLED;
+        return;
+    }
+
     NOTICE("starting '%s'\n", svc->name);
 
     pid = fork();
@@ -248,7 +254,50 @@
             setuid(svc->uid);
         }
 
-        execve(svc->args[0], (char**) svc->args, (char**) ENV);
+        if (!dynamic_args)
+            execve(svc->args[0], (char**) svc->args, (char**) ENV);
+        else {
+            char *arg_ptrs[SVC_MAXARGS+1];
+            int arg_idx;
+            char *tmp = strdup(dynamic_args);
+            char *p = tmp;
+
+            /* Copy the static arguments */
+            for (arg_idx = 0; arg_idx < svc->nargs; arg_idx++) {
+                arg_ptrs[arg_idx] = svc->args[arg_idx];
+            }
+
+            int done = 0;
+            while(!done) {
+
+                if (arg_idx == SVC_MAXARGS) 
+                    break;
+
+                /* Advance over any leading whitespace */
+                if (*p == ' ') {
+                    for (p; *p != ' '; p++);
+                    p++;
+                }
+                /* Locate next argument */
+                char *q = p;
+                while(1) {
+                    if (*q == ' ') {
+                        *q = '\0';
+                        break;
+                    } else if (*q == '\0') {
+                        done = 1;
+                        break;
+                    }
+                    q++;
+                }
+                arg_ptrs[arg_idx++] = p;
+
+                q++; // Advance q to the next string
+                p = q;
+            }
+            arg_ptrs[arg_idx] = '\0';
+            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
+        }
         _exit(127);
     }
 
@@ -379,7 +428,7 @@
 
     if (next_start_time <= gettime()) {
         svc->flags &= (~SVC_RESTARTING);
-        service_start(svc);
+        service_start(svc, NULL);
         return;
     }
 
@@ -405,13 +454,29 @@
 
 static void msg_start(const char *name)
 {
-    struct service *svc = service_find_by_name(name);
+    struct service *svc;
+    char *tmp = NULL;
+    char *args = NULL;
+
+    if (!strchr(name, ':'))
+        svc = service_find_by_name(name);
+    else {
+        tmp = strdup(name);
+        strcpy(tmp, name);
+        args = strchr(tmp, ':');
+        *args = '\0';
+        args++;
+
+        svc = service_find_by_name(tmp);
+    }
     
     if (svc) {
-        service_start(svc);
+        service_start(svc, args);
     } else {
         ERROR("no such service '%s'\n", name);
     }
+    if (tmp)
+        free(tmp);
 }
 
 static void msg_stop(const char *name)
@@ -737,7 +802,7 @@
     svc = service_find_by_keychord(id);
     if (svc) {
         INFO("starting service %s from keychord\n", svc->name);
-        service_start(svc);   
+        service_start(svc, NULL);   
     } else {
         ERROR("service for keychord %d not found\n", id);
     }
diff --git a/init/init.h b/init/init.h
index b93eb50..f306b7b 100644
--- a/init/init.h
+++ b/init/init.h
@@ -116,6 +116,8 @@
 
 #define NR_SVC_SUPP_GIDS 6    /* six supplementary groups */
 
+#define SVC_MAXARGS 64
+
 struct service {
         /* list of all services */
     struct listnode slist;
@@ -160,7 +162,7 @@
 void service_for_each_flags(unsigned matchflags,
                             void (*func)(struct service *svc));
 void service_stop(struct service *svc);
-void service_start(struct service *svc);
+void service_start(struct service *svc, const char *dynamic_args);
 void property_changed(const char *name, const char *value);
 
 struct action *action_remove_queue_head(void);
diff --git a/init/parser.c b/init/parser.c
index 30fa3de..33c1a68 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -60,8 +60,6 @@
 #endif       
 }
 
-#define MAXARGS 64
-
 #define T_EOF 0
 #define T_TEXT 1
 #define T_NEWLINE 2
@@ -357,7 +355,7 @@
 static void parse_config(const char *fn, char *s)
 {
     struct parse_state state;
-    char *args[MAXARGS];
+    char *args[SVC_MAXARGS];
     int nargs;
 
     nargs = 0;
@@ -384,7 +382,7 @@
             }
             break;
         case T_TEXT:
-            if (nargs < MAXARGS) {
+            if (nargs < SVC_MAXARGS) {
                 args[nargs++] = state.text;
             }
             break;