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