]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchctl.c
launchd-106.13.tar.gz
[apple/launchd.git] / launchd / src / launchctl.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <mach/mach.h>
25 #include <servers/bootstrap.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/fcntl.h>
32 #include <sys/event.h>
33 #include <sys/resource.h>
34 #include <sys/param.h>
35 #include <netinet/in.h>
36 #include <unistd.h>
37 #include <dirent.h>
38 #include <libgen.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <netdb.h>
45 #include <syslog.h>
46 #include <readline/readline.h>
47 #include <readline/history.h>
48 #include <dns_sd.h>
49
50 #include "launch.h"
51 #include "launch_priv.h"
52
53 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
54
55 #define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
56 #define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
57 #define MACHINIT_JOBKEY_COMMAND "Command"
58 #define MACHINIT_JOBKEY_ISKUNCSERVER "isKUNCServer"
59
60
61 static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
62 static bool launch_data_array_append(launch_data_t a, launch_data_t o);
63 static void distill_jobs(launch_data_t);
64 static void distill_config_file(launch_data_t);
65 static void sock_dict_cb(launch_data_t what, const char *key, void *context);
66 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
67 static launch_data_t CF2launch_data(CFTypeRef);
68 static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
69 static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
70 static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
71 static void readpath(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload);
72 static void readfile(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload);
73 static int _fd(int);
74 static int demux_cmd(int argc, char *const argv[]);
75 static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv);
76 static void submit_job_pass(launch_data_t jobs);
77 static void submit_mach_jobs(launch_data_t jobs);
78 static void let_go_of_mach_jobs(void);
79 static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
80 static void print_jobs(launch_data_t j, const char *label, void *context);
81 static bool is_legacy_mach_job(launch_data_t obj);
82 static bool delay_to_second_pass(launch_data_t o);
83 static void delay_to_second_pass2(launch_data_t o, const char *key, void *context);
84
85 static int load_and_unload_cmd(int argc, char *const argv[]);
86 //static int reload_cmd(int argc, char *const argv[]);
87 static int start_and_stop_cmd(int argc, char *const argv[]);
88 static int list_cmd(int argc, char *const argv[]);
89
90 static int setenv_cmd(int argc, char *const argv[]);
91 static int unsetenv_cmd(int argc, char *const argv[]);
92 static int getenv_and_export_cmd(int argc, char *const argv[]);
93
94 static int limit_cmd(int argc, char *const argv[]);
95 static int stdio_cmd(int argc, char *const argv[]);
96 static int fyi_cmd(int argc, char *const argv[]);
97 static int logupdate_cmd(int argc, char *const argv[]);
98 static int umask_cmd(int argc, char *const argv[]);
99 static int getrusage_cmd(int argc, char *const argv[]);
100
101 static int help_cmd(int argc, char *const argv[]);
102
103 static const struct {
104 const char *name;
105 int (*func)(int argc, char *const argv[]);
106 const char *desc;
107 } cmds[] = {
108 { "load", load_and_unload_cmd, "Load configuration files and/or directories" },
109 { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
110 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
111 { "start", start_and_stop_cmd, "Start specified jobs" },
112 { "stop", start_and_stop_cmd, "Stop specified jobs" },
113 { "list", list_cmd, "List jobs and information about jobs" },
114 { "setenv", setenv_cmd, "Set an environmental variable in launchd" },
115 { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
116 { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
117 { "export", getenv_and_export_cmd, "Export shell settings from launchd" },
118 { "limit", limit_cmd, "View and adjust launchd resource limits" },
119 { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
120 { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
121 { "shutdown", fyi_cmd, "Prepare for system shutdown" },
122 { "reloadttys", fyi_cmd, "Reload /etc/ttys" },
123 { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
124 { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
125 { "umask", umask_cmd, "Change launchd's umask" },
126 { "help", help_cmd, "This help output" },
127 };
128
129 int main(int argc, char *const argv[])
130 {
131 bool istty = isatty(STDIN_FILENO);
132 char *l;
133
134 if (argc > 1)
135 exit(demux_cmd(argc - 1, argv + 1));
136
137 if (NULL == readline) {
138 fprintf(stderr, "missing library: readline\n");
139 exit(EXIT_FAILURE);
140 }
141
142 while ((l = readline(istty ? "launchd% " : NULL))) {
143 char *inputstring = l, *argv2[100], **ap = argv2;
144 int i = 0;
145
146 while ((*ap = strsep(&inputstring, " \t"))) {
147 if (**ap != '\0') {
148 ap++;
149 i++;
150 }
151 }
152
153 if (i > 0)
154 demux_cmd(i, argv2);
155
156 free(l);
157 }
158
159 if (istty)
160 fputc('\n', stdout);
161
162 exit(EXIT_SUCCESS);
163 }
164
165 static int demux_cmd(int argc, char *const argv[])
166 {
167 size_t i;
168
169 optind = 1;
170 optreset = 1;
171
172 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
173 if (!strcmp(cmds[i].name, argv[0]))
174 return cmds[i].func(argc, argv);
175 }
176
177 fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]);
178 return 1;
179 }
180
181 static int unsetenv_cmd(int argc, char *const argv[])
182 {
183 launch_data_t resp, tmp, msg;
184
185 if (argc != 2) {
186 fprintf(stderr, "%s usage: unsetenv <key>\n", getprogname());
187 return 1;
188 }
189
190 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
191
192 tmp = launch_data_new_string(argv[1]);
193 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT);
194
195 resp = launch_msg(msg);
196
197 launch_data_free(msg);
198
199 if (resp) {
200 launch_data_free(resp);
201 } else {
202 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno));
203 }
204
205 return 0;
206 }
207
208 static int setenv_cmd(int argc, char *const argv[])
209 {
210 launch_data_t resp, tmp, tmpv, msg;
211
212 if (argc != 3) {
213 fprintf(stderr, "%s usage: setenv <key> <value>\n", getprogname());
214 return 1;
215 }
216
217 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
218 tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
219
220 tmpv = launch_data_new_string(argv[2]);
221 launch_data_dict_insert(tmp, tmpv, argv[1]);
222 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT);
223
224 resp = launch_msg(msg);
225 launch_data_free(msg);
226
227 if (resp) {
228 launch_data_free(resp);
229 } else {
230 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno));
231 }
232
233 return 0;
234 }
235
236 static void print_launchd_env(launch_data_t obj, const char *key, void *context)
237 {
238 bool *is_csh = context;
239
240 if (*is_csh)
241 fprintf(stdout, "setenv %s %s;\n", key, launch_data_get_string(obj));
242 else
243 fprintf(stdout, "%s=%s; export %s;\n", key, launch_data_get_string(obj), key);
244 }
245
246 static void print_key_value(launch_data_t obj, const char *key, void *context)
247 {
248 const char *k = context;
249
250 if (!strcmp(key, k))
251 fprintf(stdout, "%s\n", launch_data_get_string(obj));
252 }
253
254 static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused)))
255 {
256 launch_data_t resp, msg;
257 bool is_csh = false;
258 char *k;
259
260 if (!strcmp(argv[0], "export")) {
261 char *s = getenv("SHELL");
262 if (s)
263 is_csh = strstr(s, "csh") ? true : false;
264 } else if (argc != 2) {
265 fprintf(stderr, "%s usage: getenv <key>\n", getprogname());
266 return 1;
267 }
268
269 k = argv[1];
270
271 msg = launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT);
272
273 resp = launch_msg(msg);
274 launch_data_free(msg);
275
276 if (resp) {
277 if (!strcmp(argv[0], "export"))
278 launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
279 else
280 launch_data_dict_iterate(resp, print_key_value, k);
281 launch_data_free(resp);
282 } else {
283 fprintf(stderr, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT "\"): %s\n", strerror(errno));
284 }
285 return 0;
286 }
287
288 static void unloadjob(launch_data_t job)
289 {
290 launch_data_t resp, tmp, tmps, msg;
291 int e;
292
293 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
294 tmp = launch_data_alloc(LAUNCH_DATA_STRING);
295 tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
296
297 if (!tmps) {
298 fprintf(stderr, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL);
299 return;
300 }
301
302 launch_data_set_string(tmp, launch_data_get_string(tmps));
303 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_REMOVEJOB);
304 resp = launch_msg(msg);
305 launch_data_free(msg);
306 if (!resp) {
307 fprintf(stderr, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno));
308 return;
309 }
310 if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) {
311 if ((e = launch_data_get_errno(resp)))
312 fprintf(stderr, "%s\n", strerror(e));
313 }
314 launch_data_free(resp);
315 }
316
317 launch_data_t
318 read_plist_file(const char *file, bool editondisk, bool load)
319 {
320 CFPropertyListRef plist = CreateMyPropertyListFromFile(file);
321 launch_data_t r = NULL;
322
323 if (NULL == plist) {
324 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), file);
325 return NULL;
326 }
327
328 if (editondisk) {
329 if (load)
330 CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED));
331 else
332 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue);
333 WriteMyPropertyListToFile(plist, file);
334 }
335
336 r = CF2launch_data(plist);
337
338 CFRelease(plist);
339
340 return r;
341 }
342
343 void
344 delay_to_second_pass2(launch_data_t o, const char *key, void *context)
345 {
346 bool *res = context;
347 size_t i;
348
349 if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) {
350 *res = true;
351 return;
352 }
353
354 switch (launch_data_get_type(o)) {
355 case LAUNCH_DATA_DICTIONARY:
356 launch_data_dict_iterate(o, delay_to_second_pass2, context);
357 break;
358 case LAUNCH_DATA_ARRAY:
359 for (i = 0; i < launch_data_array_get_count(o); i++)
360 delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context);
361 break;
362 default:
363 break;
364 }
365 }
366
367 bool
368 delay_to_second_pass(launch_data_t o)
369 {
370 bool res = false;
371
372 launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS);
373
374 if (NULL == socks)
375 return false;
376
377 delay_to_second_pass2(socks, NULL, &res);
378
379 return res;
380 }
381
382 void
383 readfile(const char *what, launch_data_t pass0, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload)
384 {
385 launch_data_t tmpd, thejob;
386 bool job_disabled = false;
387
388 if (NULL == (thejob = read_plist_file(what, editondisk, load))) {
389 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), what);
390 return;
391 }
392
393 if (is_legacy_mach_job(thejob)) {
394 launch_data_array_append(pass0, thejob);
395 return;
396 }
397
398 if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
399 fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what);
400 launch_data_free(thejob);
401 return;
402 }
403
404 if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED)))
405 job_disabled = launch_data_get_bool(tmpd);
406
407 if (forceload)
408 job_disabled = false;
409
410 if (job_disabled && load) {
411 launch_data_free(thejob);
412 return;
413 }
414
415 if (delay_to_second_pass(thejob))
416 launch_data_array_append(pass2, thejob);
417 else
418 launch_data_array_append(pass1, thejob);
419 }
420
421 void
422 readpath(const char *what, launch_data_t pass0, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload)
423 {
424 char buf[MAXPATHLEN];
425 struct stat sb;
426 struct dirent *de;
427 DIR *d;
428
429 if (stat(what, &sb) == -1)
430 return;
431
432 if (S_ISREG(sb.st_mode) && !(sb.st_mode & S_IWOTH)) {
433 readfile(what, pass0, pass1, pass2, editondisk, load, forceload);
434 } else {
435 if ((d = opendir(what)) == NULL) {
436 fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname());
437 return;
438 }
439
440 while ((de = readdir(d))) {
441 if ((de->d_name[0] == '.'))
442 continue;
443 snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
444
445 readfile(buf, pass0, pass1, pass2, editondisk, load, forceload);
446 }
447 closedir(d);
448 }
449 }
450
451 struct distill_context {
452 launch_data_t base;
453 launch_data_t newsockdict;
454 };
455
456 void
457 distill_jobs(launch_data_t jobs)
458 {
459 size_t i, c = launch_data_array_get_count(jobs);
460
461 for (i = 0; i < c; i++)
462 distill_config_file(launch_data_array_get_index(jobs, i));
463 }
464
465 void
466 distill_config_file(launch_data_t id_plist)
467 {
468 struct distill_context dc = { id_plist, NULL };
469 launch_data_t tmp;
470
471 if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_SOCKETS))) {
472 dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
473 launch_data_dict_iterate(tmp, sock_dict_cb, &dc);
474 launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS);
475 }
476 }
477
478 static void sock_dict_cb(launch_data_t what, const char *key, void *context)
479 {
480 struct distill_context *dc = context;
481 launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY);
482
483 launch_data_dict_insert(dc->newsockdict, fdarray, key);
484
485 if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) {
486 sock_dict_edit_entry(what, key, fdarray, dc->base);
487 } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) {
488 launch_data_t tmp;
489 size_t i;
490
491 for (i = 0; i < launch_data_array_get_count(what); i++) {
492 tmp = launch_data_array_get_index(what, i);
493 sock_dict_edit_entry(tmp, key, fdarray, dc->base);
494 }
495 }
496 }
497
498 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
499 {
500 launch_data_t a, val;
501 int sfd, st = SOCK_STREAM;
502 bool passive = true;
503
504 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
505 if (!strcasecmp(launch_data_get_string(val), "stream")) {
506 st = SOCK_STREAM;
507 } else if (!strcasecmp(launch_data_get_string(val), "dgram")) {
508 st = SOCK_DGRAM;
509 } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) {
510 st = SOCK_SEQPACKET;
511 }
512 }
513
514 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE)))
515 passive = launch_data_get_bool(val);
516
517 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) {
518 char secdir[] = LAUNCH_SECDIR, buf[1024];
519 launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
520
521 if (NULL == uenv) {
522 uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
523 launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
524 }
525
526 mkdtemp(secdir);
527
528 sprintf(buf, "%s/%s", secdir, key);
529
530 a = launch_data_new_string(buf);
531 launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME);
532 a = launch_data_new_string(buf);
533 launch_data_dict_insert(uenv, a, launch_data_get_string(val));
534 }
535
536 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
537 struct sockaddr_un sun;
538 mode_t sun_mode = 0;
539 mode_t oldmask;
540 bool setm = false;
541
542 memset(&sun, 0, sizeof(sun));
543
544 sun.sun_family = AF_UNIX;
545
546 strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path));
547
548 if ((sfd = _fd(socket(AF_UNIX, st, 0))) == -1)
549 return;
550
551 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
552 sun_mode = (mode_t)launch_data_get_integer(val);
553 setm = true;
554 }
555
556 if (passive) {
557 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
558 close(sfd);
559 return;
560 }
561 oldmask = umask(S_IRWXG|S_IRWXO);
562 if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
563 close(sfd);
564 umask(oldmask);
565 return;
566 }
567 umask(oldmask);
568 if (setm) {
569 chmod(sun.sun_path, sun_mode);
570 }
571 if ((st == SOCK_STREAM || st == SOCK_SEQPACKET)
572 && listen(sfd, SOMAXCONN) == -1) {
573 close(sfd);
574 return;
575 }
576 } else if (connect(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
577 close(sfd);
578 return;
579 }
580
581 val = launch_data_new_fd(sfd);
582 launch_data_array_append(fdarray, val);
583 } else {
584 launch_data_t rnames = NULL;
585 const char *node = NULL, *serv = NULL, *mgroup = NULL;
586 char servnbuf[50];
587 struct addrinfo hints, *res0, *res;
588 int gerr, sock_opt = 1;
589 bool rendezvous = false;
590
591 memset(&hints, 0, sizeof(hints));
592
593 hints.ai_socktype = st;
594 if (passive)
595 hints.ai_flags |= AI_PASSIVE;
596
597 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME)))
598 node = launch_data_get_string(val);
599 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP)))
600 mgroup = launch_data_get_string(val);
601 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) {
602 if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) {
603 sprintf(servnbuf, "%lld", launch_data_get_integer(val));
604 serv = servnbuf;
605 } else {
606 serv = launch_data_get_string(val);
607 }
608 }
609 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) {
610 if (!strcasecmp("IPv4", launch_data_get_string(val)))
611 hints.ai_family = AF_INET;
612 else if (!strcasecmp("IPv6", launch_data_get_string(val)))
613 hints.ai_family = AF_INET6;
614 }
615 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) {
616 if (!strcasecmp("TCP", launch_data_get_string(val)))
617 hints.ai_protocol = IPPROTO_TCP;
618 }
619 if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) {
620 rendezvous = true;
621 if (LAUNCH_DATA_BOOL == launch_data_get_type(rnames)) {
622 rendezvous = launch_data_get_bool(rnames);
623 rnames = NULL;
624 }
625 }
626
627 if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) {
628 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
629 return;
630 }
631
632 for (res = res0; res; res = res->ai_next) {
633 launch_data_t rvs_fd = NULL;
634 if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
635 fprintf(stderr, "socket(): %s\n", strerror(errno));
636 return;
637 }
638 if (hints.ai_flags & AI_PASSIVE) {
639 if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY,
640 (void *)&sock_opt, sizeof(sock_opt))) {
641 fprintf(stderr, "setsockopt(IPV6_V6ONLY): %m");
642 return;
643 }
644 if (mgroup) {
645 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
646 fprintf(stderr, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno));
647 return;
648 }
649 } else {
650 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
651 fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
652 return;
653 }
654 }
655 if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
656 fprintf(stderr, "bind(): %s\n", strerror(errno));
657 return;
658 }
659
660 if (mgroup) {
661 do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
662 }
663 if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET)
664 && listen(sfd, SOMAXCONN) == -1) {
665 fprintf(stderr, "listen(): %s\n", strerror(errno));
666 return;
667 }
668 if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) &&
669 (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) {
670 launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS);
671 if (NULL == rvs_fds) {
672 rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY);
673 launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS);
674 }
675 if (NULL == rnames) {
676 rvs_fd = do_rendezvous_magic(res, serv);
677 if (rvs_fd)
678 launch_data_array_append(rvs_fds, rvs_fd);
679 } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) {
680 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames));
681 if (rvs_fd)
682 launch_data_array_append(rvs_fds, rvs_fd);
683 } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) {
684 size_t rn_i, rn_ac = launch_data_array_get_count(rnames);
685
686 for (rn_i = 0; rn_i < rn_ac; rn_i++) {
687 launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i);
688
689 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp));
690 if (rvs_fd)
691 launch_data_array_append(rvs_fds, rvs_fd);
692 }
693 }
694 }
695 } else {
696 if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
697 fprintf(stderr, "connect(): %s\n", strerror(errno));
698 return;
699 }
700 }
701 val = launch_data_new_fd(sfd);
702 if (rvs_fd) {
703 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
704 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
705 rendezvous = false;
706 }
707 launch_data_array_append(fdarray, val);
708 }
709 }
710 }
711
712 static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup)
713 {
714 struct addrinfo hints, *res0, *res;
715 struct ip_mreq mreq;
716 struct ipv6_mreq m6req;
717 int gerr;
718
719 memset(&hints, 0, sizeof(hints));
720
721 hints.ai_flags |= AI_PASSIVE;
722 hints.ai_family = family;
723 hints.ai_socktype = socktype;
724 hints.ai_protocol = protocol;
725
726 if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) {
727 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
728 return;
729 }
730
731 for (res = res0; res; res = res->ai_next) {
732 if (AF_INET == family) {
733 memset(&mreq, 0, sizeof(mreq));
734 mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
735 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
736 fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
737 continue;
738 }
739 break;
740 } else if (AF_INET6 == family) {
741 memset(&m6req, 0, sizeof(m6req));
742 m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
743 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, sizeof(m6req)) == -1) {
744 fprintf(stderr, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno));
745 continue;
746 }
747 break;
748 } else {
749 fprintf(stderr, "unknown family during multicast group bind!\n");
750 break;
751 }
752 }
753
754 freeaddrinfo(res0);
755 }
756
757
758 static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv)
759 {
760 struct stat sb;
761 DNSServiceRef service;
762 DNSServiceErrorType error;
763 char rvs_buf[200];
764 short port;
765 static int statres = 1;
766
767 if (1 == statres)
768 statres = stat("/usr/sbin/mDNSResponder", &sb);
769
770 if (-1 == statres)
771 return NULL;
772
773 sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp");
774
775 if (res->ai_family == AF_INET)
776 port = ((struct sockaddr_in *)res->ai_addr)->sin_port;
777 else
778 port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
779
780 error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL);
781
782 if (error == kDNSServiceErr_NoError)
783 return launch_data_new_fd(DNSServiceRefSockFD(service));
784
785 fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error);
786 return NULL;
787 }
788
789 static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile)
790 {
791 CFPropertyListRef propertyList;
792 CFStringRef errorString;
793 CFDataRef resourceData;
794 SInt32 errorCode;
795 CFURLRef fileURL;
796
797 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
798 if (!fileURL)
799 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
800 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode))
801 fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
802 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
803 if (!propertyList)
804 fprintf(stderr, "%s: propertyList is NULL\n", getprogname());
805
806 return propertyList;
807 }
808
809 static void WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
810 {
811 CFDataRef resourceData;
812 CFURLRef fileURL;
813 SInt32 errorCode;
814
815 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
816 if (!fileURL)
817 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
818 resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
819 if (resourceData == NULL)
820 fprintf(stderr, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile);
821 if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode))
822 fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
823 }
824
825 void myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
826 {
827 launch_data_t ik, iw, where = context;
828
829 ik = CF2launch_data(key);
830 iw = CF2launch_data(value);
831
832 launch_data_dict_insert(where, iw, launch_data_get_string(ik));
833 launch_data_free(ik);
834 }
835
836 static launch_data_t CF2launch_data(CFTypeRef cfr)
837 {
838 launch_data_t r;
839 CFTypeID cft = CFGetTypeID(cfr);
840
841 if (cft == CFStringGetTypeID()) {
842 char buf[4096];
843 CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
844 r = launch_data_alloc(LAUNCH_DATA_STRING);
845 launch_data_set_string(r, buf);
846 } else if (cft == CFBooleanGetTypeID()) {
847 r = launch_data_alloc(LAUNCH_DATA_BOOL);
848 launch_data_set_bool(r, CFBooleanGetValue(cfr));
849 } else if (cft == CFArrayGetTypeID()) {
850 CFIndex i, ac = CFArrayGetCount(cfr);
851 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
852 for (i = 0; i < ac; i++) {
853 CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
854 if (v) {
855 launch_data_t iv = CF2launch_data(v);
856 launch_data_array_set_index(r, iv, i);
857 }
858 }
859 } else if (cft == CFDictionaryGetTypeID()) {
860 r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
861 CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
862 } else if (cft == CFDataGetTypeID()) {
863 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
864 launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
865 } else if (cft == CFNumberGetTypeID()) {
866 long long n;
867 double d;
868 CFNumberType cfnt = CFNumberGetType(cfr);
869 switch (cfnt) {
870 case kCFNumberSInt8Type:
871 case kCFNumberSInt16Type:
872 case kCFNumberSInt32Type:
873 case kCFNumberSInt64Type:
874 case kCFNumberCharType:
875 case kCFNumberShortType:
876 case kCFNumberIntType:
877 case kCFNumberLongType:
878 case kCFNumberLongLongType:
879 CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
880 r = launch_data_alloc(LAUNCH_DATA_INTEGER);
881 launch_data_set_integer(r, n);
882 break;
883 case kCFNumberFloat32Type:
884 case kCFNumberFloat64Type:
885 case kCFNumberFloatType:
886 case kCFNumberDoubleType:
887 CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
888 r = launch_data_alloc(LAUNCH_DATA_REAL);
889 launch_data_set_real(r, d);
890 break;
891 default:
892 r = NULL;
893 break;
894 }
895 } else {
896 r = NULL;
897 }
898 return r;
899 }
900
901 static int help_cmd(int argc, char *const argv[])
902 {
903 FILE *where = stdout;
904 int l, cmdwidth = 0;
905 size_t i;
906
907 if (argc == 0 || argv == NULL)
908 where = stderr;
909
910 fprintf(where, "usage: %s <subcommand>\n", getprogname());
911 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
912 l = strlen(cmds[i].name);
913 if (l > cmdwidth)
914 cmdwidth = l;
915 }
916 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++)
917 fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc);
918
919 return 0;
920 }
921
922 static int _fd(int fd)
923 {
924 if (fd >= 0)
925 fcntl(fd, F_SETFD, 1);
926 return fd;
927 }
928
929 static int load_and_unload_cmd(int argc, char *const argv[])
930 {
931 launch_data_t pass0, pass1, pass2;
932 int i, ch;
933 bool wflag = false;
934 bool lflag = false;
935 bool Fflag = false;
936
937 if (!strcmp(argv[0], "load"))
938 lflag = true;
939
940 while ((ch = getopt(argc, argv, "wF")) != -1) {
941 switch (ch) {
942 case 'w': wflag = true; break;
943 case 'F': Fflag = true; break;
944 default:
945 fprintf(stderr, "usage: %s load [-wF] paths...\n", getprogname());
946 return 1;
947 }
948 }
949 argc -= optind;
950 argv += optind;
951
952 if (argc == 0) {
953 fprintf(stderr, "usage: %s load [-w] paths...\n", getprogname());
954 return 1;
955 }
956
957 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
958 * And loading legacy mach init jobs is extra fun.
959 *
960 * In later versions of launchd, I hope to load everything in the first pass,
961 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
962 * I haven't thought through the various complexities of reloading jobs, and therefore
963 * launchd doesn't have reload support right now.
964 */
965
966 pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
967 pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
968 pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
969
970 for (i = 0; i < argc; i++)
971 readpath(argv[i], pass0, pass1, pass2, wflag, lflag, Fflag);
972
973 if (launch_data_array_get_count(pass0) == 0 &&
974 launch_data_array_get_count(pass1) == 0 &&
975 launch_data_array_get_count(pass2) == 0) {
976 fprintf(stderr, "nothing found to %s\n", lflag ? "load" : "unload");
977 launch_data_free(pass0);
978 launch_data_free(pass1);
979 launch_data_free(pass2);
980 return 1;
981 }
982
983 if (lflag) {
984 distill_jobs(pass1);
985 submit_mach_jobs(pass0);
986 submit_job_pass(pass1);
987 let_go_of_mach_jobs();
988 distill_jobs(pass2);
989 submit_job_pass(pass2);
990 } else {
991 for (i = 0; i < (int)launch_data_array_get_count(pass1); i++)
992 unloadjob(launch_data_array_get_index(pass1, i));
993 for (i = 0; i < (int)launch_data_array_get_count(pass2); i++)
994 unloadjob(launch_data_array_get_index(pass2, i));
995 }
996
997 return 0;
998 }
999
1000 static mach_port_t *msrvs = NULL;
1001 static size_t msrvs_cnt = 0;
1002
1003 void
1004 submit_mach_jobs(launch_data_t jobs)
1005 {
1006 size_t i, c;
1007
1008 c = launch_data_array_get_count(jobs);
1009
1010 msrvs = calloc(1, sizeof(mach_port_t) * c);
1011 msrvs_cnt = c;
1012
1013 for (i = 0; i < c; i++) {
1014 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1015 const char *sn = NULL, *cmd = NULL;
1016 bool d = true, k = false;
1017 mach_port_t msr, msv, mhp;
1018 kern_return_t kr;
1019 uid_t u = getuid();
1020
1021 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND)))
1022 d = launch_data_get_bool(tmp);
1023 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ISKUNCSERVER)))
1024 k = launch_data_get_bool(tmp);
1025 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME)))
1026 sn = launch_data_get_string(tmp);
1027 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND)))
1028 cmd = launch_data_get_string(tmp);
1029
1030 if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) {
1031 fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr);
1032 continue;
1033 }
1034 if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) {
1035 fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr);
1036 mach_port_destroy(mach_task_self(), msr);
1037 continue;
1038 }
1039 if (k) {
1040 mhp = mach_host_self();
1041 if ((kr = host_set_UNDServer(mhp, msv)) != KERN_SUCCESS)
1042 fprintf(stderr, "%s: host_set_UNDServer(): %s\n", getprogname(), mach_error_string(kr));
1043 mach_port_deallocate(mach_task_self(), mhp);
1044 }
1045 mach_port_deallocate(mach_task_self(), msv);
1046 msrvs[i] = msr;
1047 }
1048 }
1049
1050 void
1051 let_go_of_mach_jobs(void)
1052 {
1053 size_t i;
1054
1055 for (i = 0; i < msrvs_cnt; i++)
1056 mach_port_destroy(mach_task_self(), msrvs[i]);
1057 }
1058
1059 void
1060 submit_job_pass(launch_data_t jobs)
1061 {
1062 launch_data_t msg, resp;
1063 size_t i;
1064 int e;
1065
1066 if (launch_data_array_get_count(jobs) == 0)
1067 return;
1068
1069 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1070
1071 launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
1072
1073 resp = launch_msg(msg);
1074
1075 if (resp) {
1076 switch (launch_data_get_type(resp)) {
1077 case LAUNCH_DATA_ERRNO:
1078 if ((e = launch_data_get_errno(resp)))
1079 fprintf(stderr, "%s\n", strerror(e));
1080 break;
1081 case LAUNCH_DATA_ARRAY:
1082 for (i = 0; i < launch_data_array_get_count(jobs); i++) {
1083 launch_data_t obatind = launch_data_array_get_index(resp, i);
1084 launch_data_t jatind = launch_data_array_get_index(jobs, i);
1085 const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
1086 if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
1087 e = launch_data_get_errno(obatind);
1088 switch (e) {
1089 case EEXIST:
1090 fprintf(stderr, "%s: %s\n", lab4job, "Already loaded");
1091 break;
1092 case ESRCH:
1093 fprintf(stderr, "%s: %s\n", lab4job, "Not loaded");
1094 break;
1095 default:
1096 fprintf(stderr, "%s: %s\n", lab4job, strerror(e));
1097 case 0:
1098 break;
1099 }
1100 }
1101 }
1102 break;
1103 default:
1104 fprintf(stderr, "unknown respose from launchd!\n");
1105 break;
1106 }
1107 launch_data_free(resp);
1108 } else {
1109 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1110 }
1111
1112 launch_data_free(msg);
1113 }
1114
1115 static int start_and_stop_cmd(int argc, char *const argv[])
1116 {
1117 launch_data_t resp, msg;
1118 const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
1119 int e, r = 0;
1120
1121 if (!strcmp(argv[0], "start"))
1122 lmsgcmd = LAUNCH_KEY_STARTJOB;
1123
1124 if (argc != 2) {
1125 fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]);
1126 return 1;
1127 }
1128
1129 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1130 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
1131
1132 resp = launch_msg(msg);
1133 launch_data_free(msg);
1134
1135 if (resp == NULL) {
1136 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1137 return 1;
1138 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1139 if ((e = launch_data_get_errno(resp))) {
1140 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1141 r = 1;
1142 }
1143 } else {
1144 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1145 r = 1;
1146 }
1147
1148 launch_data_free(resp);
1149 return r;
1150 }
1151
1152 static void print_jobs(launch_data_t j __attribute__((unused)), const char *label, void *context __attribute__((unused)))
1153 {
1154 fprintf(stdout, "%s\n", label);
1155 }
1156
1157 static int list_cmd(int argc, char *const argv[])
1158 {
1159 launch_data_t resp, msg;
1160 int ch, r = 0;
1161 bool vflag = false;
1162
1163 while ((ch = getopt(argc, argv, "v")) != -1) {
1164 switch (ch) {
1165 case 'v':
1166 vflag = true;
1167 break;
1168 default:
1169 fprintf(stderr, "usage: %s list [-v]\n", getprogname());
1170 return 1;
1171 }
1172 }
1173
1174 if (vflag) {
1175 fprintf(stderr, "usage: %s list: \"-v\" flag not implemented yet\n", getprogname());
1176 return 1;
1177 }
1178
1179 msg = launch_data_new_string(LAUNCH_KEY_GETJOBS);
1180 resp = launch_msg(msg);
1181 launch_data_free(msg);
1182
1183 if (resp == NULL) {
1184 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1185 return 1;
1186 } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
1187 launch_data_dict_iterate(resp, print_jobs, NULL);
1188 } else {
1189 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1190 r = 1;
1191 }
1192
1193 launch_data_free(resp);
1194
1195 return r;
1196 }
1197
1198 static int stdio_cmd(int argc, char *const argv[])
1199 {
1200 launch_data_t resp, msg, tmp;
1201 int e, fd = -1, r = 0;
1202
1203 if (argc != 2) {
1204 fprintf(stderr, "usage: %s %s <path>\n", getprogname(), argv[0]);
1205 return 1;
1206 }
1207
1208 fd = open(argv[1], O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE);
1209
1210 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1211
1212 if (fd == -1) {
1213 tmp = launch_data_new_string(argv[1]);
1214 } else {
1215 tmp = launch_data_new_fd(fd);
1216 }
1217
1218 if (!strcmp(argv[0], "stdout")) {
1219 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDOUT);
1220 } else {
1221 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDERR);
1222 }
1223
1224 resp = launch_msg(msg);
1225 launch_data_free(msg);
1226
1227 if (resp == NULL) {
1228 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1229 return 1;
1230 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1231 if ((e = launch_data_get_errno(resp))) {
1232 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1233 r = 1;
1234 }
1235 } else {
1236 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1237 r = 1;
1238 }
1239
1240 if (fd != -1)
1241 close(fd);
1242
1243 launch_data_free(resp);
1244
1245 return r;
1246 }
1247
1248 static int fyi_cmd(int argc, char *const argv[])
1249 {
1250 launch_data_t resp, msg;
1251 const char *lmsgk = LAUNCH_KEY_RELOADTTYS;
1252 int e, r = 0;
1253
1254 if (argc != 1) {
1255 fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]);
1256 return 1;
1257 }
1258
1259 if (!strcmp(argv[0], "shutdown"))
1260 lmsgk = LAUNCH_KEY_SHUTDOWN;
1261
1262 msg = launch_data_new_string(lmsgk);
1263 resp = launch_msg(msg);
1264 launch_data_free(msg);
1265
1266 if (resp == NULL) {
1267 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1268 return 1;
1269 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1270 if ((e = launch_data_get_errno(resp))) {
1271 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1272 r = 1;
1273 }
1274 } else {
1275 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1276 r = 1;
1277 }
1278
1279 launch_data_free(resp);
1280
1281 return r;
1282 }
1283
1284 static int logupdate_cmd(int argc, char *const argv[])
1285 {
1286 launch_data_t resp, msg;
1287 int e, i, j, r = 0, m = 0;
1288 bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
1289 const char *whichcmd = LAUNCH_KEY_SETLOGMASK;
1290 static const struct {
1291 const char *name;
1292 int level;
1293 } logtbl[] = {
1294 { "debug", LOG_DEBUG },
1295 { "info", LOG_INFO },
1296 { "notice", LOG_NOTICE },
1297 { "warning", LOG_WARNING },
1298 { "error", LOG_ERR },
1299 { "critical", LOG_CRIT },
1300 { "alert", LOG_ALERT },
1301 { "emergency", LOG_EMERG },
1302 };
1303 int logtblsz = sizeof logtbl / sizeof logtbl[0];
1304
1305 if (argc >= 2) {
1306 if (!strcmp(argv[1], "mask"))
1307 maskmode = true;
1308 else if (!strcmp(argv[1], "only"))
1309 onlymode = true;
1310 else if (!strcmp(argv[1], "level"))
1311 levelmode = true;
1312 else
1313 badargs = true;
1314 }
1315
1316 if (maskmode)
1317 m = LOG_UPTO(LOG_DEBUG);
1318
1319 if (argc > 2 && (maskmode || onlymode)) {
1320 for (i = 2; i < argc; i++) {
1321 for (j = 0; j < logtblsz; j++) {
1322 if (!strcmp(argv[i], logtbl[j].name)) {
1323 if (maskmode)
1324 m &= ~(LOG_MASK(logtbl[j].level));
1325 else
1326 m |= LOG_MASK(logtbl[j].level);
1327 break;
1328 }
1329 }
1330 if (j == logtblsz) {
1331 badargs = true;
1332 break;
1333 }
1334 }
1335 } else if (argc > 2 && levelmode) {
1336 for (j = 0; j < logtblsz; j++) {
1337 if (!strcmp(argv[2], logtbl[j].name)) {
1338 m = LOG_UPTO(logtbl[j].level);
1339 break;
1340 }
1341 }
1342 if (j == logtblsz)
1343 badargs = true;
1344 } else if (argc == 1) {
1345 whichcmd = LAUNCH_KEY_GETLOGMASK;
1346 } else {
1347 badargs = true;
1348 }
1349
1350 if (badargs) {
1351 fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
1352 return 1;
1353 }
1354
1355 if (whichcmd == LAUNCH_KEY_SETLOGMASK) {
1356 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1357 launch_data_dict_insert(msg, launch_data_new_integer(m), whichcmd);
1358 } else {
1359 msg = launch_data_new_string(whichcmd);
1360 }
1361
1362 resp = launch_msg(msg);
1363 launch_data_free(msg);
1364
1365 if (resp == NULL) {
1366 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1367 return 1;
1368 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1369 if ((e = launch_data_get_errno(resp))) {
1370 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1371 r = 1;
1372 }
1373 } else if (launch_data_get_type(resp) == LAUNCH_DATA_INTEGER) {
1374 if (whichcmd == LAUNCH_KEY_GETLOGMASK) {
1375 m = launch_data_get_integer(resp);
1376 for (j = 0; j < logtblsz; j++) {
1377 if (m & LOG_MASK(logtbl[j].level))
1378 fprintf(stdout, "%s ", logtbl[j].name);
1379 }
1380 fprintf(stdout, "\n");
1381 }
1382 } else {
1383 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1384 r = 1;
1385 }
1386
1387 launch_data_free(resp);
1388
1389 return r;
1390 }
1391
1392 static const struct {
1393 const char *name;
1394 int lim;
1395 } limlookup[] = {
1396 { "cpu", RLIMIT_CPU },
1397 { "filesize", RLIMIT_FSIZE },
1398 { "data", RLIMIT_DATA },
1399 { "stack", RLIMIT_STACK },
1400 { "core", RLIMIT_CORE },
1401 { "rss", RLIMIT_RSS },
1402 { "memlock", RLIMIT_MEMLOCK },
1403 { "maxproc", RLIMIT_NPROC },
1404 { "maxfiles", RLIMIT_NOFILE }
1405 };
1406
1407 static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
1408
1409 static ssize_t name2num(const char *n)
1410 {
1411 size_t i;
1412
1413 for (i = 0; i < limlookupcnt; i++) {
1414 if (!strcmp(limlookup[i].name, n)) {
1415 return limlookup[i].lim;
1416 }
1417 }
1418 return -1;
1419 }
1420
1421 static const char *num2name(int n)
1422 {
1423 size_t i;
1424
1425 for (i = 0; i < limlookupcnt; i++) {
1426 if (limlookup[i].lim == n)
1427 return limlookup[i].name;
1428 }
1429 return NULL;
1430 }
1431
1432 static const char *lim2str(rlim_t val, char *buf)
1433 {
1434 if (val == RLIM_INFINITY)
1435 strcpy(buf, "unlimited");
1436 else
1437 sprintf(buf, "%lld", val);
1438 return buf;
1439 }
1440
1441 static bool str2lim(const char *buf, rlim_t *res)
1442 {
1443 char *endptr;
1444 *res = strtoll(buf, &endptr, 10);
1445 if (!strcmp(buf, "unlimited")) {
1446 *res = RLIM_INFINITY;
1447 return false;
1448 } else if (*endptr == '\0') {
1449 return false;
1450 }
1451 return true;
1452 }
1453
1454 static int limit_cmd(int argc __attribute__((unused)), char *const argv[])
1455 {
1456 char slimstr[100];
1457 char hlimstr[100];
1458 struct rlimit *lmts = NULL;
1459 launch_data_t resp, resp1 = NULL, msg, tmp;
1460 int r = 0;
1461 size_t i, lsz = -1;
1462 ssize_t which = 0;
1463 rlim_t slim = -1, hlim = -1;
1464 bool badargs = false;
1465
1466 if (argc > 4)
1467 badargs = true;
1468
1469 if (argc >= 3 && str2lim(argv[2], &slim))
1470 badargs = true;
1471 else
1472 hlim = slim;
1473
1474 if (argc == 4 && str2lim(argv[3], &hlim))
1475 badargs = true;
1476
1477 if (argc >= 2 && -1 == (which = name2num(argv[1])))
1478 badargs = true;
1479
1480 if (badargs) {
1481 fprintf(stderr, "usage: %s %s [", getprogname(), argv[0]);
1482 for (i = 0; i < limlookupcnt; i++)
1483 fprintf(stderr, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
1484 fprintf(stderr, "[both | soft hard]]\n");
1485 return 1;
1486 }
1487
1488 msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
1489 resp = launch_msg(msg);
1490 launch_data_free(msg);
1491
1492 if (resp == NULL) {
1493 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1494 return 1;
1495 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
1496 lmts = launch_data_get_opaque(resp);
1497 lsz = launch_data_get_opaque_size(resp);
1498 if (argc <= 2) {
1499 for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
1500 if (argc == 2 && (size_t)which != i)
1501 continue;
1502 fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i),
1503 lim2str(lmts[i].rlim_cur, slimstr),
1504 lim2str(lmts[i].rlim_max, hlimstr));
1505 }
1506 }
1507 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
1508 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
1509 r = 1;
1510 } else {
1511 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1512 r = 1;
1513 }
1514
1515 if (argc <= 2 || r != 0) {
1516 launch_data_free(resp);
1517 return r;
1518 } else {
1519 resp1 = resp;
1520 }
1521
1522 lmts[which].rlim_cur = slim;
1523 lmts[which].rlim_max = hlim;
1524
1525 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1526 tmp = launch_data_new_opaque(lmts, lsz);
1527 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
1528 resp = launch_msg(msg);
1529 launch_data_free(msg);
1530
1531 if (resp == NULL) {
1532 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1533 return 1;
1534 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
1535 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
1536 r = 1;
1537 } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
1538 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1539 r = 1;
1540 }
1541
1542 launch_data_free(resp);
1543 launch_data_free(resp1);
1544
1545 return r;
1546 }
1547
1548 static int umask_cmd(int argc, char *const argv[])
1549 {
1550 launch_data_t resp, msg;
1551 bool badargs = false;
1552 char *endptr;
1553 long m = 0;
1554 int r = 0;
1555
1556 if (argc == 2) {
1557 m = strtol(argv[1], &endptr, 8);
1558 if (*endptr != '\0' || m > 0777)
1559 badargs = true;
1560 }
1561
1562 if (argc > 2 || badargs) {
1563 fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]);
1564 return 1;
1565 }
1566
1567
1568 if (argc == 1) {
1569 msg = launch_data_new_string(LAUNCH_KEY_GETUMASK);
1570 } else {
1571 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1572 launch_data_dict_insert(msg, launch_data_new_integer(m), LAUNCH_KEY_SETUMASK);
1573 }
1574 resp = launch_msg(msg);
1575 launch_data_free(msg);
1576
1577 if (resp == NULL) {
1578 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1579 return 1;
1580 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
1581 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
1582 r = 1;
1583 } else if (launch_data_get_type(resp) != LAUNCH_DATA_INTEGER) {
1584 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1585 r = 1;
1586 } else if (argc == 1) {
1587 fprintf(stdout, "%o\n", (unsigned int)launch_data_get_integer(resp));
1588 }
1589
1590 launch_data_free(resp);
1591
1592 return r;
1593 }
1594
1595 static int getrusage_cmd(int argc, char *const argv[])
1596 {
1597 launch_data_t resp, msg;
1598 bool badargs = false;
1599 int r = 0;
1600
1601 if (argc != 2)
1602 badargs = true;
1603 else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
1604 badargs = true;
1605
1606 if (badargs) {
1607 fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]);
1608 return 1;
1609 }
1610
1611 if (!strcmp(argv[1], "self")) {
1612 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
1613 } else {
1614 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
1615 }
1616
1617 resp = launch_msg(msg);
1618 launch_data_free(msg);
1619
1620 if (resp == NULL) {
1621 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1622 return 1;
1623 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1624 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
1625 r = 1;
1626 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
1627 struct rusage *rusage = launch_data_get_opaque(resp);
1628 fprintf(stdout, "\t%-10f\tuser time used\n",
1629 (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
1630 fprintf(stdout, "\t%-10f\tsystem time used\n",
1631 (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
1632 fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss);
1633 fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss);
1634 fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss);
1635 fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss);
1636 fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt);
1637 fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt);
1638 fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap);
1639 fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock);
1640 fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock);
1641 fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd);
1642 fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv);
1643 fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals);
1644 fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw);
1645 fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw);
1646 } else {
1647 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1648 r = 1;
1649 }
1650
1651 launch_data_free(resp);
1652
1653 return r;
1654 }
1655
1656 static bool launch_data_array_append(launch_data_t a, launch_data_t o)
1657 {
1658 size_t offt = launch_data_array_get_count(a);
1659
1660 return launch_data_array_set_index(a, o, offt);
1661 }
1662
1663 bool
1664 is_legacy_mach_job(launch_data_t obj)
1665 {
1666 bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
1667 bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
1668 bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
1669
1670 return has_command && has_servicename && !has_label;
1671 }