]>
Commit | Line | Data |
---|---|---|
e91b9f68 A |
1 | /* |
2 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. | |
3 | * | |
ed34e3c3 | 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ |
e91b9f68 | 5 | * |
ed34e3c3 A |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. | |
8 | * You may obtain a copy of the License at | |
e91b9f68 | 9 | * |
ed34e3c3 A |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, software | |
13 | * distributed under the License is distributed on an "AS IS" BASIS, | |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | * See the License for the specific language governing permissions and | |
e91b9f68 A |
16 | * limitations under the License. |
17 | * | |
ed34e3c3 | 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ |
e91b9f68 | 19 | */ |
ed34e3c3 A |
20 | |
21 | static const char *const __rcs_file_version__ = "$Revision: 1.88 $"; | |
22 | ||
e91b9f68 | 23 | #include <CoreFoundation/CoreFoundation.h> |
ed34e3c3 A |
24 | #include <CoreFoundation/CFPriv.h> |
25 | #include <NSSystemDirectories.h> | |
aa59983a | 26 | #include <mach/mach.h> |
e91b9f68 | 27 | #include <sys/types.h> |
ed34e3c3 | 28 | #include <sys/sysctl.h> |
e91b9f68 | 29 | #include <sys/time.h> |
ed34e3c3 | 30 | #include <sys/sysctl.h> |
e91b9f68 A |
31 | #include <sys/stat.h> |
32 | #include <sys/socket.h> | |
33 | #include <sys/un.h> | |
34 | #include <sys/fcntl.h> | |
35 | #include <sys/event.h> | |
36 | #include <sys/resource.h> | |
37 | #include <sys/param.h> | |
ed34e3c3 A |
38 | #include <sys/mount.h> |
39 | #include <sys/reboot.h> | |
40 | #include <net/if.h> | |
e91b9f68 | 41 | #include <netinet/in.h> |
ed34e3c3 A |
42 | #include <netinet/in_var.h> |
43 | #include <netinet6/nd6.h> | |
e91b9f68 A |
44 | #include <unistd.h> |
45 | #include <dirent.h> | |
46 | #include <libgen.h> | |
47 | #include <pwd.h> | |
48 | #include <stdio.h> | |
49 | #include <stdlib.h> | |
50 | #include <pwd.h> | |
51 | #include <grp.h> | |
52 | #include <netdb.h> | |
53 | #include <syslog.h> | |
ed34e3c3 | 54 | #include <glob.h> |
e91b9f68 A |
55 | #include <readline/readline.h> |
56 | #include <readline/history.h> | |
57 | #include <dns_sd.h> | |
ed34e3c3 A |
58 | #include <paths.h> |
59 | #include <utmp.h> | |
60 | #include <utmpx.h> | |
e91b9f68 | 61 | |
ed34e3c3 A |
62 | #include "bootstrap_public.h" |
63 | #include "bootstrap_private.h" | |
e91b9f68 A |
64 | #include "launch.h" |
65 | #include "launch_priv.h" | |
66 | ||
67 | #define LAUNCH_SECDIR "/tmp/launch-XXXXXX" | |
68 | ||
aa59983a A |
69 | #define MACHINIT_JOBKEY_ONDEMAND "OnDemand" |
70 | #define MACHINIT_JOBKEY_SERVICENAME "ServiceName" | |
71 | #define MACHINIT_JOBKEY_COMMAND "Command" | |
ed34e3c3 A |
72 | #define MACHINIT_JOBKEY_SERVERPORT "ServerPort" |
73 | #define MACHINIT_JOBKEY_SERVICEPORT "ServicePort" | |
74 | ||
75 | #define assumes(e) \ | |
76 | (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) | |
aa59983a A |
77 | |
78 | ||
ed34e3c3 A |
79 | struct load_unload_state { |
80 | launch_data_t pass0; | |
81 | launch_data_t pass1; | |
82 | launch_data_t pass2; | |
83 | char *session_type; | |
84 | unsigned int editondisk:1, load:1, forceload:1, __pad:29; | |
85 | }; | |
86 | ||
aa59983a | 87 | static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context); |
e91b9f68 | 88 | static bool launch_data_array_append(launch_data_t a, launch_data_t o); |
aa59983a | 89 | static void distill_jobs(launch_data_t); |
e91b9f68 A |
90 | static void distill_config_file(launch_data_t); |
91 | static void sock_dict_cb(launch_data_t what, const char *key, void *context); | |
92 | static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob); | |
93 | static launch_data_t CF2launch_data(CFTypeRef); | |
94 | static launch_data_t read_plist_file(const char *file, bool editondisk, bool load); | |
95 | static CFPropertyListRef CreateMyPropertyListFromFile(const char *); | |
96 | static void WriteMyPropertyListToFile(CFPropertyListRef, const char *); | |
ed34e3c3 A |
97 | static bool path_goodness_check(const char *path, bool forceload); |
98 | static void readpath(const char *, struct load_unload_state *); | |
99 | static void readfile(const char *, struct load_unload_state *); | |
e91b9f68 A |
100 | static int _fd(int); |
101 | static int demux_cmd(int argc, char *const argv[]); | |
ed34e3c3 | 102 | static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv); |
e91b9f68 | 103 | static void submit_job_pass(launch_data_t jobs); |
aa59983a | 104 | static void submit_mach_jobs(launch_data_t jobs); |
ed34e3c3 | 105 | static void let_go_of_mach_jobs(launch_data_t jobs); |
ab36757d | 106 | static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); |
ed34e3c3 A |
107 | static mach_port_t str2bsport(const char *s); |
108 | static void print_jobs(launch_data_t j); | |
109 | static void print_obj(launch_data_t obj, const char *key, void *context); | |
aa59983a | 110 | static bool is_legacy_mach_job(launch_data_t obj); |
ed34e3c3 A |
111 | static bool delay_to_second_pass(launch_data_t o); |
112 | static void delay_to_second_pass2(launch_data_t o, const char *key, void *context); | |
113 | static bool str2lim(const char *buf, rlim_t *res); | |
114 | static const char *lim2str(rlim_t val, char *buf); | |
115 | static const char *num2name(int n); | |
116 | static ssize_t name2num(const char *n); | |
117 | static void unloadjob(launch_data_t job); | |
118 | static void print_key_value(launch_data_t obj, const char *key, void *context); | |
119 | static void print_launchd_env(launch_data_t obj, const char *key, void *context); | |
120 | static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); | |
121 | static void loopback_setup_ipv4(void); | |
122 | static void loopback_setup_ipv6(void); | |
123 | static pid_t fwexec(const char *const *argv, bool _wait); | |
124 | static void do_potential_fsck(void); | |
125 | static bool path_check(const char *path); | |
126 | static bool is_safeboot(void); | |
127 | static bool is_netboot(void); | |
128 | static void apply_func_to_dir(const char *thedir, void (*thefunc)(const char *)); | |
129 | static void apply_sysctls_from_file(const char *thefile); | |
130 | static void empty_dir(const char *path); | |
131 | static int touch_file(const char *path, mode_t m); | |
132 | static void do_sysversion_sysctl(void); | |
133 | static void workaround4465949(void); | |
134 | ||
135 | static int bootstrap_cmd(int argc, char *const argv[]); | |
e91b9f68 A |
136 | static int load_and_unload_cmd(int argc, char *const argv[]); |
137 | //static int reload_cmd(int argc, char *const argv[]); | |
ed34e3c3 A |
138 | static int start_stop_remove_cmd(int argc, char *const argv[]); |
139 | static int submit_cmd(int argc, char *const argv[]); | |
e91b9f68 A |
140 | static int list_cmd(int argc, char *const argv[]); |
141 | ||
142 | static int setenv_cmd(int argc, char *const argv[]); | |
143 | static int unsetenv_cmd(int argc, char *const argv[]); | |
144 | static int getenv_and_export_cmd(int argc, char *const argv[]); | |
145 | ||
146 | static int limit_cmd(int argc, char *const argv[]); | |
147 | static int stdio_cmd(int argc, char *const argv[]); | |
148 | static int fyi_cmd(int argc, char *const argv[]); | |
149 | static int logupdate_cmd(int argc, char *const argv[]); | |
150 | static int umask_cmd(int argc, char *const argv[]); | |
151 | static int getrusage_cmd(int argc, char *const argv[]); | |
ed34e3c3 A |
152 | static int bsexec_cmd(int argc, char *const argv[]); |
153 | static int bslist_cmd(int argc, char *const argv[]); | |
e91b9f68 | 154 | |
ed34e3c3 | 155 | static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn)); |
e91b9f68 A |
156 | static int help_cmd(int argc, char *const argv[]); |
157 | ||
158 | static const struct { | |
159 | const char *name; | |
160 | int (*func)(int argc, char *const argv[]); | |
161 | const char *desc; | |
162 | } cmds[] = { | |
163 | { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, | |
164 | { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, | |
165 | // { "reload", reload_cmd, "Reload configuration files and/or directories" }, | |
ed34e3c3 A |
166 | { "start", start_stop_remove_cmd, "Start specified job" }, |
167 | { "stop", start_stop_remove_cmd, "Stop specified job" }, | |
168 | { "submit", submit_cmd, "Submit a job from the command line" }, | |
169 | { "remove", start_stop_remove_cmd, "Remove specified job" }, | |
170 | { "bootstrap", bootstrap_cmd, "Bootstrap launchd" }, | |
e91b9f68 A |
171 | { "list", list_cmd, "List jobs and information about jobs" }, |
172 | { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, | |
173 | { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, | |
174 | { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" }, | |
175 | { "export", getenv_and_export_cmd, "Export shell settings from launchd" }, | |
176 | { "limit", limit_cmd, "View and adjust launchd resource limits" }, | |
177 | { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, | |
178 | { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, | |
179 | { "shutdown", fyi_cmd, "Prepare for system shutdown" }, | |
ed34e3c3 | 180 | { "singleuser", fyi_cmd, "Switch to single-user mode" }, |
e91b9f68 A |
181 | { "reloadttys", fyi_cmd, "Reload /etc/ttys" }, |
182 | { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, | |
183 | { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, | |
184 | { "umask", umask_cmd, "Change launchd's umask" }, | |
ed34e3c3 A |
185 | { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" }, |
186 | { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" }, | |
187 | { "exit", exit_cmd, "Exit the interactive invocation of launchctl" }, | |
188 | { "quit", exit_cmd, "Quit the interactive invocation of launchctl" }, | |
e91b9f68 A |
189 | { "help", help_cmd, "This help output" }, |
190 | }; | |
191 | ||
ed34e3c3 A |
192 | static bool istty = false; |
193 | static bool verbose = false; | |
194 | ||
195 | int | |
196 | main(int argc, char *const argv[]) | |
e91b9f68 | 197 | { |
e91b9f68 A |
198 | char *l; |
199 | ||
ed34e3c3 A |
200 | do_sysversion_sysctl(); |
201 | ||
202 | istty = isatty(STDIN_FILENO); | |
203 | ||
204 | argc--, argv++; | |
205 | ||
206 | if (argc > 0 && argv[0][0] == '-') { | |
207 | char *flago; | |
208 | ||
209 | for (flago = argv[0] + 1; *flago; flago++) { | |
210 | switch (*flago) { | |
211 | case 'v': | |
212 | verbose = true; | |
213 | break; | |
214 | default: | |
215 | fprintf(stderr, "Unknown argument: '-%c'\n", *flago); | |
216 | break; | |
217 | } | |
218 | } | |
219 | argc--, argv++; | |
220 | } | |
e91b9f68 A |
221 | |
222 | if (NULL == readline) { | |
223 | fprintf(stderr, "missing library: readline\n"); | |
224 | exit(EXIT_FAILURE); | |
225 | } | |
226 | ||
ed34e3c3 A |
227 | if (argc == 0) { |
228 | while ((l = readline(istty ? "launchd% " : NULL))) { | |
229 | char *inputstring = l, *argv2[100], **ap = argv2; | |
230 | int i = 0; | |
231 | ||
232 | while ((*ap = strsep(&inputstring, " \t"))) { | |
233 | if (**ap != '\0') { | |
234 | ap++; | |
235 | i++; | |
236 | } | |
e91b9f68 | 237 | } |
e91b9f68 | 238 | |
ed34e3c3 A |
239 | if (i > 0) |
240 | demux_cmd(i, argv2); | |
241 | ||
242 | free(l); | |
243 | } | |
e91b9f68 | 244 | |
ed34e3c3 A |
245 | if (istty) { |
246 | fputc('\n', stdout); | |
247 | } | |
e91b9f68 A |
248 | } |
249 | ||
ed34e3c3 A |
250 | if (argc > 0) { |
251 | exit(demux_cmd(argc, argv)); | |
252 | } | |
e91b9f68 A |
253 | |
254 | exit(EXIT_SUCCESS); | |
255 | } | |
256 | ||
ed34e3c3 A |
257 | int |
258 | demux_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
259 | { |
260 | size_t i; | |
261 | ||
262 | optind = 1; | |
263 | optreset = 1; | |
264 | ||
265 | for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { | |
266 | if (!strcmp(cmds[i].name, argv[0])) | |
267 | return cmds[i].func(argc, argv); | |
268 | } | |
269 | ||
270 | fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]); | |
271 | return 1; | |
272 | } | |
273 | ||
ed34e3c3 A |
274 | int |
275 | unsetenv_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
276 | { |
277 | launch_data_t resp, tmp, msg; | |
278 | ||
279 | if (argc != 2) { | |
280 | fprintf(stderr, "%s usage: unsetenv <key>\n", getprogname()); | |
281 | return 1; | |
282 | } | |
283 | ||
284 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
285 | ||
286 | tmp = launch_data_new_string(argv[1]); | |
287 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT); | |
288 | ||
289 | resp = launch_msg(msg); | |
290 | ||
291 | launch_data_free(msg); | |
292 | ||
293 | if (resp) { | |
294 | launch_data_free(resp); | |
295 | } else { | |
296 | fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno)); | |
297 | } | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
ed34e3c3 A |
302 | int |
303 | setenv_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
304 | { |
305 | launch_data_t resp, tmp, tmpv, msg; | |
306 | ||
307 | if (argc != 3) { | |
308 | fprintf(stderr, "%s usage: setenv <key> <value>\n", getprogname()); | |
309 | return 1; | |
310 | } | |
311 | ||
312 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
313 | tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
314 | ||
315 | tmpv = launch_data_new_string(argv[2]); | |
316 | launch_data_dict_insert(tmp, tmpv, argv[1]); | |
317 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT); | |
318 | ||
319 | resp = launch_msg(msg); | |
320 | launch_data_free(msg); | |
321 | ||
322 | if (resp) { | |
323 | launch_data_free(resp); | |
324 | } else { | |
325 | fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno)); | |
326 | } | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
ed34e3c3 A |
331 | void |
332 | print_launchd_env(launch_data_t obj, const char *key, void *context) | |
aa59983a A |
333 | { |
334 | bool *is_csh = context; | |
335 | ||
ed34e3c3 | 336 | /* XXX escape the double quotes */ |
aa59983a | 337 | if (*is_csh) |
ed34e3c3 | 338 | fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj)); |
aa59983a | 339 | else |
ed34e3c3 | 340 | fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key); |
aa59983a A |
341 | } |
342 | ||
ed34e3c3 A |
343 | void |
344 | print_key_value(launch_data_t obj, const char *key, void *context) | |
aa59983a A |
345 | { |
346 | const char *k = context; | |
347 | ||
348 | if (!strcmp(key, k)) | |
349 | fprintf(stdout, "%s\n", launch_data_get_string(obj)); | |
350 | } | |
351 | ||
ed34e3c3 A |
352 | int |
353 | getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused))) | |
e91b9f68 A |
354 | { |
355 | launch_data_t resp, msg; | |
356 | bool is_csh = false; | |
aa59983a | 357 | char *k; |
e91b9f68 A |
358 | |
359 | if (!strcmp(argv[0], "export")) { | |
360 | char *s = getenv("SHELL"); | |
361 | if (s) | |
362 | is_csh = strstr(s, "csh") ? true : false; | |
363 | } else if (argc != 2) { | |
364 | fprintf(stderr, "%s usage: getenv <key>\n", getprogname()); | |
365 | return 1; | |
366 | } | |
367 | ||
368 | k = argv[1]; | |
369 | ||
370 | msg = launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT); | |
371 | ||
372 | resp = launch_msg(msg); | |
373 | launch_data_free(msg); | |
374 | ||
375 | if (resp) { | |
aa59983a A |
376 | if (!strcmp(argv[0], "export")) |
377 | launch_data_dict_iterate(resp, print_launchd_env, &is_csh); | |
378 | else | |
379 | launch_data_dict_iterate(resp, print_key_value, k); | |
e91b9f68 A |
380 | launch_data_free(resp); |
381 | } else { | |
382 | fprintf(stderr, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT "\"): %s\n", strerror(errno)); | |
383 | } | |
384 | return 0; | |
385 | } | |
386 | ||
ed34e3c3 A |
387 | void |
388 | unloadjob(launch_data_t job) | |
e91b9f68 A |
389 | { |
390 | launch_data_t resp, tmp, tmps, msg; | |
391 | int e; | |
392 | ||
393 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
394 | tmp = launch_data_alloc(LAUNCH_DATA_STRING); | |
395 | tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL); | |
396 | ||
397 | if (!tmps) { | |
398 | fprintf(stderr, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL); | |
399 | return; | |
400 | } | |
401 | ||
402 | launch_data_set_string(tmp, launch_data_get_string(tmps)); | |
403 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_REMOVEJOB); | |
404 | resp = launch_msg(msg); | |
405 | launch_data_free(msg); | |
406 | if (!resp) { | |
407 | fprintf(stderr, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno)); | |
408 | return; | |
409 | } | |
410 | if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) { | |
411 | if ((e = launch_data_get_errno(resp))) | |
412 | fprintf(stderr, "%s\n", strerror(e)); | |
413 | } | |
414 | launch_data_free(resp); | |
415 | } | |
416 | ||
aa59983a A |
417 | launch_data_t |
418 | read_plist_file(const char *file, bool editondisk, bool load) | |
e91b9f68 A |
419 | { |
420 | CFPropertyListRef plist = CreateMyPropertyListFromFile(file); | |
421 | launch_data_t r = NULL; | |
422 | ||
423 | if (NULL == plist) { | |
424 | fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), file); | |
425 | return NULL; | |
426 | } | |
427 | ||
428 | if (editondisk) { | |
429 | if (load) | |
430 | CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED)); | |
431 | else | |
432 | CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue); | |
433 | WriteMyPropertyListToFile(plist, file); | |
434 | } | |
435 | ||
436 | r = CF2launch_data(plist); | |
437 | ||
438 | CFRelease(plist); | |
439 | ||
440 | return r; | |
441 | } | |
442 | ||
aa59983a | 443 | void |
ed34e3c3 A |
444 | delay_to_second_pass2(launch_data_t o, const char *key, void *context) |
445 | { | |
446 | bool *res = context; | |
447 | size_t i; | |
448 | ||
449 | if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) { | |
450 | *res = true; | |
451 | return; | |
452 | } | |
453 | ||
454 | switch (launch_data_get_type(o)) { | |
455 | case LAUNCH_DATA_DICTIONARY: | |
456 | launch_data_dict_iterate(o, delay_to_second_pass2, context); | |
457 | break; | |
458 | case LAUNCH_DATA_ARRAY: | |
459 | for (i = 0; i < launch_data_array_get_count(o); i++) | |
460 | delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context); | |
461 | break; | |
462 | default: | |
463 | break; | |
464 | } | |
465 | } | |
466 | ||
467 | bool | |
468 | delay_to_second_pass(launch_data_t o) | |
469 | { | |
470 | bool res = false; | |
471 | ||
472 | launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS); | |
473 | ||
474 | if (NULL == socks) | |
475 | return false; | |
476 | ||
477 | delay_to_second_pass2(socks, NULL, &res); | |
478 | ||
479 | return res; | |
480 | } | |
481 | ||
482 | void | |
483 | readfile(const char *what, struct load_unload_state *lus) | |
e91b9f68 | 484 | { |
ed34e3c3 A |
485 | char ourhostname[1024]; |
486 | launch_data_t tmpd, tmps, thejob, tmpa; | |
e91b9f68 | 487 | bool job_disabled = false; |
ed34e3c3 A |
488 | size_t i, c; |
489 | ||
490 | gethostname(ourhostname, sizeof(ourhostname)); | |
e91b9f68 | 491 | |
ed34e3c3 | 492 | if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) { |
e91b9f68 A |
493 | fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), what); |
494 | return; | |
495 | } | |
496 | ||
aa59983a | 497 | if (is_legacy_mach_job(thejob)) { |
ed34e3c3 A |
498 | fprintf(stderr, "%s: Please convert the following to launchd: %s\n", getprogname(), what); |
499 | launch_data_array_append(lus->pass0, thejob); | |
aa59983a A |
500 | return; |
501 | } | |
502 | ||
ab36757d A |
503 | if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) { |
504 | fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what); | |
ed34e3c3 A |
505 | goto out_bad; |
506 | } | |
507 | ||
508 | if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) { | |
509 | c = launch_data_array_get_count(tmpa); | |
510 | ||
511 | for (i = 0; i < c; i++) { | |
512 | launch_data_t oai = launch_data_array_get_index(tmpa, i); | |
513 | if (!strcasecmp(ourhostname, launch_data_get_string(oai))) | |
514 | goto out_bad; | |
515 | } | |
516 | } | |
517 | ||
518 | if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) { | |
519 | c = launch_data_array_get_count(tmpa); | |
520 | ||
521 | for (i = 0; i < c; i++) { | |
522 | launch_data_t oai = launch_data_array_get_index(tmpa, i); | |
523 | if (!strcasecmp(ourhostname, launch_data_get_string(oai))) | |
524 | break; | |
525 | } | |
526 | ||
527 | if (i == c) | |
528 | goto out_bad; | |
529 | } | |
530 | ||
531 | if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) { | |
532 | const char *allowed_session; | |
533 | bool skipjob = true; | |
534 | ||
535 | if (lus->session_type) switch (launch_data_get_type(tmpa)) { | |
536 | case LAUNCH_DATA_ARRAY: | |
537 | c = launch_data_array_get_count(tmpa); | |
538 | for (i = 0; i < c; i++) { | |
539 | tmps = launch_data_array_get_index(tmpa, i); | |
540 | allowed_session = launch_data_get_string(tmps); | |
541 | if (strcasecmp(lus->session_type, allowed_session) == 0) { | |
542 | skipjob = false; | |
543 | break; | |
544 | } | |
545 | } | |
546 | break; | |
547 | case LAUNCH_DATA_STRING: | |
548 | allowed_session = launch_data_get_string(tmpa); | |
549 | if (strcasecmp(lus->session_type, allowed_session) == 0) { | |
550 | skipjob = false; | |
551 | } | |
552 | break; | |
553 | default: | |
554 | break; | |
555 | } | |
556 | ||
557 | if (skipjob) { | |
558 | goto out_bad; | |
559 | } | |
ab36757d A |
560 | } |
561 | ||
e91b9f68 A |
562 | if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) |
563 | job_disabled = launch_data_get_bool(tmpd); | |
564 | ||
ed34e3c3 | 565 | if (lus->forceload) |
ab36757d A |
566 | job_disabled = false; |
567 | ||
ed34e3c3 A |
568 | if (job_disabled && lus->load) |
569 | goto out_bad; | |
570 | ||
571 | if (delay_to_second_pass(thejob)) | |
572 | launch_data_array_append(lus->pass2, thejob); | |
573 | else | |
574 | launch_data_array_append(lus->pass1, thejob); | |
575 | ||
576 | if (verbose) | |
577 | fprintf(stdout, "Will load: %s\n", what); | |
578 | ||
579 | return; | |
580 | out_bad: | |
581 | if (verbose) | |
582 | fprintf(stdout, "Ignored: %s\n", what); | |
583 | launch_data_free(thejob); | |
584 | } | |
585 | ||
586 | bool | |
587 | path_goodness_check(const char *path, bool forceload) | |
588 | { | |
589 | struct stat sb; | |
590 | ||
591 | if (stat(path, &sb) == -1) { | |
592 | fprintf(stderr, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path, strerror(errno)); | |
593 | return false; | |
e91b9f68 A |
594 | } |
595 | ||
ed34e3c3 A |
596 | if (forceload) |
597 | return true; | |
598 | ||
599 | if (sb.st_mode & (S_IWOTH|S_IWGRP)) { | |
600 | fprintf(stderr, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path); | |
601 | return false; | |
602 | } | |
603 | ||
604 | if (sb.st_uid != 0 && sb.st_uid != getuid()) { | |
605 | fprintf(stderr, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path); | |
606 | return false; | |
607 | } | |
608 | ||
609 | if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) { | |
610 | fprintf(stderr, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path); | |
611 | return false; | |
612 | } | |
613 | ||
614 | return true; | |
e91b9f68 A |
615 | } |
616 | ||
aa59983a | 617 | void |
ed34e3c3 | 618 | readpath(const char *what, struct load_unload_state *lus) |
e91b9f68 A |
619 | { |
620 | char buf[MAXPATHLEN]; | |
621 | struct stat sb; | |
622 | struct dirent *de; | |
623 | DIR *d; | |
624 | ||
ed34e3c3 A |
625 | if (!path_goodness_check(what, lus->forceload)) |
626 | return; | |
627 | ||
e91b9f68 A |
628 | if (stat(what, &sb) == -1) |
629 | return; | |
630 | ||
ed34e3c3 A |
631 | if (S_ISREG(sb.st_mode)) { |
632 | readfile(what, lus); | |
633 | } else if (S_ISDIR(sb.st_mode)) { | |
e91b9f68 A |
634 | if ((d = opendir(what)) == NULL) { |
635 | fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname()); | |
636 | return; | |
637 | } | |
638 | ||
639 | while ((de = readdir(d))) { | |
640 | if ((de->d_name[0] == '.')) | |
641 | continue; | |
642 | snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name); | |
643 | ||
ed34e3c3 A |
644 | if (!path_goodness_check(buf, lus->forceload)) |
645 | continue; | |
646 | ||
647 | readfile(buf, lus); | |
e91b9f68 A |
648 | } |
649 | closedir(d); | |
650 | } | |
651 | } | |
652 | ||
653 | struct distill_context { | |
654 | launch_data_t base; | |
655 | launch_data_t newsockdict; | |
656 | }; | |
657 | ||
aa59983a A |
658 | void |
659 | distill_jobs(launch_data_t jobs) | |
660 | { | |
661 | size_t i, c = launch_data_array_get_count(jobs); | |
662 | ||
663 | for (i = 0; i < c; i++) | |
664 | distill_config_file(launch_data_array_get_index(jobs, i)); | |
665 | } | |
666 | ||
667 | void | |
668 | distill_config_file(launch_data_t id_plist) | |
e91b9f68 A |
669 | { |
670 | struct distill_context dc = { id_plist, NULL }; | |
671 | launch_data_t tmp; | |
672 | ||
ed34e3c3 | 673 | if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) { |
e91b9f68 A |
674 | dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY); |
675 | launch_data_dict_iterate(tmp, sock_dict_cb, &dc); | |
676 | launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS); | |
677 | } | |
678 | } | |
679 | ||
ed34e3c3 A |
680 | void |
681 | sock_dict_cb(launch_data_t what, const char *key, void *context) | |
e91b9f68 A |
682 | { |
683 | struct distill_context *dc = context; | |
684 | launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
685 | ||
686 | launch_data_dict_insert(dc->newsockdict, fdarray, key); | |
687 | ||
688 | if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) { | |
689 | sock_dict_edit_entry(what, key, fdarray, dc->base); | |
690 | } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) { | |
691 | launch_data_t tmp; | |
692 | size_t i; | |
693 | ||
694 | for (i = 0; i < launch_data_array_get_count(what); i++) { | |
695 | tmp = launch_data_array_get_index(what, i); | |
696 | sock_dict_edit_entry(tmp, key, fdarray, dc->base); | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
ed34e3c3 A |
701 | void |
702 | sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob) | |
e91b9f68 A |
703 | { |
704 | launch_data_t a, val; | |
705 | int sfd, st = SOCK_STREAM; | |
706 | bool passive = true; | |
707 | ||
708 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) { | |
709 | if (!strcasecmp(launch_data_get_string(val), "stream")) { | |
710 | st = SOCK_STREAM; | |
711 | } else if (!strcasecmp(launch_data_get_string(val), "dgram")) { | |
712 | st = SOCK_DGRAM; | |
713 | } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) { | |
714 | st = SOCK_SEQPACKET; | |
715 | } | |
716 | } | |
717 | ||
718 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) | |
719 | passive = launch_data_get_bool(val); | |
720 | ||
721 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) { | |
722 | char secdir[] = LAUNCH_SECDIR, buf[1024]; | |
723 | launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES); | |
724 | ||
725 | if (NULL == uenv) { | |
726 | uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
727 | launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES); | |
728 | } | |
729 | ||
730 | mkdtemp(secdir); | |
731 | ||
732 | sprintf(buf, "%s/%s", secdir, key); | |
733 | ||
734 | a = launch_data_new_string(buf); | |
735 | launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME); | |
736 | a = launch_data_new_string(buf); | |
737 | launch_data_dict_insert(uenv, a, launch_data_get_string(val)); | |
738 | } | |
739 | ||
740 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) { | |
741 | struct sockaddr_un sun; | |
aa59983a A |
742 | mode_t sun_mode = 0; |
743 | mode_t oldmask; | |
744 | bool setm = false; | |
e91b9f68 A |
745 | |
746 | memset(&sun, 0, sizeof(sun)); | |
747 | ||
748 | sun.sun_family = AF_UNIX; | |
749 | ||
750 | strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path)); | |
751 | ||
752 | if ((sfd = _fd(socket(AF_UNIX, st, 0))) == -1) | |
753 | return; | |
754 | ||
aa59983a A |
755 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) { |
756 | sun_mode = (mode_t)launch_data_get_integer(val); | |
757 | setm = true; | |
758 | } | |
759 | ||
e91b9f68 A |
760 | if (passive) { |
761 | if (unlink(sun.sun_path) == -1 && errno != ENOENT) { | |
762 | close(sfd); | |
763 | return; | |
764 | } | |
aa59983a | 765 | oldmask = umask(S_IRWXG|S_IRWXO); |
e91b9f68 A |
766 | if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { |
767 | close(sfd); | |
aa59983a | 768 | umask(oldmask); |
e91b9f68 A |
769 | return; |
770 | } | |
aa59983a A |
771 | umask(oldmask); |
772 | if (setm) { | |
773 | chmod(sun.sun_path, sun_mode); | |
774 | } | |
e91b9f68 A |
775 | if ((st == SOCK_STREAM || st == SOCK_SEQPACKET) |
776 | && listen(sfd, SOMAXCONN) == -1) { | |
777 | close(sfd); | |
778 | return; | |
779 | } | |
780 | } else if (connect(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { | |
781 | close(sfd); | |
782 | return; | |
783 | } | |
784 | ||
785 | val = launch_data_new_fd(sfd); | |
786 | launch_data_array_append(fdarray, val); | |
787 | } else { | |
788 | launch_data_t rnames = NULL; | |
ab36757d | 789 | const char *node = NULL, *serv = NULL, *mgroup = NULL; |
e91b9f68 A |
790 | char servnbuf[50]; |
791 | struct addrinfo hints, *res0, *res; | |
792 | int gerr, sock_opt = 1; | |
793 | bool rendezvous = false; | |
794 | ||
795 | memset(&hints, 0, sizeof(hints)); | |
796 | ||
797 | hints.ai_socktype = st; | |
798 | if (passive) | |
799 | hints.ai_flags |= AI_PASSIVE; | |
800 | ||
801 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME))) | |
802 | node = launch_data_get_string(val); | |
ab36757d A |
803 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP))) |
804 | mgroup = launch_data_get_string(val); | |
e91b9f68 A |
805 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) { |
806 | if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) { | |
807 | sprintf(servnbuf, "%lld", launch_data_get_integer(val)); | |
808 | serv = servnbuf; | |
809 | } else { | |
810 | serv = launch_data_get_string(val); | |
811 | } | |
812 | } | |
813 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) { | |
814 | if (!strcasecmp("IPv4", launch_data_get_string(val))) | |
815 | hints.ai_family = AF_INET; | |
816 | else if (!strcasecmp("IPv6", launch_data_get_string(val))) | |
817 | hints.ai_family = AF_INET6; | |
818 | } | |
819 | if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) { | |
820 | if (!strcasecmp("TCP", launch_data_get_string(val))) | |
821 | hints.ai_protocol = IPPROTO_TCP; | |
822 | } | |
823 | if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) { | |
824 | rendezvous = true; | |
825 | if (LAUNCH_DATA_BOOL == launch_data_get_type(rnames)) { | |
826 | rendezvous = launch_data_get_bool(rnames); | |
827 | rnames = NULL; | |
828 | } | |
829 | } | |
830 | ||
831 | if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) { | |
832 | fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr)); | |
833 | return; | |
834 | } | |
835 | ||
836 | for (res = res0; res; res = res->ai_next) { | |
ed34e3c3 | 837 | launch_data_t rvs_fd = NULL; |
e91b9f68 A |
838 | if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) { |
839 | fprintf(stderr, "socket(): %s\n", strerror(errno)); | |
840 | return; | |
841 | } | |
842 | if (hints.ai_flags & AI_PASSIVE) { | |
843 | if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, | |
844 | (void *)&sock_opt, sizeof(sock_opt))) { | |
845 | fprintf(stderr, "setsockopt(IPV6_V6ONLY): %m"); | |
846 | return; | |
847 | } | |
ab36757d A |
848 | if (mgroup) { |
849 | if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, sizeof(sock_opt)) == -1) { | |
850 | fprintf(stderr, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno)); | |
851 | return; | |
852 | } | |
853 | } else { | |
854 | if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof(sock_opt)) == -1) { | |
855 | fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno)); | |
856 | return; | |
857 | } | |
e91b9f68 A |
858 | } |
859 | if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) { | |
860 | fprintf(stderr, "bind(): %s\n", strerror(errno)); | |
861 | return; | |
862 | } | |
ab36757d A |
863 | |
864 | if (mgroup) { | |
865 | do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup); | |
866 | } | |
e91b9f68 A |
867 | if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET) |
868 | && listen(sfd, SOMAXCONN) == -1) { | |
869 | fprintf(stderr, "listen(): %s\n", strerror(errno)); | |
870 | return; | |
871 | } | |
872 | if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) && | |
873 | (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) { | |
ed34e3c3 A |
874 | launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS); |
875 | if (NULL == rvs_fds) { | |
876 | rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
877 | launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS); | |
878 | } | |
e91b9f68 | 879 | if (NULL == rnames) { |
ed34e3c3 A |
880 | rvs_fd = do_rendezvous_magic(res, serv); |
881 | if (rvs_fd) | |
882 | launch_data_array_append(rvs_fds, rvs_fd); | |
e91b9f68 | 883 | } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) { |
ed34e3c3 A |
884 | rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames)); |
885 | if (rvs_fd) | |
886 | launch_data_array_append(rvs_fds, rvs_fd); | |
e91b9f68 A |
887 | } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) { |
888 | size_t rn_i, rn_ac = launch_data_array_get_count(rnames); | |
889 | ||
890 | for (rn_i = 0; rn_i < rn_ac; rn_i++) { | |
891 | launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i); | |
892 | ||
ed34e3c3 A |
893 | rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp)); |
894 | if (rvs_fd) | |
895 | launch_data_array_append(rvs_fds, rvs_fd); | |
e91b9f68 A |
896 | } |
897 | } | |
898 | } | |
899 | } else { | |
900 | if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) { | |
901 | fprintf(stderr, "connect(): %s\n", strerror(errno)); | |
902 | return; | |
903 | } | |
904 | } | |
905 | val = launch_data_new_fd(sfd); | |
ed34e3c3 A |
906 | if (rvs_fd) { |
907 | /* <rdar://problem/3964648> Launchd should not register the same service more than once */ | |
908 | /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */ | |
909 | rendezvous = false; | |
910 | } | |
e91b9f68 A |
911 | launch_data_array_append(fdarray, val); |
912 | } | |
913 | } | |
914 | } | |
915 | ||
ed34e3c3 A |
916 | void |
917 | do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup) | |
ab36757d A |
918 | { |
919 | struct addrinfo hints, *res0, *res; | |
920 | struct ip_mreq mreq; | |
921 | struct ipv6_mreq m6req; | |
922 | int gerr; | |
923 | ||
924 | memset(&hints, 0, sizeof(hints)); | |
925 | ||
926 | hints.ai_flags |= AI_PASSIVE; | |
927 | hints.ai_family = family; | |
928 | hints.ai_socktype = socktype; | |
929 | hints.ai_protocol = protocol; | |
930 | ||
931 | if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) { | |
932 | fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr)); | |
933 | return; | |
934 | } | |
935 | ||
936 | for (res = res0; res; res = res->ai_next) { | |
937 | if (AF_INET == family) { | |
938 | memset(&mreq, 0, sizeof(mreq)); | |
939 | mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; | |
940 | if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { | |
941 | fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno)); | |
942 | continue; | |
943 | } | |
944 | break; | |
945 | } else if (AF_INET6 == family) { | |
946 | memset(&m6req, 0, sizeof(m6req)); | |
947 | m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; | |
948 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, sizeof(m6req)) == -1) { | |
949 | fprintf(stderr, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno)); | |
950 | continue; | |
951 | } | |
952 | break; | |
953 | } else { | |
954 | fprintf(stderr, "unknown family during multicast group bind!\n"); | |
955 | break; | |
956 | } | |
957 | } | |
958 | ||
959 | freeaddrinfo(res0); | |
960 | } | |
961 | ||
6a39f10b | 962 | |
ed34e3c3 A |
963 | launch_data_t |
964 | do_rendezvous_magic(const struct addrinfo *res, const char *serv) | |
6a39f10b | 965 | { |
ed34e3c3 | 966 | struct stat sb; |
6a39f10b | 967 | DNSServiceRef service; |
ed34e3c3 A |
968 | DNSServiceErrorType error; |
969 | char rvs_buf[200]; | |
970 | short port; | |
971 | static int statres = 1; | |
e91b9f68 | 972 | |
ed34e3c3 A |
973 | if (1 == statres) |
974 | statres = stat("/usr/sbin/mDNSResponder", &sb); | |
6a39f10b | 975 | |
ed34e3c3 A |
976 | if (-1 == statres) |
977 | return NULL; | |
e91b9f68 | 978 | |
ed34e3c3 | 979 | sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp"); |
e91b9f68 | 980 | |
ed34e3c3 A |
981 | if (res->ai_family == AF_INET) |
982 | port = ((struct sockaddr_in *)res->ai_addr)->sin_port; | |
983 | else | |
984 | port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port; | |
6a39f10b | 985 | |
ed34e3c3 | 986 | error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL); |
e91b9f68 | 987 | |
ed34e3c3 A |
988 | if (error == kDNSServiceErr_NoError) |
989 | return launch_data_new_fd(DNSServiceRefSockFD(service)); | |
e91b9f68 | 990 | |
ed34e3c3 A |
991 | fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error); |
992 | return NULL; | |
e91b9f68 A |
993 | } |
994 | ||
ed34e3c3 A |
995 | CFPropertyListRef |
996 | CreateMyPropertyListFromFile(const char *posixfile) | |
e91b9f68 A |
997 | { |
998 | CFPropertyListRef propertyList; | |
999 | CFStringRef errorString; | |
1000 | CFDataRef resourceData; | |
1001 | SInt32 errorCode; | |
1002 | CFURLRef fileURL; | |
1003 | ||
aa59983a | 1004 | fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); |
e91b9f68 A |
1005 | if (!fileURL) |
1006 | fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile); | |
1007 | if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) | |
1008 | fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode); | |
1009 | propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString); | |
1010 | if (!propertyList) | |
1011 | fprintf(stderr, "%s: propertyList is NULL\n", getprogname()); | |
1012 | ||
1013 | return propertyList; | |
1014 | } | |
1015 | ||
ed34e3c3 A |
1016 | void |
1017 | WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile) | |
e91b9f68 A |
1018 | { |
1019 | CFDataRef resourceData; | |
1020 | CFURLRef fileURL; | |
1021 | SInt32 errorCode; | |
1022 | ||
aa59983a | 1023 | fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); |
e91b9f68 A |
1024 | if (!fileURL) |
1025 | fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile); | |
1026 | resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist); | |
1027 | if (resourceData == NULL) | |
1028 | fprintf(stderr, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile); | |
1029 | if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) | |
1030 | fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode); | |
1031 | } | |
1032 | ||
ed34e3c3 A |
1033 | void |
1034 | myCFDictionaryApplyFunction(const void *key, const void *value, void *context) | |
e91b9f68 A |
1035 | { |
1036 | launch_data_t ik, iw, where = context; | |
1037 | ||
1038 | ik = CF2launch_data(key); | |
1039 | iw = CF2launch_data(value); | |
1040 | ||
1041 | launch_data_dict_insert(where, iw, launch_data_get_string(ik)); | |
1042 | launch_data_free(ik); | |
1043 | } | |
1044 | ||
ed34e3c3 A |
1045 | launch_data_t |
1046 | CF2launch_data(CFTypeRef cfr) | |
e91b9f68 A |
1047 | { |
1048 | launch_data_t r; | |
1049 | CFTypeID cft = CFGetTypeID(cfr); | |
1050 | ||
1051 | if (cft == CFStringGetTypeID()) { | |
1052 | char buf[4096]; | |
1053 | CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8); | |
ed34e3c3 A |
1054 | r = launch_data_alloc(LAUNCH_DATA_STRING); |
1055 | launch_data_set_string(r, buf); | |
e91b9f68 | 1056 | } else if (cft == CFBooleanGetTypeID()) { |
ed34e3c3 A |
1057 | r = launch_data_alloc(LAUNCH_DATA_BOOL); |
1058 | launch_data_set_bool(r, CFBooleanGetValue(cfr)); | |
e91b9f68 A |
1059 | } else if (cft == CFArrayGetTypeID()) { |
1060 | CFIndex i, ac = CFArrayGetCount(cfr); | |
1061 | r = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
1062 | for (i = 0; i < ac; i++) { | |
1063 | CFTypeRef v = CFArrayGetValueAtIndex(cfr, i); | |
1064 | if (v) { | |
1065 | launch_data_t iv = CF2launch_data(v); | |
1066 | launch_data_array_set_index(r, iv, i); | |
1067 | } | |
1068 | } | |
1069 | } else if (cft == CFDictionaryGetTypeID()) { | |
1070 | r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1071 | CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r); | |
1072 | } else if (cft == CFDataGetTypeID()) { | |
ed34e3c3 A |
1073 | r = launch_data_alloc(LAUNCH_DATA_ARRAY); |
1074 | launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); | |
e91b9f68 A |
1075 | } else if (cft == CFNumberGetTypeID()) { |
1076 | long long n; | |
1077 | double d; | |
1078 | CFNumberType cfnt = CFNumberGetType(cfr); | |
1079 | switch (cfnt) { | |
1080 | case kCFNumberSInt8Type: | |
1081 | case kCFNumberSInt16Type: | |
1082 | case kCFNumberSInt32Type: | |
1083 | case kCFNumberSInt64Type: | |
1084 | case kCFNumberCharType: | |
1085 | case kCFNumberShortType: | |
1086 | case kCFNumberIntType: | |
1087 | case kCFNumberLongType: | |
1088 | case kCFNumberLongLongType: | |
1089 | CFNumberGetValue(cfr, kCFNumberLongLongType, &n); | |
ed34e3c3 A |
1090 | r = launch_data_alloc(LAUNCH_DATA_INTEGER); |
1091 | launch_data_set_integer(r, n); | |
e91b9f68 A |
1092 | break; |
1093 | case kCFNumberFloat32Type: | |
1094 | case kCFNumberFloat64Type: | |
1095 | case kCFNumberFloatType: | |
1096 | case kCFNumberDoubleType: | |
1097 | CFNumberGetValue(cfr, kCFNumberDoubleType, &d); | |
ed34e3c3 A |
1098 | r = launch_data_alloc(LAUNCH_DATA_REAL); |
1099 | launch_data_set_real(r, d); | |
e91b9f68 A |
1100 | break; |
1101 | default: | |
1102 | r = NULL; | |
1103 | break; | |
1104 | } | |
1105 | } else { | |
1106 | r = NULL; | |
1107 | } | |
1108 | return r; | |
1109 | } | |
1110 | ||
ed34e3c3 A |
1111 | int |
1112 | help_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
1113 | { |
1114 | FILE *where = stdout; | |
1115 | int l, cmdwidth = 0; | |
1116 | size_t i; | |
1117 | ||
1118 | if (argc == 0 || argv == NULL) | |
1119 | where = stderr; | |
1120 | ||
1121 | fprintf(where, "usage: %s <subcommand>\n", getprogname()); | |
ed34e3c3 | 1122 | |
e91b9f68 A |
1123 | for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { |
1124 | l = strlen(cmds[i].name); | |
1125 | if (l > cmdwidth) | |
1126 | cmdwidth = l; | |
1127 | } | |
ed34e3c3 | 1128 | |
e91b9f68 A |
1129 | for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) |
1130 | fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc); | |
1131 | ||
1132 | return 0; | |
1133 | } | |
1134 | ||
ed34e3c3 A |
1135 | int |
1136 | exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused))) | |
1137 | { | |
1138 | exit(0); | |
1139 | } | |
1140 | ||
1141 | int | |
1142 | _fd(int fd) | |
e91b9f68 A |
1143 | { |
1144 | if (fd >= 0) | |
1145 | fcntl(fd, F_SETFD, 1); | |
1146 | return fd; | |
1147 | } | |
1148 | ||
ed34e3c3 A |
1149 | int |
1150 | bootstrap_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused))) | |
e91b9f68 | 1151 | { |
ed34e3c3 A |
1152 | int memmib[] = { CTL_HW, HW_MEMSIZE }; |
1153 | int mvnmib[] = { CTL_KERN, KERN_MAXVNODES }; | |
1154 | int hnmib[] = { CTL_KERN, KERN_HOSTNAME }; | |
1155 | uint64_t mem = 0; | |
1156 | uint32_t mvn; | |
1157 | size_t memsz = sizeof(mem); | |
1158 | struct group *tfp_gr; | |
1159 | ||
1160 | if (assumes((tfp_gr = getgrnam("procview")) != NULL)) { | |
1161 | int tfp_r_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_READ_GROUP }; | |
1162 | gid_t tfp_r_gid = tfp_gr->gr_gid; | |
1163 | assumes(sysctl(tfp_r_mib, 3, NULL, NULL, &tfp_r_gid, sizeof(tfp_r_gid)) != -1); | |
1164 | } | |
1165 | ||
1166 | if (assumes((tfp_gr = getgrnam("procmod")) != NULL)) { | |
1167 | int tfp_rw_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_RW_GROUP }; | |
1168 | gid_t tfp_rw_gid = tfp_gr->gr_gid; | |
1169 | assumes(sysctl(tfp_rw_mib, 3, NULL, NULL, &tfp_rw_gid, sizeof(tfp_rw_gid)) != -1); | |
1170 | } | |
1171 | ||
1172 | if (assumes(sysctl(memmib, 2, &mem, &memsz, NULL, 0) != -1)) { | |
1173 | mvn = mem / (64 * 1024) + 1024; | |
1174 | assumes(sysctl(mvnmib, 2, NULL, NULL, &mvn, sizeof(mvn)) != -1); | |
1175 | } | |
1176 | assumes(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) != -1); | |
1177 | ||
1178 | loopback_setup_ipv4(); | |
1179 | loopback_setup_ipv6(); | |
1180 | ||
1181 | apply_sysctls_from_file("/etc/sysctl-macosxserver.conf"); | |
1182 | apply_sysctls_from_file("/etc/sysctl.conf"); | |
1183 | ||
1184 | if (path_check("/System/Installation") && path_check("/etc/rc.cdrom")) { | |
1185 | const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL }; | |
1186 | assumes(fwexec(rccdrom_tool, true) != -1); | |
1187 | assumes(reboot(RB_HALT) != -1); | |
1188 | _exit(EXIT_FAILURE); | |
1189 | } else if (is_netboot()) { | |
1190 | const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL }; | |
1191 | if (!assumes(fwexec(rcnetboot_tool, true) != -1)) { | |
1192 | assumes(reboot(RB_HALT) != -1); | |
1193 | _exit(EXIT_FAILURE); | |
1194 | } | |
1195 | } else { | |
1196 | do_potential_fsck(); | |
1197 | } | |
1198 | ||
1199 | if (path_check("/var/account/acct")) { | |
1200 | assumes(acct("/var/account/acct") != -1); | |
1201 | } | |
1202 | ||
1203 | if (path_check("/etc/fstab")) { | |
1204 | const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL }; | |
1205 | assumes(fwexec(mount_tool, true) != -1); | |
1206 | } | |
1207 | ||
1208 | if (path_check("/etc/rc.installer_cleanup")) { | |
1209 | const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL }; | |
1210 | assumes(fwexec(rccleanup_tool, true) != -1); | |
1211 | } | |
1212 | ||
1213 | apply_func_to_dir(_PATH_VARRUN, empty_dir); | |
1214 | apply_func_to_dir(_PATH_TMP, empty_dir); | |
1215 | remove(_PATH_NOLOGIN); | |
1216 | ||
1217 | // XXX --> RMRF_ITEMS="/var/tmp/folders.* | |
1218 | ||
1219 | // 775 www:www /var/run/davlocks 4489695 | |
1220 | // 775 root:daemon /var/run/StartupItems | |
1221 | ||
1222 | assumes(touch_file(_PATH_UTMP, DEFFILEMODE) != -1); | |
1223 | assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1); | |
1224 | assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1); | |
1225 | ||
1226 | if (!path_check("/var/db/netinfo/local.nidb.migrated") && !path_check("/var/db/netinfo/local.nidb")) { | |
1227 | const char *create_nidb_tool[] = { "/usr/libexec/create_nidb", NULL }; | |
1228 | ||
1229 | fprintf(stderr, "NetInfo database missing. Creating...\n"); | |
1230 | ||
1231 | mkdir("/var/db/netinfo", ACCESSPERMS); | |
1232 | remove("/var/db/.AppleSetupDone"); | |
1233 | assumes(fwexec(create_nidb_tool, true) != -1); | |
1234 | } | |
1235 | ||
1236 | if (path_check("/etc/security/rc.audit")) { | |
1237 | const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL }; | |
1238 | assumes(fwexec(audit_tool, true) != -1); | |
1239 | } | |
1240 | ||
1241 | if (path_check("/Library/Preferences/com.apple.sharing.firewall.plist")) { | |
1242 | const char *fw_tool[] = { "/usr/libexec/FirewallTool", NULL }; | |
1243 | assumes(fwexec(fw_tool, true) != -1); | |
1244 | } | |
1245 | ||
1246 | const char *bcc_tool[] = { "BootCacheControl", "start", NULL }; | |
1247 | assumes(fwexec(bcc_tool, true) != -1); | |
1248 | ||
1249 | char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL }; | |
1250 | if (is_safeboot()) | |
1251 | load_launchd_items[2] = "system"; | |
1252 | assumes(load_and_unload_cmd(4, load_launchd_items) == 0); | |
1253 | ||
1254 | const char *bcc_tag_tool[] = { "BootCacheControl", "tag", NULL }; | |
1255 | assumes(fwexec(bcc_tag_tool, true) != -1); | |
1256 | ||
1257 | const char *SystemStarter_tool[] = { "SystemStarter", NULL }; | |
1258 | assumes(fwexec(SystemStarter_tool, false) != -1); | |
1259 | ||
1260 | workaround4465949(); | |
1261 | ||
1262 | if (path_check("/etc/rc.local")) { | |
1263 | const char *rc_local_tool[] = { _PATH_BSHELL, "/etc/rc.local", NULL }; | |
1264 | assumes(fwexec(rc_local_tool, false) != -1); | |
1265 | } | |
1266 | ||
1267 | return 0; | |
1268 | } | |
1269 | ||
1270 | void | |
1271 | workaround4465949(void) | |
1272 | { | |
1273 | const char *pbs_tool[] = { "/System/Library/CoreServices/pbs", NULL }; | |
1274 | const char *lca_tool[] = { "/System/Library/CoreServices/Language Chooser.app/Contents/MacOS/Language Chooser", NULL}; | |
1275 | char *const reloadttys_argv[] = { "reloadttys", NULL }; | |
1276 | int wstatus; | |
1277 | pid_t pbs_p; | |
1278 | ||
1279 | if (path_check("/System/Library/LaunchDaemons/com.apple.loginwindow.plist")) { | |
1280 | return; | |
1281 | } | |
1282 | ||
1283 | if (path_check(pbs_tool[0]) && path_check(lca_tool[0]) && | |
1284 | !path_check("/var/db/.AppleSetupDone") && | |
1285 | path_check("/var/db/.RunLanguageChooserToo")) { | |
1286 | if (assumes((pbs_p = fwexec(pbs_tool, false)) != -1)) { | |
1287 | assumes(fwexec(lca_tool, true) != -1); | |
1288 | assumes(kill(pbs_p, SIGTERM) != -1); | |
1289 | assumes(waitpid(pbs_p, &wstatus, 0) != -1); | |
1290 | } | |
1291 | } | |
1292 | ||
1293 | assumes(fyi_cmd(1, reloadttys_argv) == 0); | |
1294 | } | |
1295 | ||
1296 | int | |
1297 | load_and_unload_cmd(int argc, char *const argv[]) | |
1298 | { | |
1299 | NSSearchPathEnumerationState es = 0; | |
1300 | char nspath[PATH_MAX * 2]; /* safe side, we need to append */ | |
1301 | bool badopts = false; | |
1302 | struct load_unload_state lus; | |
1303 | size_t i; | |
1304 | int ch; | |
1305 | ||
1306 | memset(&lus, 0, sizeof(lus)); | |
e91b9f68 A |
1307 | |
1308 | if (!strcmp(argv[0], "load")) | |
ed34e3c3 | 1309 | lus.load = true; |
e91b9f68 | 1310 | |
ed34e3c3 | 1311 | while ((ch = getopt(argc, argv, "wFS:D:")) != -1) { |
e91b9f68 | 1312 | switch (ch) { |
ed34e3c3 A |
1313 | case 'w': |
1314 | lus.editondisk = true; | |
1315 | break; | |
1316 | case 'F': | |
1317 | lus.forceload = true; | |
1318 | break; | |
1319 | case 'S': | |
1320 | lus.session_type = optarg; | |
1321 | break; | |
1322 | case 'D': | |
1323 | if (strcasecmp(optarg, "all") == 0) { | |
1324 | es |= NSAllDomainsMask; | |
1325 | } else if (strcasecmp(optarg, "user") == 0) { | |
1326 | es |= NSUserDomainMask; | |
1327 | } else if (strcasecmp(optarg, "local") == 0) { | |
1328 | es |= NSLocalDomainMask; | |
1329 | } else if (strcasecmp(optarg, "network") == 0) { | |
1330 | es |= NSNetworkDomainMask; | |
1331 | } else if (strcasecmp(optarg, "system") == 0) { | |
1332 | es |= NSSystemDomainMask; | |
1333 | } else { | |
1334 | badopts = true; | |
1335 | } | |
1336 | break; | |
1337 | case '?': | |
e91b9f68 | 1338 | default: |
ed34e3c3 A |
1339 | badopts = true; |
1340 | break; | |
e91b9f68 A |
1341 | } |
1342 | } | |
1343 | argc -= optind; | |
1344 | argv += optind; | |
1345 | ||
ed34e3c3 A |
1346 | if (lus.session_type == NULL) |
1347 | es &= ~NSUserDomainMask; | |
1348 | ||
1349 | if (argc == 0 && es == 0) | |
1350 | badopts = true; | |
1351 | ||
1352 | if (badopts) { | |
1353 | fprintf(stderr, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname()); | |
e91b9f68 A |
1354 | return 1; |
1355 | } | |
1356 | ||
ed34e3c3 | 1357 | /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too. |
aa59983a | 1358 | * And loading legacy mach init jobs is extra fun. |
e91b9f68 A |
1359 | * |
1360 | * In later versions of launchd, I hope to load everything in the first pass, | |
1361 | * then do the Bonjour magic on the jobs that need it, and reload them, but for now, | |
1362 | * I haven't thought through the various complexities of reloading jobs, and therefore | |
1363 | * launchd doesn't have reload support right now. | |
1364 | */ | |
1365 | ||
ed34e3c3 A |
1366 | lus.pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY); |
1367 | lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
1368 | lus.pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
e91b9f68 | 1369 | |
ed34e3c3 A |
1370 | es = NSStartSearchPathEnumeration(NSLibraryDirectory, es); |
1371 | ||
1372 | while ((es = NSGetNextSearchPathEnumeration(es, nspath))) { | |
1373 | glob_t g; | |
1374 | ||
1375 | if (lus.session_type) { | |
1376 | strcat(nspath, "/LaunchAgents"); | |
1377 | } else { | |
1378 | strcat(nspath, "/LaunchDaemons"); | |
1379 | } | |
e91b9f68 | 1380 | |
ed34e3c3 A |
1381 | if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) { |
1382 | for (i = 0; i < g.gl_pathc; i++) { | |
1383 | readpath(g.gl_pathv[i], &lus); | |
1384 | } | |
1385 | globfree(&g); | |
1386 | } | |
1387 | } | |
1388 | ||
1389 | for (i = 0; i < (size_t)argc; i++) | |
1390 | readpath(argv[i], &lus); | |
1391 | ||
1392 | if (launch_data_array_get_count(lus.pass0) == 0 && | |
1393 | launch_data_array_get_count(lus.pass1) == 0 && | |
1394 | launch_data_array_get_count(lus.pass2) == 0) { | |
1395 | fprintf(stderr, "nothing found to %s\n", lus.load ? "load" : "unload"); | |
1396 | launch_data_free(lus.pass0); | |
1397 | launch_data_free(lus.pass1); | |
1398 | launch_data_free(lus.pass2); | |
e91b9f68 A |
1399 | return 1; |
1400 | } | |
1401 | ||
ed34e3c3 A |
1402 | if (lus.load) { |
1403 | distill_jobs(lus.pass1); | |
1404 | submit_mach_jobs(lus.pass0); | |
1405 | submit_job_pass(lus.pass1); | |
1406 | let_go_of_mach_jobs(lus.pass0); | |
1407 | distill_jobs(lus.pass2); | |
1408 | submit_job_pass(lus.pass2); | |
e91b9f68 | 1409 | } else { |
ed34e3c3 A |
1410 | for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) |
1411 | unloadjob(launch_data_array_get_index(lus.pass1, i)); | |
1412 | for (i = 0; i < launch_data_array_get_count(lus.pass2); i++) | |
1413 | unloadjob(launch_data_array_get_index(lus.pass2, i)); | |
e91b9f68 A |
1414 | } |
1415 | ||
1416 | return 0; | |
1417 | } | |
1418 | ||
aa59983a A |
1419 | void |
1420 | submit_mach_jobs(launch_data_t jobs) | |
1421 | { | |
1422 | size_t i, c; | |
1423 | ||
1424 | c = launch_data_array_get_count(jobs); | |
1425 | ||
aa59983a A |
1426 | for (i = 0; i < c; i++) { |
1427 | launch_data_t tmp, oai = launch_data_array_get_index(jobs, i); | |
1428 | const char *sn = NULL, *cmd = NULL; | |
ed34e3c3 A |
1429 | bool d = true; |
1430 | mach_port_t msr, msv; | |
aa59983a A |
1431 | kern_return_t kr; |
1432 | uid_t u = getuid(); | |
1433 | ||
1434 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND))) | |
1435 | d = launch_data_get_bool(tmp); | |
aa59983a A |
1436 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME))) |
1437 | sn = launch_data_get_string(tmp); | |
1438 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND))) | |
1439 | cmd = launch_data_get_string(tmp); | |
1440 | ||
1441 | if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) { | |
1442 | fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr); | |
1443 | continue; | |
1444 | } | |
1445 | if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) { | |
1446 | fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr); | |
1447 | mach_port_destroy(mach_task_self(), msr); | |
1448 | continue; | |
1449 | } | |
ed34e3c3 A |
1450 | launch_data_dict_insert(oai, launch_data_new_machport(msr), MACHINIT_JOBKEY_SERVERPORT); |
1451 | launch_data_dict_insert(oai, launch_data_new_machport(msv), MACHINIT_JOBKEY_SERVICEPORT); | |
aa59983a A |
1452 | } |
1453 | } | |
1454 | ||
1455 | void | |
ed34e3c3 | 1456 | let_go_of_mach_jobs(launch_data_t jobs) |
aa59983a | 1457 | { |
ed34e3c3 | 1458 | size_t i, c = launch_data_array_get_count(jobs); |
aa59983a | 1459 | |
ed34e3c3 A |
1460 | for (i = 0; i < c; i++) { |
1461 | launch_data_t tmp, oai = launch_data_array_get_index(jobs, i); | |
1462 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICEPORT))) { | |
1463 | mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp)); | |
1464 | } else { | |
1465 | fprintf(stderr, "%s: ack! missing service port!\n", getprogname()); | |
1466 | } | |
1467 | if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVERPORT))) { | |
1468 | mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp)); | |
1469 | } else { | |
1470 | fprintf(stderr, "%s: ack! missing server port!\n", getprogname()); | |
1471 | } | |
1472 | } | |
aa59983a A |
1473 | } |
1474 | ||
1475 | void | |
1476 | submit_job_pass(launch_data_t jobs) | |
e91b9f68 A |
1477 | { |
1478 | launch_data_t msg, resp; | |
1479 | size_t i; | |
1480 | int e; | |
1481 | ||
aa59983a A |
1482 | if (launch_data_array_get_count(jobs) == 0) |
1483 | return; | |
e91b9f68 A |
1484 | |
1485 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1486 | ||
1487 | launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB); | |
1488 | ||
1489 | resp = launch_msg(msg); | |
1490 | ||
1491 | if (resp) { | |
1492 | switch (launch_data_get_type(resp)) { | |
1493 | case LAUNCH_DATA_ERRNO: | |
1494 | if ((e = launch_data_get_errno(resp))) | |
1495 | fprintf(stderr, "%s\n", strerror(e)); | |
1496 | break; | |
1497 | case LAUNCH_DATA_ARRAY: | |
1498 | for (i = 0; i < launch_data_array_get_count(jobs); i++) { | |
1499 | launch_data_t obatind = launch_data_array_get_index(resp, i); | |
1500 | launch_data_t jatind = launch_data_array_get_index(jobs, i); | |
1501 | const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL)); | |
1502 | if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) { | |
1503 | e = launch_data_get_errno(obatind); | |
1504 | switch (e) { | |
1505 | case EEXIST: | |
1506 | fprintf(stderr, "%s: %s\n", lab4job, "Already loaded"); | |
1507 | break; | |
1508 | case ESRCH: | |
1509 | fprintf(stderr, "%s: %s\n", lab4job, "Not loaded"); | |
1510 | break; | |
1511 | default: | |
1512 | fprintf(stderr, "%s: %s\n", lab4job, strerror(e)); | |
1513 | case 0: | |
1514 | break; | |
1515 | } | |
1516 | } | |
1517 | } | |
1518 | break; | |
1519 | default: | |
1520 | fprintf(stderr, "unknown respose from launchd!\n"); | |
1521 | break; | |
1522 | } | |
1523 | launch_data_free(resp); | |
1524 | } else { | |
1525 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1526 | } | |
1527 | ||
1528 | launch_data_free(msg); | |
1529 | } | |
1530 | ||
ed34e3c3 A |
1531 | int |
1532 | start_stop_remove_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
1533 | { |
1534 | launch_data_t resp, msg; | |
1535 | const char *lmsgcmd = LAUNCH_KEY_STOPJOB; | |
1536 | int e, r = 0; | |
1537 | ||
ed34e3c3 | 1538 | if (0 == strcmp(argv[0], "start")) |
e91b9f68 A |
1539 | lmsgcmd = LAUNCH_KEY_STARTJOB; |
1540 | ||
ed34e3c3 A |
1541 | if (0 == strcmp(argv[0], "remove")) |
1542 | lmsgcmd = LAUNCH_KEY_REMOVEJOB; | |
1543 | ||
e91b9f68 A |
1544 | if (argc != 2) { |
1545 | fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]); | |
1546 | return 1; | |
1547 | } | |
1548 | ||
1549 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1550 | launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd); | |
1551 | ||
1552 | resp = launch_msg(msg); | |
1553 | launch_data_free(msg); | |
1554 | ||
1555 | if (resp == NULL) { | |
1556 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1557 | return 1; | |
1558 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1559 | if ((e = launch_data_get_errno(resp))) { | |
1560 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1561 | r = 1; | |
1562 | } | |
1563 | } else { | |
1564 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1565 | r = 1; | |
1566 | } | |
1567 | ||
1568 | launch_data_free(resp); | |
1569 | return r; | |
1570 | } | |
1571 | ||
ed34e3c3 A |
1572 | void |
1573 | print_jobs(launch_data_t j) | |
e91b9f68 | 1574 | { |
ed34e3c3 A |
1575 | static size_t depth = 0; |
1576 | launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL); | |
1577 | launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID); | |
1578 | launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS); | |
1579 | launch_data_t sjobs = launch_data_dict_lookup(j, LAUNCH_JOBKEY_SUBJOBS); | |
1580 | const char *label = launch_data_get_string(lo); | |
1581 | size_t i, c; | |
1582 | ||
1583 | if (pido) { | |
1584 | fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido)); | |
1585 | } else if (stato) { | |
1586 | int wstatus = (int)launch_data_get_integer(stato); | |
1587 | if (WIFEXITED(wstatus)) { | |
1588 | fprintf(stdout, "-\t%d\t", WEXITSTATUS(wstatus)); | |
1589 | } else if (WIFSIGNALED(wstatus)) { | |
1590 | fprintf(stdout, "-\t-%d\t", WTERMSIG(wstatus)); | |
1591 | } else { | |
1592 | fprintf(stdout, "-\t???\t"); | |
1593 | } | |
1594 | } else { | |
1595 | fprintf(stdout, "-\t-\t"); | |
1596 | } | |
1597 | for (i = 0; i < depth; i++) | |
1598 | fprintf(stdout, "\t"); | |
1599 | ||
e91b9f68 | 1600 | fprintf(stdout, "%s\n", label); |
ed34e3c3 A |
1601 | |
1602 | if (sjobs) { | |
1603 | launch_data_t oai; | |
1604 | ||
1605 | c = launch_data_array_get_count(sjobs); | |
1606 | ||
1607 | depth++; | |
1608 | for (i = 0; i < c; i++) { | |
1609 | oai = launch_data_array_get_index(sjobs, i); | |
1610 | print_jobs(oai); | |
1611 | } | |
1612 | depth--; | |
1613 | } | |
e91b9f68 A |
1614 | } |
1615 | ||
ed34e3c3 A |
1616 | void |
1617 | print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused))) | |
e91b9f68 | 1618 | { |
ed34e3c3 A |
1619 | static size_t indent = 0; |
1620 | size_t i, c; | |
e91b9f68 | 1621 | |
ed34e3c3 A |
1622 | for (i = 0; i < indent; i++) |
1623 | fprintf(stdout, "\t"); | |
1624 | ||
1625 | if (key) | |
1626 | fprintf(stdout, "\"%s\" = ", key); | |
1627 | ||
1628 | switch (launch_data_get_type(obj)) { | |
1629 | case LAUNCH_DATA_STRING: | |
1630 | fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj)); | |
1631 | break; | |
1632 | case LAUNCH_DATA_INTEGER: | |
1633 | fprintf(stdout, "%lld;\n", launch_data_get_integer(obj)); | |
1634 | break; | |
1635 | case LAUNCH_DATA_REAL: | |
1636 | fprintf(stdout, "%f;\n", launch_data_get_real(obj)); | |
1637 | break; | |
1638 | case LAUNCH_DATA_BOOL: | |
1639 | fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false"); | |
1640 | break; | |
1641 | case LAUNCH_DATA_ARRAY: | |
1642 | c = launch_data_array_get_count(obj); | |
1643 | fprintf(stdout, "(\n"); | |
1644 | indent++; | |
1645 | for (i = 0; i < c; i++) | |
1646 | print_obj(launch_data_array_get_index(obj, i), NULL, NULL); | |
1647 | indent--; | |
1648 | for (i = 0; i < indent; i++) | |
1649 | fprintf(stdout, "\t"); | |
1650 | fprintf(stdout, ");\n"); | |
1651 | break; | |
1652 | case LAUNCH_DATA_DICTIONARY: | |
1653 | fprintf(stdout, "{\n"); | |
1654 | indent++; | |
1655 | launch_data_dict_iterate(obj, print_obj, NULL); | |
1656 | indent--; | |
1657 | for (i = 0; i < indent; i++) | |
1658 | fprintf(stdout, "\t"); | |
1659 | fprintf(stdout, "};\n"); | |
1660 | break; | |
1661 | case LAUNCH_DATA_FD: | |
1662 | fprintf(stdout, "file-descriptor-object;\n"); | |
1663 | break; | |
1664 | case LAUNCH_DATA_MACHPORT: | |
1665 | fprintf(stdout, "mach-port-object;\n"); | |
1666 | break; | |
1667 | default: | |
1668 | fprintf(stdout, "???;\n"); | |
1669 | break; | |
e91b9f68 | 1670 | } |
ed34e3c3 | 1671 | } |
e91b9f68 | 1672 | |
ed34e3c3 A |
1673 | int |
1674 | list_cmd(int argc, char *const argv[]) | |
1675 | { | |
1676 | launch_data_t resp, msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1677 | int r = 0; | |
1678 | ||
1679 | if (argc > 2) { | |
1680 | fprintf(stderr, "usage: %s list [label]\n", getprogname()); | |
e91b9f68 | 1681 | return 1; |
ed34e3c3 A |
1682 | } else if (argc == 2) { |
1683 | launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB); | |
1684 | } else { | |
1685 | launch_data_dict_insert(msg, launch_data_new_string(""), LAUNCH_KEY_GETJOB); | |
e91b9f68 A |
1686 | } |
1687 | ||
e91b9f68 A |
1688 | resp = launch_msg(msg); |
1689 | launch_data_free(msg); | |
1690 | ||
1691 | if (resp == NULL) { | |
1692 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1693 | return 1; | |
1694 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) { | |
ed34e3c3 A |
1695 | if (argc == 1) { |
1696 | fprintf(stdout, "PID\tStatus\tLabel\n"); | |
1697 | print_jobs(resp); | |
1698 | } else { | |
1699 | print_obj(resp, NULL, NULL); | |
1700 | } | |
e91b9f68 A |
1701 | } else { |
1702 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1703 | r = 1; | |
1704 | } | |
1705 | ||
1706 | launch_data_free(resp); | |
1707 | ||
1708 | return r; | |
1709 | } | |
1710 | ||
ed34e3c3 A |
1711 | int |
1712 | stdio_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
1713 | { |
1714 | launch_data_t resp, msg, tmp; | |
1715 | int e, fd = -1, r = 0; | |
1716 | ||
1717 | if (argc != 2) { | |
1718 | fprintf(stderr, "usage: %s %s <path>\n", getprogname(), argv[0]); | |
1719 | return 1; | |
1720 | } | |
1721 | ||
ed34e3c3 | 1722 | fd = open(argv[1], O_CREAT|O_APPEND|O_WRONLY|O_NOCTTY, DEFFILEMODE); |
e91b9f68 A |
1723 | |
1724 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1725 | ||
1726 | if (fd == -1) { | |
1727 | tmp = launch_data_new_string(argv[1]); | |
1728 | } else { | |
1729 | tmp = launch_data_new_fd(fd); | |
1730 | } | |
1731 | ||
1732 | if (!strcmp(argv[0], "stdout")) { | |
1733 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDOUT); | |
1734 | } else { | |
1735 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDERR); | |
1736 | } | |
1737 | ||
1738 | resp = launch_msg(msg); | |
1739 | launch_data_free(msg); | |
1740 | ||
1741 | if (resp == NULL) { | |
1742 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1743 | return 1; | |
1744 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1745 | if ((e = launch_data_get_errno(resp))) { | |
1746 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1747 | r = 1; | |
1748 | } | |
1749 | } else { | |
1750 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1751 | r = 1; | |
1752 | } | |
1753 | ||
1754 | if (fd != -1) | |
1755 | close(fd); | |
1756 | ||
1757 | launch_data_free(resp); | |
1758 | ||
1759 | return r; | |
1760 | } | |
1761 | ||
ed34e3c3 A |
1762 | int |
1763 | fyi_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
1764 | { |
1765 | launch_data_t resp, msg; | |
1766 | const char *lmsgk = LAUNCH_KEY_RELOADTTYS; | |
1767 | int e, r = 0; | |
1768 | ||
1769 | if (argc != 1) { | |
1770 | fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]); | |
1771 | return 1; | |
1772 | } | |
1773 | ||
ed34e3c3 | 1774 | if (!strcmp(argv[0], "shutdown")) { |
e91b9f68 | 1775 | lmsgk = LAUNCH_KEY_SHUTDOWN; |
ed34e3c3 A |
1776 | } else if (!strcmp(argv[0], "singleuser")) { |
1777 | lmsgk = LAUNCH_KEY_SINGLEUSER; | |
1778 | } | |
e91b9f68 A |
1779 | |
1780 | msg = launch_data_new_string(lmsgk); | |
1781 | resp = launch_msg(msg); | |
1782 | launch_data_free(msg); | |
1783 | ||
1784 | if (resp == NULL) { | |
1785 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1786 | return 1; | |
1787 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1788 | if ((e = launch_data_get_errno(resp))) { | |
1789 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1790 | r = 1; | |
1791 | } | |
1792 | } else { | |
1793 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1794 | r = 1; | |
1795 | } | |
1796 | ||
1797 | launch_data_free(resp); | |
1798 | ||
1799 | return r; | |
1800 | } | |
1801 | ||
ed34e3c3 A |
1802 | int |
1803 | logupdate_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
1804 | { |
1805 | launch_data_t resp, msg; | |
1806 | int e, i, j, r = 0, m = 0; | |
1807 | bool badargs = false, maskmode = false, onlymode = false, levelmode = false; | |
1808 | const char *whichcmd = LAUNCH_KEY_SETLOGMASK; | |
1809 | static const struct { | |
1810 | const char *name; | |
1811 | int level; | |
1812 | } logtbl[] = { | |
1813 | { "debug", LOG_DEBUG }, | |
1814 | { "info", LOG_INFO }, | |
1815 | { "notice", LOG_NOTICE }, | |
1816 | { "warning", LOG_WARNING }, | |
1817 | { "error", LOG_ERR }, | |
1818 | { "critical", LOG_CRIT }, | |
1819 | { "alert", LOG_ALERT }, | |
1820 | { "emergency", LOG_EMERG }, | |
1821 | }; | |
1822 | int logtblsz = sizeof logtbl / sizeof logtbl[0]; | |
1823 | ||
1824 | if (argc >= 2) { | |
1825 | if (!strcmp(argv[1], "mask")) | |
1826 | maskmode = true; | |
1827 | else if (!strcmp(argv[1], "only")) | |
1828 | onlymode = true; | |
1829 | else if (!strcmp(argv[1], "level")) | |
1830 | levelmode = true; | |
1831 | else | |
1832 | badargs = true; | |
1833 | } | |
1834 | ||
1835 | if (maskmode) | |
1836 | m = LOG_UPTO(LOG_DEBUG); | |
1837 | ||
1838 | if (argc > 2 && (maskmode || onlymode)) { | |
1839 | for (i = 2; i < argc; i++) { | |
1840 | for (j = 0; j < logtblsz; j++) { | |
1841 | if (!strcmp(argv[i], logtbl[j].name)) { | |
1842 | if (maskmode) | |
1843 | m &= ~(LOG_MASK(logtbl[j].level)); | |
1844 | else | |
1845 | m |= LOG_MASK(logtbl[j].level); | |
1846 | break; | |
1847 | } | |
1848 | } | |
1849 | if (j == logtblsz) { | |
1850 | badargs = true; | |
1851 | break; | |
1852 | } | |
1853 | } | |
1854 | } else if (argc > 2 && levelmode) { | |
1855 | for (j = 0; j < logtblsz; j++) { | |
1856 | if (!strcmp(argv[2], logtbl[j].name)) { | |
1857 | m = LOG_UPTO(logtbl[j].level); | |
1858 | break; | |
1859 | } | |
1860 | } | |
1861 | if (j == logtblsz) | |
1862 | badargs = true; | |
1863 | } else if (argc == 1) { | |
1864 | whichcmd = LAUNCH_KEY_GETLOGMASK; | |
1865 | } else { | |
1866 | badargs = true; | |
1867 | } | |
1868 | ||
1869 | if (badargs) { | |
1870 | fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname()); | |
1871 | return 1; | |
1872 | } | |
1873 | ||
1874 | if (whichcmd == LAUNCH_KEY_SETLOGMASK) { | |
1875 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
1876 | launch_data_dict_insert(msg, launch_data_new_integer(m), whichcmd); | |
1877 | } else { | |
1878 | msg = launch_data_new_string(whichcmd); | |
1879 | } | |
1880 | ||
1881 | resp = launch_msg(msg); | |
1882 | launch_data_free(msg); | |
1883 | ||
1884 | if (resp == NULL) { | |
1885 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
1886 | return 1; | |
1887 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
1888 | if ((e = launch_data_get_errno(resp))) { | |
1889 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e)); | |
1890 | r = 1; | |
1891 | } | |
1892 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_INTEGER) { | |
1893 | if (whichcmd == LAUNCH_KEY_GETLOGMASK) { | |
1894 | m = launch_data_get_integer(resp); | |
1895 | for (j = 0; j < logtblsz; j++) { | |
1896 | if (m & LOG_MASK(logtbl[j].level)) | |
1897 | fprintf(stdout, "%s ", logtbl[j].name); | |
1898 | } | |
1899 | fprintf(stdout, "\n"); | |
1900 | } | |
1901 | } else { | |
1902 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
1903 | r = 1; | |
1904 | } | |
1905 | ||
1906 | launch_data_free(resp); | |
1907 | ||
1908 | return r; | |
1909 | } | |
1910 | ||
aa59983a A |
1911 | static const struct { |
1912 | const char *name; | |
1913 | int lim; | |
1914 | } limlookup[] = { | |
1915 | { "cpu", RLIMIT_CPU }, | |
1916 | { "filesize", RLIMIT_FSIZE }, | |
1917 | { "data", RLIMIT_DATA }, | |
1918 | { "stack", RLIMIT_STACK }, | |
1919 | { "core", RLIMIT_CORE }, | |
1920 | { "rss", RLIMIT_RSS }, | |
1921 | { "memlock", RLIMIT_MEMLOCK }, | |
1922 | { "maxproc", RLIMIT_NPROC }, | |
1923 | { "maxfiles", RLIMIT_NOFILE } | |
1924 | }; | |
1925 | ||
1926 | static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0]; | |
1927 | ||
ed34e3c3 A |
1928 | ssize_t |
1929 | name2num(const char *n) | |
aa59983a A |
1930 | { |
1931 | size_t i; | |
1932 | ||
1933 | for (i = 0; i < limlookupcnt; i++) { | |
1934 | if (!strcmp(limlookup[i].name, n)) { | |
1935 | return limlookup[i].lim; | |
1936 | } | |
1937 | } | |
1938 | return -1; | |
1939 | } | |
1940 | ||
ed34e3c3 A |
1941 | const char * |
1942 | num2name(int n) | |
aa59983a A |
1943 | { |
1944 | size_t i; | |
1945 | ||
1946 | for (i = 0; i < limlookupcnt; i++) { | |
1947 | if (limlookup[i].lim == n) | |
1948 | return limlookup[i].name; | |
1949 | } | |
1950 | return NULL; | |
1951 | } | |
1952 | ||
ed34e3c3 A |
1953 | const char * |
1954 | lim2str(rlim_t val, char *buf) | |
aa59983a A |
1955 | { |
1956 | if (val == RLIM_INFINITY) | |
1957 | strcpy(buf, "unlimited"); | |
1958 | else | |
1959 | sprintf(buf, "%lld", val); | |
1960 | return buf; | |
1961 | } | |
1962 | ||
ed34e3c3 A |
1963 | bool |
1964 | str2lim(const char *buf, rlim_t *res) | |
aa59983a A |
1965 | { |
1966 | char *endptr; | |
1967 | *res = strtoll(buf, &endptr, 10); | |
1968 | if (!strcmp(buf, "unlimited")) { | |
1969 | *res = RLIM_INFINITY; | |
1970 | return false; | |
1971 | } else if (*endptr == '\0') { | |
1972 | return false; | |
1973 | } | |
1974 | return true; | |
1975 | } | |
1976 | ||
ed34e3c3 A |
1977 | int |
1978 | limit_cmd(int argc __attribute__((unused)), char *const argv[]) | |
e91b9f68 A |
1979 | { |
1980 | char slimstr[100]; | |
1981 | char hlimstr[100]; | |
1982 | struct rlimit *lmts = NULL; | |
1983 | launch_data_t resp, resp1 = NULL, msg, tmp; | |
1984 | int r = 0; | |
aa59983a A |
1985 | size_t i, lsz = -1; |
1986 | ssize_t which = 0; | |
e91b9f68 A |
1987 | rlim_t slim = -1, hlim = -1; |
1988 | bool badargs = false; | |
e91b9f68 A |
1989 | |
1990 | if (argc > 4) | |
1991 | badargs = true; | |
1992 | ||
1993 | if (argc >= 3 && str2lim(argv[2], &slim)) | |
1994 | badargs = true; | |
1995 | else | |
1996 | hlim = slim; | |
1997 | ||
1998 | if (argc == 4 && str2lim(argv[3], &hlim)) | |
1999 | badargs = true; | |
2000 | ||
aa59983a | 2001 | if (argc >= 2 && -1 == (which = name2num(argv[1]))) |
e91b9f68 A |
2002 | badargs = true; |
2003 | ||
2004 | if (badargs) { | |
2005 | fprintf(stderr, "usage: %s %s [", getprogname(), argv[0]); | |
2006 | for (i = 0; i < limlookupcnt; i++) | |
2007 | fprintf(stderr, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| "); | |
2008 | fprintf(stderr, "[both | soft hard]]\n"); | |
2009 | return 1; | |
2010 | } | |
2011 | ||
2012 | msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS); | |
2013 | resp = launch_msg(msg); | |
2014 | launch_data_free(msg); | |
2015 | ||
2016 | if (resp == NULL) { | |
2017 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
2018 | return 1; | |
2019 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) { | |
2020 | lmts = launch_data_get_opaque(resp); | |
2021 | lsz = launch_data_get_opaque_size(resp); | |
2022 | if (argc <= 2) { | |
2023 | for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) { | |
aa59983a | 2024 | if (argc == 2 && (size_t)which != i) |
e91b9f68 A |
2025 | continue; |
2026 | fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i), | |
2027 | lim2str(lmts[i].rlim_cur, slimstr), | |
2028 | lim2str(lmts[i].rlim_max, hlimstr)); | |
2029 | } | |
2030 | } | |
2031 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) { | |
2032 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp)); | |
2033 | r = 1; | |
2034 | } else { | |
2035 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
2036 | r = 1; | |
2037 | } | |
2038 | ||
2039 | if (argc <= 2 || r != 0) { | |
2040 | launch_data_free(resp); | |
2041 | return r; | |
2042 | } else { | |
2043 | resp1 = resp; | |
2044 | } | |
2045 | ||
2046 | lmts[which].rlim_cur = slim; | |
2047 | lmts[which].rlim_max = hlim; | |
2048 | ||
2049 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
2050 | tmp = launch_data_new_opaque(lmts, lsz); | |
2051 | launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS); | |
2052 | resp = launch_msg(msg); | |
2053 | launch_data_free(msg); | |
2054 | ||
2055 | if (resp == NULL) { | |
2056 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
2057 | return 1; | |
2058 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) { | |
2059 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp)); | |
2060 | r = 1; | |
2061 | } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) { | |
2062 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
2063 | r = 1; | |
2064 | } | |
2065 | ||
2066 | launch_data_free(resp); | |
2067 | launch_data_free(resp1); | |
2068 | ||
2069 | return r; | |
2070 | } | |
2071 | ||
ed34e3c3 A |
2072 | int |
2073 | umask_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
2074 | { |
2075 | launch_data_t resp, msg; | |
2076 | bool badargs = false; | |
2077 | char *endptr; | |
2078 | long m = 0; | |
2079 | int r = 0; | |
2080 | ||
2081 | if (argc == 2) { | |
2082 | m = strtol(argv[1], &endptr, 8); | |
2083 | if (*endptr != '\0' || m > 0777) | |
2084 | badargs = true; | |
2085 | } | |
2086 | ||
2087 | if (argc > 2 || badargs) { | |
2088 | fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]); | |
2089 | return 1; | |
2090 | } | |
2091 | ||
2092 | ||
2093 | if (argc == 1) { | |
2094 | msg = launch_data_new_string(LAUNCH_KEY_GETUMASK); | |
2095 | } else { | |
2096 | msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
2097 | launch_data_dict_insert(msg, launch_data_new_integer(m), LAUNCH_KEY_SETUMASK); | |
2098 | } | |
2099 | resp = launch_msg(msg); | |
2100 | launch_data_free(msg); | |
2101 | ||
2102 | if (resp == NULL) { | |
2103 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
2104 | return 1; | |
2105 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) { | |
2106 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp)); | |
2107 | r = 1; | |
2108 | } else if (launch_data_get_type(resp) != LAUNCH_DATA_INTEGER) { | |
2109 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
2110 | r = 1; | |
2111 | } else if (argc == 1) { | |
2112 | fprintf(stdout, "%o\n", (unsigned int)launch_data_get_integer(resp)); | |
2113 | } | |
2114 | ||
2115 | launch_data_free(resp); | |
2116 | ||
2117 | return r; | |
2118 | } | |
2119 | ||
ed34e3c3 A |
2120 | int |
2121 | submit_cmd(int argc, char *const argv[]) | |
2122 | { | |
2123 | launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
2124 | launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
2125 | launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
2126 | int ch, i, r = 0; | |
2127 | ||
2128 | launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND); | |
2129 | ||
2130 | while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) { | |
2131 | switch (ch) { | |
2132 | case 'l': | |
2133 | launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL); | |
2134 | break; | |
2135 | case 'p': | |
2136 | launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM); | |
2137 | break; | |
2138 | case 'o': | |
2139 | launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH); | |
2140 | break; | |
2141 | case 'e': | |
2142 | launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH); | |
2143 | break; | |
2144 | default: | |
2145 | fprintf(stderr, "usage: %s submit ...\n", getprogname()); | |
2146 | return 1; | |
2147 | } | |
2148 | } | |
2149 | argc -= optind; | |
2150 | argv += optind; | |
2151 | ||
2152 | for (i = 0; argv[i]; i++) { | |
2153 | launch_data_array_append(largv, launch_data_new_string(argv[i])); | |
2154 | } | |
2155 | ||
2156 | launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS); | |
2157 | ||
2158 | launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB); | |
2159 | ||
2160 | resp = launch_msg(msg); | |
2161 | launch_data_free(msg); | |
2162 | ||
2163 | if (resp == NULL) { | |
2164 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
2165 | return 1; | |
2166 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
2167 | errno = launch_data_get_errno(resp); | |
2168 | if (errno) { | |
2169 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(errno)); | |
2170 | r = 1; | |
2171 | } | |
2172 | } else { | |
2173 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], "unknown response"); | |
2174 | } | |
2175 | ||
2176 | launch_data_free(resp); | |
2177 | ||
2178 | return r; | |
2179 | } | |
2180 | ||
2181 | int | |
2182 | getrusage_cmd(int argc, char *const argv[]) | |
e91b9f68 A |
2183 | { |
2184 | launch_data_t resp, msg; | |
2185 | bool badargs = false; | |
2186 | int r = 0; | |
2187 | ||
2188 | if (argc != 2) | |
2189 | badargs = true; | |
2190 | else if (strcmp(argv[1], "self") && strcmp(argv[1], "children")) | |
2191 | badargs = true; | |
2192 | ||
2193 | if (badargs) { | |
2194 | fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]); | |
2195 | return 1; | |
2196 | } | |
2197 | ||
2198 | if (!strcmp(argv[1], "self")) { | |
2199 | msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF); | |
2200 | } else { | |
2201 | msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN); | |
2202 | } | |
2203 | ||
2204 | resp = launch_msg(msg); | |
2205 | launch_data_free(msg); | |
2206 | ||
2207 | if (resp == NULL) { | |
2208 | fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); | |
2209 | return 1; | |
2210 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { | |
2211 | fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp))); | |
2212 | r = 1; | |
2213 | } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) { | |
2214 | struct rusage *rusage = launch_data_get_opaque(resp); | |
2215 | fprintf(stdout, "\t%-10f\tuser time used\n", | |
2216 | (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000); | |
2217 | fprintf(stdout, "\t%-10f\tsystem time used\n", | |
2218 | (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000); | |
2219 | fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss); | |
2220 | fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss); | |
2221 | fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss); | |
2222 | fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss); | |
2223 | fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt); | |
2224 | fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt); | |
2225 | fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap); | |
2226 | fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock); | |
2227 | fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock); | |
2228 | fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd); | |
2229 | fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv); | |
2230 | fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals); | |
2231 | fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw); | |
2232 | fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw); | |
2233 | } else { | |
2234 | fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); | |
2235 | r = 1; | |
2236 | } | |
2237 | ||
2238 | launch_data_free(resp); | |
2239 | ||
2240 | return r; | |
2241 | } | |
2242 | ||
ed34e3c3 A |
2243 | bool |
2244 | launch_data_array_append(launch_data_t a, launch_data_t o) | |
e91b9f68 A |
2245 | { |
2246 | size_t offt = launch_data_array_get_count(a); | |
2247 | ||
2248 | return launch_data_array_set_index(a, o, offt); | |
2249 | } | |
aa59983a | 2250 | |
ed34e3c3 A |
2251 | mach_port_t |
2252 | str2bsport(const char *s) | |
2253 | { | |
2254 | bool getrootbs = strcmp(s, "/") == 0; | |
2255 | mach_port_t last_bport, bport = bootstrap_port; | |
2256 | task_t task = mach_task_self(); | |
2257 | kern_return_t result; | |
2258 | ||
2259 | if (strcmp(s, "..") == 0 || getrootbs) { | |
2260 | do { | |
2261 | last_bport = bport; | |
2262 | result = bootstrap_parent(last_bport, &bport); | |
2263 | ||
2264 | if (result == BOOTSTRAP_NOT_PRIVILEGED) { | |
2265 | fprintf(stderr, "Permission denied\n"); | |
2266 | return 1; | |
2267 | } else if (result != BOOTSTRAP_SUCCESS) { | |
2268 | fprintf(stderr, "bootstrap_parent() %d\n", result); | |
2269 | return 1; | |
2270 | } | |
2271 | } while (getrootbs && last_bport != bport); | |
2272 | } else { | |
2273 | int pid = atoi(s); | |
2274 | ||
2275 | result = task_for_pid(mach_task_self(), pid, &task); | |
2276 | ||
2277 | if (result != KERN_SUCCESS) { | |
2278 | fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result)); | |
2279 | return 1; | |
2280 | } | |
2281 | ||
2282 | result = task_get_bootstrap_port(task, &bport); | |
2283 | ||
2284 | if (result != KERN_SUCCESS) { | |
2285 | fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result)); | |
2286 | return 1; | |
2287 | } | |
2288 | } | |
2289 | ||
2290 | return bport; | |
2291 | } | |
2292 | ||
2293 | int | |
2294 | bsexec_cmd(int argc, char *const argv[]) | |
2295 | { | |
2296 | kern_return_t result; | |
2297 | mach_port_t bport; | |
2298 | ||
2299 | if (argc < 3) { | |
2300 | fprintf(stderr, "usage: %s bsexec <PID> prog...\n", getprogname()); | |
2301 | return 1; | |
2302 | } | |
2303 | ||
2304 | bport = str2bsport(argv[1]); | |
2305 | ||
2306 | result = task_set_bootstrap_port(mach_task_self(), bport); | |
2307 | ||
2308 | if (result != KERN_SUCCESS) { | |
2309 | fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result)); | |
2310 | return 1; | |
2311 | } | |
2312 | ||
2313 | setgid(getgid()); | |
2314 | setuid(getuid()); | |
2315 | ||
2316 | execvp(argv[2], argv + 2); | |
2317 | fprintf(stderr, "execvp(): %s\n", strerror(errno)); | |
2318 | return 1; | |
2319 | } | |
2320 | ||
2321 | int | |
2322 | bslist_cmd(int argc, char *const argv[]) | |
2323 | { | |
2324 | kern_return_t result; | |
2325 | mach_port_t bport = bootstrap_port; | |
2326 | name_array_t service_names; | |
2327 | mach_msg_type_number_t service_cnt, service_active_cnt; | |
2328 | bootstrap_status_array_t service_actives; | |
2329 | unsigned int i; | |
2330 | ||
2331 | if (argc == 2) | |
2332 | bport = str2bsport(argv[1]); | |
2333 | ||
2334 | if (bport == MACH_PORT_NULL) { | |
2335 | fprintf(stderr, "Invalid bootstrap port\n"); | |
2336 | return 1; | |
2337 | } | |
2338 | ||
2339 | result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt); | |
2340 | if (result != BOOTSTRAP_SUCCESS) { | |
2341 | fprintf(stderr, "bootstrap_info(): %d\n", result); | |
2342 | return 1; | |
2343 | } | |
2344 | ||
2345 | #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I") | |
2346 | ||
2347 | for (i = 0; i < service_cnt ; i++) | |
2348 | fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]); | |
2349 | ||
2350 | return 0; | |
2351 | } | |
2352 | ||
aa59983a A |
2353 | bool |
2354 | is_legacy_mach_job(launch_data_t obj) | |
2355 | { | |
2356 | bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME); | |
2357 | bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND); | |
2358 | bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL); | |
2359 | ||
2360 | return has_command && has_servicename && !has_label; | |
2361 | } | |
ed34e3c3 A |
2362 | |
2363 | void | |
2364 | _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test) | |
2365 | { | |
2366 | int saved_errno = errno; | |
2367 | char buf[100]; | |
2368 | const char *file = strrchr(path, '/'); | |
2369 | char *rcs_rev_tmp = strchr(rcs_rev, ' '); | |
2370 | ||
2371 | if (!file) { | |
2372 | file = path; | |
2373 | } else { | |
2374 | file += 1; | |
2375 | } | |
2376 | ||
2377 | if (!rcs_rev_tmp) { | |
2378 | strlcpy(buf, rcs_rev, sizeof(buf)); | |
2379 | } else { | |
2380 | strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf)); | |
2381 | rcs_rev_tmp = strchr(buf, ' '); | |
2382 | if (rcs_rev_tmp) | |
2383 | *rcs_rev_tmp = '\0'; | |
2384 | } | |
2385 | ||
2386 | fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test); | |
2387 | } | |
2388 | ||
2389 | void | |
2390 | loopback_setup_ipv4(void) | |
2391 | { | |
2392 | struct ifaliasreq ifra; | |
2393 | struct ifreq ifr; | |
2394 | int s; | |
2395 | ||
2396 | memset(&ifr, 0, sizeof(ifr)); | |
2397 | strcpy(ifr.ifr_name, "lo0"); | |
2398 | ||
2399 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
2400 | return; | |
2401 | ||
2402 | if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) { | |
2403 | ifr.ifr_flags |= IFF_UP; | |
2404 | assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1); | |
2405 | } | |
2406 | ||
2407 | memset(&ifra, 0, sizeof(ifra)); | |
2408 | strcpy(ifra.ifra_name, "lo0"); | |
2409 | ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET; | |
2410 | ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
2411 | ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in); | |
2412 | ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET; | |
2413 | ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET); | |
2414 | ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in); | |
2415 | ||
2416 | assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1); | |
2417 | ||
2418 | assumes(close(s) == 0); | |
2419 | } | |
2420 | ||
2421 | void | |
2422 | loopback_setup_ipv6(void) | |
2423 | { | |
2424 | struct in6_aliasreq ifra6; | |
2425 | struct ifreq ifr; | |
2426 | int s6; | |
2427 | ||
2428 | memset(&ifr, 0, sizeof(ifr)); | |
2429 | strcpy(ifr.ifr_name, "lo0"); | |
2430 | ||
2431 | if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) | |
2432 | return; | |
2433 | ||
2434 | memset(&ifr, 0, sizeof(ifr)); | |
2435 | strcpy(ifr.ifr_name, "lo0"); | |
2436 | ||
2437 | if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) { | |
2438 | ifr.ifr_flags |= IFF_UP; | |
2439 | assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1); | |
2440 | } | |
2441 | ||
2442 | memset(&ifra6, 0, sizeof(ifra6)); | |
2443 | strcpy(ifra6.ifra_name, "lo0"); | |
2444 | ||
2445 | ifra6.ifra_addr.sin6_family = AF_INET6; | |
2446 | ifra6.ifra_addr.sin6_addr = in6addr_loopback; | |
2447 | ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); | |
2448 | ifra6.ifra_prefixmask.sin6_family = AF_INET6; | |
2449 | memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr)); | |
2450 | ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); | |
2451 | ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; | |
2452 | ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; | |
2453 | ||
2454 | assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1); | |
2455 | ||
2456 | assumes(close(s6) == 0); | |
2457 | } | |
2458 | ||
2459 | pid_t | |
2460 | fwexec(const char *const *argv, bool _wait) | |
2461 | { | |
2462 | int wstatus; | |
2463 | pid_t p; | |
2464 | ||
2465 | switch ((p = fork())) { | |
2466 | case -1: | |
2467 | break; | |
2468 | case 0: | |
2469 | setsid(); | |
2470 | execvp(argv[0], (char *const *)argv); | |
2471 | _exit(EXIT_FAILURE); | |
2472 | break; | |
2473 | default: | |
2474 | if (!_wait) | |
2475 | return p; | |
2476 | if (p == waitpid(p, &wstatus, 0)) { | |
2477 | if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS) | |
2478 | return p; | |
2479 | } | |
2480 | break; | |
2481 | } | |
2482 | ||
2483 | return -1; | |
2484 | } | |
2485 | ||
2486 | void | |
2487 | do_potential_fsck(void) | |
2488 | { | |
2489 | const char *safe_fsck_tool[] = { "fsck", "-fy", NULL }; | |
2490 | const char *fsck_tool[] = { "fsck", "-p", NULL }; | |
2491 | const char *remount_tool[] = { "mount", "-uw", "/", NULL }; | |
2492 | struct statfs sfs; | |
2493 | ||
2494 | if (assumes(statfs("/", &sfs) != -1)) { | |
2495 | if (!(sfs.f_flags & MNT_RDONLY)) { | |
2496 | fprintf(stdout, "Root file system is read-write, skipping fsck.\n"); | |
2497 | return; | |
2498 | } | |
2499 | } | |
2500 | ||
2501 | if (!is_safeboot()) { | |
2502 | if (fwexec(fsck_tool, true) != -1) | |
2503 | goto out; | |
2504 | } | |
2505 | ||
2506 | if (fwexec(safe_fsck_tool, true) != -1) { | |
2507 | goto out; | |
2508 | } | |
2509 | ||
2510 | fprintf(stderr, "fsck failed! Leaving the root file system read-only...\n"); | |
2511 | ||
2512 | return; | |
2513 | out: | |
2514 | assumes(fwexec(remount_tool, true) != -1); | |
2515 | } | |
2516 | ||
2517 | bool | |
2518 | path_check(const char *path) | |
2519 | { | |
2520 | struct stat sb; | |
2521 | ||
2522 | if (stat(path, &sb) == 0) | |
2523 | return true; | |
2524 | return false; | |
2525 | } | |
2526 | ||
2527 | bool | |
2528 | is_safeboot(void) | |
2529 | { | |
2530 | int sbmib[] = { CTL_KERN, KERN_SAFEBOOT }; | |
2531 | uint32_t sb = 0; | |
2532 | size_t sbsz = sizeof(sb); | |
2533 | ||
2534 | if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0)) | |
2535 | return false; | |
2536 | ||
2537 | return (bool)sb; | |
2538 | } | |
2539 | ||
2540 | bool | |
2541 | is_netboot(void) | |
2542 | { | |
2543 | int nbmib[] = { CTL_KERN, KERN_NETBOOT }; | |
2544 | uint32_t nb = 0; | |
2545 | size_t nbsz = sizeof(nb); | |
2546 | ||
2547 | if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0)) | |
2548 | return false; | |
2549 | ||
2550 | return (bool)nb; | |
2551 | } | |
2552 | ||
2553 | void | |
2554 | apply_func_to_dir(const char *thedir, void (*thefunc)(const char *)) | |
2555 | { | |
2556 | struct dirent *de; | |
2557 | DIR *od; | |
2558 | int currend_dir_fd; | |
2559 | ||
2560 | if (!assumes((currend_dir_fd = open(".", 0)) != -1)) | |
2561 | return; | |
2562 | ||
2563 | if (!assumes(chdir(thedir) != -1)) | |
2564 | goto out; | |
2565 | ||
2566 | if (!assumes(od = opendir("."))) | |
2567 | goto out; | |
2568 | ||
2569 | while ((de = readdir(od))) { | |
2570 | struct stat sb; | |
2571 | ||
2572 | if (strcmp(de->d_name, ".") == 0) | |
2573 | continue; | |
2574 | if (strcmp(de->d_name, "..") == 0) | |
2575 | continue; | |
2576 | ||
2577 | if (assumes(stat(de->d_name, &sb) != -1)) { | |
2578 | if (S_ISDIR(sb.st_mode)) | |
2579 | apply_func_to_dir(de->d_name, thefunc); | |
2580 | thefunc(de->d_name); | |
2581 | } | |
2582 | } | |
2583 | ||
2584 | assumes(closedir(od) != -1); | |
2585 | ||
2586 | out: | |
2587 | assumes(fchdir(currend_dir_fd) != -1); | |
2588 | assumes(close(currend_dir_fd) != -1); | |
2589 | } | |
2590 | ||
2591 | void | |
2592 | empty_dir(const char *path) | |
2593 | { | |
2594 | assumes(chflags(path, 0) != -1); | |
2595 | assumes(remove(path) != -1); | |
2596 | } | |
2597 | ||
2598 | int | |
2599 | touch_file(const char *path, mode_t m) | |
2600 | { | |
2601 | int fd = open(path, O_CREAT, m); | |
2602 | ||
2603 | if (fd == -1) | |
2604 | return -1; | |
2605 | ||
2606 | return close(fd); | |
2607 | } | |
2608 | ||
2609 | void | |
2610 | apply_sysctls_from_file(const char *thefile) | |
2611 | { | |
2612 | const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL }; | |
2613 | size_t ln_len = 0; | |
2614 | char *val, *tmpstr; | |
2615 | FILE *sf; | |
2616 | ||
2617 | if (!(sf = fopen(thefile, "r"))) | |
2618 | return; | |
2619 | ||
2620 | while ((val = fgetln(sf, &ln_len))) { | |
2621 | if (ln_len == 0) | |
2622 | continue; | |
2623 | if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL)) | |
2624 | continue; | |
2625 | memcpy(tmpstr, val, ln_len); | |
2626 | tmpstr[ln_len] = 0; | |
2627 | val = tmpstr; | |
2628 | ||
2629 | while (*val && isspace(*val)) | |
2630 | val++; | |
2631 | if (*val == '\0' || *val == '#') | |
2632 | goto skip_sysctl_tool; | |
2633 | sysctl_tool[2] = val; | |
2634 | assumes(fwexec(sysctl_tool, true) != -1); | |
2635 | skip_sysctl_tool: | |
2636 | free(tmpstr); | |
2637 | } | |
2638 | ||
2639 | assumes(fclose(sf) == 0); | |
2640 | } | |
2641 | ||
2642 | void | |
2643 | do_sysversion_sysctl(void) | |
2644 | { | |
2645 | int mib[] = { CTL_KERN, KERN_OSVERSION }; | |
2646 | CFDictionaryRef versdict; | |
2647 | CFStringRef buildvers; | |
2648 | char buf[1024]; | |
2649 | size_t bufsz = sizeof(buf); | |
2650 | ||
2651 | /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */ | |
2652 | ||
2653 | if (getuid() != 0) | |
2654 | return; | |
2655 | ||
2656 | if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) { | |
2657 | fprintf(stderr, "sysctl(): %s\n", strerror(errno)); | |
2658 | return; | |
2659 | } | |
2660 | ||
2661 | if (buf[0] != '\0') | |
2662 | return; | |
2663 | ||
2664 | versdict = _CFCopySystemVersionDictionary(); | |
2665 | buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey); | |
2666 | CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8); | |
2667 | ||
2668 | if (sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) == -1) { | |
2669 | fprintf(stderr, "sysctl(): %s\n", strerror(errno)); | |
2670 | } | |
2671 | ||
2672 | CFRelease(versdict); | |
2673 | } |