]> git.saurik.com Git - apple/launchd.git/blame - launchd/src/launchctl.c
launchd-106.13.tar.gz
[apple/launchd.git] / launchd / src / launchctl.c
CommitLineData
e91b9f68
A
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>
aa59983a
A
24#include <mach/mach.h>
25#include <servers/bootstrap.h>
e91b9f68
A
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
aa59983a
A
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
61static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
e91b9f68 62static bool launch_data_array_append(launch_data_t a, launch_data_t o);
aa59983a 63static void distill_jobs(launch_data_t);
e91b9f68
A
64static void distill_config_file(launch_data_t);
65static void sock_dict_cb(launch_data_t what, const char *key, void *context);
66static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
67static launch_data_t CF2launch_data(CFTypeRef);
68static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
69static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
70static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
aa59983a
A
71static void readpath(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload);
72static void readfile(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload);
e91b9f68
A
73static int _fd(int);
74static int demux_cmd(int argc, char *const argv[]);
75static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv);
76static void submit_job_pass(launch_data_t jobs);
aa59983a
A
77static void submit_mach_jobs(launch_data_t jobs);
78static void let_go_of_mach_jobs(void);
ab36757d 79static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
aa59983a
A
80static void print_jobs(launch_data_t j, const char *label, void *context);
81static bool is_legacy_mach_job(launch_data_t obj);
82static bool delay_to_second_pass(launch_data_t o);
83static void delay_to_second_pass2(launch_data_t o, const char *key, void *context);
e91b9f68
A
84
85static int load_and_unload_cmd(int argc, char *const argv[]);
86//static int reload_cmd(int argc, char *const argv[]);
87static int start_and_stop_cmd(int argc, char *const argv[]);
88static int list_cmd(int argc, char *const argv[]);
89
90static int setenv_cmd(int argc, char *const argv[]);
91static int unsetenv_cmd(int argc, char *const argv[]);
92static int getenv_and_export_cmd(int argc, char *const argv[]);
93
94static int limit_cmd(int argc, char *const argv[]);
95static int stdio_cmd(int argc, char *const argv[]);
96static int fyi_cmd(int argc, char *const argv[]);
97static int logupdate_cmd(int argc, char *const argv[]);
98static int umask_cmd(int argc, char *const argv[]);
99static int getrusage_cmd(int argc, char *const argv[]);
100
101static int help_cmd(int argc, char *const argv[]);
102
103static 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
129int 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
165static 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
181static 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
208static 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
aa59983a
A
236static 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
246static 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
e91b9f68
A
254static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused)))
255{
256 launch_data_t resp, msg;
257 bool is_csh = false;
aa59983a 258 char *k;
e91b9f68
A
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) {
aa59983a
A
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);
e91b9f68
A
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
288static 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
aa59983a
A
317launch_data_t
318read_plist_file(const char *file, bool editondisk, bool load)
e91b9f68
A
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
aa59983a
A
343void
344delay_to_second_pass2(launch_data_t o, const char *key, void *context)
e91b9f68
A
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
aa59983a
A
367bool
368delay_to_second_pass(launch_data_t o)
e91b9f68
A
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
aa59983a
A
382void
383readfile(const char *what, launch_data_t pass0, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload)
e91b9f68
A
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
aa59983a
A
393 if (is_legacy_mach_job(thejob)) {
394 launch_data_array_append(pass0, thejob);
395 return;
396 }
397
ab36757d
A
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
e91b9f68
A
404 if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED)))
405 job_disabled = launch_data_get_bool(tmpd);
406
ab36757d
A
407 if (forceload)
408 job_disabled = false;
409
e91b9f68
A
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
aa59983a
A
421void
422readpath(const char *what, launch_data_t pass0, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload)
e91b9f68
A
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)) {
aa59983a 433 readfile(what, pass0, pass1, pass2, editondisk, load, forceload);
e91b9f68
A
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
aa59983a 445 readfile(buf, pass0, pass1, pass2, editondisk, load, forceload);
e91b9f68
A
446 }
447 closedir(d);
448 }
449}
450
451struct distill_context {
452 launch_data_t base;
453 launch_data_t newsockdict;
454};
455
aa59983a
A
456void
457distill_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
465void
466distill_config_file(launch_data_t id_plist)
e91b9f68
A
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
478static 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
498static 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;
aa59983a
A
538 mode_t sun_mode = 0;
539 mode_t oldmask;
540 bool setm = false;
e91b9f68
A
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
aa59983a
A
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
e91b9f68
A
556 if (passive) {
557 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
558 close(sfd);
559 return;
560 }
aa59983a 561 oldmask = umask(S_IRWXG|S_IRWXO);
e91b9f68
A
562 if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
563 close(sfd);
aa59983a 564 umask(oldmask);
e91b9f68
A
565 return;
566 }
aa59983a
A
567 umask(oldmask);
568 if (setm) {
569 chmod(sun.sun_path, sun_mode);
570 }
e91b9f68
A
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;
ab36757d 585 const char *node = NULL, *serv = NULL, *mgroup = NULL;
e91b9f68
A
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);
ab36757d
A
599 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP)))
600 mgroup = launch_data_get_string(val);
e91b9f68
A
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 }
ab36757d
A
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 }
e91b9f68
A
654 }
655 if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
656 fprintf(stderr, "bind(): %s\n", strerror(errno));
657 return;
658 }
ab36757d
A
659
660 if (mgroup) {
661 do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
662 }
e91b9f68
A
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
ab36757d
A
712static 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
e91b9f68
A
758static 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
789static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile)
790{
791 CFPropertyListRef propertyList;
792 CFStringRef errorString;
793 CFDataRef resourceData;
794 SInt32 errorCode;
795 CFURLRef fileURL;
796
aa59983a 797 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
e91b9f68
A
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
809static void WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
810{
811 CFDataRef resourceData;
812 CFURLRef fileURL;
813 SInt32 errorCode;
814
aa59983a 815 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
e91b9f68
A
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
825void 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
836static 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
901static 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
922static int _fd(int fd)
923{
924 if (fd >= 0)
925 fcntl(fd, F_SETFD, 1);
926 return fd;
927}
928
929static int load_and_unload_cmd(int argc, char *const argv[])
930{
aa59983a 931 launch_data_t pass0, pass1, pass2;
e91b9f68
A
932 int i, ch;
933 bool wflag = false;
934 bool lflag = false;
ab36757d 935 bool Fflag = false;
e91b9f68
A
936
937 if (!strcmp(argv[0], "load"))
938 lflag = true;
939
ab36757d 940 while ((ch = getopt(argc, argv, "wF")) != -1) {
e91b9f68 941 switch (ch) {
ab36757d
A
942 case 'w': wflag = true; break;
943 case 'F': Fflag = true; break;
e91b9f68 944 default:
ab36757d 945 fprintf(stderr, "usage: %s load [-wF] paths...\n", getprogname());
e91b9f68
A
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
aa59983a
A
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.
e91b9f68
A
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
aa59983a 966 pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
e91b9f68
A
967 pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
968 pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
969
970 for (i = 0; i < argc; i++)
aa59983a 971 readpath(argv[i], pass0, pass1, pass2, wflag, lflag, Fflag);
e91b9f68 972
aa59983a
A
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) {
e91b9f68 976 fprintf(stderr, "nothing found to %s\n", lflag ? "load" : "unload");
aa59983a 977 launch_data_free(pass0);
e91b9f68
A
978 launch_data_free(pass1);
979 launch_data_free(pass2);
980 return 1;
981 }
982
983 if (lflag) {
aa59983a
A
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);
e91b9f68
A
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
aa59983a
A
1000static mach_port_t *msrvs = NULL;
1001static size_t msrvs_cnt = 0;
1002
1003void
1004submit_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
1050void
1051let_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
1059void
1060submit_job_pass(launch_data_t jobs)
e91b9f68
A
1061{
1062 launch_data_t msg, resp;
1063 size_t i;
1064 int e;
1065
aa59983a
A
1066 if (launch_data_array_get_count(jobs) == 0)
1067 return;
e91b9f68
A
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
1115static 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
1152static 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
1157static 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
1198static 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
1248static 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
1284static 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
aa59983a
A
1392static 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
1407static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
1408
1409static 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
1421static 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
1432static 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
1441static 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
e91b9f68
A
1454static 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;
aa59983a
A
1461 size_t i, lsz = -1;
1462 ssize_t which = 0;
e91b9f68
A
1463 rlim_t slim = -1, hlim = -1;
1464 bool badargs = false;
e91b9f68
A
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
aa59983a 1477 if (argc >= 2 && -1 == (which = name2num(argv[1])))
e91b9f68
A
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++) {
aa59983a 1500 if (argc == 2 && (size_t)which != i)
e91b9f68
A
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
1548static 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
1595static 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
1656static 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}
aa59983a
A
1662
1663bool
1664is_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}