]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchd.c
launchd-258.18.tar.gz
[apple/launchd.git] / launchd / src / launchd.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
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
9 *
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
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 static const char *const __rcs_file_version__ = "$Revision: 23506 $";
22
23 #include "config.h"
24 #include "launchd.h"
25
26 #if HAVE_SECURITY
27 #include <Security/Authorization.h>
28 #include <Security/AuthorizationTags.h>
29 #include <Security/AuthSession.h>
30 #endif
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>
45 #include <sys/kern_event.h>
46 #include <sys/reboot.h>
47 #include <net/if.h>
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50 #include <netinet6/nd6.h>
51 #include <ifaddrs.h>
52 #include <unistd.h>
53 #include <signal.h>
54 #include <errno.h>
55 #include <libgen.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <stdarg.h>
59 #include <stdbool.h>
60 #include <paths.h>
61 #include <pwd.h>
62 #include <grp.h>
63 #include <ttyent.h>
64 #include <dlfcn.h>
65 #include <dirent.h>
66 #include <string.h>
67 #include <setjmp.h>
68 #include <spawn.h>
69 #include <sched.h>
70
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"
77 #include "launchd_core_logic.h"
78 #include "launchd_unix_ipc.h"
79
80 #define LAUNCHD_CONF ".launchd.conf"
81 #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security"
82 #define SHUTDOWN_LOG_DIR "/var/log/shutdown"
83
84
85 extern char **environ;
86
87 static void pfsystem_callback(void *, struct kevent *);
88
89 static kq_callback kqpfsystem_callback = pfsystem_callback;
90
91 static void pid1_magic_init(void);
92
93 static void testfd_or_openfd(int fd, const char *path, int flags);
94 static bool get_network_state(void);
95 static void monitor_networking_state(void);
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);
99
100 static bool re_exec_in_single_user_mode = false;
101 static void *crash_addr;
102 static pid_t crash_pid;
103
104 static bool shutdown_in_progress = false;
105 bool debug_shutdown_hangs = false;
106 bool network_up = false;
107
108 int
109 main(int argc, char *const *argv)
110 {
111 bool sflag = false;
112 int ch;
113
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);
117
118 while ((ch = getopt(argc, argv, "s")) != -1) {
119 switch (ch) {
120 case 's': sflag = true; break; /* single user */
121 case '?': /* we should do something with the global optopt variable here */
122 default:
123 fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname());
124 break;
125 }
126 }
127
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);
131 }
132
133 launchd_runtime_init();
134
135 if (NULL == getenv("PATH")) {
136 setenv("PATH", _PATH_STDPATH, 1);
137 }
138
139 if (getpid() == 1) {
140 pid1_magic_init();
141 } else {
142 ipc_server_init();
143 }
144
145 monitor_networking_state();
146
147 if (getpid() == 1) {
148 handle_pid1_crashes_separately();
149 }
150
151 jobmgr_init(sflag);
152
153 launchd_runtime_init2();
154
155 launchd_runtime();
156 }
157
158 void
159 handle_pid1_crashes_separately(void)
160 {
161 struct sigaction fsa;
162
163 fsa.sa_sigaction = fatal_signal_handler;
164 fsa.sa_flags = SA_SIGINFO;
165 sigemptyset(&fsa.sa_mask);
166
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);
171 }
172
173 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
174
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;
179
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;
187
188 crash_addr = si->si_addr;
189 crash_pid = si->si_pid;
190
191 unlink(PID1_CRASH_LOGFILE);
192
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;
203 }
204
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;
219 }
220 }
221
222 void
223 pid1_magic_init(void)
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);
229 }
230
231
232 int
233 _fd(int fd)
234 {
235 if (fd >= 0) {
236 launchd_assumes(fcntl(fd, F_SETFD, 1) != -1);
237 }
238 return fd;
239 }
240
241 void
242 prep_shutdown_log_dir(void)
243 {
244 launchd_assumes(mkdir(SHUTDOWN_LOG_DIR, S_IRWXU) != -1 || errno == EEXIST);
245 }
246
247 void
248 launchd_shutdown(void)
249 {
250 struct stat sb;
251
252 if (shutdown_in_progress) {
253 return;
254 }
255
256 shutdown_in_progress = true;
257
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 }
267
268 launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL);
269 }
270
271 void
272 launchd_single_user(void)
273 {
274 runtime_syslog(LOG_NOTICE, "Going to single-user mode");
275
276 re_exec_in_single_user_mode = true;
277
278 launchd_shutdown();
279
280 sleep(3);
281
282 runtime_kill(-1, SIGKILL);
283 }
284
285 void
286 launchd_SessionCreate(void)
287 {
288 #if HAVE_SECURITY
289 OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes);
290 void *seclib;
291
292 if (launchd_assumes((seclib = dlopen(SECURITY_LIB, RTLD_LAZY)) != NULL)) {
293 if (launchd_assumes((sescr = dlsym(seclib, "SessionCreate")) != NULL)) {
294 launchd_assumes(sescr(0, 0) == noErr);
295 }
296 launchd_assumes(dlclose(seclib) != -1);
297 }
298 #endif
299 }
300
301 void
302 testfd_or_openfd(int fd, const char *path, int flags)
303 {
304 int tmpfd;
305
306 if (-1 != (tmpfd = dup(fd))) {
307 launchd_assumes(runtime_close(tmpfd) == 0);
308 } else {
309 if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) {
310 runtime_syslog(LOG_ERR, "open(\"%s\", ...): %m", path);
311 } else if (tmpfd != fd) {
312 launchd_assumes(dup2(tmpfd, fd) != -1);
313 launchd_assumes(runtime_close(tmpfd) == 0);
314 }
315 }
316 }
317
318 bool
319 get_network_state(void)
320 {
321 struct ifaddrs *ifa, *ifai;
322 bool up = false;
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 }
330
331 if (!launchd_assumes(r != -1)) {
332 return network_up;
333 }
334
335 for (ifai = ifa; ifai; ifai = ifai->ifa_next) {
336 if (!(ifai->ifa_flags & IFF_UP)) {
337 continue;
338 }
339 if (ifai->ifa_flags & IFF_LOOPBACK) {
340 continue;
341 }
342 if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) {
343 continue;
344 }
345 up = true;
346 break;
347 }
348
349 freeifaddrs(ifa);
350
351 return up;
352 }
353
354 void
355 monitor_networking_state(void)
356 {
357 int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT));
358 struct kev_request kev_req;
359
360 network_up = get_network_state();
361
362 if (!launchd_assumes(pfs != -1)) {
363 return;
364 }
365
366 memset(&kev_req, 0, sizeof(kev_req));
367 kev_req.vendor_code = KEV_VENDOR_APPLE;
368 kev_req.kev_class = KEV_NETWORK_CLASS;
369
370 if (!launchd_assumes(ioctl(pfs, SIOCSKEVFILT, &kev_req) != -1)) {
371 runtime_close(pfs);
372 return;
373 }
374
375 launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1);
376 }
377
378 void
379 pfsystem_callback(void *obj, struct kevent *kev)
380 {
381 bool new_networking_state;
382 char buf[1024];
383
384 launchd_assumes(read(kev->ident, &buf, sizeof(buf)) != -1);
385
386 new_networking_state = get_network_state();
387
388 if (new_networking_state != network_up) {
389 network_up = new_networking_state;
390 jobmgr_dispatch_all_semaphores(root_jobmgr);
391 }
392 }
393
394 void
395 _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
396 {
397 int saved_errno = errno;
398 char buf[100];
399 const char *file = strrchr(path, '/');
400 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
401
402 if (!file) {
403 file = path;
404 } else {
405 file += 1;
406 }
407
408 if (!rcs_rev_tmp) {
409 strlcpy(buf, rcs_rev, sizeof(buf));
410 } else {
411 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
412 rcs_rev_tmp = strchr(buf, ' ');
413 if (rcs_rev_tmp) {
414 *rcs_rev_tmp = '\0';
415 }
416 }
417
418 runtime_syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
419 }