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