]>
Commit | Line | Data |
---|---|---|
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 | ||
61 | static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context); | |
e91b9f68 | 62 | static bool launch_data_array_append(launch_data_t a, launch_data_t o); |
aa59983a | 63 | static void distill_jobs(launch_data_t); |
e91b9f68 A |
64 | static void distill_config_file(launch_data_t); |
65 | static void sock_dict_cb(launch_data_t what, const char *key, void *context); | |
66 | static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob); | |
67 | static launch_data_t CF2launch_data(CFTypeRef); | |
68 | static launch_data_t read_plist_file(const char *file, bool editondisk, bool load); | |
69 | static CFPropertyListRef CreateMyPropertyListFromFile(const char *); | |
70 | static void WriteMyPropertyListToFile(CFPropertyListRef, const char *); | |
aa59983a A |
71 | static void readpath(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload); |
72 | static void readfile(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload); | |
e91b9f68 A |
73 | static int _fd(int); |
74 | static int demux_cmd(int argc, char *const argv[]); | |
75 | static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv); | |
76 | static void submit_job_pass(launch_data_t jobs); | |
aa59983a A |
77 | static void submit_mach_jobs(launch_data_t jobs); |
78 | static void let_go_of_mach_jobs(void); | |
ab36757d | 79 | static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); |
aa59983a A |
80 | static void print_jobs(launch_data_t j, const char *label, void *context); |
81 | static bool is_legacy_mach_job(launch_data_t obj); | |
82 | static bool delay_to_second_pass(launch_data_t o); | |
83 | static void delay_to_second_pass2(launch_data_t o, const char *key, void *context); | |
e91b9f68 A |
84 | |
85 | static int load_and_unload_cmd(int argc, char *const argv[]); | |
86 | //static int reload_cmd(int argc, char *const argv[]); | |
87 | static int start_and_stop_cmd(int argc, char *const argv[]); | |
88 | static int list_cmd(int argc, char *const argv[]); | |
89 | ||
90 | static int setenv_cmd(int argc, char *const argv[]); | |
91 | static int unsetenv_cmd(int argc, char *const argv[]); | |
92 | static int getenv_and_export_cmd(int argc, char *const argv[]); | |
93 | ||
94 | static int limit_cmd(int argc, char *const argv[]); | |
95 | static int stdio_cmd(int argc, char *const argv[]); | |
96 | static int fyi_cmd(int argc, char *const argv[]); | |
97 | static int logupdate_cmd(int argc, char *const argv[]); | |
98 | static int umask_cmd(int argc, char *const argv[]); | |
99 | static int getrusage_cmd(int argc, char *const argv[]); | |
100 | ||
101 | static int help_cmd(int argc, char *const argv[]); | |
102 | ||
103 | static const struct { | |
104 | const char *name; | |
105 | int (*func)(int argc, char *const argv[]); | |
106 | const char *desc; | |
107 | } cmds[] = { | |
108 | { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, | |
109 | { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, | |
110 | // { "reload", reload_cmd, "Reload configuration files and/or directories" }, | |
111 | { "start", start_and_stop_cmd, "Start specified jobs" }, | |
112 | { "stop", start_and_stop_cmd, "Stop specified jobs" }, | |
113 | { "list", list_cmd, "List jobs and information about jobs" }, | |
114 | { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, | |
115 | { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, | |
116 | { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" }, | |
117 | { "export", getenv_and_export_cmd, "Export shell settings from launchd" }, | |
118 | { "limit", limit_cmd, "View and adjust launchd resource limits" }, | |
119 | { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, | |
120 | { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, | |
121 | { "shutdown", fyi_cmd, "Prepare for system shutdown" }, | |
122 | { "reloadttys", fyi_cmd, "Reload /etc/ttys" }, | |
123 | { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, | |
124 | { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, | |
125 | { "umask", umask_cmd, "Change launchd's umask" }, | |
126 | { "help", help_cmd, "This help output" }, | |
127 | }; | |
128 | ||
129 | int main(int argc, char *const argv[]) | |
130 | { | |
131 | bool istty = isatty(STDIN_FILENO); | |
132 | char *l; | |
133 | ||
134 | if (argc > 1) | |
135 | exit(demux_cmd(argc - 1, argv + 1)); | |
136 | ||
137 | if (NULL == readline) { | |
138 | fprintf(stderr, "missing library: readline\n"); | |
139 | exit(EXIT_FAILURE); | |
140 | } | |
141 | ||
142 | while ((l = readline(istty ? "launchd% " : NULL))) { | |
143 | char *inputstring = l, *argv2[100], **ap = argv2; | |
144 | int i = 0; | |
145 | ||
146 | while ((*ap = strsep(&inputstring, " \t"))) { | |
147 | if (**ap != '\0') { | |
148 | ap++; | |
149 | i++; | |
150 | } | |
151 | } | |
152 | ||
153 | if (i > 0) | |
154 | demux_cmd(i, argv2); | |
155 | ||
156 | free(l); | |
157 | } | |
158 | ||
159 | if (istty) | |
160 | fputc('\n', stdout); | |
161 | ||
162 | exit(EXIT_SUCCESS); | |
163 | } | |
164 | ||
165 | static int demux_cmd(int argc, char *const argv[]) | |
166 | { | |
167 | size_t i; | |
168 | ||
169 | optind = 1; | |
170 | optreset = 1; | |
171 | ||
172 | for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { | |
173 | if (!strcmp(cmds[i].name, argv[0])) | |
174 | return cmds[i].func(argc, argv); | |
175 | } | |
176 | ||
177 | fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]); | |
178 | return 1; | |
179 | } | |
180 | ||
181 | static int unsetenv_cmd(int argc, char *const argv[]) | |
182 | { | |
183 | launch_data_t resp, tmp, msg; | |
184 | ||
185 | if (argc != 2) { | |
186 | fprintf(stderr, "%s usage: unsetenv <key>\n", getprogname()); | |
187 | return 1; | |
188 | } | |
189 | ||
190 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
191 | ||
192 | tmp = launch_data_new_string(argv[1]); | |
193 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT); | |
194 | ||
195 | resp = launch_msg(msg); | |
196 | ||
197 | launch_data_free(msg); | |
198 | ||
199 | if (resp) { | |
200 | launch_data_free(resp); | |
201 | } else { | |
202 | fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno)); | |
203 | } | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int setenv_cmd(int argc, char *const argv[]) | |
209 | { | |
210 | launch_data_t resp, tmp, tmpv, msg; | |
211 | ||
212 | if (argc != 3) { | |
213 | fprintf(stderr, "%s usage: setenv <key> <value>\n", getprogname()); | |
214 | return 1; | |
215 | } | |
216 | ||
217 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
218 | tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
219 | ||
220 | tmpv = launch_data_new_string(argv[2]); | |
221 | launch_data_dict_insert(tmp, tmpv, argv[1]); | |
222 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT); | |
223 | ||
224 | resp = launch_msg(msg); | |
225 | launch_data_free(msg); | |
226 | ||
227 | if (resp) { | |
228 | launch_data_free(resp); | |
229 | } else { | |
230 | fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno)); | |
231 | } | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
aa59983a A |
236 | static void print_launchd_env(launch_data_t obj, const char *key, void *context) |
237 | { | |
238 | bool *is_csh = context; | |
239 | ||
240 | if (*is_csh) | |
241 | fprintf(stdout, "setenv %s %s;\n", key, launch_data_get_string(obj)); | |
242 | else | |
243 | fprintf(stdout, "%s=%s; export %s;\n", key, launch_data_get_string(obj), key); | |
244 | } | |
245 | ||
246 | static void print_key_value(launch_data_t obj, const char *key, void *context) | |
247 | { | |
248 | const char *k = context; | |
249 | ||
250 | if (!strcmp(key, k)) | |
251 | fprintf(stdout, "%s\n", launch_data_get_string(obj)); | |
252 | } | |
253 | ||
e91b9f68 A |
254 | static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused))) |
255 | { | |
256 | launch_data_t resp, msg; | |
257 | bool is_csh = false; | |
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 | ||
288 | static void unloadjob(launch_data_t job) | |
289 | { | |
290 | launch_data_t resp, tmp, tmps, msg; | |
291 | int e; | |
292 | ||
293 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
294 | tmp = launch_data_alloc(LAUNCH_DATA_STRING); | |
295 | tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL); | |
296 | ||
297 | if (!tmps) { | |
298 | fprintf(stderr, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL); | |
299 | return; | |
300 | } | |
301 | ||
302 | launch_data_set_string(tmp, launch_data_get_string(tmps)); | |
303 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_REMOVEJOB); | |
304 | resp = launch_msg(msg); | |
305 | launch_data_free(msg); | |
306 | if (!resp) { | |
307 | fprintf(stderr, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno)); | |
308 | return; | |
309 | } | |
310 | if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) { | |
311 | if ((e = launch_data_get_errno(resp))) | |
312 | fprintf(stderr, "%s\n", strerror(e)); | |
313 | } | |
314 | launch_data_free(resp); | |
315 | } | |
316 | ||
aa59983a A |
317 | launch_data_t |
318 | read_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 |
343 | void |
344 | delay_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 |
367 | bool |
368 | delay_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 |
382 | void |
383 | readfile(const char *what, launch_data_t pass0, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload) | |
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 |
421 | void |
422 | readpath(const char *what, launch_data_t pass0, launch_data_t pass1, launch_data_t pass2, bool editondisk, bool load, bool forceload) | |
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 | ||
451 | struct distill_context { | |
452 | launch_data_t base; | |
453 | launch_data_t newsockdict; | |
454 | }; | |
455 | ||
aa59983a A |
456 | void |
457 | distill_jobs(launch_data_t jobs) | |
458 | { | |
459 | size_t i, c = launch_data_array_get_count(jobs); | |
460 | ||
461 | for (i = 0; i < c; i++) | |
462 | distill_config_file(launch_data_array_get_index(jobs, i)); | |
463 | } | |
464 | ||
465 | void | |
466 | distill_config_file(launch_data_t id_plist) | |
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 | ||
478 | static void sock_dict_cb(launch_data_t what, const char *key, void *context) | |
479 | { | |
480 | struct distill_context *dc = context; | |
481 | launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
482 | ||
483 | launch_data_dict_insert(dc->newsockdict, fdarray, key); | |
484 | ||
485 | if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) { | |
486 | sock_dict_edit_entry(what, key, fdarray, dc->base); | |
487 | } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) { | |
488 | launch_data_t tmp; | |
489 | size_t i; | |
490 | ||
491 | for (i = 0; i < launch_data_array_get_count(what); i++) { | |
492 | tmp = launch_data_array_get_index(what, i); | |
493 | sock_dict_edit_entry(tmp, key, fdarray, dc->base); | |
494 | } | |
495 | } | |
496 | } | |
497 | ||
498 | static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob) | |
499 | { | |
500 | launch_data_t a, val; | |
501 | int sfd, st = SOCK_STREAM; | |
502 | bool passive = true; | |
503 | ||
504 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) { | |
505 | if (!strcasecmp(launch_data_get_string(val), "stream")) { | |
506 | st = SOCK_STREAM; | |
507 | } else if (!strcasecmp(launch_data_get_string(val), "dgram")) { | |
508 | st = SOCK_DGRAM; | |
509 | } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) { | |
510 | st = SOCK_SEQPACKET; | |
511 | } | |
512 | } | |
513 | ||
514 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) | |
515 | passive = launch_data_get_bool(val); | |
516 | ||
517 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) { | |
518 | char secdir[] = LAUNCH_SECDIR, buf[1024]; | |
519 | launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES); | |
520 | ||
521 | if (NULL == uenv) { | |
522 | uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
523 | launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES); | |
524 | } | |
525 | ||
526 | mkdtemp(secdir); | |
527 | ||
528 | sprintf(buf, "%s/%s", secdir, key); | |
529 | ||
530 | a = launch_data_new_string(buf); | |
531 | launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME); | |
532 | a = launch_data_new_string(buf); | |
533 | launch_data_dict_insert(uenv, a, launch_data_get_string(val)); | |
534 | } | |
535 | ||
536 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) { | |
537 | struct sockaddr_un sun; | |
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 |
712 | static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup) |
713 | { | |
714 | struct addrinfo hints, *res0, *res; | |
715 | struct ip_mreq mreq; | |
716 | struct ipv6_mreq m6req; | |
717 | int gerr; | |
718 | ||
719 | memset(&hints, 0, sizeof(hints)); | |
720 | ||
721 | hints.ai_flags |= AI_PASSIVE; | |
722 | hints.ai_family = family; | |
723 | hints.ai_socktype = socktype; | |
724 | hints.ai_protocol = protocol; | |
725 | ||
726 | if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) { | |
727 | fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr)); | |
728 | return; | |
729 | } | |
730 | ||
731 | for (res = res0; res; res = res->ai_next) { | |
732 | if (AF_INET == family) { | |
733 | memset(&mreq, 0, sizeof(mreq)); | |
734 | mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; | |
735 | if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { | |
736 | fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno)); | |
737 | continue; | |
738 | } | |
739 | break; | |
740 | } else if (AF_INET6 == family) { | |
741 | memset(&m6req, 0, sizeof(m6req)); | |
742 | m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; | |
743 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, sizeof(m6req)) == -1) { | |
744 | fprintf(stderr, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno)); | |
745 | continue; | |
746 | } | |
747 | break; | |
748 | } else { | |
749 | fprintf(stderr, "unknown family during multicast group bind!\n"); | |
750 | break; | |
751 | } | |
752 | } | |
753 | ||
754 | freeaddrinfo(res0); | |
755 | } | |
756 | ||
757 | ||
e91b9f68 A |
758 | static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv) |
759 | { | |
760 | struct stat sb; | |
761 | DNSServiceRef service; | |
762 | DNSServiceErrorType error; | |
763 | char rvs_buf[200]; | |
764 | short port; | |
765 | static int statres = 1; | |
766 | ||
767 | if (1 == statres) | |
768 | statres = stat("/usr/sbin/mDNSResponder", &sb); | |
769 | ||
770 | if (-1 == statres) | |
771 | return NULL; | |
772 | ||
773 | sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp"); | |
774 | ||
775 | if (res->ai_family == AF_INET) | |
776 | port = ((struct sockaddr_in *)res->ai_addr)->sin_port; | |
777 | else | |
778 | port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port; | |
779 | ||
780 | error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL); | |
781 | ||
782 | if (error == kDNSServiceErr_NoError) | |
783 | return launch_data_new_fd(DNSServiceRefSockFD(service)); | |
784 | ||
785 | fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error); | |
786 | return NULL; | |
787 | } | |
788 | ||
789 | static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile) | |
790 | { | |
791 | CFPropertyListRef propertyList; | |
792 | CFStringRef errorString; | |
793 | CFDataRef resourceData; | |
794 | SInt32 errorCode; | |
795 | CFURLRef fileURL; | |
796 | ||
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 | ||
809 | static 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 | ||
825 | void myCFDictionaryApplyFunction(const void *key, const void *value, void *context) | |
826 | { | |
827 | launch_data_t ik, iw, where = context; | |
828 | ||
829 | ik = CF2launch_data(key); | |
830 | iw = CF2launch_data(value); | |
831 | ||
832 | launch_data_dict_insert(where, iw, launch_data_get_string(ik)); | |
833 | launch_data_free(ik); | |
834 | } | |
835 | ||
836 | static launch_data_t CF2launch_data(CFTypeRef cfr) | |
837 | { | |
838 | launch_data_t r; | |
839 | CFTypeID cft = CFGetTypeID(cfr); | |
840 | ||
841 | if (cft == CFStringGetTypeID()) { | |
842 | char buf[4096]; | |
843 | CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8); | |
844 | r = launch_data_alloc(LAUNCH_DATA_STRING); | |
845 | launch_data_set_string(r, buf); | |
846 | } else if (cft == CFBooleanGetTypeID()) { | |
847 | r = launch_data_alloc(LAUNCH_DATA_BOOL); | |
848 | launch_data_set_bool(r, CFBooleanGetValue(cfr)); | |
849 | } else if (cft == CFArrayGetTypeID()) { | |
850 | CFIndex i, ac = CFArrayGetCount(cfr); | |
851 | r = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
852 | for (i = 0; i < ac; i++) { | |
853 | CFTypeRef v = CFArrayGetValueAtIndex(cfr, i); | |
854 | if (v) { | |
855 | launch_data_t iv = CF2launch_data(v); | |
856 | launch_data_array_set_index(r, iv, i); | |
857 | } | |
858 | } | |
859 | } else if (cft == CFDictionaryGetTypeID()) { | |
860 | r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
861 | CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r); | |
862 | } else if (cft == CFDataGetTypeID()) { | |
863 | r = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
864 | launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); | |
865 | } else if (cft == CFNumberGetTypeID()) { | |
866 | long long n; | |
867 | double d; | |
868 | CFNumberType cfnt = CFNumberGetType(cfr); | |
869 | switch (cfnt) { | |
870 | case kCFNumberSInt8Type: | |
871 | case kCFNumberSInt16Type: | |
872 | case kCFNumberSInt32Type: | |
873 | case kCFNumberSInt64Type: | |
874 | case kCFNumberCharType: | |
875 | case kCFNumberShortType: | |
876 | case kCFNumberIntType: | |
877 | case kCFNumberLongType: | |
878 | case kCFNumberLongLongType: | |
879 | CFNumberGetValue(cfr, kCFNumberLongLongType, &n); | |
880 | r = launch_data_alloc(LAUNCH_DATA_INTEGER); | |
881 | launch_data_set_integer(r, n); | |
882 | break; | |
883 | case kCFNumberFloat32Type: | |
884 | case kCFNumberFloat64Type: | |
885 | case kCFNumberFloatType: | |
886 | case kCFNumberDoubleType: | |
887 | CFNumberGetValue(cfr, kCFNumberDoubleType, &d); | |
888 | r = launch_data_alloc(LAUNCH_DATA_REAL); | |
889 | launch_data_set_real(r, d); | |
890 | break; | |
891 | default: | |
892 | r = NULL; | |
893 | break; | |
894 | } | |
895 | } else { | |
896 | r = NULL; | |
897 | } | |
898 | return r; | |
899 | } | |
900 | ||
901 | static int help_cmd(int argc, char *const argv[]) | |
902 | { | |
903 | FILE *where = stdout; | |
904 | int l, cmdwidth = 0; | |
905 | size_t i; | |
906 | ||
907 | if (argc == 0 || argv == NULL) | |
908 | where = stderr; | |
909 | ||
910 | fprintf(where, "usage: %s <subcommand>\n", getprogname()); | |
911 | for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { | |
912 | l = strlen(cmds[i].name); | |
913 | if (l > cmdwidth) | |
914 | cmdwidth = l; | |
915 | } | |
916 | for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) | |
917 | fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc); | |
918 | ||
919 | return 0; | |
920 | } | |
921 | ||
922 | static int _fd(int fd) | |
923 | { | |
924 | if (fd >= 0) | |
925 | fcntl(fd, F_SETFD, 1); | |
926 | return fd; | |
927 | } | |
928 | ||
929 | static int load_and_unload_cmd(int argc, char *const argv[]) | |
930 | { | |
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 |
1000 | static mach_port_t *msrvs = NULL; |
1001 | static size_t msrvs_cnt = 0; | |
1002 | ||
1003 | void | |
1004 | submit_mach_jobs(launch_data_t jobs) | |
1005 | { | |
1006 | size_t i, c; | |
1007 | ||
1008 | c = launch_data_array_get_count(jobs); | |
1009 | ||
1010 | msrvs = calloc(1, sizeof(mach_port_t) * c); | |
1011 | msrvs_cnt = c; | |
1012 | ||
1013 | for (i = 0; i < c; i++) { | |
1014 | launch_data_t tmp, oai = launch_data_array_get_index(jobs, i); | |
1015 | const char *sn = NULL, *cmd = NULL; | |
1016 | bool d = true, k = false; | |
1017 | mach_port_t msr, msv, mhp; | |
1018 | kern_return_t kr; | |
1019 | uid_t u = getuid(); | |
1020 | ||
1021 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND))) | |
1022 | d = launch_data_get_bool(tmp); | |
1023 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ISKUNCSERVER))) | |
1024 | k = launch_data_get_bool(tmp); | |
1025 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME))) | |
1026 | sn = launch_data_get_string(tmp); | |
1027 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND))) | |
1028 | cmd = launch_data_get_string(tmp); | |
1029 | ||
1030 | if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) { | |
1031 | fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr); | |
1032 | continue; | |
1033 | } | |
1034 | if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) { | |
1035 | fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr); | |
1036 | mach_port_destroy(mach_task_self(), msr); | |
1037 | continue; | |
1038 | } | |
1039 | if (k) { | |
1040 | mhp = mach_host_self(); | |
1041 | if ((kr = host_set_UNDServer(mhp, msv)) != KERN_SUCCESS) | |
1042 | fprintf(stderr, "%s: host_set_UNDServer(): %s\n", getprogname(), mach_error_string(kr)); | |
1043 | mach_port_deallocate(mach_task_self(), mhp); | |
1044 | } | |
1045 | mach_port_deallocate(mach_task_self(), msv); | |
1046 | msrvs[i] = msr; | |
1047 | } | |
1048 | } | |
1049 | ||
1050 | void | |
1051 | let_go_of_mach_jobs(void) | |
1052 | { | |
1053 | size_t i; | |
1054 | ||
1055 | for (i = 0; i < msrvs_cnt; i++) | |
1056 | mach_port_destroy(mach_task_self(), msrvs[i]); | |
1057 | } | |
1058 | ||
1059 | void | |
1060 | submit_job_pass(launch_data_t jobs) | |
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 | ||
1115 | static int start_and_stop_cmd(int argc, char *const argv[]) | |
1116 | { | |
1117 | launch_data_t resp, msg; | |
1118 | const char *lmsgcmd = LAUNCH_KEY_STOPJOB; | |
1119 | int e, r = 0; | |
1120 | ||
1121 | if (!strcmp(argv[0], "start")) | |
1122 | lmsgcmd = LAUNCH_KEY_STARTJOB; | |
1123 | ||
1124 | if (argc != 2) { | |
1125 | fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]); | |
1126 | return 1; | |
1127 | } | |
1128 | ||
1129 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1130 | launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd); | |
1131 | ||
1132 | resp = launch_msg(msg); | |
1133 | launch_data_free(msg); | |
1134 | ||
1135 | if (resp == NULL) { | |
1136 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1137 | return 1; | |
1138 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1139 | if ((e = launch_data_get_errno(resp))) { | |
1140 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1141 | r = 1; | |
1142 | } | |
1143 | } else { | |
1144 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1145 | r = 1; | |
1146 | } | |
1147 | ||
1148 | launch_data_free(resp); | |
1149 | return r; | |
1150 | } | |
1151 | ||
1152 | static void print_jobs(launch_data_t j __attribute__((unused)), const char *label, void *context __attribute__((unused))) | |
1153 | { | |
1154 | fprintf(stdout, "%s\n", label); | |
1155 | } | |
1156 | ||
1157 | static int list_cmd(int argc, char *const argv[]) | |
1158 | { | |
1159 | launch_data_t resp, msg; | |
1160 | int ch, r = 0; | |
1161 | bool vflag = false; | |
1162 | ||
1163 | while ((ch = getopt(argc, argv, "v")) != -1) { | |
1164 | switch (ch) { | |
1165 | case 'v': | |
1166 | vflag = true; | |
1167 | break; | |
1168 | default: | |
1169 | fprintf(stderr, "usage: %s list [-v]\n", getprogname()); | |
1170 | return 1; | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | if (vflag) { | |
1175 | fprintf(stderr, "usage: %s list: \"-v\" flag not implemented yet\n", getprogname()); | |
1176 | return 1; | |
1177 | } | |
1178 | ||
1179 | msg = launch_data_new_string(LAUNCH_KEY_GETJOBS); | |
1180 | resp = launch_msg(msg); | |
1181 | launch_data_free(msg); | |
1182 | ||
1183 | if (resp == NULL) { | |
1184 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1185 | return 1; | |
1186 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) { | |
1187 | launch_data_dict_iterate(resp, print_jobs, NULL); | |
1188 | } else { | |
1189 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1190 | r = 1; | |
1191 | } | |
1192 | ||
1193 | launch_data_free(resp); | |
1194 | ||
1195 | return r; | |
1196 | } | |
1197 | ||
1198 | static int stdio_cmd(int argc, char *const argv[]) | |
1199 | { | |
1200 | launch_data_t resp, msg, tmp; | |
1201 | int e, fd = -1, r = 0; | |
1202 | ||
1203 | if (argc != 2) { | |
1204 | fprintf(stderr, "usage: %s %s <path>\n", getprogname(), argv[0]); | |
1205 | return 1; | |
1206 | } | |
1207 | ||
1208 | fd = open(argv[1], O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE); | |
1209 | ||
1210 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1211 | ||
1212 | if (fd == -1) { | |
1213 | tmp = launch_data_new_string(argv[1]); | |
1214 | } else { | |
1215 | tmp = launch_data_new_fd(fd); | |
1216 | } | |
1217 | ||
1218 | if (!strcmp(argv[0], "stdout")) { | |
1219 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDOUT); | |
1220 | } else { | |
1221 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDERR); | |
1222 | } | |
1223 | ||
1224 | resp = launch_msg(msg); | |
1225 | launch_data_free(msg); | |
1226 | ||
1227 | if (resp == NULL) { | |
1228 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1229 | return 1; | |
1230 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1231 | if ((e = launch_data_get_errno(resp))) { | |
1232 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1233 | r = 1; | |
1234 | } | |
1235 | } else { | |
1236 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1237 | r = 1; | |
1238 | } | |
1239 | ||
1240 | if (fd != -1) | |
1241 | close(fd); | |
1242 | ||
1243 | launch_data_free(resp); | |
1244 | ||
1245 | return r; | |
1246 | } | |
1247 | ||
1248 | static int fyi_cmd(int argc, char *const argv[]) | |
1249 | { | |
1250 | launch_data_t resp, msg; | |
1251 | const char *lmsgk = LAUNCH_KEY_RELOADTTYS; | |
1252 | int e, r = 0; | |
1253 | ||
1254 | if (argc != 1) { | |
1255 | fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]); | |
1256 | return 1; | |
1257 | } | |
1258 | ||
1259 | if (!strcmp(argv[0], "shutdown")) | |
1260 | lmsgk = LAUNCH_KEY_SHUTDOWN; | |
1261 | ||
1262 | msg = launch_data_new_string(lmsgk); | |
1263 | resp = launch_msg(msg); | |
1264 | launch_data_free(msg); | |
1265 | ||
1266 | if (resp == NULL) { | |
1267 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1268 | return 1; | |
1269 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1270 | if ((e = launch_data_get_errno(resp))) { | |
1271 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1272 | r = 1; | |
1273 | } | |
1274 | } else { | |
1275 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1276 | r = 1; | |
1277 | } | |
1278 | ||
1279 | launch_data_free(resp); | |
1280 | ||
1281 | return r; | |
1282 | } | |
1283 | ||
1284 | static int logupdate_cmd(int argc, char *const argv[]) | |
1285 | { | |
1286 | launch_data_t resp, msg; | |
1287 | int e, i, j, r = 0, m = 0; | |
1288 | bool badargs = false, maskmode = false, onlymode = false, levelmode = false; | |
1289 | const char *whichcmd = LAUNCH_KEY_SETLOGMASK; | |
1290 | static const struct { | |
1291 | const char *name; | |
1292 | int level; | |
1293 | } logtbl[] = { | |
1294 | { "debug", LOG_DEBUG }, | |
1295 | { "info", LOG_INFO }, | |
1296 | { "notice", LOG_NOTICE }, | |
1297 | { "warning", LOG_WARNING }, | |
1298 | { "error", LOG_ERR }, | |
1299 | { "critical", LOG_CRIT }, | |
1300 | { "alert", LOG_ALERT }, | |
1301 | { "emergency", LOG_EMERG }, | |
1302 | }; | |
1303 | int logtblsz = sizeof logtbl / sizeof logtbl[0]; | |
1304 | ||
1305 | if (argc >= 2) { | |
1306 | if (!strcmp(argv[1], "mask")) | |
1307 | maskmode = true; | |
1308 | else if (!strcmp(argv[1], "only")) | |
1309 | onlymode = true; | |
1310 | else if (!strcmp(argv[1], "level")) | |
1311 | levelmode = true; | |
1312 | else | |
1313 | badargs = true; | |
1314 | } | |
1315 | ||
1316 | if (maskmode) | |
1317 | m = LOG_UPTO(LOG_DEBUG); | |
1318 | ||
1319 | if (argc > 2 && (maskmode || onlymode)) { | |
1320 | for (i = 2; i < argc; i++) { | |
1321 | for (j = 0; j < logtblsz; j++) { | |
1322 | if (!strcmp(argv[i], logtbl[j].name)) { | |
1323 | if (maskmode) | |
1324 | m &= ~(LOG_MASK(logtbl[j].level)); | |
1325 | else | |
1326 | m |= LOG_MASK(logtbl[j].level); | |
1327 | break; | |
1328 | } | |
1329 | } | |
1330 | if (j == logtblsz) { | |
1331 | badargs = true; | |
1332 | break; | |
1333 | } | |
1334 | } | |
1335 | } else if (argc > 2 && levelmode) { | |
1336 | for (j = 0; j < logtblsz; j++) { | |
1337 | if (!strcmp(argv[2], logtbl[j].name)) { | |
1338 | m = LOG_UPTO(logtbl[j].level); | |
1339 | break; | |
1340 | } | |
1341 | } | |
1342 | if (j == logtblsz) | |
1343 | badargs = true; | |
1344 | } else if (argc == 1) { | |
1345 | whichcmd = LAUNCH_KEY_GETLOGMASK; | |
1346 | } else { | |
1347 | badargs = true; | |
1348 | } | |
1349 | ||
1350 | if (badargs) { | |
1351 | fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname()); | |
1352 | return 1; | |
1353 | } | |
1354 | ||
1355 | if (whichcmd == LAUNCH_KEY_SETLOGMASK) { | |
1356 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1357 | launch_data_dict_insert(msg, launch_data_new_integer(m), whichcmd); | |
1358 | } else { | |
1359 | msg = launch_data_new_string(whichcmd); | |
1360 | } | |
1361 | ||
1362 | resp = launch_msg(msg); | |
1363 | launch_data_free(msg); | |
1364 | ||
1365 | if (resp == NULL) { | |
1366 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1367 | return 1; | |
1368 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1369 | if ((e = launch_data_get_errno(resp))) { | |
1370 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1371 | r = 1; | |
1372 | } | |
1373 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_INTEGER) { | |
1374 | if (whichcmd == LAUNCH_KEY_GETLOGMASK) { | |
1375 | m = launch_data_get_integer(resp); | |
1376 | for (j = 0; j < logtblsz; j++) { | |
1377 | if (m & LOG_MASK(logtbl[j].level)) | |
1378 | fprintf(stdout, "%s ", logtbl[j].name); | |
1379 | } | |
1380 | fprintf(stdout, "\n"); | |
1381 | } | |
1382 | } else { | |
1383 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1384 | r = 1; | |
1385 | } | |
1386 | ||
1387 | launch_data_free(resp); | |
1388 | ||
1389 | return r; | |
1390 | } | |
1391 | ||
aa59983a A |
1392 | static const struct { |
1393 | const char *name; | |
1394 | int lim; | |
1395 | } limlookup[] = { | |
1396 | { "cpu", RLIMIT_CPU }, | |
1397 | { "filesize", RLIMIT_FSIZE }, | |
1398 | { "data", RLIMIT_DATA }, | |
1399 | { "stack", RLIMIT_STACK }, | |
1400 | { "core", RLIMIT_CORE }, | |
1401 | { "rss", RLIMIT_RSS }, | |
1402 | { "memlock", RLIMIT_MEMLOCK }, | |
1403 | { "maxproc", RLIMIT_NPROC }, | |
1404 | { "maxfiles", RLIMIT_NOFILE } | |
1405 | }; | |
1406 | ||
1407 | static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0]; | |
1408 | ||
1409 | static ssize_t name2num(const char *n) | |
1410 | { | |
1411 | size_t i; | |
1412 | ||
1413 | for (i = 0; i < limlookupcnt; i++) { | |
1414 | if (!strcmp(limlookup[i].name, n)) { | |
1415 | return limlookup[i].lim; | |
1416 | } | |
1417 | } | |
1418 | return -1; | |
1419 | } | |
1420 | ||
1421 | static const char *num2name(int n) | |
1422 | { | |
1423 | size_t i; | |
1424 | ||
1425 | for (i = 0; i < limlookupcnt; i++) { | |
1426 | if (limlookup[i].lim == n) | |
1427 | return limlookup[i].name; | |
1428 | } | |
1429 | return NULL; | |
1430 | } | |
1431 | ||
1432 | static const char *lim2str(rlim_t val, char *buf) | |
1433 | { | |
1434 | if (val == RLIM_INFINITY) | |
1435 | strcpy(buf, "unlimited"); | |
1436 | else | |
1437 | sprintf(buf, "%lld", val); | |
1438 | return buf; | |
1439 | } | |
1440 | ||
1441 | static bool str2lim(const char *buf, rlim_t *res) | |
1442 | { | |
1443 | char *endptr; | |
1444 | *res = strtoll(buf, &endptr, 10); | |
1445 | if (!strcmp(buf, "unlimited")) { | |
1446 | *res = RLIM_INFINITY; | |
1447 | return false; | |
1448 | } else if (*endptr == '\0') { | |
1449 | return false; | |
1450 | } | |
1451 | return true; | |
1452 | } | |
1453 | ||
e91b9f68 A |
1454 | static int limit_cmd(int argc __attribute__((unused)), char *const argv[]) |
1455 | { | |
1456 | char slimstr[100]; | |
1457 | char hlimstr[100]; | |
1458 | struct rlimit *lmts = NULL; | |
1459 | launch_data_t resp, resp1 = NULL, msg, tmp; | |
1460 | int r = 0; | |
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 | ||
1548 | static int umask_cmd(int argc, char *const argv[]) | |
1549 | { | |
1550 | launch_data_t resp, msg; | |
1551 | bool badargs = false; | |
1552 | char *endptr; | |
1553 | long m = 0; | |
1554 | int r = 0; | |
1555 | ||
1556 | if (argc == 2) { | |
1557 | m = strtol(argv[1], &endptr, 8); | |
1558 | if (*endptr != '\0' || m > 0777) | |
1559 | badargs = true; | |
1560 | } | |
1561 | ||
1562 | if (argc > 2 || badargs) { | |
1563 | fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]); | |
1564 | return 1; | |
1565 | } | |
1566 | ||
1567 | ||
1568 | if (argc == 1) { | |
1569 | msg = launch_data_new_string(LAUNCH_KEY_GETUMASK); | |
1570 | } else { | |
1571 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1572 | launch_data_dict_insert(msg, launch_data_new_integer(m), LAUNCH_KEY_SETUMASK); | |
1573 | } | |
1574 | resp = launch_msg(msg); | |
1575 | launch_data_free(msg); | |
1576 | ||
1577 | if (resp == NULL) { | |
1578 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1579 | return 1; | |
1580 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) { | |
1581 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp)); | |
1582 | r = 1; | |
1583 | } else if (launch_data_get_type(resp) != LAUNCH_DATA_INTEGER) { | |
1584 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1585 | r = 1; | |
1586 | } else if (argc == 1) { | |
1587 | fprintf(stdout, "%o\n", (unsigned int)launch_data_get_integer(resp)); | |
1588 | } | |
1589 | ||
1590 | launch_data_free(resp); | |
1591 | ||
1592 | return r; | |
1593 | } | |
1594 | ||
1595 | static int getrusage_cmd(int argc, char *const argv[]) | |
1596 | { | |
1597 | launch_data_t resp, msg; | |
1598 | bool badargs = false; | |
1599 | int r = 0; | |
1600 | ||
1601 | if (argc != 2) | |
1602 | badargs = true; | |
1603 | else if (strcmp(argv[1], "self") && strcmp(argv[1], "children")) | |
1604 | badargs = true; | |
1605 | ||
1606 | if (badargs) { | |
1607 | fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]); | |
1608 | return 1; | |
1609 | } | |
1610 | ||
1611 | if (!strcmp(argv[1], "self")) { | |
1612 | msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF); | |
1613 | } else { | |
1614 | msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN); | |
1615 | } | |
1616 | ||
1617 | resp = launch_msg(msg); | |
1618 | launch_data_free(msg); | |
1619 | ||
1620 | if (resp == NULL) { | |
1621 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1622 | return 1; | |
1623 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1624 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp))); | |
1625 | r = 1; | |
1626 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) { | |
1627 | struct rusage *rusage = launch_data_get_opaque(resp); | |
1628 | fprintf(stdout, "\t%-10f\tuser time used\n", | |
1629 | (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000); | |
1630 | fprintf(stdout, "\t%-10f\tsystem time used\n", | |
1631 | (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000); | |
1632 | fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss); | |
1633 | fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss); | |
1634 | fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss); | |
1635 | fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss); | |
1636 | fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt); | |
1637 | fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt); | |
1638 | fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap); | |
1639 | fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock); | |
1640 | fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock); | |
1641 | fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd); | |
1642 | fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv); | |
1643 | fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals); | |
1644 | fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw); | |
1645 | fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw); | |
1646 | } else { | |
1647 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1648 | r = 1; | |
1649 | } | |
1650 | ||
1651 | launch_data_free(resp); | |
1652 | ||
1653 | return r; | |
1654 | } | |
1655 | ||
1656 | static bool launch_data_array_append(launch_data_t a, launch_data_t o) | |
1657 | { | |
1658 | size_t offt = launch_data_array_get_count(a); | |
1659 | ||
1660 | return launch_data_array_set_index(a, o, offt); | |
1661 | } | |
aa59983a A |
1662 | |
1663 | bool | |
1664 | is_legacy_mach_job(launch_data_t obj) | |
1665 | { | |
1666 | bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME); | |
1667 | bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND); | |
1668 | bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL); | |
1669 | ||
1670 | return has_command && has_servicename && !has_label; | |
1671 | } |