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