]>
Commit | Line | Data |
---|---|---|
e91b9f68 A |
1 | /* |
2 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. | |
3 | * | |
ed34e3c3 | 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ |
e91b9f68 | 5 | * |
ed34e3c3 A |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. | |
8 | * You may obtain a copy of the License at | |
e91b9f68 | 9 | * |
ed34e3c3 A |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, software | |
13 | * distributed under the License is distributed on an "AS IS" BASIS, | |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | * See the License for the specific language governing permissions and | |
e91b9f68 A |
16 | * limitations under the License. |
17 | * | |
ed34e3c3 | 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ |
e91b9f68 | 19 | */ |
ed34e3c3 | 20 | |
dcace88f | 21 | static 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 | |
88 | extern char **environ; | |
89 | ||
ed34e3c3 | 90 | static void pfsystem_callback(void *, struct kevent *); |
e91b9f68 | 91 | |
ed34e3c3 A |
92 | static kq_callback kqpfsystem_callback = pfsystem_callback; |
93 | ||
5b0a4722 | 94 | static void pid1_magic_init(void); |
e91b9f68 | 95 | |
aa59983a | 96 | static void testfd_or_openfd(int fd, const char *path, int flags); |
ed34e3c3 A |
97 | static bool get_network_state(void); |
98 | static void monitor_networking_state(void); | |
5b0a4722 A |
99 | static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); |
100 | static void handle_pid1_crashes_separately(void); | |
dcace88f | 101 | static void do_pid1_crash_diagnosis_mode(const char *msg); |
ddbbfbc1 | 102 | static int basic_fork(void); |
dcace88f | 103 | static bool do_pid1_crash_diagnosis_mode2(const char *msg); |
ed34e3c3 | 104 | |
ef398931 | 105 | static void *update_thread(void *nothing); |
ef398931 | 106 | |
ddbbfbc1 | 107 | static bool re_exec_in_single_user_mode; |
5b0a4722 A |
108 | static void *crash_addr; |
109 | static pid_t crash_pid; | |
ed34e3c3 | 110 | |
ddbbfbc1 A |
111 | bool shutdown_in_progress; |
112 | bool fake_shutdown_in_progress; | |
113 | bool network_up; | |
114 | char g_username[128] = "__Uninitialized__"; | |
115 | char g_my_label[128] = "__Uninitialized__"; | |
116 | char g_launchd_database_dir[PATH_MAX]; | |
117 | FILE *g_console = NULL; | |
118 | int32_t g_sync_frequency = 30; | |
e91b9f68 | 119 | |
ed34e3c3 A |
120 | int |
121 | main(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 |
256 | void |
257 | handle_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 |
273 | void *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 */ |
290 | static __attribute__((unused)) typeof(sync) *__junk_dyld_trick1 = sync; | |
291 | static __attribute__((unused)) typeof(sleep) *__junk_dyld_trick2 = sleep; | |
292 | static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot; | |
ab36757d | 293 | |
ddbbfbc1 | 294 | void |
dcace88f | 295 | do_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 | ||
308 | int | |
309 | basic_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 | ||
333 | bool | |
dcace88f | 334 | do_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 | 380 | void |
ef398931 | 381 | fatal_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 | 424 | void |
5b0a4722 | 425 | pid1_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 |
455 | char * |
456 | launchd_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 |
481 | int |
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 |
490 | void |
491 | launchd_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 |
527 | void |
528 | launchd_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 |
541 | void |
542 | launchd_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 |
561 | void |
562 | testfd_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 |
578 | bool |
579 | get_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 |
614 | void |
615 | monitor_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 | 638 | void |
ef398931 | 639 | pfsystem_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 | 654 | void |
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 | } |