]> git.saurik.com Git - apple/launchd.git/blobdiff - launchd/src/launchctl.c
launchd-106.14.tar.gz
[apple/launchd.git] / launchd / src / launchctl.c
index 5142250a8a2da0bd49083c88127590d12aa91b46..fbdbfb3616e9a93bb8d7ade948c994d92524b0cf 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 #include <CoreFoundation/CoreFoundation.h>
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/stat.h>
 
 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
 
+#define MACHINIT_JOBKEY_ONDEMAND       "OnDemand"
+#define MACHINIT_JOBKEY_SERVICENAME    "ServiceName"
+#define MACHINIT_JOBKEY_COMMAND                "Command"
+#define MACHINIT_JOBKEY_ISKUNCSERVER   "isKUNCServer"
+
+
+static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
 static bool launch_data_array_append(launch_data_t a, launch_data_t o);
+static void distill_jobs(launch_data_t);
 static void distill_config_file(launch_data_t);
 static void sock_dict_cb(launch_data_t what, const char *key, void *context);
 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
@@ -59,11 +69,17 @@ static launch_data_t read_plist_file(const char *file, bool editondisk, bool loa
 static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
 static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
 static void readpath(const char *, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload);
+static void readfile(const char *, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload);
 static int _fd(int);
 static int demux_cmd(int argc, char *const argv[]);
-static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv);
+static void do_rendezvous_magic(const struct addrinfo *res, const char *serv, const char *label);
+static void workaround_bonjour_asynchronously(void);
 static void submit_job_pass(launch_data_t jobs);
+static void submit_mach_jobs(launch_data_t jobs);
+static void let_go_of_mach_jobs(void);
 static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
+static void print_jobs(launch_data_t j, const char *label, void *context);
+static bool is_legacy_mach_job(launch_data_t obj);
 
 static int load_and_unload_cmd(int argc, char *const argv[]);
 //static int reload_cmd(int argc, char *const argv[]);
@@ -216,21 +232,29 @@ static int setenv_cmd(int argc, char *const argv[])
        return 0;
 }
 
+static void print_launchd_env(launch_data_t obj, const char *key, void *context)
+{
+       bool *is_csh = context;
+
+       if (*is_csh)
+               fprintf(stdout, "setenv %s %s;\n", key, launch_data_get_string(obj));
+       else
+               fprintf(stdout, "%s=%s; export %s;\n", key, launch_data_get_string(obj), key);
+}
+
+static void print_key_value(launch_data_t obj, const char *key, void *context)
+{
+       const char *k = context;
+
+       if (!strcmp(key, k))
+               fprintf(stdout, "%s\n", launch_data_get_string(obj));
+}
+
 static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused)))
 {
        launch_data_t resp, msg;
        bool is_csh = false;
-       const char *k;
-       void print_launchd_env(launch_data_t obj, const char *key, void *context __attribute__((unused))) {
-               if (is_csh)
-                       fprintf(stdout, "setenv %s %s;\n", key, launch_data_get_string(obj));
-               else
-                       fprintf(stdout, "%s=%s; export %s;\n", key, launch_data_get_string(obj), key);
-       }
-       void print_key_value(launch_data_t obj, const char *key, void *context __attribute__((unused))) {
-               if (!strcmp(key, k))
-                       fprintf(stdout, "%s\n", launch_data_get_string(obj));
-       }
+       char *k;
        
        if (!strcmp(argv[0], "export")) {
                char *s = getenv("SHELL");
@@ -249,7 +273,10 @@ static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unu
        launch_data_free(msg);
 
        if (resp) {
-               launch_data_dict_iterate(resp, (!strcmp(argv[0], "export")) ? print_launchd_env : print_key_value, NULL);
+               if (!strcmp(argv[0], "export"))
+                       launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
+               else
+                       launch_data_dict_iterate(resp, print_key_value, k);
                launch_data_free(resp);
        } else {
                fprintf(stderr, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT "\"): %s\n", strerror(errno));
@@ -286,7 +313,8 @@ static void unloadjob(launch_data_t job)
        launch_data_free(resp);
 }
 
-static launch_data_t read_plist_file(const char *file, bool editondisk, bool load)
+launch_data_t
+read_plist_file(const char *file, bool editondisk, bool load)
 {
        CFPropertyListRef plist = CreateMyPropertyListFromFile(file);
        launch_data_t r = NULL;
@@ -311,44 +339,8 @@ static launch_data_t read_plist_file(const char *file, bool editondisk, bool loa
        return r;
 }
 
-static void delay_to_second_pass2(launch_data_t o, const char *key, void *context)
-{
-       bool *res = context;
-       size_t i;
-
-       if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) {
-               *res = true;
-               return;
-       }
-
-       switch (launch_data_get_type(o)) {
-       case LAUNCH_DATA_DICTIONARY:
-               launch_data_dict_iterate(o, delay_to_second_pass2, context);
-               break;
-       case LAUNCH_DATA_ARRAY:
-               for (i = 0; i < launch_data_array_get_count(o); i++)
-                       delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context);
-               break;
-       default:
-               break;
-       }
-}
-
-static bool delay_to_second_pass(launch_data_t o)
-{
-       bool res = false;
-
-       launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS);
-
-       if (NULL == socks)
-               return false;
-
-       delay_to_second_pass2(socks, NULL, &res);
-
-       return res;
-}
-
-static void readfile(const char *what, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload)
+void
+readfile(const char *what, launch_data_t pass0, launch_data_t pass1, bool editondisk, bool load, bool forceload)
 {
        launch_data_t tmpd, thejob;
        bool job_disabled = false;
@@ -358,6 +350,11 @@ static void readfile(const char *what, launch_data_t pass1, launch_data_t pass2,
                return;
        }
 
+       if (is_legacy_mach_job(thejob)) {
+               launch_data_array_append(pass0, thejob);
+               return;
+       }
+
        if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
                fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what);
                launch_data_free(thejob);
@@ -375,13 +372,11 @@ static void readfile(const char *what, launch_data_t pass1, launch_data_t pass2,
                return;
        }
 
-       if (delay_to_second_pass(thejob))
-               launch_data_array_append(pass2, thejob);
-       else
-               launch_data_array_append(pass1, thejob);
+       launch_data_array_append(pass1, thejob);
 }
 
-static void readpath(const char *what, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload)
+void
+readpath(const char *what, launch_data_t pass0, launch_data_t pass1, bool editondisk, bool load, bool forceload)
 {
        char buf[MAXPATHLEN];
        struct stat sb;
@@ -392,7 +387,7 @@ static void readpath(const char *what, launch_data_t pass1, launch_data_t pass2,
                return;
 
        if (S_ISREG(sb.st_mode) && !(sb.st_mode & S_IWOTH)) {
-               readfile(what, pass1, pass2, editondisk, load, forceload);
+               readfile(what, pass0, pass1, editondisk, load, forceload);
        } else {
                if ((d = opendir(what)) == NULL) {
                        fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname());
@@ -404,7 +399,7 @@ static void readpath(const char *what, launch_data_t pass1, launch_data_t pass2,
                                continue;
                        snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
 
-                       readfile(buf, pass1, pass2, editondisk, load, forceload);
+                       readfile(buf, pass0, pass1, editondisk, load, forceload);
                }
                closedir(d);
        }
@@ -415,7 +410,17 @@ struct distill_context {
        launch_data_t newsockdict;
 };
 
-static void distill_config_file(launch_data_t id_plist)
+void
+distill_jobs(launch_data_t jobs)
+{
+       size_t i, c = launch_data_array_get_count(jobs);
+
+       for (i = 0; i < c; i++)
+               distill_config_file(launch_data_array_get_index(jobs, i));
+}
+
+void
+distill_config_file(launch_data_t id_plist)
 {
        struct distill_context dc = { id_plist, NULL };
        launch_data_t tmp;
@@ -450,9 +455,13 @@ static void sock_dict_cb(launch_data_t what, const char *key, void *context)
 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
 {
        launch_data_t a, val;
+       const char *joblabel;
        int sfd, st = SOCK_STREAM;
        bool passive = true;
 
+       assert((val = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) != NULL);
+       joblabel = launch_data_get_string(val);
+
        if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
                if (!strcasecmp(launch_data_get_string(val), "stream")) {
                        st = SOCK_STREAM;
@@ -487,6 +496,9 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data
                
        if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
                struct sockaddr_un sun;
+               mode_t sun_mode = 0;
+               mode_t oldmask;
+               bool setm = false;
 
                memset(&sun, 0, sizeof(sun));
 
@@ -497,15 +509,26 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data
                if ((sfd = _fd(socket(AF_UNIX, st, 0))) == -1)
                        return;
 
+               if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
+                       sun_mode = (mode_t)launch_data_get_integer(val);
+                       setm = true;
+               }
+
                if (passive) {                  
                        if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
                                close(sfd);     
                                return;
                        }
+                       oldmask = umask(S_IRWXG|S_IRWXO);
                        if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
                                close(sfd);
+                               umask(oldmask);
                                return;
                        }
+                       umask(oldmask);
+                       if (setm) {
+                               chmod(sun.sun_path, sun_mode);
+                       }
                        if ((st == SOCK_STREAM || st == SOCK_SEQPACKET)
                                        && listen(sfd, SOMAXCONN) == -1) {
                                close(sfd);
@@ -568,7 +591,6 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data
                }
 
                for (res = res0; res; res = res->ai_next) {
-                       launch_data_t rvs_fd = NULL;
                        if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
                                fprintf(stderr, "socket(): %s\n", strerror(errno));
                                return;
@@ -605,30 +627,22 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data
                                }
                                if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) &&
                                                (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) {
-                                       launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS);
-                                       if (NULL == rvs_fds) {
-                                               rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY);
-                                               launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS);
-                                       }
                                        if (NULL == rnames) {
-                                               rvs_fd = do_rendezvous_magic(res, serv);
-                                               if (rvs_fd)
-                                                       launch_data_array_append(rvs_fds, rvs_fd);
+                                               do_rendezvous_magic(res, serv, joblabel);
                                        } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) {
-                                               rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames));
-                                               if (rvs_fd)
-                                                       launch_data_array_append(rvs_fds, rvs_fd);
+                                               do_rendezvous_magic(res, launch_data_get_string(rnames), joblabel);
                                        } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) {
                                                size_t rn_i, rn_ac = launch_data_array_get_count(rnames);
 
                                                for (rn_i = 0; rn_i < rn_ac; rn_i++) {
                                                        launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i);
 
-                                                       rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp));
-                                                       if (rvs_fd)
-                                                               launch_data_array_append(rvs_fds, rvs_fd);
+                                                       do_rendezvous_magic(res, launch_data_get_string(rn_tmp), joblabel);
                                                }
                                        }
+                                       /* <rdar://problem/3964648> Launchd should not register the same service more than once */
+                                       /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
+                                       rendezvous = false;
                                }
                        } else {
                                if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
@@ -637,11 +651,6 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data
                                }
                        }
                        val = launch_data_new_fd(sfd);
-                       if (rvs_fd) {
-                               /* <rdar://problem/3964648> Launchd should not register the same service more than once */
-                               /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
-                               rendezvous = false;
-                       }
                        launch_data_array_append(fdarray, val);
                }
        }
@@ -692,36 +701,87 @@ static void do_mgroup_join(int fd, int family, int socktype, int protocol, const
        freeaddrinfo(res0);
 }
 
+struct bonjour_magic {
+       SLIST_ENTRY(bonjour_magic) sle;
+       char *str;
+       int port;
+       char label[0];
+};
+
+static SLIST_HEAD(, bonjour_magic) bm_later = { NULL };
 
-static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv)
+void
+do_rendezvous_magic(const struct addrinfo *res, const char *serv, const char *joblabel)
 {
-       struct stat sb;
-       DNSServiceRef service;
+       struct bonjour_magic *bm = calloc(1, sizeof(struct bonjour_magic) + strlen(joblabel) + 1);
+       const char *typestr = "udp";
+
+       if (res->ai_socktype == SOCK_STREAM)
+               typestr = "tcp";
+
+       strcpy(bm->label, joblabel);
+
+       asprintf(&bm->str, "_%s._%s.", serv, typestr);
+
+       if (res->ai_family == AF_INET) {
+               bm->port = ((struct sockaddr_in *)res->ai_addr)->sin_port;
+       } else {
+               bm->port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
+       }
+
+       SLIST_INSERT_HEAD(&bm_later, bm, sle);
+}
+
+
+void
+workaround_bonjour_asynchronously(void)
+{
+       launch_data_t resp, msg, msgpayload, tmpa;
+       struct bonjour_magic *bm;
        DNSServiceErrorType error;
-       char rvs_buf[200];
-       short port;
-       static int statres = 1;
+       DNSServiceRef service;
+       int fd;
 
-       if (1 == statres)
-               statres = stat("/usr/sbin/mDNSResponder", &sb);
+       if (fork() != 0)
+               return;
+       
+       signal(SIGHUP, SIG_IGN);
+       setsid();
+       sleep(30);
 
-       if (-1 == statres)
-               return NULL;
+       msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+       msgpayload = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+
+       SLIST_FOREACH(bm, &bm_later, sle) {
+               service = NULL;
+               error = DNSServiceRegister(&service, 0, 0, NULL, bm->str, NULL, NULL, bm->port, 0, NULL, NULL, NULL);
+               if (error != kDNSServiceErr_NoError) {
+                       fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", bm->str, error);
+                       continue;
+               }
+               fd = DNSServiceRefSockFD(service);
+               tmpa = launch_data_dict_lookup(msgpayload, bm->label);
+               if (!tmpa) {
+                       tmpa = launch_data_alloc(LAUNCH_DATA_ARRAY);
+                       launch_data_dict_insert(msgpayload, tmpa, bm->label);
+               }
+               launch_data_array_append(tmpa, launch_data_new_fd(fd));
+       }
 
-       sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp");
+       launch_data_dict_insert(msg, msgpayload, LAUNCH_KEY_WORKAROUNDBONJOUR);
 
-       if (res->ai_family == AF_INET)
-               port = ((struct sockaddr_in *)res->ai_addr)->sin_port;
-       else
-               port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
+       resp = launch_msg(msg);
 
-       error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL);
+       launch_data_free(msg);
 
-       if (error == kDNSServiceErr_NoError)
-               return launch_data_new_fd(DNSServiceRefSockFD(service));
+       if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
+               errno = launch_data_get_errno(resp);
+               fprintf(stderr, "Workaround Bonjour: %s\n", strerror(errno));
+       }
 
-       fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error);
-       return NULL;
+       launch_data_free(resp);
+
+       _exit(EXIT_SUCCESS);
 }
 
 static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile)
@@ -732,7 +792,7 @@ static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile)
        SInt32            errorCode;
        CFURLRef          fileURL;
 
-       fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, posixfile, strlen(posixfile), false);
+       fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
        if (!fileURL)
                fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
        if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode))
@@ -750,7 +810,7 @@ static void WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posix
        CFURLRef        fileURL;
        SInt32          errorCode;
 
-       fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, posixfile, strlen(posixfile), false);
+       fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
        if (!fileURL)
                fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
        resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
@@ -866,7 +926,7 @@ static int _fd(int fd)
 
 static int load_and_unload_cmd(int argc, char *const argv[])
 {
-       launch_data_t pass1, pass2;
+       launch_data_t pass0, pass1;
        int i, ch;
        bool wflag = false;
        bool lflag = false;
@@ -892,7 +952,8 @@ static int load_and_unload_cmd(int argc, char *const argv[])
                return 1;
        }
 
-       /* I wish I didn't need to do two passes, but I need to load mDNSResponder and use it too.
+       /* I wish I didn't need to do multiple passes, but I need to load mDNSResponder and use it too.
+        * And loading legacy mach init jobs is extra fun.
         *
         * In later versions of launchd, I hope to load everything in the first pass,
         * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
@@ -900,44 +961,102 @@ static int load_and_unload_cmd(int argc, char *const argv[])
         * launchd doesn't have reload support right now.
         */
 
+       pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
        pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
-       pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
 
        for (i = 0; i < argc; i++)
-               readpath(argv[i], pass1, pass2, wflag, lflag, Fflag);
+               readpath(argv[i], pass0, pass1, wflag, lflag, Fflag);
 
-       if (0 == launch_data_array_get_count(pass1) && 0 == launch_data_array_get_count(pass2)) {
+       if (launch_data_array_get_count(pass0) == 0 &&
+                       launch_data_array_get_count(pass1) == 0) {
                fprintf(stderr, "nothing found to %s\n", lflag ? "load" : "unload");
+               launch_data_free(pass0);
                launch_data_free(pass1);
-               launch_data_free(pass2);
                return 1;
        }
        
        if (lflag) {
-               if (0 < launch_data_array_get_count(pass1)) {
-                       submit_job_pass(pass1);
-               }
-               if (0 < launch_data_array_get_count(pass2)) {
-                       submit_job_pass(pass2);
-               }
+               distill_jobs(pass1);
+               submit_mach_jobs(pass0);
+               workaround_bonjour_asynchronously();
+               submit_job_pass(pass1);
+               let_go_of_mach_jobs();
        } else {
                for (i = 0; i < (int)launch_data_array_get_count(pass1); i++)
                        unloadjob(launch_data_array_get_index(pass1, i));
-               for (i = 0; i < (int)launch_data_array_get_count(pass2); i++)
-                       unloadjob(launch_data_array_get_index(pass2, i));
        }
 
        return 0;
 }
 
-static void submit_job_pass(launch_data_t jobs)
+static mach_port_t *msrvs = NULL;
+static size_t msrvs_cnt = 0;
+
+void
+submit_mach_jobs(launch_data_t jobs)
+{
+       size_t i, c;
+
+       c = launch_data_array_get_count(jobs);
+
+       msrvs = calloc(1, sizeof(mach_port_t) * c);
+       msrvs_cnt = c;
+
+       for (i = 0; i < c; i++) {
+               launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
+               const char *sn = NULL, *cmd = NULL;
+               bool d = true, k = false;
+               mach_port_t msr, msv, mhp;
+               kern_return_t kr;
+               uid_t u = getuid();
+
+               if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND)))
+                       d = launch_data_get_bool(tmp);
+               if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ISKUNCSERVER)))
+                       k = launch_data_get_bool(tmp);
+               if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME)))
+                       sn = launch_data_get_string(tmp);
+               if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND)))
+                       cmd = launch_data_get_string(tmp);
+
+               if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) {
+                       fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr);
+                       continue;
+               }
+               if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) {
+                       fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr);
+                       mach_port_destroy(mach_task_self(), msr);
+                       continue;
+               }
+               if (k) {
+                       mhp = mach_host_self();
+                       if ((kr = host_set_UNDServer(mhp, msv)) != KERN_SUCCESS)
+                               fprintf(stderr, "%s: host_set_UNDServer(): %s\n", getprogname(), mach_error_string(kr));
+                       mach_port_deallocate(mach_task_self(), mhp);
+               }
+               mach_port_deallocate(mach_task_self(), msv);
+               msrvs[i] = msr;
+       }
+}
+
+void
+let_go_of_mach_jobs(void)
+{
+       size_t i;
+
+       for (i = 0; i < msrvs_cnt; i++)
+               mach_port_destroy(mach_task_self(), msrvs[i]);
+}
+
+void
+submit_job_pass(launch_data_t jobs)
 {
        launch_data_t msg, resp;
        size_t i;
        int e;
 
-       for (i = 0; i < launch_data_array_get_count(jobs); i++)
-               distill_config_file(launch_data_array_get_index(jobs, i));
+       if (launch_data_array_get_count(jobs) == 0)
+               return;
 
        msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
 
@@ -1262,6 +1381,68 @@ static int logupdate_cmd(int argc, char *const argv[])
        return r;
 }
 
+static const struct {
+       const char *name;
+       int lim;
+} limlookup[] = {
+       { "cpu",        RLIMIT_CPU },
+       { "filesize",   RLIMIT_FSIZE },
+       { "data",       RLIMIT_DATA },
+       { "stack",      RLIMIT_STACK },
+       { "core",       RLIMIT_CORE },
+       { "rss",        RLIMIT_RSS },
+       { "memlock",    RLIMIT_MEMLOCK },
+       { "maxproc",    RLIMIT_NPROC },
+       { "maxfiles",   RLIMIT_NOFILE }
+};
+
+static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
+
+static ssize_t name2num(const char *n)
+{
+       size_t i;
+
+       for (i = 0; i < limlookupcnt; i++) {
+               if (!strcmp(limlookup[i].name, n)) {
+                       return limlookup[i].lim;
+               }
+       }
+       return -1;
+}
+
+static const char *num2name(int n)
+{
+       size_t i;
+
+       for (i = 0; i < limlookupcnt; i++) {
+               if (limlookup[i].lim == n)
+                       return limlookup[i].name;
+       }
+       return NULL;
+}
+
+static const char *lim2str(rlim_t val, char *buf)
+{
+       if (val == RLIM_INFINITY)
+               strcpy(buf, "unlimited");
+       else
+               sprintf(buf, "%lld", val);
+       return buf;
+}
+
+static bool str2lim(const char *buf, rlim_t *res)
+{
+       char *endptr;
+       *res = strtoll(buf, &endptr, 10);
+       if (!strcmp(buf, "unlimited")) {
+               *res = RLIM_INFINITY;
+               return false;
+       } else if (*endptr == '\0') {
+                return false;
+       }
+       return true;
+}
+
 static int limit_cmd(int argc __attribute__((unused)), char *const argv[])
 {
        char slimstr[100];
@@ -1269,58 +1450,10 @@ static int limit_cmd(int argc __attribute__((unused)), char *const argv[])
        struct rlimit *lmts = NULL;
        launch_data_t resp, resp1 = NULL, msg, tmp;
        int r = 0;
-       size_t i, lsz = -1, which = 0;
+       size_t i, lsz = -1;
+       ssize_t which = 0;
        rlim_t slim = -1, hlim = -1;
        bool badargs = false;
-       static const struct {
-               const char *name;
-               int lim;
-       } limlookup[] = {
-               { "cpu",        RLIMIT_CPU },
-               { "filesize",   RLIMIT_FSIZE },
-               { "data",       RLIMIT_DATA },
-               { "stack",      RLIMIT_STACK },
-               { "core",       RLIMIT_CORE },
-               { "rss",        RLIMIT_RSS },
-               { "memlock",    RLIMIT_MEMLOCK },
-               { "maxproc",    RLIMIT_NPROC },
-               { "maxfiles",   RLIMIT_NOFILE }
-       };
-       size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
-       bool name2num(const char *n) {
-               for (i = 0; i < limlookupcnt; i++) {
-                       if (!strcmp(limlookup[i].name, n)) {
-                               which = limlookup[i].lim;
-                               return false;
-                       }
-               }
-               return true;
-       };
-       const char *num2name(int n) {
-               for (i = 0; i < limlookupcnt; i++) {
-                       if (limlookup[i].lim == n)
-                               return limlookup[i].name;
-               }
-               return NULL;
-       };
-       const char *lim2str(rlim_t val, char *buf) {
-               if (val == RLIM_INFINITY)
-                       strcpy(buf, "unlimited");
-               else
-                       sprintf(buf, "%lld", val);
-               return buf;
-       };
-       bool str2lim(const char *buf, rlim_t *res) {
-               char *endptr;
-               *res = strtoll(buf, &endptr, 10);
-               if (!strcmp(buf, "unlimited")) {
-                       *res = RLIM_INFINITY;
-                       return false;
-               } else if (*endptr == '\0') {
-                        return false;
-               }
-               return true;
-       };
 
        if (argc > 4)
                badargs = true;
@@ -1333,7 +1466,7 @@ static int limit_cmd(int argc __attribute__((unused)), char *const argv[])
        if (argc == 4 && str2lim(argv[3], &hlim))
                badargs = true;
 
-       if (argc >= 2 && name2num(argv[1]))
+       if (argc >= 2 && -1 == (which = name2num(argv[1])))
                badargs = true;
 
        if (badargs) {
@@ -1356,7 +1489,7 @@ static int limit_cmd(int argc __attribute__((unused)), char *const argv[])
                lsz = launch_data_get_opaque_size(resp);
                if (argc <= 2) {
                        for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
-                               if (argc == 2 && which != i)
+                               if (argc == 2 && (size_t)which != i)
                                        continue;
                                fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i),
                                                lim2str(lmts[i].rlim_cur, slimstr),
@@ -1518,3 +1651,13 @@ static bool launch_data_array_append(launch_data_t a, launch_data_t o)
 
        return launch_data_array_set_index(a, o, offt);
 }
+
+bool
+is_legacy_mach_job(launch_data_t obj)
+{
+       bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
+       bool has_command  = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
+       bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
+
+       return has_command && has_servicename && !has_label;
+}