]> git.saurik.com Git - apple/launchd.git/blame - launchd/src/launchctl.c
launchd-258.1.tar.gz
[apple/launchd.git] / launchd / src / launchctl.c
CommitLineData
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 20
fe044cc9 21static const char *const __rcs_file_version__ = "$Revision: 23457 $";
5b0a4722
A
22
23#include "liblaunch_public.h"
24#include "liblaunch_private.h"
25#include "libbootstrap_public.h"
26#include "libvproc_public.h"
27#include "libvproc_private.h"
28#include "libvproc_internal.h"
ed34e3c3 29
e91b9f68 30#include <CoreFoundation/CoreFoundation.h>
ed34e3c3 31#include <CoreFoundation/CFPriv.h>
5b0a4722
A
32#include <TargetConditionals.h>
33#if !TARGET_OS_EMBEDDED
34#include <Security/Security.h>
35#include <Security/AuthSession.h>
36#endif
37#include <IOKit/IOKitLib.h>
ed34e3c3 38#include <NSSystemDirectories.h>
aa59983a 39#include <mach/mach.h>
e91b9f68 40#include <sys/types.h>
ed34e3c3 41#include <sys/sysctl.h>
e91b9f68 42#include <sys/time.h>
ed34e3c3 43#include <sys/sysctl.h>
e91b9f68
A
44#include <sys/stat.h>
45#include <sys/socket.h>
46#include <sys/un.h>
47#include <sys/fcntl.h>
48#include <sys/event.h>
49#include <sys/resource.h>
50#include <sys/param.h>
ed34e3c3
A
51#include <sys/mount.h>
52#include <sys/reboot.h>
53#include <net/if.h>
e91b9f68 54#include <netinet/in.h>
ed34e3c3
A
55#include <netinet/in_var.h>
56#include <netinet6/nd6.h>
e91b9f68
A
57#include <unistd.h>
58#include <dirent.h>
59#include <libgen.h>
60#include <pwd.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <pwd.h>
64#include <grp.h>
65#include <netdb.h>
66#include <syslog.h>
ed34e3c3 67#include <glob.h>
e91b9f68
A
68#include <readline/readline.h>
69#include <readline/history.h>
70#include <dns_sd.h>
ed34e3c3
A
71#include <paths.h>
72#include <utmp.h>
73#include <utmpx.h>
5b0a4722
A
74#include <bootfiles.h>
75#include <sysexits.h>
76#include <util.h>
e91b9f68 77
e91b9f68
A
78
79#define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
80
aa59983a
A
81#define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
82#define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
83#define MACHINIT_JOBKEY_COMMAND "Command"
ed34e3c3
A
84#define MACHINIT_JOBKEY_SERVERPORT "ServerPort"
85#define MACHINIT_JOBKEY_SERVICEPORT "ServicePort"
86
5b0a4722
A
87#define assumes(e) \
88 (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true)
aa59983a
A
89
90
ed34e3c3
A
91struct load_unload_state {
92 launch_data_t pass0;
93 launch_data_t pass1;
94 launch_data_t pass2;
95 char *session_type;
96 unsigned int editondisk:1, load:1, forceload:1, __pad:29;
97};
98
aa59983a 99static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
e91b9f68 100static bool launch_data_array_append(launch_data_t a, launch_data_t o);
aa59983a 101static void distill_jobs(launch_data_t);
e91b9f68
A
102static void distill_config_file(launch_data_t);
103static void sock_dict_cb(launch_data_t what, const char *key, void *context);
104static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
105static launch_data_t CF2launch_data(CFTypeRef);
106static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
107static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
108static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
ed34e3c3
A
109static bool path_goodness_check(const char *path, bool forceload);
110static void readpath(const char *, struct load_unload_state *);
111static void readfile(const char *, struct load_unload_state *);
e91b9f68
A
112static int _fd(int);
113static int demux_cmd(int argc, char *const argv[]);
ed34e3c3 114static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv);
e91b9f68 115static void submit_job_pass(launch_data_t jobs);
aa59983a 116static void submit_mach_jobs(launch_data_t jobs);
ed34e3c3 117static void let_go_of_mach_jobs(launch_data_t jobs);
ab36757d 118static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
ed34e3c3 119static mach_port_t str2bsport(const char *s);
5b0a4722 120static void print_jobs(launch_data_t j, const char *key, void *context);
ed34e3c3 121static void print_obj(launch_data_t obj, const char *key, void *context);
aa59983a 122static bool is_legacy_mach_job(launch_data_t obj);
ed34e3c3
A
123static bool delay_to_second_pass(launch_data_t o);
124static void delay_to_second_pass2(launch_data_t o, const char *key, void *context);
125static bool str2lim(const char *buf, rlim_t *res);
126static const char *lim2str(rlim_t val, char *buf);
127static const char *num2name(int n);
128static ssize_t name2num(const char *n);
129static void unloadjob(launch_data_t job);
130static void print_key_value(launch_data_t obj, const char *key, void *context);
131static void print_launchd_env(launch_data_t obj, const char *key, void *context);
132static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test);
133static void loopback_setup_ipv4(void);
134static void loopback_setup_ipv6(void);
135static pid_t fwexec(const char *const *argv, bool _wait);
136static void do_potential_fsck(void);
137static bool path_check(const char *path);
138static bool is_safeboot(void);
139static bool is_netboot(void);
ed34e3c3 140static void apply_sysctls_from_file(const char *thefile);
5b0a4722 141static void empty_dir(const char *thedir, struct stat *psb);
ed34e3c3
A
142static int touch_file(const char *path, mode_t m);
143static void do_sysversion_sysctl(void);
5b0a4722
A
144static void do_application_firewall_magic(int sfd, launch_data_t thejob);
145static void preheat_page_cache_hack(void);
146static void do_bootroot_magic(void);
147static void do_single_user_mode(bool);
148static bool do_single_user_mode2(void);
149static void read_launchd_conf(void);
150static bool job_disabled_logic(launch_data_t obj);
fe044cc9 151static void fix_bogus_file_metadata(void);
5b0a4722
A
152
153typedef enum {
154 BOOTCACHE_START = 1,
155 BOOTCACHE_TAG,
156 BOOTCACHE_STOP,
157} BootCache_action_t;
158
159static void do_BootCache_magic(BootCache_action_t what);
ed34e3c3
A
160
161static int bootstrap_cmd(int argc, char *const argv[]);
e91b9f68
A
162static int load_and_unload_cmd(int argc, char *const argv[]);
163//static int reload_cmd(int argc, char *const argv[]);
ed34e3c3
A
164static int start_stop_remove_cmd(int argc, char *const argv[]);
165static int submit_cmd(int argc, char *const argv[]);
e91b9f68
A
166static int list_cmd(int argc, char *const argv[]);
167
168static int setenv_cmd(int argc, char *const argv[]);
169static int unsetenv_cmd(int argc, char *const argv[]);
170static int getenv_and_export_cmd(int argc, char *const argv[]);
171
172static int limit_cmd(int argc, char *const argv[]);
173static int stdio_cmd(int argc, char *const argv[]);
174static int fyi_cmd(int argc, char *const argv[]);
175static int logupdate_cmd(int argc, char *const argv[]);
176static int umask_cmd(int argc, char *const argv[]);
177static int getrusage_cmd(int argc, char *const argv[]);
ed34e3c3
A
178static int bsexec_cmd(int argc, char *const argv[]);
179static int bslist_cmd(int argc, char *const argv[]);
e91b9f68 180
ed34e3c3 181static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn));
e91b9f68
A
182static int help_cmd(int argc, char *const argv[]);
183
184static const struct {
185 const char *name;
186 int (*func)(int argc, char *const argv[]);
187 const char *desc;
188} cmds[] = {
189 { "load", load_and_unload_cmd, "Load configuration files and/or directories" },
190 { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
191// { "reload", reload_cmd, "Reload configuration files and/or directories" },
ed34e3c3
A
192 { "start", start_stop_remove_cmd, "Start specified job" },
193 { "stop", start_stop_remove_cmd, "Stop specified job" },
194 { "submit", submit_cmd, "Submit a job from the command line" },
195 { "remove", start_stop_remove_cmd, "Remove specified job" },
196 { "bootstrap", bootstrap_cmd, "Bootstrap launchd" },
e91b9f68
A
197 { "list", list_cmd, "List jobs and information about jobs" },
198 { "setenv", setenv_cmd, "Set an environmental variable in launchd" },
199 { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
200 { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
201 { "export", getenv_and_export_cmd, "Export shell settings from launchd" },
202 { "limit", limit_cmd, "View and adjust launchd resource limits" },
203 { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
204 { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
205 { "shutdown", fyi_cmd, "Prepare for system shutdown" },
ed34e3c3 206 { "singleuser", fyi_cmd, "Switch to single-user mode" },
e91b9f68
A
207 { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
208 { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
209 { "umask", umask_cmd, "Change launchd's umask" },
ed34e3c3
A
210 { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" },
211 { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" },
212 { "exit", exit_cmd, "Exit the interactive invocation of launchctl" },
213 { "quit", exit_cmd, "Quit the interactive invocation of launchctl" },
e91b9f68
A
214 { "help", help_cmd, "This help output" },
215};
216
5b0a4722
A
217static bool istty;
218static bool verbose;
219static bool is_managed;
ed34e3c3
A
220
221int
222main(int argc, char *const argv[])
e91b9f68 223{
5b0a4722 224 int64_t is_managed_val = 0;
e91b9f68
A
225 char *l;
226
5b0a4722
A
227 if (vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed_val) == NULL && is_managed_val) {
228 is_managed = true;
229 }
230
231 if (getuid() == 0 && !is_managed) {
232 mach_port_t root_bs = str2bsport("/");
233 task_set_bootstrap_port(mach_task_self(), root_bs);
234 mach_port_deallocate(mach_task_self(), bootstrap_port);
235 bootstrap_port = root_bs;
236 }
ed34e3c3
A
237
238 istty = isatty(STDIN_FILENO);
239
240 argc--, argv++;
241
242 if (argc > 0 && argv[0][0] == '-') {
243 char *flago;
244
245 for (flago = argv[0] + 1; *flago; flago++) {
246 switch (*flago) {
247 case 'v':
248 verbose = true;
249 break;
250 default:
251 fprintf(stderr, "Unknown argument: '-%c'\n", *flago);
252 break;
253 }
254 }
255 argc--, argv++;
256 }
e91b9f68
A
257
258 if (NULL == readline) {
259 fprintf(stderr, "missing library: readline\n");
260 exit(EXIT_FAILURE);
261 }
262
ed34e3c3
A
263 if (argc == 0) {
264 while ((l = readline(istty ? "launchd% " : NULL))) {
265 char *inputstring = l, *argv2[100], **ap = argv2;
266 int i = 0;
267
268 while ((*ap = strsep(&inputstring, " \t"))) {
269 if (**ap != '\0') {
270 ap++;
271 i++;
272 }
e91b9f68 273 }
e91b9f68 274
5b0a4722 275 if (i > 0) {
ed34e3c3 276 demux_cmd(i, argv2);
5b0a4722 277 }
ed34e3c3
A
278
279 free(l);
280 }
e91b9f68 281
ed34e3c3
A
282 if (istty) {
283 fputc('\n', stdout);
284 }
e91b9f68
A
285 }
286
ed34e3c3
A
287 if (argc > 0) {
288 exit(demux_cmd(argc, argv));
289 }
e91b9f68
A
290
291 exit(EXIT_SUCCESS);
292}
293
ed34e3c3
A
294int
295demux_cmd(int argc, char *const argv[])
e91b9f68
A
296{
297 size_t i;
298
299 optind = 1;
300 optreset = 1;
301
302 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
5b0a4722 303 if (!strcmp(cmds[i].name, argv[0])) {
e91b9f68 304 return cmds[i].func(argc, argv);
5b0a4722 305 }
e91b9f68
A
306 }
307
308 fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]);
309 return 1;
310}
311
5b0a4722
A
312void
313read_launchd_conf(void)
314{
315 char s[1000], *c, *av[100];
316 size_t len, i;
317 FILE *f;
318
319 if (!(f = fopen("/etc/launchd.conf", "r"))) {
320 return;
321 }
322
323 while ((c = fgets(s, sizeof(s), f))) {
324 len = strlen(c);
325 if (len && c[len - 1] == '\n') {
326 c[len - 1] = '\0';
327 }
328
329 i = 0;
330
331 while ((av[i] = strsep(&c, " \t"))) {
332 if (*(av[i]) != '\0') {
333 i++;
334 }
335 }
336
337 if (i > 0) {
338 demux_cmd(i, av);
339 }
340 }
341
342 fclose(f);
343}
344
ed34e3c3
A
345int
346unsetenv_cmd(int argc, char *const argv[])
e91b9f68
A
347{
348 launch_data_t resp, tmp, msg;
349
350 if (argc != 2) {
351 fprintf(stderr, "%s usage: unsetenv <key>\n", getprogname());
352 return 1;
353 }
354
355 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
356
357 tmp = launch_data_new_string(argv[1]);
358 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT);
359
360 resp = launch_msg(msg);
361
362 launch_data_free(msg);
363
364 if (resp) {
365 launch_data_free(resp);
366 } else {
367 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno));
368 }
369
370 return 0;
371}
372
ed34e3c3
A
373int
374setenv_cmd(int argc, char *const argv[])
e91b9f68
A
375{
376 launch_data_t resp, tmp, tmpv, msg;
377
378 if (argc != 3) {
379 fprintf(stderr, "%s usage: setenv <key> <value>\n", getprogname());
380 return 1;
381 }
382
383 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
384 tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
385
386 tmpv = launch_data_new_string(argv[2]);
387 launch_data_dict_insert(tmp, tmpv, argv[1]);
388 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT);
389
390 resp = launch_msg(msg);
391 launch_data_free(msg);
392
393 if (resp) {
394 launch_data_free(resp);
395 } else {
396 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno));
397 }
398
399 return 0;
400}
401
ed34e3c3
A
402void
403print_launchd_env(launch_data_t obj, const char *key, void *context)
aa59983a
A
404{
405 bool *is_csh = context;
406
ed34e3c3 407 /* XXX escape the double quotes */
5b0a4722 408 if (*is_csh) {
ed34e3c3 409 fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj));
5b0a4722 410 } else {
ed34e3c3 411 fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key);
5b0a4722 412 }
aa59983a
A
413}
414
ed34e3c3
A
415void
416print_key_value(launch_data_t obj, const char *key, void *context)
aa59983a
A
417{
418 const char *k = context;
419
5b0a4722 420 if (!strcmp(key, k)) {
aa59983a 421 fprintf(stdout, "%s\n", launch_data_get_string(obj));
5b0a4722 422 }
aa59983a
A
423}
424
ed34e3c3 425int
5b0a4722 426getenv_and_export_cmd(int argc, char *const argv[])
e91b9f68 427{
5b0a4722 428 launch_data_t resp;
e91b9f68 429 bool is_csh = false;
aa59983a 430 char *k;
e91b9f68
A
431
432 if (!strcmp(argv[0], "export")) {
433 char *s = getenv("SHELL");
5b0a4722 434 if (s) {
e91b9f68 435 is_csh = strstr(s, "csh") ? true : false;
5b0a4722 436 }
e91b9f68
A
437 } else if (argc != 2) {
438 fprintf(stderr, "%s usage: getenv <key>\n", getprogname());
439 return 1;
440 }
441
442 k = argv[1];
443
5b0a4722
A
444 if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &resp) == NULL) {
445 if (!strcmp(argv[0], "export")) {
aa59983a 446 launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
5b0a4722 447 } else {
aa59983a 448 launch_data_dict_iterate(resp, print_key_value, k);
5b0a4722 449 }
e91b9f68 450 launch_data_free(resp);
5b0a4722 451 return 0;
e91b9f68 452 } else {
5b0a4722 453 return 1;
e91b9f68 454 }
5b0a4722 455
e91b9f68
A
456 return 0;
457}
458
ed34e3c3
A
459void
460unloadjob(launch_data_t job)
e91b9f68 461{
5b0a4722 462 launch_data_t tmps;
e91b9f68 463
e91b9f68
A
464 tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
465
466 if (!tmps) {
467 fprintf(stderr, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL);
468 return;
469 }
470
5b0a4722
A
471 if (_vproc_send_signal_by_label(launch_data_get_string(tmps), VPROC_MAGIC_UNLOAD_SIGNAL) != NULL) {
472 fprintf(stderr, "%s: Error unloading: %s\n", getprogname(), launch_data_get_string(tmps));
e91b9f68 473 }
e91b9f68
A
474}
475
aa59983a
A
476launch_data_t
477read_plist_file(const char *file, bool editondisk, bool load)
e91b9f68
A
478{
479 CFPropertyListRef plist = CreateMyPropertyListFromFile(file);
480 launch_data_t r = NULL;
481
482 if (NULL == plist) {
483 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), file);
484 return NULL;
485 }
486
487 if (editondisk) {
5b0a4722 488 if (load) {
e91b9f68 489 CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED));
5b0a4722 490 } else {
e91b9f68 491 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue);
5b0a4722 492 }
e91b9f68
A
493 WriteMyPropertyListToFile(plist, file);
494 }
495
496 r = CF2launch_data(plist);
497
498 CFRelease(plist);
499
500 return r;
501}
502
aa59983a 503void
ed34e3c3
A
504delay_to_second_pass2(launch_data_t o, const char *key, void *context)
505{
506 bool *res = context;
507 size_t i;
508
509 if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) {
510 *res = true;
511 return;
512 }
513
514 switch (launch_data_get_type(o)) {
515 case LAUNCH_DATA_DICTIONARY:
516 launch_data_dict_iterate(o, delay_to_second_pass2, context);
517 break;
518 case LAUNCH_DATA_ARRAY:
5b0a4722 519 for (i = 0; i < launch_data_array_get_count(o); i++) {
ed34e3c3 520 delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context);
5b0a4722 521 }
ed34e3c3
A
522 break;
523 default:
524 break;
525 }
526}
527
528bool
529delay_to_second_pass(launch_data_t o)
530{
531 bool res = false;
532
533 launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS);
534
5b0a4722 535 if (NULL == socks) {
ed34e3c3 536 return false;
5b0a4722 537 }
ed34e3c3
A
538
539 delay_to_second_pass2(socks, NULL, &res);
540
541 return res;
542}
543
544void
545readfile(const char *what, struct load_unload_state *lus)
e91b9f68 546{
ed34e3c3
A
547 char ourhostname[1024];
548 launch_data_t tmpd, tmps, thejob, tmpa;
e91b9f68 549 bool job_disabled = false;
ed34e3c3
A
550 size_t i, c;
551
552 gethostname(ourhostname, sizeof(ourhostname));
e91b9f68 553
ed34e3c3 554 if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) {
e91b9f68
A
555 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), what);
556 return;
557 }
558
aa59983a 559 if (is_legacy_mach_job(thejob)) {
ed34e3c3
A
560 fprintf(stderr, "%s: Please convert the following to launchd: %s\n", getprogname(), what);
561 launch_data_array_append(lus->pass0, thejob);
aa59983a
A
562 return;
563 }
564
ab36757d
A
565 if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
566 fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what);
ed34e3c3
A
567 goto out_bad;
568 }
569
570 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) {
571 c = launch_data_array_get_count(tmpa);
572
573 for (i = 0; i < c; i++) {
574 launch_data_t oai = launch_data_array_get_index(tmpa, i);
5b0a4722 575 if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
ed34e3c3 576 goto out_bad;
5b0a4722 577 }
ed34e3c3
A
578 }
579 }
580
581 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) {
582 c = launch_data_array_get_count(tmpa);
583
584 for (i = 0; i < c; i++) {
585 launch_data_t oai = launch_data_array_get_index(tmpa, i);
5b0a4722 586 if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
ed34e3c3 587 break;
5b0a4722 588 }
ed34e3c3
A
589 }
590
5b0a4722 591 if (i == c) {
ed34e3c3 592 goto out_bad;
5b0a4722
A
593 }
594 }
595
596 if (lus->session_type && !(tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
597 tmpa = launch_data_new_string("Aqua");
598 launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
ed34e3c3
A
599 }
600
601 if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
602 const char *allowed_session;
603 bool skipjob = true;
604
605 if (lus->session_type) switch (launch_data_get_type(tmpa)) {
606 case LAUNCH_DATA_ARRAY:
607 c = launch_data_array_get_count(tmpa);
608 for (i = 0; i < c; i++) {
609 tmps = launch_data_array_get_index(tmpa, i);
610 allowed_session = launch_data_get_string(tmps);
611 if (strcasecmp(lus->session_type, allowed_session) == 0) {
612 skipjob = false;
5b0a4722
A
613 /* we have to do the following so job_reparent_hack() works within launchd */
614 tmpa = launch_data_new_string(lus->session_type);
615 launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
ed34e3c3
A
616 break;
617 }
618 }
619 break;
620 case LAUNCH_DATA_STRING:
621 allowed_session = launch_data_get_string(tmpa);
622 if (strcasecmp(lus->session_type, allowed_session) == 0) {
623 skipjob = false;
624 }
625 break;
626 default:
627 break;
628 }
629
630 if (skipjob) {
631 goto out_bad;
632 }
ab36757d
A
633 }
634
5b0a4722
A
635 if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) {
636 job_disabled = job_disabled_logic(tmpd);
637 }
e91b9f68 638
5b0a4722 639 if (lus->forceload) {
ab36757d 640 job_disabled = false;
5b0a4722 641 }
ab36757d 642
5b0a4722 643 if (job_disabled && lus->load) {
ed34e3c3 644 goto out_bad;
5b0a4722 645 }
ed34e3c3 646
5b0a4722 647 if (delay_to_second_pass(thejob)) {
ed34e3c3 648 launch_data_array_append(lus->pass2, thejob);
5b0a4722 649 } else {
ed34e3c3 650 launch_data_array_append(lus->pass1, thejob);
5b0a4722 651 }
ed34e3c3 652
5b0a4722 653 if (verbose) {
ed34e3c3 654 fprintf(stdout, "Will load: %s\n", what);
5b0a4722 655 }
ed34e3c3
A
656
657 return;
658out_bad:
5b0a4722 659 if (verbose) {
ed34e3c3 660 fprintf(stdout, "Ignored: %s\n", what);
5b0a4722 661 }
ed34e3c3
A
662 launch_data_free(thejob);
663}
664
5b0a4722
A
665static bool
666sysctl_hw_streq(int mib_slot, const char *str)
667{
668 char buf[1000];
669 size_t bufsz = sizeof(buf);
670 int mib[] = { CTL_HW, mib_slot };
671
672 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) != -1) {
673 if (strcmp(buf, str) == 0) {
674 return true;
675 }
676 }
677
678 return false;
679}
680
681static void
682job_disabled_dict_logic(launch_data_t obj, const char *key, void *context)
683{
684 bool *r = context;
685
686 if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
687 return;
688 }
689
690 if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MACHINETYPE) == 0) {
691 if (sysctl_hw_streq(HW_MACHINE, launch_data_get_string(obj))) {
692 *r = true;
693 }
694 } else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MODELNAME) == 0) {
695 if (sysctl_hw_streq(HW_MODEL, launch_data_get_string(obj))) {
696 *r = true;
697 }
698 }
699}
700
701bool
702job_disabled_logic(launch_data_t obj)
703{
704 bool r = false;
705
706 switch (launch_data_get_type(obj)) {
707 case LAUNCH_DATA_DICTIONARY:
708 launch_data_dict_iterate(obj, job_disabled_dict_logic, &r);
709 break;
710 case LAUNCH_DATA_BOOL:
711 r = launch_data_get_bool(obj);
712 break;
713 default:
714 break;
715 }
716
717 return r;
718}
719
ed34e3c3
A
720bool
721path_goodness_check(const char *path, bool forceload)
722{
723 struct stat sb;
724
725 if (stat(path, &sb) == -1) {
726 fprintf(stderr, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path, strerror(errno));
727 return false;
e91b9f68
A
728 }
729
5b0a4722 730 if (forceload) {
ed34e3c3 731 return true;
5b0a4722 732 }
ed34e3c3
A
733
734 if (sb.st_mode & (S_IWOTH|S_IWGRP)) {
735 fprintf(stderr, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path);
736 return false;
737 }
738
739 if (sb.st_uid != 0 && sb.st_uid != getuid()) {
740 fprintf(stderr, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path);
741 return false;
742 }
743
744 if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) {
5b0a4722 745 fprintf(stderr, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path);
ed34e3c3
A
746 return false;
747 }
748
749 return true;
e91b9f68
A
750}
751
aa59983a 752void
ed34e3c3 753readpath(const char *what, struct load_unload_state *lus)
e91b9f68
A
754{
755 char buf[MAXPATHLEN];
756 struct stat sb;
757 struct dirent *de;
758 DIR *d;
759
5b0a4722 760 if (!path_goodness_check(what, lus->forceload)) {
ed34e3c3 761 return;
5b0a4722 762 }
ed34e3c3 763
5b0a4722 764 if (stat(what, &sb) == -1) {
e91b9f68 765 return;
5b0a4722 766 }
e91b9f68 767
ed34e3c3
A
768 if (S_ISREG(sb.st_mode)) {
769 readfile(what, lus);
770 } else if (S_ISDIR(sb.st_mode)) {
e91b9f68
A
771 if ((d = opendir(what)) == NULL) {
772 fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname());
773 return;
774 }
775
776 while ((de = readdir(d))) {
5b0a4722 777 if ((de->d_name[0] == '.')) {
e91b9f68 778 continue;
5b0a4722 779 }
e91b9f68
A
780 snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
781
5b0a4722 782 if (!path_goodness_check(buf, lus->forceload)) {
ed34e3c3 783 continue;
5b0a4722 784 }
ed34e3c3
A
785
786 readfile(buf, lus);
e91b9f68
A
787 }
788 closedir(d);
789 }
790}
791
792struct distill_context {
793 launch_data_t base;
794 launch_data_t newsockdict;
795};
796
aa59983a
A
797void
798distill_jobs(launch_data_t jobs)
799{
800 size_t i, c = launch_data_array_get_count(jobs);
801
802 for (i = 0; i < c; i++)
803 distill_config_file(launch_data_array_get_index(jobs, i));
804}
805
806void
807distill_config_file(launch_data_t id_plist)
e91b9f68
A
808{
809 struct distill_context dc = { id_plist, NULL };
810 launch_data_t tmp;
811
ed34e3c3 812 if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) {
e91b9f68
A
813 dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
814 launch_data_dict_iterate(tmp, sock_dict_cb, &dc);
815 launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS);
816 }
817}
818
ed34e3c3
A
819void
820sock_dict_cb(launch_data_t what, const char *key, void *context)
e91b9f68
A
821{
822 struct distill_context *dc = context;
823 launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY);
824
825 launch_data_dict_insert(dc->newsockdict, fdarray, key);
826
827 if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) {
828 sock_dict_edit_entry(what, key, fdarray, dc->base);
829 } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) {
830 launch_data_t tmp;
831 size_t i;
832
833 for (i = 0; i < launch_data_array_get_count(what); i++) {
834 tmp = launch_data_array_get_index(what, i);
835 sock_dict_edit_entry(tmp, key, fdarray, dc->base);
836 }
837 }
838}
839
ed34e3c3
A
840void
841sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
e91b9f68
A
842{
843 launch_data_t a, val;
844 int sfd, st = SOCK_STREAM;
845 bool passive = true;
846
847 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
848 if (!strcasecmp(launch_data_get_string(val), "stream")) {
849 st = SOCK_STREAM;
850 } else if (!strcasecmp(launch_data_get_string(val), "dgram")) {
851 st = SOCK_DGRAM;
852 } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) {
853 st = SOCK_SEQPACKET;
854 }
855 }
856
5b0a4722 857 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) {
e91b9f68 858 passive = launch_data_get_bool(val);
5b0a4722 859 }
e91b9f68
A
860
861 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) {
862 char secdir[] = LAUNCH_SECDIR, buf[1024];
863 launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
864
865 if (NULL == uenv) {
866 uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
867 launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
868 }
869
870 mkdtemp(secdir);
871
872 sprintf(buf, "%s/%s", secdir, key);
873
874 a = launch_data_new_string(buf);
875 launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME);
876 a = launch_data_new_string(buf);
877 launch_data_dict_insert(uenv, a, launch_data_get_string(val));
878 }
879
880 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
881 struct sockaddr_un sun;
aa59983a
A
882 mode_t sun_mode = 0;
883 mode_t oldmask;
884 bool setm = false;
e91b9f68
A
885
886 memset(&sun, 0, sizeof(sun));
887
888 sun.sun_family = AF_UNIX;
889
890 strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path));
891
5b0a4722 892 if ((sfd = _fd(socket(AF_UNIX, st, 0))) == -1) {
e91b9f68 893 return;
5b0a4722 894 }
e91b9f68 895
aa59983a
A
896 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
897 sun_mode = (mode_t)launch_data_get_integer(val);
898 setm = true;
899 }
900
5b0a4722 901 if (passive) {
e91b9f68 902 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
5b0a4722 903 close(sfd);
e91b9f68
A
904 return;
905 }
aa59983a 906 oldmask = umask(S_IRWXG|S_IRWXO);
e91b9f68
A
907 if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
908 close(sfd);
aa59983a 909 umask(oldmask);
e91b9f68
A
910 return;
911 }
aa59983a
A
912 umask(oldmask);
913 if (setm) {
914 chmod(sun.sun_path, sun_mode);
915 }
5b0a4722 916 if ((st == SOCK_STREAM || st == SOCK_SEQPACKET) && listen(sfd, SOMAXCONN) == -1) {
e91b9f68
A
917 close(sfd);
918 return;
919 }
920 } else if (connect(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
921 close(sfd);
922 return;
923 }
924
925 val = launch_data_new_fd(sfd);
926 launch_data_array_append(fdarray, val);
927 } else {
928 launch_data_t rnames = NULL;
ab36757d 929 const char *node = NULL, *serv = NULL, *mgroup = NULL;
e91b9f68
A
930 char servnbuf[50];
931 struct addrinfo hints, *res0, *res;
932 int gerr, sock_opt = 1;
933 bool rendezvous = false;
934
935 memset(&hints, 0, sizeof(hints));
936
937 hints.ai_socktype = st;
5b0a4722 938 if (passive) {
e91b9f68 939 hints.ai_flags |= AI_PASSIVE;
5b0a4722 940 }
e91b9f68 941
5b0a4722 942 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME))) {
e91b9f68 943 node = launch_data_get_string(val);
5b0a4722
A
944 }
945 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP))) {
ab36757d 946 mgroup = launch_data_get_string(val);
5b0a4722 947 }
e91b9f68
A
948 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) {
949 if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) {
950 sprintf(servnbuf, "%lld", launch_data_get_integer(val));
951 serv = servnbuf;
952 } else {
953 serv = launch_data_get_string(val);
954 }
955 }
956 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) {
5b0a4722 957 if (!strcasecmp("IPv4", launch_data_get_string(val))) {
e91b9f68 958 hints.ai_family = AF_INET;
5b0a4722 959 } else if (!strcasecmp("IPv6", launch_data_get_string(val))) {
e91b9f68 960 hints.ai_family = AF_INET6;
5b0a4722 961 }
e91b9f68
A
962 }
963 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) {
5b0a4722 964 if (!strcasecmp("TCP", launch_data_get_string(val))) {
e91b9f68 965 hints.ai_protocol = IPPROTO_TCP;
5b0a4722
A
966 } else if (!strcasecmp("UDP", launch_data_get_string(val))) {
967 hints.ai_protocol = IPPROTO_UDP;
968 }
e91b9f68
A
969 }
970 if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) {
971 rendezvous = true;
972 if (LAUNCH_DATA_BOOL == launch_data_get_type(rnames)) {
973 rendezvous = launch_data_get_bool(rnames);
974 rnames = NULL;
975 }
976 }
977
978 if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) {
979 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
980 return;
981 }
982
983 for (res = res0; res; res = res->ai_next) {
ed34e3c3 984 launch_data_t rvs_fd = NULL;
e91b9f68
A
985 if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
986 fprintf(stderr, "socket(): %s\n", strerror(errno));
987 return;
988 }
5b0a4722
A
989
990 do_application_firewall_magic(sfd, thejob);
991
e91b9f68
A
992 if (hints.ai_flags & AI_PASSIVE) {
993 if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY,
994 (void *)&sock_opt, sizeof(sock_opt))) {
995 fprintf(stderr, "setsockopt(IPV6_V6ONLY): %m");
996 return;
997 }
ab36757d
A
998 if (mgroup) {
999 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
1000 fprintf(stderr, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno));
1001 return;
1002 }
1003 } else {
1004 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
1005 fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
1006 return;
1007 }
e91b9f68
A
1008 }
1009 if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1010 fprintf(stderr, "bind(): %s\n", strerror(errno));
1011 return;
1012 }
5b0a4722
A
1013 /* The kernel may have dynamically assigned some part of the
1014 * address. (The port being a common example.)
1015 */
1016 if (getsockname(sfd, res->ai_addr, &res->ai_addrlen) == -1) {
1017 fprintf(stderr, "getsockname(): %s\n", strerror(errno));
1018 return;
1019 }
ab36757d
A
1020
1021 if (mgroup) {
1022 do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
1023 }
5b0a4722 1024 if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET) && listen(sfd, SOMAXCONN) == -1) {
e91b9f68
A
1025 fprintf(stderr, "listen(): %s\n", strerror(errno));
1026 return;
1027 }
1028 if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) &&
1029 (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) {
ed34e3c3
A
1030 launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS);
1031 if (NULL == rvs_fds) {
1032 rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY);
1033 launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS);
1034 }
e91b9f68 1035 if (NULL == rnames) {
ed34e3c3 1036 rvs_fd = do_rendezvous_magic(res, serv);
5b0a4722 1037 if (rvs_fd) {
ed34e3c3 1038 launch_data_array_append(rvs_fds, rvs_fd);
5b0a4722 1039 }
e91b9f68 1040 } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) {
ed34e3c3 1041 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames));
5b0a4722 1042 if (rvs_fd) {
ed34e3c3 1043 launch_data_array_append(rvs_fds, rvs_fd);
5b0a4722 1044 }
e91b9f68
A
1045 } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) {
1046 size_t rn_i, rn_ac = launch_data_array_get_count(rnames);
1047
1048 for (rn_i = 0; rn_i < rn_ac; rn_i++) {
1049 launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i);
1050
ed34e3c3 1051 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp));
5b0a4722 1052 if (rvs_fd) {
ed34e3c3 1053 launch_data_array_append(rvs_fds, rvs_fd);
5b0a4722 1054 }
e91b9f68
A
1055 }
1056 }
1057 }
1058 } else {
1059 if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1060 fprintf(stderr, "connect(): %s\n", strerror(errno));
1061 return;
1062 }
1063 }
1064 val = launch_data_new_fd(sfd);
ed34e3c3
A
1065 if (rvs_fd) {
1066 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
1067 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
1068 rendezvous = false;
1069 }
e91b9f68
A
1070 launch_data_array_append(fdarray, val);
1071 }
1072 }
1073}
1074
ed34e3c3
A
1075void
1076do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup)
ab36757d
A
1077{
1078 struct addrinfo hints, *res0, *res;
1079 struct ip_mreq mreq;
1080 struct ipv6_mreq m6req;
1081 int gerr;
1082
1083 memset(&hints, 0, sizeof(hints));
1084
1085 hints.ai_flags |= AI_PASSIVE;
1086 hints.ai_family = family;
1087 hints.ai_socktype = socktype;
1088 hints.ai_protocol = protocol;
1089
1090 if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) {
1091 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
1092 return;
1093 }
1094
1095 for (res = res0; res; res = res->ai_next) {
1096 if (AF_INET == family) {
1097 memset(&mreq, 0, sizeof(mreq));
1098 mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
1099 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
1100 fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
1101 continue;
1102 }
1103 break;
1104 } else if (AF_INET6 == family) {
1105 memset(&m6req, 0, sizeof(m6req));
1106 m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1107 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, sizeof(m6req)) == -1) {
1108 fprintf(stderr, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno));
1109 continue;
1110 }
1111 break;
1112 } else {
1113 fprintf(stderr, "unknown family during multicast group bind!\n");
1114 break;
1115 }
1116 }
1117
1118 freeaddrinfo(res0);
1119}
1120
6a39f10b 1121
ed34e3c3
A
1122launch_data_t
1123do_rendezvous_magic(const struct addrinfo *res, const char *serv)
6a39f10b 1124{
ed34e3c3 1125 struct stat sb;
6a39f10b 1126 DNSServiceRef service;
ed34e3c3
A
1127 DNSServiceErrorType error;
1128 char rvs_buf[200];
1129 short port;
1130 static int statres = 1;
e91b9f68 1131
5b0a4722 1132 if (1 == statres) {
ed34e3c3 1133 statres = stat("/usr/sbin/mDNSResponder", &sb);
5b0a4722 1134 }
6a39f10b 1135
5b0a4722 1136 if (-1 == statres) {
ed34e3c3 1137 return NULL;
5b0a4722 1138 }
e91b9f68 1139
ed34e3c3 1140 sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp");
e91b9f68 1141
5b0a4722 1142 if (res->ai_family == AF_INET) {
ed34e3c3 1143 port = ((struct sockaddr_in *)res->ai_addr)->sin_port;
5b0a4722 1144 } else {
ed34e3c3 1145 port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
5b0a4722 1146 }
6a39f10b 1147
ed34e3c3 1148 error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL);
e91b9f68 1149
5b0a4722 1150 if (error == kDNSServiceErr_NoError) {
ed34e3c3 1151 return launch_data_new_fd(DNSServiceRefSockFD(service));
5b0a4722 1152 }
e91b9f68 1153
ed34e3c3
A
1154 fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error);
1155 return NULL;
e91b9f68
A
1156}
1157
ed34e3c3
A
1158CFPropertyListRef
1159CreateMyPropertyListFromFile(const char *posixfile)
e91b9f68
A
1160{
1161 CFPropertyListRef propertyList;
1162 CFStringRef errorString;
1163 CFDataRef resourceData;
1164 SInt32 errorCode;
1165 CFURLRef fileURL;
1166
aa59983a 1167 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
5b0a4722 1168 if (!fileURL) {
e91b9f68 1169 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
5b0a4722
A
1170 }
1171 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
e91b9f68 1172 fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
5b0a4722 1173 }
e91b9f68 1174 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
5b0a4722 1175 if (!propertyList) {
e91b9f68 1176 fprintf(stderr, "%s: propertyList is NULL\n", getprogname());
5b0a4722 1177 }
e91b9f68
A
1178
1179 return propertyList;
1180}
1181
ed34e3c3
A
1182void
1183WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
e91b9f68
A
1184{
1185 CFDataRef resourceData;
1186 CFURLRef fileURL;
1187 SInt32 errorCode;
1188
aa59983a 1189 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
5b0a4722 1190 if (!fileURL) {
e91b9f68 1191 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
5b0a4722 1192 }
e91b9f68 1193 resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
5b0a4722 1194 if (resourceData == NULL) {
e91b9f68 1195 fprintf(stderr, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile);
5b0a4722
A
1196 }
1197 if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) {
e91b9f68 1198 fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
5b0a4722 1199 }
e91b9f68
A
1200}
1201
ed34e3c3
A
1202void
1203myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
e91b9f68
A
1204{
1205 launch_data_t ik, iw, where = context;
1206
1207 ik = CF2launch_data(key);
1208 iw = CF2launch_data(value);
1209
1210 launch_data_dict_insert(where, iw, launch_data_get_string(ik));
1211 launch_data_free(ik);
1212}
1213
ed34e3c3
A
1214launch_data_t
1215CF2launch_data(CFTypeRef cfr)
e91b9f68
A
1216{
1217 launch_data_t r;
1218 CFTypeID cft = CFGetTypeID(cfr);
1219
1220 if (cft == CFStringGetTypeID()) {
1221 char buf[4096];
1222 CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
ed34e3c3
A
1223 r = launch_data_alloc(LAUNCH_DATA_STRING);
1224 launch_data_set_string(r, buf);
e91b9f68 1225 } else if (cft == CFBooleanGetTypeID()) {
ed34e3c3
A
1226 r = launch_data_alloc(LAUNCH_DATA_BOOL);
1227 launch_data_set_bool(r, CFBooleanGetValue(cfr));
e91b9f68
A
1228 } else if (cft == CFArrayGetTypeID()) {
1229 CFIndex i, ac = CFArrayGetCount(cfr);
1230 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
1231 for (i = 0; i < ac; i++) {
1232 CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
1233 if (v) {
1234 launch_data_t iv = CF2launch_data(v);
1235 launch_data_array_set_index(r, iv, i);
1236 }
1237 }
1238 } else if (cft == CFDictionaryGetTypeID()) {
1239 r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1240 CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
1241 } else if (cft == CFDataGetTypeID()) {
ed34e3c3
A
1242 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
1243 launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
e91b9f68
A
1244 } else if (cft == CFNumberGetTypeID()) {
1245 long long n;
1246 double d;
1247 CFNumberType cfnt = CFNumberGetType(cfr);
1248 switch (cfnt) {
1249 case kCFNumberSInt8Type:
1250 case kCFNumberSInt16Type:
1251 case kCFNumberSInt32Type:
1252 case kCFNumberSInt64Type:
1253 case kCFNumberCharType:
1254 case kCFNumberShortType:
1255 case kCFNumberIntType:
1256 case kCFNumberLongType:
1257 case kCFNumberLongLongType:
1258 CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
ed34e3c3
A
1259 r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1260 launch_data_set_integer(r, n);
e91b9f68
A
1261 break;
1262 case kCFNumberFloat32Type:
1263 case kCFNumberFloat64Type:
1264 case kCFNumberFloatType:
1265 case kCFNumberDoubleType:
1266 CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
ed34e3c3
A
1267 r = launch_data_alloc(LAUNCH_DATA_REAL);
1268 launch_data_set_real(r, d);
e91b9f68
A
1269 break;
1270 default:
1271 r = NULL;
1272 break;
1273 }
1274 } else {
1275 r = NULL;
1276 }
1277 return r;
1278}
1279
ed34e3c3
A
1280int
1281help_cmd(int argc, char *const argv[])
e91b9f68
A
1282{
1283 FILE *where = stdout;
1284 int l, cmdwidth = 0;
1285 size_t i;
1286
1287 if (argc == 0 || argv == NULL)
1288 where = stderr;
1289
1290 fprintf(where, "usage: %s <subcommand>\n", getprogname());
ed34e3c3 1291
e91b9f68
A
1292 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
1293 l = strlen(cmds[i].name);
1294 if (l > cmdwidth)
1295 cmdwidth = l;
1296 }
ed34e3c3 1297
5b0a4722 1298 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
e91b9f68 1299 fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc);
5b0a4722 1300 }
e91b9f68
A
1301
1302 return 0;
1303}
1304
ed34e3c3
A
1305int
1306exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused)))
1307{
1308 exit(0);
1309}
1310
1311int
1312_fd(int fd)
e91b9f68
A
1313{
1314 if (fd >= 0)
1315 fcntl(fd, F_SETFD, 1);
1316 return fd;
1317}
1318
5b0a4722
A
1319void
1320do_single_user_mode(bool sflag)
e91b9f68 1321{
5b0a4722
A
1322 if (sflag) {
1323 while (!do_single_user_mode2()) {
1324 sleep(1);
1325 }
ed34e3c3 1326 }
5b0a4722 1327}
ed34e3c3 1328
5b0a4722
A
1329bool
1330do_single_user_mode2(void)
1331{
1332 bool runcom_fsck = true; /* should_fsck(); */
1333 int wstatus;
1334 int fd;
1335 pid_t p;
1336
1337 switch ((p = fork())) {
1338 case -1:
1339 syslog(LOG_ERR, "can't fork single-user shell, trying again: %m");
1340 return false;
1341 case 0:
1342 break;
1343 default:
1344 assumes(waitpid(p, &wstatus, 0) != -1);
1345 if (WIFEXITED(wstatus)) {
1346 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
1347 return true;
1348 } else {
1349 fprintf(stdout, "single user mode: exit status: %d\n", WEXITSTATUS(wstatus));
1350 }
1351 } else {
1352 fprintf(stdout, "single user mode shell: %s\n", strsignal(WTERMSIG(wstatus)));
1353 }
1354 return false;
ed34e3c3
A
1355 }
1356
5b0a4722
A
1357 revoke(_PATH_CONSOLE);
1358 if (!assumes((fd = open(_PATH_CONSOLE, O_RDWR)) != -1)) {
1359 _exit(EXIT_FAILURE);
1360 }
1361 if (!assumes(login_tty(fd) != -1)) {
1362 _exit(EXIT_FAILURE);
ed34e3c3 1363 }
5b0a4722
A
1364 setenv("TERM", "vt100", 1);
1365 if (runcom_fsck) {
1366 fprintf(stdout, "Singleuser boot -- fsck not done\n");
1367 fprintf(stdout, "Root device is mounted read-only\n\n");
1368 fprintf(stdout, "If you want to make modifications to files:\n");
1369 fprintf(stdout, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n\n");
1370 fprintf(stdout, "If you wish to boot the system:\n");
1371 fprintf(stdout, "\texit\n\n");
1372 fflush(stdout);
1373 }
1374
1375 execl(_PATH_BSHELL, "-sh", NULL);
1376 syslog(LOG_ERR, "can't exec %s for single user: %m", _PATH_BSHELL);
1377 _exit(EXIT_FAILURE);
1378}
1379
1380static void
1381system_specific_bootstrap(bool sflag)
1382{
1383 int hnmib[] = { CTL_KERN, KERN_HOSTNAME };
1384 struct kevent kev;
1385 int kq;
1386
1387
1388 do_sysversion_sysctl();
1389
1390 do_single_user_mode(sflag);
1391
1392 assumes((kq = kqueue()) != -1);
1393
1394 EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 60, 0);
1395 assumes(kevent(kq, &kev, 1, NULL, 0, NULL) != -1);
1396
1397 EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
1398 assumes(kevent(kq, &kev, 1, NULL, 0, NULL) != -1);
1399 assumes(signal(SIGTERM, SIG_IGN) != SIG_ERR);
1400
ed34e3c3
A
1401 assumes(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) != -1);
1402
1403 loopback_setup_ipv4();
1404 loopback_setup_ipv6();
1405
5b0a4722
A
1406 if (path_check("/etc/rc.server")) {
1407 const char *rcserver_tool[] = { _PATH_BSHELL, "/etc/rc.server", NULL };
1408 assumes(fwexec(rcserver_tool, true) != -1);
1409 }
1410
ed34e3c3
A
1411 apply_sysctls_from_file("/etc/sysctl.conf");
1412
5b0a4722 1413 if (path_check("/etc/rc.cdrom")) {
ed34e3c3
A
1414 const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL };
1415 assumes(fwexec(rccdrom_tool, true) != -1);
1416 assumes(reboot(RB_HALT) != -1);
1417 _exit(EXIT_FAILURE);
1418 } else if (is_netboot()) {
1419 const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL };
1420 if (!assumes(fwexec(rcnetboot_tool, true) != -1)) {
1421 assumes(reboot(RB_HALT) != -1);
1422 _exit(EXIT_FAILURE);
1423 }
1424 } else {
1425 do_potential_fsck();
1426 }
1427
5b0a4722
A
1428 read_launchd_conf();
1429
ed34e3c3
A
1430 if (path_check("/var/account/acct")) {
1431 assumes(acct("/var/account/acct") != -1);
1432 }
1433
1434 if (path_check("/etc/fstab")) {
1435 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
1436 assumes(fwexec(mount_tool, true) != -1);
1437 }
1438
1439 if (path_check("/etc/rc.installer_cleanup")) {
1440 const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL };
1441 assumes(fwexec(rccleanup_tool, true) != -1);
1442 }
1443
5b0a4722
A
1444 empty_dir(_PATH_VARRUN, NULL);
1445 empty_dir(_PATH_TMP, NULL);
ed34e3c3
A
1446 remove(_PATH_NOLOGIN);
1447
5b0a4722
A
1448 if (path_check("/usr/libexec/dirhelper")) {
1449 const char *dirhelper_tool[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL };
1450 assumes(fwexec(dirhelper_tool, true) != -1);
1451 }
ed34e3c3 1452
ed34e3c3
A
1453 assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1);
1454 assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1);
1455
ed34e3c3
A
1456 if (path_check("/etc/security/rc.audit")) {
1457 const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
1458 assumes(fwexec(audit_tool, true) != -1);
1459 }
1460
5b0a4722 1461 do_BootCache_magic(BOOTCACHE_START);
ed34e3c3 1462
5b0a4722
A
1463 preheat_page_cache_hack();
1464
1465 _vproc_set_global_on_demand(true);
ed34e3c3
A
1466
1467 char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL };
5b0a4722
A
1468
1469 if (is_safeboot()) {
ed34e3c3 1470 load_launchd_items[2] = "system";
5b0a4722
A
1471 }
1472
ed34e3c3
A
1473 assumes(load_and_unload_cmd(4, load_launchd_items) == 0);
1474
5b0a4722
A
1475 /*
1476 * 5066316
1477 *
1478 * We need to revisit this after Leopard ships.
1479 *
1480 * I want a plist defined knob for jobs to give advisory hints that
1481 * will "hopefully" serialize bootstrap. Reasons for doing so include
1482 * pragmatic performance optimizations and attempts to workaround bugs
1483 * in jobs. My current thought is something like what follows.
1484 *
1485 * The BootCache would switch to launchd and add this to the plist:
1486 *
1487 * <key>HopefullyStartsSerially<key>
1488 * <dict>
1489 * <key>ReadyTimeout</key>
1490 * <integer>2</integer>
1491 * </dict>
1492 *
1493 * And kextd would add the following:
1494 *
1495 * <key>HopefullyStartsSerially<key>
1496 * <dict>
1497 * <key>ReadyTimeout</key>
1498 * <integer>5</integer>
1499 * <key>HopefullyStartsAfter</key>
1500 * <string>com.apple.BootCache.daemon</string>
1501 * </dict>
1502 *
1503 *
1504 * Then both the BootCache and kextd could call something like:
1505 *
1506 * vproc_declare_ready_state();
1507 *
1508 * To tell launchd to short circuit the readiness timeout and let the
1509 * next wave of jobs start.
1510 *
1511 * Yes, this mechanism smells a lot like SystemStarter, rc.d and
1512 * friends. I think as long as we document that artificial
1513 * serialization is only advisory and not guaranteed, we should be
1514 * fine. Remember: IPC is the preferred way to serialize operations.
1515 *
1516 */
1517 mach_timespec_t w = { 5, 0 };
1518 IOKitWaitQuiet(kIOMasterPortDefault, &w);
ed34e3c3 1519
5b0a4722 1520 do_BootCache_magic(BOOTCACHE_TAG);
ed34e3c3 1521
5b0a4722 1522 do_bootroot_magic();
ed34e3c3 1523
5b0a4722 1524 _vproc_set_global_on_demand(false);
ed34e3c3 1525
5b0a4722
A
1526 assumes(kevent(kq, NULL, 0, &kev, 1, NULL) == 1);
1527
1528 do_BootCache_magic(BOOTCACHE_STOP);
1529
1530 assumes(close(kq) != -1);
ed34e3c3
A
1531}
1532
1533void
5b0a4722 1534do_BootCache_magic(BootCache_action_t what)
ed34e3c3 1535{
5b0a4722 1536 const char *bcc_tool[] = { "BootCacheControl", "-f", "/var/db/BootCache.playlist", NULL, NULL };
ed34e3c3 1537
5b0a4722 1538 if (is_safeboot()) {
ed34e3c3
A
1539 return;
1540 }
1541
5b0a4722
A
1542 switch (what) {
1543 case BOOTCACHE_START:
1544 bcc_tool[3] = "start";
1545 break;
1546 case BOOTCACHE_TAG:
1547 bcc_tool[3] = "tag";
1548 break;
1549 case BOOTCACHE_STOP:
1550 bcc_tool[3] = "stop";
1551 break;
1552 default:
1553 assumes(false);
1554 return;
1555 }
1556
1557 fwexec(bcc_tool, true);
1558}
1559
1560int
1561bootstrap_cmd(int argc, char *const argv[])
1562{
1563 char *session_type = NULL;
1564 bool sflag = false;
1565 int ch;
1566
1567 while ((ch = getopt(argc, argv, "sS:")) != -1) {
1568 switch (ch) {
1569 case 's':
1570 sflag = true;
1571 break;
1572 case 'S':
1573 session_type = optarg;
1574 break;
1575 case '?':
1576 default:
1577 break;
1578 }
1579 }
1580
1581 optind = 1;
1582 optreset = 1;
1583
1584 if (!session_type) {
1585 fprintf(stderr, "usage: %s bootstrap [-s] -S <session-type>\n", getprogname());
1586 return 1;
1587 }
1588
1589 if (strcasecmp(session_type, "System") == 0) {
1590 system_specific_bootstrap(sflag);
1591 } else {
1592 char *load_launchd_items[] = { "load", "-S", session_type, "-D", "all", NULL, NULL, NULL, NULL };
1593 int the_argc = 5;
1594
1595 if (is_safeboot()) {
1596 load_launchd_items[4] = "system";
1597 }
1598
1599 if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0 || strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
1600 load_launchd_items[4] = "system";
1601 if (!is_safeboot()) {
1602 load_launchd_items[5] = "-D";
1603 load_launchd_items[6] = "local";
1604 the_argc += 2;
1605 }
1606 if (strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
1607 load_launchd_items[the_argc] = "/etc/mach_init_per_login_session.d";
1608 the_argc += 1;
1609 }
1610 } else if (strcasecmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
1611 load_launchd_items[5] = "/etc/mach_init_per_user.d";
1612 the_argc += 1;
ed34e3c3 1613 }
5b0a4722
A
1614
1615#if !TARGET_OS_EMBEDDED
1616 if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0) {
1617 assumes(SessionCreate(sessionKeepCurrentBootstrap, 0) == 0);
1618 }
1619#endif
1620
1621 return load_and_unload_cmd(the_argc, load_launchd_items);
ed34e3c3
A
1622 }
1623
5b0a4722 1624 return 0;
ed34e3c3
A
1625}
1626
1627int
1628load_and_unload_cmd(int argc, char *const argv[])
1629{
5b0a4722 1630 NSSearchPathEnumerationState es = 0;
ed34e3c3
A
1631 char nspath[PATH_MAX * 2]; /* safe side, we need to append */
1632 bool badopts = false;
1633 struct load_unload_state lus;
1634 size_t i;
1635 int ch;
1636
1637 memset(&lus, 0, sizeof(lus));
e91b9f68 1638
5b0a4722 1639 if (strcmp(argv[0], "load") == 0) {
ed34e3c3 1640 lus.load = true;
5b0a4722 1641 }
e91b9f68 1642
ed34e3c3 1643 while ((ch = getopt(argc, argv, "wFS:D:")) != -1) {
e91b9f68 1644 switch (ch) {
ed34e3c3
A
1645 case 'w':
1646 lus.editondisk = true;
1647 break;
1648 case 'F':
1649 lus.forceload = true;
1650 break;
1651 case 'S':
1652 lus.session_type = optarg;
1653 break;
1654 case 'D':
5b0a4722
A
1655 if (strcasecmp(optarg, "all") == 0) {
1656 es |= NSAllDomainsMask;
1657 } else if (strcasecmp(optarg, "user") == 0) {
1658 es |= NSUserDomainMask;
1659 } else if (strcasecmp(optarg, "local") == 0) {
1660 es |= NSLocalDomainMask;
1661 } else if (strcasecmp(optarg, "network") == 0) {
1662 es |= NSNetworkDomainMask;
1663 } else if (strcasecmp(optarg, "system") == 0) {
1664 es |= NSSystemDomainMask;
1665 } else {
ed34e3c3 1666 badopts = true;
5b0a4722
A
1667 }
1668 break;
ed34e3c3 1669 case '?':
e91b9f68 1670 default:
ed34e3c3
A
1671 badopts = true;
1672 break;
e91b9f68
A
1673 }
1674 }
1675 argc -= optind;
1676 argv += optind;
1677
5b0a4722 1678 if (lus.session_type == NULL) {
ed34e3c3 1679 es &= ~NSUserDomainMask;
5b0a4722 1680 }
ed34e3c3 1681
5b0a4722 1682 if (argc == 0 && es == 0) {
ed34e3c3 1683 badopts = true;
5b0a4722 1684 }
ed34e3c3
A
1685
1686 if (badopts) {
1687 fprintf(stderr, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname());
e91b9f68
A
1688 return 1;
1689 }
1690
ed34e3c3 1691 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
aa59983a 1692 * And loading legacy mach init jobs is extra fun.
e91b9f68
A
1693 *
1694 * In later versions of launchd, I hope to load everything in the first pass,
1695 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
1696 * I haven't thought through the various complexities of reloading jobs, and therefore
1697 * launchd doesn't have reload support right now.
1698 */
1699
ed34e3c3
A
1700 lus.pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1701 lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1702 lus.pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
e91b9f68 1703
ed34e3c3
A
1704 es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
1705
1706 while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
1707 glob_t g;
1708
1709 if (lus.session_type) {
1710 strcat(nspath, "/LaunchAgents");
1711 } else {
1712 strcat(nspath, "/LaunchDaemons");
1713 }
e91b9f68 1714
ed34e3c3
A
1715 if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
1716 for (i = 0; i < g.gl_pathc; i++) {
1717 readpath(g.gl_pathv[i], &lus);
1718 }
1719 globfree(&g);
1720 }
1721 }
1722
5b0a4722 1723 for (i = 0; i < (size_t)argc; i++) {
ed34e3c3 1724 readpath(argv[i], &lus);
5b0a4722 1725 }
ed34e3c3
A
1726
1727 if (launch_data_array_get_count(lus.pass0) == 0 &&
1728 launch_data_array_get_count(lus.pass1) == 0 &&
1729 launch_data_array_get_count(lus.pass2) == 0) {
5b0a4722
A
1730 if (!is_managed) {
1731 fprintf(stderr, "nothing found to %s\n", lus.load ? "load" : "unload");
1732 }
ed34e3c3
A
1733 launch_data_free(lus.pass0);
1734 launch_data_free(lus.pass1);
1735 launch_data_free(lus.pass2);
5b0a4722 1736 return is_managed ? 0 : 1;
e91b9f68
A
1737 }
1738
ed34e3c3
A
1739 if (lus.load) {
1740 distill_jobs(lus.pass1);
1741 submit_mach_jobs(lus.pass0);
1742 submit_job_pass(lus.pass1);
1743 let_go_of_mach_jobs(lus.pass0);
1744 distill_jobs(lus.pass2);
1745 submit_job_pass(lus.pass2);
e91b9f68 1746 } else {
5b0a4722 1747 for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) {
ed34e3c3 1748 unloadjob(launch_data_array_get_index(lus.pass1, i));
5b0a4722
A
1749 }
1750 for (i = 0; i < launch_data_array_get_count(lus.pass2); i++) {
ed34e3c3 1751 unloadjob(launch_data_array_get_index(lus.pass2, i));
5b0a4722 1752 }
e91b9f68
A
1753 }
1754
1755 return 0;
1756}
1757
aa59983a
A
1758void
1759submit_mach_jobs(launch_data_t jobs)
1760{
1761 size_t i, c;
1762
1763 c = launch_data_array_get_count(jobs);
1764
aa59983a
A
1765 for (i = 0; i < c; i++) {
1766 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1767 const char *sn = NULL, *cmd = NULL;
ed34e3c3
A
1768 bool d = true;
1769 mach_port_t msr, msv;
aa59983a
A
1770 kern_return_t kr;
1771 uid_t u = getuid();
1772
1773 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND)))
1774 d = launch_data_get_bool(tmp);
aa59983a
A
1775 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME)))
1776 sn = launch_data_get_string(tmp);
1777 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND)))
1778 cmd = launch_data_get_string(tmp);
1779
1780 if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) {
1781 fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr);
1782 continue;
1783 }
1784 if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) {
1785 fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr);
1786 mach_port_destroy(mach_task_self(), msr);
1787 continue;
1788 }
ed34e3c3
A
1789 launch_data_dict_insert(oai, launch_data_new_machport(msr), MACHINIT_JOBKEY_SERVERPORT);
1790 launch_data_dict_insert(oai, launch_data_new_machport(msv), MACHINIT_JOBKEY_SERVICEPORT);
aa59983a
A
1791 }
1792}
1793
1794void
ed34e3c3 1795let_go_of_mach_jobs(launch_data_t jobs)
aa59983a 1796{
ed34e3c3 1797 size_t i, c = launch_data_array_get_count(jobs);
aa59983a 1798
ed34e3c3
A
1799 for (i = 0; i < c; i++) {
1800 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1801 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICEPORT))) {
1802 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1803 } else {
1804 fprintf(stderr, "%s: ack! missing service port!\n", getprogname());
1805 }
1806 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVERPORT))) {
1807 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1808 } else {
1809 fprintf(stderr, "%s: ack! missing server port!\n", getprogname());
1810 }
1811 }
aa59983a
A
1812}
1813
1814void
1815submit_job_pass(launch_data_t jobs)
e91b9f68
A
1816{
1817 launch_data_t msg, resp;
1818 size_t i;
1819 int e;
1820
aa59983a
A
1821 if (launch_data_array_get_count(jobs) == 0)
1822 return;
e91b9f68
A
1823
1824 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1825
1826 launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
1827
1828 resp = launch_msg(msg);
1829
1830 if (resp) {
1831 switch (launch_data_get_type(resp)) {
1832 case LAUNCH_DATA_ERRNO:
1833 if ((e = launch_data_get_errno(resp)))
1834 fprintf(stderr, "%s\n", strerror(e));
1835 break;
1836 case LAUNCH_DATA_ARRAY:
1837 for (i = 0; i < launch_data_array_get_count(jobs); i++) {
1838 launch_data_t obatind = launch_data_array_get_index(resp, i);
1839 launch_data_t jatind = launch_data_array_get_index(jobs, i);
1840 const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
1841 if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
1842 e = launch_data_get_errno(obatind);
1843 switch (e) {
1844 case EEXIST:
1845 fprintf(stderr, "%s: %s\n", lab4job, "Already loaded");
1846 break;
1847 case ESRCH:
1848 fprintf(stderr, "%s: %s\n", lab4job, "Not loaded");
1849 break;
1850 default:
1851 fprintf(stderr, "%s: %s\n", lab4job, strerror(e));
1852 case 0:
1853 break;
1854 }
1855 }
1856 }
1857 break;
1858 default:
1859 fprintf(stderr, "unknown respose from launchd!\n");
1860 break;
1861 }
1862 launch_data_free(resp);
1863 } else {
1864 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1865 }
1866
1867 launch_data_free(msg);
1868}
1869
ed34e3c3
A
1870int
1871start_stop_remove_cmd(int argc, char *const argv[])
e91b9f68
A
1872{
1873 launch_data_t resp, msg;
1874 const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
1875 int e, r = 0;
1876
ed34e3c3 1877 if (0 == strcmp(argv[0], "start"))
e91b9f68
A
1878 lmsgcmd = LAUNCH_KEY_STARTJOB;
1879
ed34e3c3
A
1880 if (0 == strcmp(argv[0], "remove"))
1881 lmsgcmd = LAUNCH_KEY_REMOVEJOB;
1882
e91b9f68
A
1883 if (argc != 2) {
1884 fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]);
1885 return 1;
1886 }
1887
1888 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1889 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
1890
1891 resp = launch_msg(msg);
1892 launch_data_free(msg);
1893
1894 if (resp == NULL) {
1895 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1896 return 1;
1897 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1898 if ((e = launch_data_get_errno(resp))) {
1899 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1900 r = 1;
1901 }
1902 } else {
1903 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1904 r = 1;
1905 }
1906
1907 launch_data_free(resp);
1908 return r;
1909}
1910
ed34e3c3 1911void
5b0a4722 1912print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused)))
e91b9f68 1913{
ed34e3c3
A
1914 static size_t depth = 0;
1915 launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL);
1916 launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID);
1917 launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS);
ed34e3c3 1918 const char *label = launch_data_get_string(lo);
5b0a4722 1919 size_t i;
ed34e3c3
A
1920
1921 if (pido) {
1922 fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido));
1923 } else if (stato) {
1924 int wstatus = (int)launch_data_get_integer(stato);
1925 if (WIFEXITED(wstatus)) {
1926 fprintf(stdout, "-\t%d\t", WEXITSTATUS(wstatus));
1927 } else if (WIFSIGNALED(wstatus)) {
1928 fprintf(stdout, "-\t-%d\t", WTERMSIG(wstatus));
1929 } else {
1930 fprintf(stdout, "-\t???\t");
1931 }
1932 } else {
1933 fprintf(stdout, "-\t-\t");
1934 }
1935 for (i = 0; i < depth; i++)
1936 fprintf(stdout, "\t");
1937
e91b9f68
A
1938 fprintf(stdout, "%s\n", label);
1939}
1940
ed34e3c3
A
1941void
1942print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused)))
e91b9f68 1943{
ed34e3c3
A
1944 static size_t indent = 0;
1945 size_t i, c;
e91b9f68 1946
ed34e3c3
A
1947 for (i = 0; i < indent; i++)
1948 fprintf(stdout, "\t");
1949
1950 if (key)
1951 fprintf(stdout, "\"%s\" = ", key);
1952
1953 switch (launch_data_get_type(obj)) {
1954 case LAUNCH_DATA_STRING:
1955 fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj));
1956 break;
1957 case LAUNCH_DATA_INTEGER:
1958 fprintf(stdout, "%lld;\n", launch_data_get_integer(obj));
1959 break;
1960 case LAUNCH_DATA_REAL:
1961 fprintf(stdout, "%f;\n", launch_data_get_real(obj));
1962 break;
1963 case LAUNCH_DATA_BOOL:
1964 fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false");
1965 break;
1966 case LAUNCH_DATA_ARRAY:
1967 c = launch_data_array_get_count(obj);
1968 fprintf(stdout, "(\n");
1969 indent++;
1970 for (i = 0; i < c; i++)
1971 print_obj(launch_data_array_get_index(obj, i), NULL, NULL);
1972 indent--;
1973 for (i = 0; i < indent; i++)
1974 fprintf(stdout, "\t");
1975 fprintf(stdout, ");\n");
1976 break;
1977 case LAUNCH_DATA_DICTIONARY:
1978 fprintf(stdout, "{\n");
1979 indent++;
1980 launch_data_dict_iterate(obj, print_obj, NULL);
1981 indent--;
1982 for (i = 0; i < indent; i++)
1983 fprintf(stdout, "\t");
1984 fprintf(stdout, "};\n");
1985 break;
1986 case LAUNCH_DATA_FD:
1987 fprintf(stdout, "file-descriptor-object;\n");
1988 break;
1989 case LAUNCH_DATA_MACHPORT:
1990 fprintf(stdout, "mach-port-object;\n");
1991 break;
1992 default:
1993 fprintf(stdout, "???;\n");
1994 break;
e91b9f68 1995 }
ed34e3c3 1996}
e91b9f68 1997
ed34e3c3
A
1998int
1999list_cmd(int argc, char *const argv[])
2000{
5b0a4722 2001 launch_data_t resp, msg;
ed34e3c3
A
2002 int r = 0;
2003
2004 if (argc > 2) {
2005 fprintf(stderr, "usage: %s list [label]\n", getprogname());
e91b9f68 2006 return 1;
ed34e3c3 2007 } else if (argc == 2) {
5b0a4722 2008 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
ed34e3c3 2009 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB);
5b0a4722
A
2010 } else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
2011 fprintf(stdout, "PID\tStatus\tLabel\n");
2012 launch_data_dict_iterate(resp, print_jobs, NULL);
2013 launch_data_free(resp);
2014 return 0;
ed34e3c3 2015 } else {
5b0a4722 2016 return 1;
e91b9f68
A
2017 }
2018
e91b9f68
A
2019 resp = launch_msg(msg);
2020 launch_data_free(msg);
2021
2022 if (resp == NULL) {
2023 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2024 return 1;
2025 } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
5b0a4722 2026 print_obj(resp, NULL, NULL);
e91b9f68
A
2027 } else {
2028 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2029 r = 1;
2030 }
2031
2032 launch_data_free(resp);
2033
2034 return r;
2035}
2036
ed34e3c3 2037int
5b0a4722 2038stdio_cmd(int argc __attribute__((unused)), char *const argv[])
e91b9f68 2039{
5b0a4722
A
2040 fprintf(stderr, "%s %s: This sub-command no longer does anything\n", getprogname(), argv[0]);
2041 return 1;
e91b9f68
A
2042}
2043
ed34e3c3
A
2044int
2045fyi_cmd(int argc, char *const argv[])
e91b9f68
A
2046{
2047 launch_data_t resp, msg;
5b0a4722 2048 const char *lmsgk = NULL;
e91b9f68
A
2049 int e, r = 0;
2050
2051 if (argc != 1) {
2052 fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]);
2053 return 1;
2054 }
2055
ed34e3c3 2056 if (!strcmp(argv[0], "shutdown")) {
e91b9f68 2057 lmsgk = LAUNCH_KEY_SHUTDOWN;
ed34e3c3
A
2058 } else if (!strcmp(argv[0], "singleuser")) {
2059 lmsgk = LAUNCH_KEY_SINGLEUSER;
5b0a4722
A
2060 } else {
2061 return 1;
ed34e3c3 2062 }
e91b9f68
A
2063
2064 msg = launch_data_new_string(lmsgk);
2065 resp = launch_msg(msg);
2066 launch_data_free(msg);
2067
2068 if (resp == NULL) {
2069 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2070 return 1;
2071 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2072 if ((e = launch_data_get_errno(resp))) {
2073 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
2074 r = 1;
2075 }
2076 } else {
2077 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2078 r = 1;
2079 }
2080
2081 launch_data_free(resp);
2082
2083 return r;
2084}
2085
ed34e3c3
A
2086int
2087logupdate_cmd(int argc, char *const argv[])
e91b9f68 2088{
5b0a4722
A
2089 int64_t inval, outval;
2090 int i, j, m = 0;
e91b9f68 2091 bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
e91b9f68
A
2092 static const struct {
2093 const char *name;
2094 int level;
2095 } logtbl[] = {
2096 { "debug", LOG_DEBUG },
2097 { "info", LOG_INFO },
2098 { "notice", LOG_NOTICE },
2099 { "warning", LOG_WARNING },
2100 { "error", LOG_ERR },
2101 { "critical", LOG_CRIT },
2102 { "alert", LOG_ALERT },
2103 { "emergency", LOG_EMERG },
2104 };
2105 int logtblsz = sizeof logtbl / sizeof logtbl[0];
2106
2107 if (argc >= 2) {
2108 if (!strcmp(argv[1], "mask"))
2109 maskmode = true;
2110 else if (!strcmp(argv[1], "only"))
2111 onlymode = true;
2112 else if (!strcmp(argv[1], "level"))
2113 levelmode = true;
2114 else
2115 badargs = true;
2116 }
2117
2118 if (maskmode)
2119 m = LOG_UPTO(LOG_DEBUG);
2120
2121 if (argc > 2 && (maskmode || onlymode)) {
2122 for (i = 2; i < argc; i++) {
2123 for (j = 0; j < logtblsz; j++) {
2124 if (!strcmp(argv[i], logtbl[j].name)) {
2125 if (maskmode)
2126 m &= ~(LOG_MASK(logtbl[j].level));
2127 else
2128 m |= LOG_MASK(logtbl[j].level);
2129 break;
2130 }
2131 }
2132 if (j == logtblsz) {
2133 badargs = true;
2134 break;
2135 }
2136 }
2137 } else if (argc > 2 && levelmode) {
2138 for (j = 0; j < logtblsz; j++) {
2139 if (!strcmp(argv[2], logtbl[j].name)) {
2140 m = LOG_UPTO(logtbl[j].level);
2141 break;
2142 }
2143 }
2144 if (j == logtblsz)
2145 badargs = true;
5b0a4722 2146 } else if (argc != 1) {
e91b9f68
A
2147 badargs = true;
2148 }
2149
2150 if (badargs) {
2151 fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
2152 return 1;
2153 }
2154
5b0a4722 2155 inval = m;
e91b9f68 2156
5b0a4722
A
2157 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_LOG_MASK, argc != 1 ? &inval : NULL, &outval) == NULL) {
2158 if (argc == 1) {
e91b9f68 2159 for (j = 0; j < logtblsz; j++) {
5b0a4722 2160 if (outval & LOG_MASK(logtbl[j].level)) {
e91b9f68 2161 fprintf(stdout, "%s ", logtbl[j].name);
5b0a4722 2162 }
e91b9f68
A
2163 }
2164 fprintf(stdout, "\n");
2165 }
5b0a4722 2166 return 0;
e91b9f68 2167 } else {
5b0a4722 2168 return 1;
e91b9f68 2169 }
e91b9f68
A
2170}
2171
aa59983a
A
2172static const struct {
2173 const char *name;
2174 int lim;
2175} limlookup[] = {
2176 { "cpu", RLIMIT_CPU },
2177 { "filesize", RLIMIT_FSIZE },
2178 { "data", RLIMIT_DATA },
2179 { "stack", RLIMIT_STACK },
2180 { "core", RLIMIT_CORE },
2181 { "rss", RLIMIT_RSS },
2182 { "memlock", RLIMIT_MEMLOCK },
2183 { "maxproc", RLIMIT_NPROC },
2184 { "maxfiles", RLIMIT_NOFILE }
2185};
2186
2187static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
2188
ed34e3c3
A
2189ssize_t
2190name2num(const char *n)
aa59983a
A
2191{
2192 size_t i;
2193
2194 for (i = 0; i < limlookupcnt; i++) {
2195 if (!strcmp(limlookup[i].name, n)) {
2196 return limlookup[i].lim;
2197 }
2198 }
2199 return -1;
2200}
2201
ed34e3c3
A
2202const char *
2203num2name(int n)
aa59983a
A
2204{
2205 size_t i;
2206
2207 for (i = 0; i < limlookupcnt; i++) {
2208 if (limlookup[i].lim == n)
2209 return limlookup[i].name;
2210 }
2211 return NULL;
2212}
2213
ed34e3c3
A
2214const char *
2215lim2str(rlim_t val, char *buf)
aa59983a
A
2216{
2217 if (val == RLIM_INFINITY)
2218 strcpy(buf, "unlimited");
2219 else
2220 sprintf(buf, "%lld", val);
2221 return buf;
2222}
2223
ed34e3c3
A
2224bool
2225str2lim(const char *buf, rlim_t *res)
aa59983a
A
2226{
2227 char *endptr;
2228 *res = strtoll(buf, &endptr, 10);
2229 if (!strcmp(buf, "unlimited")) {
2230 *res = RLIM_INFINITY;
2231 return false;
2232 } else if (*endptr == '\0') {
2233 return false;
2234 }
2235 return true;
2236}
2237
ed34e3c3
A
2238int
2239limit_cmd(int argc __attribute__((unused)), char *const argv[])
e91b9f68
A
2240{
2241 char slimstr[100];
2242 char hlimstr[100];
2243 struct rlimit *lmts = NULL;
2244 launch_data_t resp, resp1 = NULL, msg, tmp;
2245 int r = 0;
aa59983a
A
2246 size_t i, lsz = -1;
2247 ssize_t which = 0;
e91b9f68
A
2248 rlim_t slim = -1, hlim = -1;
2249 bool badargs = false;
e91b9f68
A
2250
2251 if (argc > 4)
2252 badargs = true;
2253
2254 if (argc >= 3 && str2lim(argv[2], &slim))
2255 badargs = true;
2256 else
2257 hlim = slim;
2258
2259 if (argc == 4 && str2lim(argv[3], &hlim))
2260 badargs = true;
2261
aa59983a 2262 if (argc >= 2 && -1 == (which = name2num(argv[1])))
e91b9f68
A
2263 badargs = true;
2264
2265 if (badargs) {
2266 fprintf(stderr, "usage: %s %s [", getprogname(), argv[0]);
2267 for (i = 0; i < limlookupcnt; i++)
2268 fprintf(stderr, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
2269 fprintf(stderr, "[both | soft hard]]\n");
2270 return 1;
2271 }
2272
2273 msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
2274 resp = launch_msg(msg);
2275 launch_data_free(msg);
2276
2277 if (resp == NULL) {
2278 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2279 return 1;
2280 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2281 lmts = launch_data_get_opaque(resp);
2282 lsz = launch_data_get_opaque_size(resp);
2283 if (argc <= 2) {
2284 for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
aa59983a 2285 if (argc == 2 && (size_t)which != i)
e91b9f68
A
2286 continue;
2287 fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i),
2288 lim2str(lmts[i].rlim_cur, slimstr),
2289 lim2str(lmts[i].rlim_max, hlimstr));
2290 }
2291 }
2292 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2293 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2294 r = 1;
2295 } else {
2296 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2297 r = 1;
2298 }
2299
2300 if (argc <= 2 || r != 0) {
2301 launch_data_free(resp);
2302 return r;
2303 } else {
2304 resp1 = resp;
2305 }
2306
2307 lmts[which].rlim_cur = slim;
2308 lmts[which].rlim_max = hlim;
2309
2310 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2311 tmp = launch_data_new_opaque(lmts, lsz);
2312 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
2313 resp = launch_msg(msg);
2314 launch_data_free(msg);
2315
2316 if (resp == NULL) {
2317 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2318 return 1;
2319 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2320 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2321 r = 1;
2322 } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
2323 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2324 r = 1;
2325 }
2326
2327 launch_data_free(resp);
2328 launch_data_free(resp1);
2329
2330 return r;
2331}
2332
ed34e3c3
A
2333int
2334umask_cmd(int argc, char *const argv[])
e91b9f68 2335{
e91b9f68
A
2336 bool badargs = false;
2337 char *endptr;
2338 long m = 0;
5b0a4722 2339 int64_t inval, outval;
e91b9f68
A
2340
2341 if (argc == 2) {
2342 m = strtol(argv[1], &endptr, 8);
2343 if (*endptr != '\0' || m > 0777)
2344 badargs = true;
2345 }
2346
2347 if (argc > 2 || badargs) {
2348 fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]);
2349 return 1;
2350 }
2351
5b0a4722 2352 inval = m;
e91b9f68 2353
5b0a4722
A
2354 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_UMASK, argc == 2 ? &inval : NULL, &outval) == NULL) {
2355 if (argc == 1) {
2356 fprintf(stdout, "%o\n", (unsigned int)outval);
2357 }
2358 return 0;
e91b9f68 2359 } else {
e91b9f68 2360 return 1;
e91b9f68 2361 }
e91b9f68
A
2362}
2363
ed34e3c3
A
2364int
2365submit_cmd(int argc, char *const argv[])
2366{
2367 launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2368 launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2369 launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY);
2370 int ch, i, r = 0;
2371
2372 launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND);
2373
2374 while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) {
2375 switch (ch) {
2376 case 'l':
2377 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL);
2378 break;
2379 case 'p':
2380 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM);
2381 break;
2382 case 'o':
2383 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH);
2384 break;
2385 case 'e':
2386 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH);
2387 break;
2388 default:
2389 fprintf(stderr, "usage: %s submit ...\n", getprogname());
2390 return 1;
2391 }
2392 }
2393 argc -= optind;
2394 argv += optind;
2395
2396 for (i = 0; argv[i]; i++) {
2397 launch_data_array_append(largv, launch_data_new_string(argv[i]));
2398 }
2399
2400 launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
2401
2402 launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
2403
2404 resp = launch_msg(msg);
2405 launch_data_free(msg);
2406
2407 if (resp == NULL) {
2408 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2409 return 1;
2410 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2411 errno = launch_data_get_errno(resp);
2412 if (errno) {
2413 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(errno));
2414 r = 1;
2415 }
2416 } else {
2417 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], "unknown response");
2418 }
2419
2420 launch_data_free(resp);
2421
2422 return r;
2423}
2424
2425int
2426getrusage_cmd(int argc, char *const argv[])
e91b9f68
A
2427{
2428 launch_data_t resp, msg;
2429 bool badargs = false;
2430 int r = 0;
2431
2432 if (argc != 2)
2433 badargs = true;
2434 else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
2435 badargs = true;
2436
2437 if (badargs) {
2438 fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]);
2439 return 1;
2440 }
2441
2442 if (!strcmp(argv[1], "self")) {
2443 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
2444 } else {
2445 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
2446 }
2447
2448 resp = launch_msg(msg);
2449 launch_data_free(msg);
2450
2451 if (resp == NULL) {
2452 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2453 return 1;
2454 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2455 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
2456 r = 1;
2457 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2458 struct rusage *rusage = launch_data_get_opaque(resp);
2459 fprintf(stdout, "\t%-10f\tuser time used\n",
2460 (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
2461 fprintf(stdout, "\t%-10f\tsystem time used\n",
2462 (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
2463 fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss);
2464 fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss);
2465 fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss);
2466 fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss);
2467 fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt);
2468 fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt);
2469 fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap);
2470 fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock);
2471 fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock);
2472 fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd);
2473 fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv);
2474 fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals);
2475 fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw);
2476 fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw);
2477 } else {
2478 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2479 r = 1;
2480 }
2481
2482 launch_data_free(resp);
2483
2484 return r;
2485}
2486
ed34e3c3
A
2487bool
2488launch_data_array_append(launch_data_t a, launch_data_t o)
e91b9f68
A
2489{
2490 size_t offt = launch_data_array_get_count(a);
2491
2492 return launch_data_array_set_index(a, o, offt);
2493}
aa59983a 2494
ed34e3c3
A
2495mach_port_t
2496str2bsport(const char *s)
2497{
2498 bool getrootbs = strcmp(s, "/") == 0;
2499 mach_port_t last_bport, bport = bootstrap_port;
2500 task_t task = mach_task_self();
2501 kern_return_t result;
2502
2503 if (strcmp(s, "..") == 0 || getrootbs) {
2504 do {
2505 last_bport = bport;
2506 result = bootstrap_parent(last_bport, &bport);
2507
2508 if (result == BOOTSTRAP_NOT_PRIVILEGED) {
2509 fprintf(stderr, "Permission denied\n");
2510 return 1;
2511 } else if (result != BOOTSTRAP_SUCCESS) {
2512 fprintf(stderr, "bootstrap_parent() %d\n", result);
2513 return 1;
2514 }
2515 } while (getrootbs && last_bport != bport);
2516 } else {
2517 int pid = atoi(s);
2518
2519 result = task_for_pid(mach_task_self(), pid, &task);
2520
2521 if (result != KERN_SUCCESS) {
2522 fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result));
2523 return 1;
2524 }
2525
2526 result = task_get_bootstrap_port(task, &bport);
2527
2528 if (result != KERN_SUCCESS) {
2529 fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result));
2530 return 1;
2531 }
2532 }
2533
2534 return bport;
2535}
2536
2537int
2538bsexec_cmd(int argc, char *const argv[])
2539{
2540 kern_return_t result;
2541 mach_port_t bport;
2542
2543 if (argc < 3) {
2544 fprintf(stderr, "usage: %s bsexec <PID> prog...\n", getprogname());
2545 return 1;
2546 }
2547
2548 bport = str2bsport(argv[1]);
2549
2550 result = task_set_bootstrap_port(mach_task_self(), bport);
2551
2552 if (result != KERN_SUCCESS) {
2553 fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result));
2554 return 1;
2555 }
2556
2557 setgid(getgid());
2558 setuid(getuid());
2559
5b0a4722
A
2560 if (fwexec((const char *const *)argv + 2, true) == -1) {
2561 fprintf(stderr, "%s bsexec failed: %s\n", getprogname(), strerror(errno));
2562 return 1;
2563 }
2564
2565 return 0;
ed34e3c3
A
2566}
2567
2568int
2569bslist_cmd(int argc, char *const argv[])
2570{
2571 kern_return_t result;
2572 mach_port_t bport = bootstrap_port;
2573 name_array_t service_names;
2574 mach_msg_type_number_t service_cnt, service_active_cnt;
2575 bootstrap_status_array_t service_actives;
2576 unsigned int i;
2577
2578 if (argc == 2)
2579 bport = str2bsport(argv[1]);
2580
2581 if (bport == MACH_PORT_NULL) {
2582 fprintf(stderr, "Invalid bootstrap port\n");
2583 return 1;
2584 }
2585
2586 result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt);
2587 if (result != BOOTSTRAP_SUCCESS) {
2588 fprintf(stderr, "bootstrap_info(): %d\n", result);
2589 return 1;
2590 }
2591
2592#define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
2593
2594 for (i = 0; i < service_cnt ; i++)
2595 fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]);
2596
2597 return 0;
2598}
2599
aa59983a
A
2600bool
2601is_legacy_mach_job(launch_data_t obj)
2602{
2603 bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
5b0a4722 2604 bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
aa59983a
A
2605 bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
2606
2607 return has_command && has_servicename && !has_label;
2608}
ed34e3c3
A
2609
2610void
2611_log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
2612{
2613 int saved_errno = errno;
2614 char buf[100];
2615 const char *file = strrchr(path, '/');
2616 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
2617
2618 if (!file) {
2619 file = path;
2620 } else {
2621 file += 1;
2622 }
2623
2624 if (!rcs_rev_tmp) {
2625 strlcpy(buf, rcs_rev, sizeof(buf));
2626 } else {
2627 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
2628 rcs_rev_tmp = strchr(buf, ' ');
2629 if (rcs_rev_tmp)
2630 *rcs_rev_tmp = '\0';
2631 }
2632
2633 fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test);
2634}
2635
2636void
2637loopback_setup_ipv4(void)
2638{
2639 struct ifaliasreq ifra;
2640 struct ifreq ifr;
2641 int s;
2642
2643 memset(&ifr, 0, sizeof(ifr));
2644 strcpy(ifr.ifr_name, "lo0");
2645
2646 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
2647 return;
2648
2649 if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) {
2650 ifr.ifr_flags |= IFF_UP;
2651 assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1);
2652 }
2653
2654 memset(&ifra, 0, sizeof(ifra));
2655 strcpy(ifra.ifra_name, "lo0");
2656 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
2657 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2658 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
2659 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
2660 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
2661 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
2662
2663 assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1);
2664
2665 assumes(close(s) == 0);
2666}
2667
2668void
2669loopback_setup_ipv6(void)
2670{
2671 struct in6_aliasreq ifra6;
2672 struct ifreq ifr;
2673 int s6;
2674
2675 memset(&ifr, 0, sizeof(ifr));
2676 strcpy(ifr.ifr_name, "lo0");
2677
2678 if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
2679 return;
2680
2681 memset(&ifr, 0, sizeof(ifr));
2682 strcpy(ifr.ifr_name, "lo0");
2683
2684 if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) {
2685 ifr.ifr_flags |= IFF_UP;
2686 assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1);
2687 }
2688
2689 memset(&ifra6, 0, sizeof(ifra6));
2690 strcpy(ifra6.ifra_name, "lo0");
2691
2692 ifra6.ifra_addr.sin6_family = AF_INET6;
2693 ifra6.ifra_addr.sin6_addr = in6addr_loopback;
2694 ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
2695 ifra6.ifra_prefixmask.sin6_family = AF_INET6;
2696 memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
2697 ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
2698 ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
2699 ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
2700
2701 assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1);
2702
2703 assumes(close(s6) == 0);
2704}
2705
2706pid_t
2707fwexec(const char *const *argv, bool _wait)
2708{
2709 int wstatus;
2710 pid_t p;
2711
2712 switch ((p = fork())) {
2713 case -1:
2714 break;
2715 case 0:
5b0a4722
A
2716 if (!_wait) {
2717 setsid();
2718 }
ed34e3c3
A
2719 execvp(argv[0], (char *const *)argv);
2720 _exit(EXIT_FAILURE);
2721 break;
2722 default:
2723 if (!_wait)
2724 return p;
2725 if (p == waitpid(p, &wstatus, 0)) {
2726 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS)
2727 return p;
2728 }
2729 break;
2730 }
2731
2732 return -1;
2733}
2734
2735void
2736do_potential_fsck(void)
2737{
2738 const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
2739 const char *fsck_tool[] = { "fsck", "-p", NULL };
2740 const char *remount_tool[] = { "mount", "-uw", "/", NULL };
2741 struct statfs sfs;
2742
5b0a4722
A
2743 if (!assumes(statfs("/", &sfs) != -1)) {
2744 return;
2745 }
2746
2747 if (!(sfs.f_flags & MNT_RDONLY)) {
2748 return;
ed34e3c3
A
2749 }
2750
2751 if (!is_safeboot()) {
5b0a4722
A
2752#if 0
2753 /* We have disabled this block for now. We need to revisit this optimization after Leopard. */
2754 if (sfs.f_flags & MNT_JOURNALED) {
ed34e3c3 2755 goto out;
5b0a4722
A
2756 }
2757#endif
2758
2759 if (fwexec(fsck_tool, true) != -1) {
2760 goto out;
2761 }
ed34e3c3
A
2762 }
2763
2764 if (fwexec(safe_fsck_tool, true) != -1) {
2765 goto out;
2766 }
2767
fe044cc9
A
2768 fprintf(stderr, "fsck failed!\n");
2769
2770 /* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
2771 assumes(reboot(RB_HALT) != -1);
ed34e3c3
A
2772
2773 return;
2774out:
5b0a4722
A
2775 /*
2776 * Once this is fixed:
2777 *
2778 * <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount()
2779 *
2780 * We can then do this one system call instead of calling out a full blown process.
2781 *
2782 * assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1);
2783 */
2784
ed34e3c3 2785 assumes(fwexec(remount_tool, true) != -1);
fe044cc9
A
2786
2787 fix_bogus_file_metadata();
2788}
2789
2790void
2791fix_bogus_file_metadata(void)
2792{
2793 static const struct {
2794 const char *path;
2795 const uid_t owner;
2796 const gid_t group;
2797 const mode_t needed_bits;
2798 const mode_t bad_bits;
2799 } f[] = {
2800 { "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH },
2801 { _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
2802 { _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
2803 };
2804 struct stat sb;
2805 size_t i;
2806
2807 for (i = 0; i < (sizeof(f) / sizeof(f[0])); i++) {
2808 mode_t i_needed_bits;
2809 mode_t i_bad_bits;
2810 bool fix_mode = false;
2811 bool fix_id = false;
2812
2813 if (!assumes(stat(f[i].path, &sb) != -1)) {
2814 continue;
2815 }
2816
2817 i_needed_bits = ~sb.st_mode & f[i].needed_bits;
2818 i_bad_bits = sb.st_mode & f[i].bad_bits;
2819
2820 if (i_bad_bits) {
2821 fprintf(stderr, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s\n", i_bad_bits, f[i].path);
2822 fix_mode = true;
2823 }
2824 if (i_needed_bits) {
2825 fprintf(stderr, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s\n", i_needed_bits, f[i].path);
2826 fix_mode = true;
2827 }
2828 if (sb.st_uid != f[i].owner) {
2829 fprintf(stderr, "Crucial filesystem check: Fixing bogus UID %u on path: %s\n", sb.st_uid, f[i].path);
2830 fix_id = true;
2831 }
2832 if (sb.st_gid != f[i].group) {
2833 fprintf(stderr, "Crucial filesystem check: Fixing bogus GID %u on path: %s\n", sb.st_gid, f[i].path);
2834 fix_id = true;
2835 }
2836
2837 if (fix_mode) {
2838 assumes(chmod(f[i].path, (sb.st_mode & ~i_bad_bits) | i_needed_bits) != -1);
2839 }
2840 if (fix_id) {
2841 assumes(chown(f[i].path, f[i].owner, f[i].group) != -1);
2842 }
2843 }
ed34e3c3
A
2844}
2845
2846bool
2847path_check(const char *path)
2848{
2849 struct stat sb;
2850
2851 if (stat(path, &sb) == 0)
2852 return true;
2853 return false;
2854}
2855
2856bool
2857is_safeboot(void)
2858{
2859 int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
2860 uint32_t sb = 0;
2861 size_t sbsz = sizeof(sb);
2862
2863 if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0))
2864 return false;
2865
2866 return (bool)sb;
2867}
2868
2869bool
2870is_netboot(void)
2871{
2872 int nbmib[] = { CTL_KERN, KERN_NETBOOT };
2873 uint32_t nb = 0;
2874 size_t nbsz = sizeof(nb);
2875
2876 if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0))
2877 return false;
2878
2879 return (bool)nb;
2880}
2881
2882void
5b0a4722 2883empty_dir(const char *thedir, struct stat *psb)
ed34e3c3
A
2884{
2885 struct dirent *de;
5b0a4722 2886 struct stat psb2;
ed34e3c3
A
2887 DIR *od;
2888 int currend_dir_fd;
2889
5b0a4722
A
2890 if (!psb) {
2891 psb = &psb2;
2892 if (!assumes(lstat(thedir, psb) != -1)) {
2893 return;
2894 }
2895 }
2896
2897 if (!assumes((currend_dir_fd = open(".", 0)) != -1)) {
ed34e3c3 2898 return;
5b0a4722 2899 }
ed34e3c3 2900
5b0a4722 2901 if (!assumes(chdir(thedir) != -1)) {
ed34e3c3 2902 goto out;
5b0a4722 2903 }
ed34e3c3 2904
5b0a4722 2905 if (!assumes(od = opendir("."))) {
ed34e3c3 2906 goto out;
5b0a4722 2907 }
ed34e3c3
A
2908
2909 while ((de = readdir(od))) {
2910 struct stat sb;
2911
5b0a4722 2912 if (strcmp(de->d_name, ".") == 0) {
ed34e3c3 2913 continue;
5b0a4722
A
2914 }
2915
2916 if (strcmp(de->d_name, "..") == 0) {
ed34e3c3 2917 continue;
5b0a4722 2918 }
ed34e3c3 2919
5b0a4722
A
2920 if (!assumes(lstat(de->d_name, &sb) != -1)) {
2921 continue;
ed34e3c3 2922 }
5b0a4722
A
2923
2924 if (psb->st_dev != sb.st_dev) {
2925 assumes(unmount(de->d_name, MNT_FORCE) != -1);
2926
2927 /* Let's lstat() again to see if the unmount() worked and what was under it */
2928 if (!assumes(lstat(de->d_name, &sb) != -1)) {
2929 continue;
2930 }
2931
2932 if (!assumes(psb->st_dev == sb.st_dev)) {
2933 continue;
2934 }
2935 }
2936
2937 if (S_ISDIR(sb.st_mode)) {
2938 empty_dir(de->d_name, &sb);
2939 }
2940
2941 assumes(lchflags(de->d_name, 0) != -1);
2942 assumes(remove(de->d_name) != -1);
ed34e3c3
A
2943 }
2944
2945 assumes(closedir(od) != -1);
2946
2947out:
2948 assumes(fchdir(currend_dir_fd) != -1);
2949 assumes(close(currend_dir_fd) != -1);
2950}
2951
ed34e3c3
A
2952int
2953touch_file(const char *path, mode_t m)
2954{
2955 int fd = open(path, O_CREAT, m);
2956
2957 if (fd == -1)
2958 return -1;
2959
2960 return close(fd);
2961}
2962
2963void
2964apply_sysctls_from_file(const char *thefile)
2965{
2966 const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
2967 size_t ln_len = 0;
2968 char *val, *tmpstr;
2969 FILE *sf;
2970
2971 if (!(sf = fopen(thefile, "r")))
2972 return;
2973
2974 while ((val = fgetln(sf, &ln_len))) {
5b0a4722 2975 if (ln_len == 0) {
ed34e3c3 2976 continue;
5b0a4722
A
2977 }
2978 if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL)) {
ed34e3c3 2979 continue;
5b0a4722 2980 }
ed34e3c3
A
2981 memcpy(tmpstr, val, ln_len);
2982 tmpstr[ln_len] = 0;
2983 val = tmpstr;
2984
5b0a4722
A
2985 if (val[ln_len - 1] == '\n' || val[ln_len - 1] == '\r') {
2986 val[ln_len - 1] = '\0';
2987 }
2988
ed34e3c3
A
2989 while (*val && isspace(*val))
2990 val++;
5b0a4722 2991 if (*val == '\0' || *val == '#') {
ed34e3c3 2992 goto skip_sysctl_tool;
5b0a4722 2993 }
ed34e3c3
A
2994 sysctl_tool[2] = val;
2995 assumes(fwexec(sysctl_tool, true) != -1);
2996skip_sysctl_tool:
2997 free(tmpstr);
2998 }
2999
3000 assumes(fclose(sf) == 0);
3001}
3002
3003void
3004do_sysversion_sysctl(void)
3005{
3006 int mib[] = { CTL_KERN, KERN_OSVERSION };
3007 CFDictionaryRef versdict;
3008 CFStringRef buildvers;
3009 char buf[1024];
3010 size_t bufsz = sizeof(buf);
3011
3012 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
3013
ed34e3c3
A
3014 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
3015 fprintf(stderr, "sysctl(): %s\n", strerror(errno));
3016 return;
3017 }
3018
5b0a4722 3019 if (buf[0] != '\0') {
ed34e3c3 3020 return;
5b0a4722 3021 }
ed34e3c3 3022
5b0a4722
A
3023 if (!assumes((versdict = _CFCopySystemVersionDictionary()))) {
3024 return;
3025 }
ed34e3c3 3026
5b0a4722
A
3027 if (assumes((buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey)))) {
3028 CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
3029
3030 assumes(sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) != -1);
ed34e3c3
A
3031 }
3032
3033 CFRelease(versdict);
3034}
5b0a4722
A
3035
3036void
3037do_application_firewall_magic(int sfd, launch_data_t thejob)
3038{
3039 const char *prog = NULL, *partialprog = NULL;
3040 char *path, *pathtmp, **pathstmp;
3041 char *paths[100];
3042 launch_data_t tmp;
3043
3044 /*
3045 * Sigh...
3046 * <rdar://problem/4684434> setsockopt() with the executable path as the argument
3047 */
3048
3049 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM))) {
3050 prog = launch_data_get_string(tmp);
3051 }
3052
3053 if (!prog) {
3054 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
3055 if ((tmp = launch_data_array_get_index(tmp, 0))) {
3056 if (assumes((partialprog = launch_data_get_string(tmp)) != NULL)) {
3057 if (partialprog[0] == '/') {
3058 prog = partialprog;
3059 }
3060 }
3061 }
3062 }
3063 }
3064
3065 if (!prog) {
3066 pathtmp = path = strdup(getenv("PATH"));
3067
3068 pathstmp = paths;
3069
3070 while ((*pathstmp = strsep(&pathtmp, ":"))) {
3071 if (**pathstmp != '\0') {
3072 pathstmp++;
3073 }
3074 }
3075
3076 free(path);
3077 pathtmp = alloca(MAXPATHLEN);
3078
3079 pathstmp = paths;
3080
3081 for (; *pathstmp; pathstmp++) {
3082 snprintf(pathtmp, MAXPATHLEN, "%s/%s", *pathstmp, partialprog);
3083 if (path_check(pathtmp)) {
3084 prog = pathtmp;
3085 break;
3086 }
3087 }
3088 }
3089
3090 if (assumes(prog != NULL)) {
3091 /* The networking team has asked us to ignore the failure of this API if errno == ENOPROTOOPT */
3092 assumes(setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, strlen(prog) + 1) != -1 || errno == ENOPROTOOPT);
3093 }
3094}
3095
3096
3097void
3098preheat_page_cache_hack(void)
3099{
3100 struct dirent *de;
3101 DIR *thedir;
3102
3103 /* Disable this hack for now */
3104 return;
3105
3106 if ((thedir = opendir("/etc/preheat_at_boot")) == NULL) {
3107 return;
3108 }
3109
3110 while ((de = readdir(thedir))) {
3111 struct stat sb;
3112 void *junkbuf;
3113 int fd;
3114
3115 if (de->d_name[0] == '.') {
3116 continue;
3117 }
3118
3119 if ((fd = open(de->d_name, O_RDONLY)) == -1) {
3120 continue;
3121 }
3122
3123 if (fstat(fd, &sb) != -1) {
3124 if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) {
3125 assumes(read(fd, junkbuf, (size_t)sb.st_size) == (ssize_t)sb.st_size);
3126 free(junkbuf);
3127 }
3128 }
3129
3130 close(fd);
3131 }
3132
3133 closedir(thedir);
3134}
3135
3136
3137void
3138do_bootroot_magic(void)
3139{
3140 const char *kextcache_tool[] = { "kextcache", "-U", "/", NULL };
3141 CFTypeRef bootrootProp;
3142 io_service_t chosen;
3143 int wstatus;
3144 pid_t p;
3145
3146 chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
3147
3148 if (!assumes(chosen)) {
3149 return;
3150 }
3151
3152 bootrootProp = IORegistryEntryCreateCFProperty(chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault, 0);
3153
3154 IOObjectRelease(chosen);
3155
3156 if (!bootrootProp) {
3157 return;
3158 }
3159
3160 CFRelease(bootrootProp);
3161
3162 if (!assumes((p = fwexec(kextcache_tool, false)) != -1)) {
3163 return;
3164 }
3165
3166 if (!assumes(waitpid(p, &wstatus, 0) != -1)) {
3167 return;
3168 }
3169
3170 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EX_OSFILE) {
3171 assumes(reboot(RB_AUTOBOOT) != -1);
3172 }
3173}