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