]>
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 | |
f36da725 | 21 | static const char *const __rcs_file_version__ = "$Revision: 23506 $"; |
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> |
e91b9f68 A |
47 | #include <net/if.h> |
48 | #include <netinet/in.h> | |
49 | #include <netinet/in_var.h> | |
50 | #include <netinet6/nd6.h> | |
ed34e3c3 | 51 | #include <ifaddrs.h> |
e91b9f68 A |
52 | #include <unistd.h> |
53 | #include <signal.h> | |
54 | #include <errno.h> | |
e91b9f68 A |
55 | #include <libgen.h> |
56 | #include <stdio.h> | |
57 | #include <stdlib.h> | |
58 | #include <stdarg.h> | |
59 | #include <stdbool.h> | |
e91b9f68 A |
60 | #include <paths.h> |
61 | #include <pwd.h> | |
62 | #include <grp.h> | |
ed34e3c3 | 63 | #include <ttyent.h> |
e91b9f68 A |
64 | #include <dlfcn.h> |
65 | #include <dirent.h> | |
ed34e3c3 | 66 | #include <string.h> |
5b0a4722 A |
67 | #include <setjmp.h> |
68 | #include <spawn.h> | |
69 | #include <sched.h> | |
e91b9f68 | 70 | |
5b0a4722 A |
71 | #include "libbootstrap_public.h" |
72 | #include "libvproc_public.h" | |
73 | #include "libvproc_internal.h" | |
74 | #include "liblaunch_public.h" | |
75 | ||
76 | #include "launchd_runtime.h" | |
ed34e3c3 A |
77 | #include "launchd_core_logic.h" |
78 | #include "launchd_unix_ipc.h" | |
79 | ||
e91b9f68 | 80 | #define LAUNCHD_CONF ".launchd.conf" |
e91b9f68 | 81 | #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security" |
5b0a4722 A |
82 | #define SHUTDOWN_LOG_DIR "/var/log/shutdown" |
83 | ||
e91b9f68 A |
84 | |
85 | extern char **environ; | |
86 | ||
ed34e3c3 | 87 | static void pfsystem_callback(void *, struct kevent *); |
e91b9f68 | 88 | |
ed34e3c3 A |
89 | static kq_callback kqpfsystem_callback = pfsystem_callback; |
90 | ||
5b0a4722 | 91 | static void pid1_magic_init(void); |
e91b9f68 | 92 | |
aa59983a | 93 | static void testfd_or_openfd(int fd, const char *path, int flags); |
ed34e3c3 A |
94 | static bool get_network_state(void); |
95 | static void monitor_networking_state(void); | |
5b0a4722 A |
96 | static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); |
97 | static void handle_pid1_crashes_separately(void); | |
98 | static void prep_shutdown_log_dir(void); | |
ed34e3c3 | 99 | |
ed34e3c3 | 100 | static bool re_exec_in_single_user_mode = false; |
5b0a4722 A |
101 | static void *crash_addr; |
102 | static pid_t crash_pid; | |
ed34e3c3 | 103 | |
5b0a4722 A |
104 | static bool shutdown_in_progress = false; |
105 | bool debug_shutdown_hangs = false; | |
ed34e3c3 | 106 | bool network_up = false; |
e91b9f68 | 107 | |
ed34e3c3 A |
108 | int |
109 | main(int argc, char *const *argv) | |
e91b9f68 | 110 | { |
5b0a4722 A |
111 | bool sflag = false; |
112 | int ch; | |
fc89531e | 113 | |
5b0a4722 A |
114 | testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY); |
115 | testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY); | |
116 | testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY); | |
e91b9f68 | 117 | |
5b0a4722 | 118 | while ((ch = getopt(argc, argv, "s")) != -1) { |
e91b9f68 | 119 | switch (ch) { |
5b0a4722 | 120 | case 's': sflag = true; break; /* single user */ |
ed34e3c3 | 121 | case '?': /* we should do something with the global optopt variable here */ |
e91b9f68 | 122 | default: |
5b0a4722 | 123 | fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname()); |
e91b9f68 A |
124 | break; |
125 | } | |
126 | } | |
e91b9f68 | 127 | |
5b0a4722 A |
128 | if (getpid() != 1 && getppid() != 1) { |
129 | fprintf(stderr, "%s: This program is not meant to be run directly.\n", getprogname()); | |
130 | exit(EXIT_FAILURE); | |
e91b9f68 A |
131 | } |
132 | ||
5b0a4722 | 133 | launchd_runtime_init(); |
e91b9f68 | 134 | |
5b0a4722 | 135 | if (NULL == getenv("PATH")) { |
ed34e3c3 | 136 | setenv("PATH", _PATH_STDPATH, 1); |
5b0a4722 | 137 | } |
aa59983a | 138 | |
ed34e3c3 | 139 | if (getpid() == 1) { |
5b0a4722 | 140 | pid1_magic_init(); |
ed34e3c3 | 141 | } else { |
5b0a4722 | 142 | ipc_server_init(); |
aa59983a A |
143 | } |
144 | ||
ed34e3c3 | 145 | monitor_networking_state(); |
e91b9f68 | 146 | |
5b0a4722 A |
147 | if (getpid() == 1) { |
148 | handle_pid1_crashes_separately(); | |
e91b9f68 A |
149 | } |
150 | ||
5b0a4722 | 151 | jobmgr_init(sflag); |
e91b9f68 | 152 | |
5b0a4722 | 153 | launchd_runtime_init2(); |
e91b9f68 | 154 | |
5b0a4722 | 155 | launchd_runtime(); |
e91b9f68 | 156 | } |
e91b9f68 | 157 | |
5b0a4722 A |
158 | void |
159 | handle_pid1_crashes_separately(void) | |
e91b9f68 | 160 | { |
5b0a4722 | 161 | struct sigaction fsa; |
ab36757d | 162 | |
5b0a4722 A |
163 | fsa.sa_sigaction = fatal_signal_handler; |
164 | fsa.sa_flags = SA_SIGINFO; | |
165 | sigemptyset(&fsa.sa_mask); | |
e91b9f68 | 166 | |
5b0a4722 A |
167 | launchd_assumes(sigaction(SIGILL, &fsa, NULL) != -1); |
168 | launchd_assumes(sigaction(SIGFPE, &fsa, NULL) != -1); | |
169 | launchd_assumes(sigaction(SIGBUS, &fsa, NULL) != -1); | |
170 | launchd_assumes(sigaction(SIGSEGV, &fsa, NULL) != -1); | |
e91b9f68 A |
171 | } |
172 | ||
5b0a4722 | 173 | #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash" |
e91b9f68 | 174 | |
5b0a4722 A |
175 | /* This hack forces the dynamic linker to resolve these symbols ASAP */ |
176 | static __attribute__((unused)) typeof(sync) *__junk_dyld_trick1 = sync; | |
177 | static __attribute__((unused)) typeof(sleep) *__junk_dyld_trick2 = sleep; | |
178 | static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot; | |
ab36757d | 179 | |
5b0a4722 A |
180 | void |
181 | fatal_signal_handler(int sig, siginfo_t *si, void *uap) | |
182 | { | |
183 | const char *doom_why = "at instruction"; | |
184 | char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL }; | |
185 | pid_t sample_p; | |
186 | int wstatus; | |
e91b9f68 | 187 | |
5b0a4722 A |
188 | crash_addr = si->si_addr; |
189 | crash_pid = si->si_pid; | |
ab36757d | 190 | |
5b0a4722 | 191 | unlink(PID1_CRASH_LOGFILE); |
e91b9f68 | 192 | |
5b0a4722 A |
193 | switch ((sample_p = vfork())) { |
194 | case 0: | |
195 | execve(sample_args[0], sample_args, environ); | |
196 | _exit(EXIT_FAILURE); | |
197 | break; | |
198 | default: | |
199 | waitpid(sample_p, &wstatus, 0); | |
200 | break; | |
201 | case -1: | |
202 | break; | |
e91b9f68 A |
203 | } |
204 | ||
5b0a4722 A |
205 | switch (sig) { |
206 | default: | |
207 | case 0: | |
208 | break; | |
209 | case SIGBUS: | |
210 | case SIGSEGV: | |
211 | doom_why = "trying to read/write"; | |
212 | case SIGILL: | |
213 | case SIGFPE: | |
214 | runtime_syslog(LOG_EMERG, "We crashed %s: %p (sent by PID %u)", doom_why, crash_addr, crash_pid); | |
215 | sync(); | |
216 | sleep(3); | |
217 | reboot(0); | |
218 | break; | |
e91b9f68 | 219 | } |
ed34e3c3 | 220 | } |
e91b9f68 | 221 | |
ed34e3c3 | 222 | void |
5b0a4722 | 223 | pid1_magic_init(void) |
ed34e3c3 A |
224 | { |
225 | launchd_assumes(setsid() != -1); | |
226 | launchd_assumes(chdir("/") != -1); | |
227 | launchd_assumes(setlogin("root") != -1); | |
228 | launchd_assumes(mount("fdesc", "/dev", MNT_UNION, NULL) != -1); | |
e91b9f68 A |
229 | } |
230 | ||
e91b9f68 | 231 | |
ed34e3c3 A |
232 | int |
233 | _fd(int fd) | |
e91b9f68 | 234 | { |
5b0a4722 | 235 | if (fd >= 0) { |
ed34e3c3 | 236 | launchd_assumes(fcntl(fd, F_SETFD, 1) != -1); |
5b0a4722 | 237 | } |
ed34e3c3 A |
238 | return fd; |
239 | } | |
fc89531e | 240 | |
ed34e3c3 | 241 | void |
5b0a4722 | 242 | prep_shutdown_log_dir(void) |
ed34e3c3 | 243 | { |
5b0a4722 | 244 | launchd_assumes(mkdir(SHUTDOWN_LOG_DIR, S_IRWXU) != -1 || errno == EEXIST); |
e91b9f68 A |
245 | } |
246 | ||
ed34e3c3 A |
247 | void |
248 | launchd_shutdown(void) | |
e91b9f68 | 249 | { |
5b0a4722 A |
250 | struct stat sb; |
251 | ||
252 | if (shutdown_in_progress) { | |
253 | return; | |
254 | } | |
ed34e3c3 | 255 | |
5b0a4722 | 256 | shutdown_in_progress = true; |
fc89531e | 257 | |
5b0a4722 A |
258 | if (getpid() == 1 && stat("/var/db/debugShutdownHangs", &sb) != -1) { |
259 | /* | |
260 | * When this changes to a more sustainable API, update this: | |
261 | * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown | |
262 | */ | |
263 | runtime_setlogmask(LOG_UPTO(LOG_DEBUG)); | |
264 | prep_shutdown_log_dir(); | |
265 | debug_shutdown_hangs = true; | |
266 | } | |
fc89531e | 267 | |
5b0a4722 | 268 | launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL); |
e91b9f68 A |
269 | } |
270 | ||
ed34e3c3 A |
271 | void |
272 | launchd_single_user(void) | |
e91b9f68 | 273 | { |
5b0a4722 | 274 | runtime_syslog(LOG_NOTICE, "Going to single-user mode"); |
fc89531e | 275 | |
ed34e3c3 | 276 | re_exec_in_single_user_mode = true; |
e91b9f68 | 277 | |
5b0a4722 | 278 | launchd_shutdown(); |
e91b9f68 | 279 | |
5b0a4722 | 280 | sleep(3); |
e91b9f68 | 281 | |
5b0a4722 | 282 | runtime_kill(-1, SIGKILL); |
e91b9f68 A |
283 | } |
284 | ||
ed34e3c3 A |
285 | void |
286 | launchd_SessionCreate(void) | |
e91b9f68 | 287 | { |
f36da725 | 288 | #if HAVE_SECURITY |
ed34e3c3 A |
289 | OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes); |
290 | void *seclib; | |
e91b9f68 | 291 | |
ed34e3c3 | 292 | if (launchd_assumes((seclib = dlopen(SECURITY_LIB, RTLD_LAZY)) != NULL)) { |
5b0a4722 | 293 | if (launchd_assumes((sescr = dlsym(seclib, "SessionCreate")) != NULL)) { |
ed34e3c3 | 294 | launchd_assumes(sescr(0, 0) == noErr); |
5b0a4722 | 295 | } |
ed34e3c3 | 296 | launchd_assumes(dlclose(seclib) != -1); |
e91b9f68 | 297 | } |
f36da725 | 298 | #endif |
e91b9f68 A |
299 | } |
300 | ||
ed34e3c3 A |
301 | void |
302 | testfd_or_openfd(int fd, const char *path, int flags) | |
e91b9f68 | 303 | { |
ed34e3c3 | 304 | int tmpfd; |
e91b9f68 | 305 | |
ed34e3c3 | 306 | if (-1 != (tmpfd = dup(fd))) { |
5b0a4722 | 307 | launchd_assumes(runtime_close(tmpfd) == 0); |
ed34e3c3 | 308 | } else { |
5b0a4722 A |
309 | if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) { |
310 | runtime_syslog(LOG_ERR, "open(\"%s\", ...): %m", path); | |
ed34e3c3 A |
311 | } else if (tmpfd != fd) { |
312 | launchd_assumes(dup2(tmpfd, fd) != -1); | |
5b0a4722 | 313 | launchd_assumes(runtime_close(tmpfd) == 0); |
e91b9f68 | 314 | } |
e91b9f68 A |
315 | } |
316 | } | |
317 | ||
ed34e3c3 A |
318 | bool |
319 | get_network_state(void) | |
320 | { | |
321 | struct ifaddrs *ifa, *ifai; | |
322 | bool up = false; | |
5b0a4722 A |
323 | int r; |
324 | ||
325 | /* Workaround 4978696: getifaddrs() reports false ENOMEM */ | |
326 | while ((r = getifaddrs(&ifa)) == -1 && errno == ENOMEM) { | |
327 | runtime_syslog(LOG_DEBUG, "Worked around bug: 4978696"); | |
328 | launchd_assumes(sched_yield() != -1); | |
329 | } | |
ed34e3c3 | 330 | |
5b0a4722 | 331 | if (!launchd_assumes(r != -1)) { |
ed34e3c3 | 332 | return network_up; |
5b0a4722 | 333 | } |
ed34e3c3 A |
334 | |
335 | for (ifai = ifa; ifai; ifai = ifai->ifa_next) { | |
5b0a4722 | 336 | if (!(ifai->ifa_flags & IFF_UP)) { |
ed34e3c3 | 337 | continue; |
5b0a4722 A |
338 | } |
339 | if (ifai->ifa_flags & IFF_LOOPBACK) { | |
ed34e3c3 | 340 | continue; |
5b0a4722 A |
341 | } |
342 | if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) { | |
ed34e3c3 | 343 | continue; |
5b0a4722 | 344 | } |
ed34e3c3 | 345 | up = true; |
e91b9f68 A |
346 | break; |
347 | } | |
ed34e3c3 A |
348 | |
349 | freeifaddrs(ifa); | |
350 | ||
351 | return up; | |
e91b9f68 A |
352 | } |
353 | ||
ed34e3c3 A |
354 | void |
355 | monitor_networking_state(void) | |
e91b9f68 | 356 | { |
ed34e3c3 A |
357 | int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT)); |
358 | struct kev_request kev_req; | |
e91b9f68 | 359 | |
ed34e3c3 | 360 | network_up = get_network_state(); |
e91b9f68 | 361 | |
5b0a4722 | 362 | if (!launchd_assumes(pfs != -1)) { |
ed34e3c3 | 363 | return; |
5b0a4722 | 364 | } |
e91b9f68 | 365 | |
ed34e3c3 A |
366 | memset(&kev_req, 0, sizeof(kev_req)); |
367 | kev_req.vendor_code = KEV_VENDOR_APPLE; | |
368 | kev_req.kev_class = KEV_NETWORK_CLASS; | |
e91b9f68 | 369 | |
ed34e3c3 | 370 | if (!launchd_assumes(ioctl(pfs, SIOCSKEVFILT, &kev_req) != -1)) { |
5b0a4722 | 371 | runtime_close(pfs); |
e91b9f68 | 372 | return; |
e91b9f68 A |
373 | } |
374 | ||
ed34e3c3 | 375 | launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1); |
e91b9f68 A |
376 | } |
377 | ||
ed34e3c3 A |
378 | void |
379 | pfsystem_callback(void *obj, struct kevent *kev) | |
e91b9f68 | 380 | { |
ed34e3c3 A |
381 | bool new_networking_state; |
382 | char buf[1024]; | |
e91b9f68 | 383 | |
ed34e3c3 | 384 | launchd_assumes(read(kev->ident, &buf, sizeof(buf)) != -1); |
e91b9f68 | 385 | |
ed34e3c3 | 386 | new_networking_state = get_network_state(); |
e91b9f68 | 387 | |
ed34e3c3 A |
388 | if (new_networking_state != network_up) { |
389 | network_up = new_networking_state; | |
5b0a4722 | 390 | jobmgr_dispatch_all_semaphores(root_jobmgr); |
2a27bb34 | 391 | } |
e91b9f68 A |
392 | } |
393 | ||
6a39f10b | 394 | void |
ed34e3c3 | 395 | _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test) |
6a39f10b | 396 | { |
ed34e3c3 A |
397 | int saved_errno = errno; |
398 | char buf[100]; | |
399 | const char *file = strrchr(path, '/'); | |
400 | char *rcs_rev_tmp = strchr(rcs_rev, ' '); | |
6a39f10b | 401 | |
ed34e3c3 A |
402 | if (!file) { |
403 | file = path; | |
6a39f10b | 404 | } else { |
ed34e3c3 | 405 | file += 1; |
e91b9f68 | 406 | } |
aa59983a | 407 | |
ed34e3c3 A |
408 | if (!rcs_rev_tmp) { |
409 | strlcpy(buf, rcs_rev, sizeof(buf)); | |
aa59983a | 410 | } else { |
ed34e3c3 A |
411 | strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf)); |
412 | rcs_rev_tmp = strchr(buf, ' '); | |
5b0a4722 | 413 | if (rcs_rev_tmp) { |
ed34e3c3 | 414 | *rcs_rev_tmp = '\0'; |
2a27bb34 | 415 | } |
2a27bb34 A |
416 | } |
417 | ||
5b0a4722 | 418 | runtime_syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test); |
fc89531e | 419 | } |