xref: /trueos/sbin/launchd/launchd.c (revision 92472002bd7850ff771c53bf11b4cfb864789193)
1 /*
2  * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
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
9  *
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
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 
21 #include "config.h"
22 #include "launchd.h"
23 
24 #include <sys/types.h>
25 #include <sys/queue.h>
26 #include <sys/event.h>
27 #include <sys/stat.h>
28 #include <sys/ucred.h>
29 #include <sys/fcntl.h>
30 #include <sys/un.h>
31 #include <sys/wait.h>
32 #include <sys/sysctl.h>
33 #include <sys/sockio.h>
34 #include <sys/time.h>
35 #include <sys/resource.h>
36 #include <sys/ioctl.h>
37 #include <sys/mount.h>
38 #include <sys/kern_event.h>
39 #include <sys/reboot.h>
40 #include <sys/socket.h>
41 #include <sys/syscall.h>
42 #include <net/if.h>
43 #include <net/if_var.h>
44 #include <netinet/in.h>
45 #include <netinet/in_var.h>
46 #include <netinet6/nd6.h>
47 #include <ifaddrs.h>
48 #include <unistd.h>
49 #include <signal.h>
50 #include <errno.h>
51 #include <libgen.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <stdarg.h>
55 #include <stdbool.h>
56 #include <paths.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <ttyent.h>
60 #include <dlfcn.h>
61 #include <dirent.h>
62 #include <string.h>
63 #include <setjmp.h>
64 #include <spawn.h>
65 #include <sched.h>
66 #include <pthread.h>
67 #include <util.h>
68 #include <os/assumes.h>
69 
70 #if HAVE_LIBAUDITD
71 #include <bsm/auditd_lib.h>
72 #include <bsm/audit_session.h>
73 #endif
74 
75 #include "bootstrap.h"
76 #include "vproc.h"
77 #include "vproc_priv.h"
78 #include "vproc_internal.h"
79 #include "launch.h"
80 #include "launch_internal.h"
81 
82 #include "runtime.h"
83 #include "core.h"
84 #include "ipc.h"
85 #include "shim.h"
86 
87 #define _LD_LIBRARY_PATH_STDPATH "/lib:/usr/lib:/usr/local/lib"
88 #define LAUNCHD_CONF ".launchd.conf"
89 
90 extern char **environ;
91 
92 static void pfsystem_callback(void *, struct kevent *);
93 
94 static kq_callback kqpfsystem_callback = pfsystem_callback;
95 
96 static void pid1_magic_init(void);
97 
98 static void testfd_or_openfd(int fd, const char *path, int flags);
99 static bool get_network_state(void);
100 static void monitor_networking_state(void);
101 static void fatal_signal_handler(int sig, siginfo_t *si, void *uap);
102 static void handle_pid1_crashes_separately(void);
103 static void do_pid1_crash_diagnosis_mode(const char *msg);
104 static int basic_fork(void);
105 static bool do_pid1_crash_diagnosis_mode2(const char *msg);
106 
107 static void *update_thread(void *nothing);
108 
109 static void *crash_addr;
110 static pid_t crash_pid;
111 
112 static const char *_launchd_database_dir;
113 static const char *_launchd_log_dir;
114 
115 bool launchd_shutting_down;
116 bool network_up;
117 uid_t launchd_uid;
118 FILE *launchd_console = NULL;
119 int32_t launchd_sync_frequency = 30;
120 
121 
122 /* gdb can't cope with copy relocations (caused by global variables in shared libraries) */
123 #ifdef MACH_DEBUG
124 mach_port_t bootstrap_port;
125 #endif
126 
127 void
launchd_exit(int code)128 launchd_exit(int code)
129 {
130 	syslog(LOG_ERR, "exiting code %d errno %d", code, errno);
131 	sleep(1);
132 	_exit(code);
133 }
134 
135 
136 __inline int
posix_spawnattr_setbinpref_np(posix_spawnattr_t * __restrict a __unused,size_t b __unused,cpu_type_t * __restrict c __unused,size_t * __restrict d __unused)137 posix_spawnattr_setbinpref_np(posix_spawnattr_t * __restrict a __unused,
138 							  size_t b __unused, cpu_type_t *__restrict c __unused, size_t *__restrict d __unused)
139 {
140 	return 0;
141 }
142 
143 bool uflag = false;
144 
145 int
main(int argc,char * const * argv)146 main(int argc, char *const *argv)
147 {
148 	bool sflag = false;
149 	int ch;
150 
151 	/* This needs to be cleaned up. Currently, we risk tripping assumes() macros
152 	 * before we've properly set things like launchd's log database paths, the
153 	 * global launchd label for syslog messages and the like. Luckily, these are
154 	 * operations that will probably never fail, like test_of_openfd(), the
155 	 * stuff in launchd_runtime_init() and the stuff in
156 	 * handle_pid1_crashes_separately().
157 	 */
158 /*
159 	 * Note that this does NOT open a file...
160 	 * Does 'init' deserve its own facility number?
161 	 */
162 	openlog("launchd", LOG_CONS, LOG_AUTH);
163 
164 	testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY);
165 	testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY);
166 	testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY);
167 
168 	if (launchd_use_gmalloc) {
169 		if (!getenv("DYLD_INSERT_LIBRARIES")) {
170 			setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1);
171 			setenv("MALLOC_STRICT_SIZE", "1", 1);
172 			execv(argv[0], argv);
173 		} else {
174 			unsetenv("DYLD_INSERT_LIBRARIES");
175 			unsetenv("MALLOC_STRICT_SIZE");
176 		}
177 	} else if (launchd_malloc_log_stacks) {
178 		if (!getenv("MallocStackLogging")) {
179 			setenv("MallocStackLogging", "1", 1);
180 			execv(argv[0], argv);
181 		} else {
182 			unsetenv("MallocStackLogging");
183 		}
184 	}
185 
186 	while ((ch = getopt(argc, argv, "su")) != -1) {
187 		switch (ch) {
188 		case 's': sflag = true; break;	/* single user */
189 		case 'u': uflag = true; break; /* run as non-pid1 */
190 		case '?': /* we should do something with the global optopt variable here */
191 		default:
192 			fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname());
193 			break;
194 		}
195 	}
196 
197 	if (uflag == false && getpid() != 1 && getppid() != 1) {
198 		fprintf(stderr, "%s: This program is not meant to be run directly.\n", getprogname());
199 		DEBUG_EXIT(EXIT_FAILURE);
200 	}
201 
202 	launchd_runtime_init();
203 
204 	if (NULL == getenv("PATH")) {
205 		setenv("PATH", _PATH_STDPATH, 1);
206 	}
207 
208 	if (NULL == getenv("LD_LIBRARY_PATH")) {
209 		setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH_STDPATH, 1);
210 	}
211 	if (pid1_magic) {
212 		pid1_magic_init();
213 		int cfd = -1;
214 		if ((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) {
215 			_fd(cfd);
216 			if (!(launchd_console = fdopen(cfd, "w"))) {
217 				(void)close(cfd);
218 			}
219 		}
220 
221 		const char *extra = "";
222 		if (launchd_osinstaller) {
223 			extra = " in the OS Installer";
224 		} else if (sflag) {
225 			extra = " in single-user mode";
226 		}
227 
228 		launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up%s. ***", extra);
229 		if (launchd_use_gmalloc) {
230 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Using libgmalloc. ***");
231 		}
232 
233 		if (launchd_verbose_boot) {
234 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***");
235 		}
236 
237 		if (launchd_shutdown_debugging) {
238 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***");
239 		}
240 
241 		if (launchd_log_shutdown) {
242 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown logging is enabled. ***");
243 		}
244 
245 		if (launchd_log_perf) {
246 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Performance logging is enabled. ***");
247 		}
248 
249 		if (launchd_log_debug) {
250 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Debug logging is enabled. ***");
251 		}
252 
253 		handle_pid1_crashes_separately();
254 
255 		/* Start the update thread.
256 		 *
257 		 * <rdar://problem/5039559&6153301>
258 		 */
259 		pthread_t t = NULL;
260 		(void)os_assumes_zero(pthread_create(&t, NULL, update_thread, NULL));
261 		(void)os_assumes_zero(pthread_detach(t));
262 
263 		/* PID 1 doesn't have a flat namespace. */
264 		launchd_flat_mach_namespace = false;
265 		fflush(launchd_console);
266 	} else {
267 		launchd_uid = getuid();
268 		launchd_var_available = true;
269 		if (asprintf((char **)&launchd_label, "com.apple.launchd.peruser.%u", launchd_uid) == 0) {
270 			launchd_label = "com.apple.launchd.peruser.unknown";
271 		}
272 
273 		struct passwd *pwent = getpwuid(launchd_uid);
274 		if (pwent) {
275 			launchd_username = strdup(pwent->pw_name);
276 		} else {
277 			launchd_username = "(unknown)";
278 		}
279 
280 		if (asprintf((char **)&_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) {
281 			_launchd_database_dir = "";
282 		}
283 
284 		if (asprintf((char **)&_launchd_log_dir, LAUNCHD_LOG_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) {
285 			_launchd_log_dir = "";
286 		}
287 
288 		if (launchd_allow_global_dyld_envvars) {
289 			launchd_syslog(LOG_WARNING, "Per-user launchd will allow DYLD_* environment variables in the global environment.");
290 		}
291 
292 		ipc_server_init();
293 		launchd_log_push();
294 
295 		auditinfo_addr_t auinfo;
296 		if (posix_assumes_zero(getaudit_addr(&auinfo, sizeof(auinfo))) != -1) {
297 			launchd_audit_session = auinfo.ai_asid;
298 			launchd_syslog(LOG_DEBUG, "Our audit session ID is %i", launchd_audit_session);
299 		}
300 
301 		launchd_audit_port = _audit_session_self();
302 #if 0
303 		vproc_transaction_begin(NULL);
304 		vproc_transaction_end(NULL, NULL);
305 #endif
306 		launchd_syslog(LOG_DEBUG, "Per-user launchd started (UID/username): %u/%s.", launchd_uid, launchd_username);
307 	}
308 
309 	monitor_networking_state();
310 	jobmgr_init(sflag);
311 	launchd_runtime_init2();
312 #if 0
313 	if (getpid() == 1 /* && !job_active(rlcj) */) {
314 			init_pre_kevent(sflag);
315 	}
316 #endif
317 	sleep(1);
318 	launchd_runtime();
319 }
320 
321 void
handle_pid1_crashes_separately(void)322 handle_pid1_crashes_separately(void)
323 {
324 	struct sigaction fsa;
325 
326 	fsa.sa_sigaction = fatal_signal_handler;
327 	fsa.sa_flags = SA_SIGINFO;
328 	sigemptyset(&fsa.sa_mask);
329 
330 	(void)posix_assumes_zero(sigaction(SIGILL, &fsa, NULL));
331 	(void)posix_assumes_zero(sigaction(SIGFPE, &fsa, NULL));
332 	(void)posix_assumes_zero(sigaction(SIGBUS, &fsa, NULL));
333 	(void)posix_assumes_zero(sigaction(SIGTRAP, &fsa, NULL));
334 	(void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL));
335 	(void)posix_assumes_zero(sigaction(SIGSEGV, &fsa, NULL));
336 }
337 
338 void *
update_thread(void * nothing)339 update_thread(void *nothing __attribute__((unused)))
340 {
341 	(void)posix_assumes_zero(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE));
342 
343 	while (launchd_sync_frequency) {
344 		sync();
345 		sleep(launchd_sync_frequency);
346 	}
347 
348 	launchd_syslog(LOG_DEBUG, "Update thread exiting.");
349 	return NULL;
350 }
351 
352 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
353 
354 /* This hack forces the dynamic linker to resolve these symbols ASAP */
355 static __attribute__((unused)) typeof(sync) *__junk_dyld_trick1 = sync;
356 static __attribute__((unused)) typeof(sleep) *__junk_dyld_trick2 = sleep;
357 static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot;
358 
359 void
do_pid1_crash_diagnosis_mode(const char * msg)360 do_pid1_crash_diagnosis_mode(const char *msg)
361 {
362 	if (launchd_wsp) {
363 		kill(launchd_wsp, SIGKILL);
364 		sleep(3);
365 		launchd_wsp = 0;
366 	}
367 
368 	while (launchd_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) {
369 		sleep(1);
370 	}
371 }
372 
373 int
basic_fork(void)374 basic_fork(void)
375 {
376 	int wstatus = 0;
377 	pid_t p;
378 
379 	switch ((p = fork())) {
380 	case -1:
381 		launchd_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m");
382 		return p;
383 	case 0:
384 		return p;
385 	default:
386 		do {
387 			(void)waitpid(p, &wstatus, 0);
388 		} while(!WIFEXITED(wstatus));
389 
390 		fprintf(stdout, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus));
391 
392 		return 1;
393 	}
394 
395 	return -1;
396 }
397 
398 bool
do_pid1_crash_diagnosis_mode2(const char * msg)399 do_pid1_crash_diagnosis_mode2(const char *msg)
400 {
401 	if (basic_fork() == 0) {
402 		/* Neuter our bootstrap port so that the shell doesn't try talking to us
403 		 * while we're blocked waiting on it.
404 		 */
405 		if (launchd_console) {
406 			fflush(launchd_console);
407 		}
408 
409 		task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL);
410 		if (basic_fork() != 0) {
411 			if (launchd_console) {
412 				fflush(launchd_console);
413 			}
414 
415 			return true;
416 		}
417 	} else {
418 		return true;
419 	}
420 
421 	int fd;
422 	revoke(_PATH_CONSOLE);
423 	if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) {
424 		DEBUG_EXIT(2);
425 	}
426 	if (login_tty(fd) == -1) {
427 		DEBUG_EXIT(3);
428 	}
429 
430 	setenv("TERM", "vt100", 1);
431 	fprintf(stdout, "\n");
432 	fprintf(stdout, "Entering launchd PID 1 debugging mode...\n");
433 	fprintf(stdout, "The PID 1 launchd has crashed %s.\n", msg);
434 	fprintf(stdout, "It has fork(2)ed itself for debugging.\n");
435 	fprintf(stdout, "To debug the crashing thread of PID 1:\n");
436 	fprintf(stdout, "    gdb attach %d\n", getppid());
437 	fprintf(stdout, "To exit this shell and shut down:\n");
438 	fprintf(stdout, "    kill -9 1\n");
439 	fprintf(stdout, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE);
440 	fprintf(stdout, "\n");
441 	fflush(stdout);
442 
443 	execl(_PATH_BSHELL, "-sh", NULL);
444 	syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL);
445 	DEBUG_EXIT(EXIT_FAILURE);
446 }
447 
448 void
fatal_signal_handler(int sig,siginfo_t * si,void * uap)449 fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
450 {
451 	const char *doom_why = "at instruction";
452 	char msg[128];
453 #if 0
454 	char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL };
455 	pid_t sample_p;
456 	int wstatus;
457 #endif
458 
459 	crash_addr = si->si_addr;
460 	crash_pid = si->si_pid;
461 #if 0
462 	setenv("XPC_SERVICES_UNAVAILABLE", "1", 0);
463 	unlink(PID1_CRASH_LOGFILE);
464 
465 	switch ((sample_p = vfork())) {
466 	case 0:
467 		execve(sample_args[0], sample_args, environ);
468 		DEBUG_EXIT(EXIT_FAILURE);
469 		break;
470 	default:
471 		waitpid(sample_p, &wstatus, 0);
472 		break;
473 	case -1:
474 		break;
475 	}
476 #endif
477 	switch (sig) {
478 	default:
479 	case 0:
480 		break;
481 	case SIGBUS:
482 	case SIGSEGV:
483 		doom_why = "trying to read/write";
484 	case SIGILL:
485 	case SIGFPE:
486 	case SIGTRAP:
487 		snprintf(msg, sizeof(msg), "%s: %p (%s sent by PID %u)", doom_why, crash_addr, strsignal(sig), crash_pid);
488 		sync();
489 		do_pid1_crash_diagnosis_mode(msg);
490 		sleep(3);
491 		reboot(0);
492 		break;
493 	}
494 }
495 
496 void
pid1_magic_init(void)497 pid1_magic_init(void)
498 {
499 	launchd_label = "com.apple.launchd";
500 	launchd_username = "system";
501 
502 	_launchd_database_dir = LAUNCHD_DB_PREFIX "/com.apple.launchd";
503 	_launchd_log_dir = LAUNCHD_LOG_PREFIX "/com.apple.launchd";
504 
505 	(void)posix_assumes_zero(setsid());
506 	(void)posix_assumes_zero(chdir("/"));
507 	(void)posix_assumes_zero(setlogin("root"));
508 
509 #if !TARGET_OS_EMBEDDED
510 	auditinfo_addr_t auinfo = {
511 		.ai_termid = {
512 			.at_type = AU_IPv4
513 		},
514 		.ai_asid = AU_ASSIGN_ASID,
515 		.ai_auid = AU_DEFAUDITID,
516 		.ai_flags = AU_SESSION_FLAG_IS_INITIAL,
517 	};
518 
519 	if (setaudit_addr(&auinfo, sizeof(auinfo)) == -1) {
520 		launchd_syslog(LOG_WARNING | LOG_CONSOLE, "Could not set audit session: %d: %s.", errno, strerror(errno));
521 		DEBUG_EXIT(EXIT_FAILURE);
522 	}
523 
524 	launchd_audit_session = auinfo.ai_asid;
525 	launchd_syslog(LOG_DEBUG, "Audit Session ID: %i", launchd_audit_session);
526 
527 	launchd_audit_port = _audit_session_self();
528 #endif // !TARGET_OS_EMBEDDED
529 }
530 
531 char *
launchd_copy_persistent_store(int type,const char * file)532 launchd_copy_persistent_store(int type, const char *file)
533 {
534 	char *result = NULL;
535 	if (!file) {
536 		file = "";
537 	}
538 
539 	switch (type) {
540 	case LAUNCHD_PERSISTENT_STORE_DB:
541 		(void)asprintf(&result, "%s/%s", _launchd_database_dir, file);
542 		break;
543 	case LAUNCHD_PERSISTENT_STORE_LOGS:
544 		(void)asprintf(&result, "%s/%s", _launchd_log_dir, file);
545 		break;
546 	default:
547 		break;
548 	}
549 
550 	return result;
551 }
552 
553 int
_fd(int fd)554 _fd(int fd)
555 {
556 	if (fd >= 0) {
557 		(void)posix_assumes_zero(fcntl(fd, F_SETFD, FD_CLOEXEC));
558 	}
559 	return fd;
560 }
561 
562 void
launchd_shutdown(void)563 launchd_shutdown(void)
564 {
565 	int64_t now;
566 
567 	if (launchd_shutting_down) {
568 		return;
569 	}
570 
571 	runtime_ktrace0(RTKT_LAUNCHD_EXITING);
572 
573 	launchd_shutting_down = true;
574 	launchd_log_push();
575 
576 	now = runtime_get_wall_time();
577 
578 	const char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for ";
579 	launchd_syslog(LOG_CRIT, "%s%s began", term_who, pid1_magic ? "" : launchd_username);
580 
581 	os_assert(jobmgr_shutdown(root_jobmgr) != NULL);
582 
583 #if HAVE_LIBAUDITD
584 	if (pid1_magic) {
585 		(void)os_assumes_zero(audit_quick_stop());
586 	}
587 #endif
588 }
589 
590 void
launchd_SessionCreate(void)591 launchd_SessionCreate(void)
592 {
593 #if !TARGET_OS_EMBEDDED
594 	auditinfo_addr_t auinfo = {
595 		.ai_termid = { .at_type = AU_IPv4 },
596 		.ai_asid = AU_ASSIGN_ASID,
597 		.ai_auid = getuid(),
598 		.ai_flags = 0,
599 	};
600 	if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
601 		char session[16];
602 		snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
603 		setenv("SECURITYSESSIONID", session, 1);
604 	} else {
605 		launchd_syslog(LOG_WARNING, "Could not set audit session: %d: %s.", errno, strerror(errno));
606 	}
607 #endif // !TARGET_OS_EMBEDDED
608 }
609 
610 void
testfd_or_openfd(int fd,const char * path,int flags)611 testfd_or_openfd(int fd, const char *path, int flags)
612 {
613 	int tmpfd;
614 
615 	if (-1 != (tmpfd = dup(fd))) {
616 		(void)posix_assumes_zero(runtime_close(tmpfd));
617 	} else {
618 		if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) {
619 			launchd_syslog(LOG_ERR, "open(\"%s\", ...): %m", path);
620 		} else if (tmpfd != fd) {
621 			(void)posix_assumes_zero(dup2(tmpfd, fd));
622 			(void)posix_assumes_zero(runtime_close(tmpfd));
623 		}
624 	}
625 }
626 
627 bool
get_network_state(void)628 get_network_state(void)
629 {
630 	struct ifaddrs *ifa, *ifai;
631 	bool up = false;
632 	int r;
633 
634 	/* Workaround 4978696: getifaddrs() reports false ENOMEM */
635 	while ((r = getifaddrs(&ifa)) == -1 && errno == ENOMEM) {
636 		launchd_syslog(LOG_DEBUG, "Worked around bug: 4978696");
637 		(void)posix_assumes_zero(sched_yield());
638 	}
639 
640 	if (posix_assumes_zero(r) == -1) {
641 		return network_up;
642 	}
643 
644 	for (ifai = ifa; ifai; ifai = ifai->ifa_next) {
645 		if (!(ifai->ifa_flags & IFF_UP)) {
646 			continue;
647 		}
648 		if (ifai->ifa_flags & IFF_LOOPBACK) {
649 			continue;
650 		}
651 		if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) {
652 			continue;
653 		}
654 		up = true;
655 		break;
656 	}
657 
658 	freeifaddrs(ifa);
659 
660 	return up;
661 }
662 
663 void
monitor_networking_state(void)664 monitor_networking_state(void)
665 {
666 	int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT));
667 	struct kev_request kev_req;
668 
669 	network_up = get_network_state();
670 
671 	if (pfs == -1) {
672 		(void)os_assumes_zero(errno);
673 		return;
674 	}
675 
676 	memset(&kev_req, 0, sizeof(kev_req));
677 	kev_req.vendor_code = KEV_VENDOR_APPLE;
678 	kev_req.kev_class = KEV_NETWORK_CLASS;
679 
680 	if (posix_assumes_zero(ioctl(pfs, SIOCSKEVFILT, &kev_req)) == -1) {
681 		runtime_close(pfs);
682 		return;
683 	}
684 
685 	(void)posix_assumes_zero(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback));
686 }
687 
688 void
pfsystem_callback(void * obj,struct kevent * kev)689 pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev)
690 {
691 	bool new_networking_state;
692 	char buf[1024];
693 
694 	(void)posix_assumes_zero(read((int)kev->ident, &buf, sizeof(buf)));
695 
696 	new_networking_state = get_network_state();
697 
698 	if (new_networking_state != network_up) {
699 		network_up = new_networking_state;
700 		jobmgr_dispatch_all_semaphores(root_jobmgr);
701 	}
702 }
703 
704 
705 
706