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