Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/openlaunchd
Path: blob/master/launchd/launchd.c
374 views
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
#include "config.h"
22
#include "launchd.h"
23
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>
38
#include <sys/kern_event.h>
39
#include <sys/reboot.h>
40
#include <sys/socket.h>
41
#include <sys/syscall.h>
42
#include <net/if.h>
43
#include <netinet/in.h>
44
#include <netinet/in_var.h>
45
#include <netinet6/nd6.h>
46
#include <ifaddrs.h>
47
#include <unistd.h>
48
#include <signal.h>
49
#include <errno.h>
50
#include <libgen.h>
51
#include <stdio.h>
52
#include <stdlib.h>
53
#include <stdarg.h>
54
#include <stdbool.h>
55
#include <paths.h>
56
#include <pwd.h>
57
#include <grp.h>
58
#include <ttyent.h>
59
#include <dlfcn.h>
60
#include <dirent.h>
61
#include <string.h>
62
#include <setjmp.h>
63
#include <spawn.h>
64
#include <sched.h>
65
#include <pthread.h>
66
#include <util.h>
67
#include <os/assumes.h>
68
69
#if HAVE_LIBAUDITD
70
#include <bsm/auditd_lib.h>
71
#include <bsm/audit_session.h>
72
#endif
73
74
#include "bootstrap.h"
75
#include "vproc.h"
76
#include "vproc_priv.h"
77
#include "vproc_internal.h"
78
#include "launch.h"
79
#include "launch_internal.h"
80
81
#include "runtime.h"
82
#include "core.h"
83
#include "ipc.h"
84
85
#define LAUNCHD_CONF ".launchd.conf"
86
87
extern char **environ;
88
89
static void pfsystem_callback(void *, struct kevent *);
90
91
static kq_callback kqpfsystem_callback = pfsystem_callback;
92
93
static void pid1_magic_init(void);
94
95
static void testfd_or_openfd(int fd, const char *path, int flags);
96
static bool get_network_state(void);
97
static void monitor_networking_state(void);
98
static void fatal_signal_handler(int sig, siginfo_t *si, void *uap);
99
static void handle_pid1_crashes_separately(void);
100
static void do_pid1_crash_diagnosis_mode(const char *msg);
101
static int basic_fork(void);
102
static bool do_pid1_crash_diagnosis_mode2(const char *msg);
103
104
static void *update_thread(void *nothing);
105
106
static void *crash_addr;
107
static pid_t crash_pid;
108
109
char *_launchd_database_dir;
110
char *_launchd_log_dir;
111
112
bool launchd_shutting_down;
113
bool network_up;
114
uid_t launchd_uid;
115
FILE *launchd_console = NULL;
116
int32_t launchd_sync_frequency = 30;
117
118
int
119
main(int argc, char *const *argv)
120
{
121
bool sflag = false;
122
int ch;
123
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
*/
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);
134
135
if (launchd_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
} else if (launchd_malloc_log_stacks) {
145
if (!getenv("MallocStackLogging")) {
146
setenv("MallocStackLogging", "1", 1);
147
execv(argv[0], argv);
148
} else {
149
unsetenv("MallocStackLogging");
150
}
151
}
152
153
while ((ch = getopt(argc, argv, "s")) != -1) {
154
switch (ch) {
155
case 's': sflag = true; break; /* single user */
156
case '?': /* we should do something with the global optopt variable here */
157
default:
158
fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname());
159
break;
160
}
161
}
162
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);
166
}
167
168
launchd_runtime_init();
169
170
if (NULL == getenv("PATH")) {
171
setenv("PATH", _PATH_STDPATH, 1);
172
}
173
174
if (pid1_magic) {
175
pid1_magic_init();
176
177
int cfd = -1;
178
if ((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) {
179
_fd(cfd);
180
if (!(launchd_console = fdopen(cfd, "w"))) {
181
(void)close(cfd);
182
}
183
}
184
185
char *extra = "";
186
if (launchd_osinstaller) {
187
extra = " in the OS Installer";
188
} else if (sflag) {
189
extra = " in single-user mode";
190
}
191
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. ***");
195
}
196
197
if (launchd_verbose_boot) {
198
launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***");
199
}
200
201
if (launchd_shutdown_debugging) {
202
launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***");
203
}
204
205
if (launchd_log_shutdown) {
206
launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown logging is enabled. ***");
207
}
208
209
if (launchd_log_perf) {
210
launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Performance logging is enabled. ***");
211
}
212
213
if (launchd_log_debug) {
214
launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Debug logging is enabled. ***");
215
}
216
217
handle_pid1_crashes_separately();
218
219
/* Start the update thread.
220
*
221
* <rdar://problem/5039559&6153301>
222
*/
223
pthread_t t = NULL;
224
(void)os_assumes_zero(pthread_create(&t, NULL, update_thread, NULL));
225
(void)os_assumes_zero(pthread_detach(t));
226
227
/* PID 1 doesn't have a flat namespace. */
228
launchd_flat_mach_namespace = false;
229
fflush(launchd_console);
230
} else {
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";
235
}
236
237
struct passwd *pwent = getpwuid(launchd_uid);
238
if (pwent) {
239
launchd_username = strdup(pwent->pw_name);
240
} else {
241
launchd_username = "(unknown)";
242
}
243
244
if (asprintf(&_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) {
245
_launchd_database_dir = "";
246
}
247
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);
271
}
272
273
monitor_networking_state();
274
jobmgr_init(sflag);
275
276
launchd_runtime_init2();
277
launchd_runtime();
278
}
279
280
void
281
handle_pid1_crashes_separately(void)
282
{
283
struct sigaction fsa;
284
285
fsa.sa_sigaction = fatal_signal_handler;
286
fsa.sa_flags = SA_SIGINFO;
287
sigemptyset(&fsa.sa_mask);
288
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));
295
}
296
297
void *
298
update_thread(void *nothing __attribute__((unused)))
299
{
300
(void)posix_assumes_zero(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE));
301
302
while (launchd_sync_frequency) {
303
sync();
304
sleep(launchd_sync_frequency);
305
}
306
307
launchd_syslog(LOG_DEBUG, "Update thread exiting.");
308
return NULL;
309
}
310
311
#define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
312
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;
317
318
void
319
do_pid1_crash_diagnosis_mode(const char *msg)
320
{
321
if (launchd_wsp) {
322
kill(launchd_wsp, SIGKILL);
323
sleep(3);
324
launchd_wsp = 0;
325
}
326
327
while (launchd_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) {
328
sleep(1);
329
}
330
}
331
332
int
333
basic_fork(void)
334
{
335
int wstatus = 0;
336
pid_t p;
337
338
switch ((p = fork())) {
339
case -1:
340
launchd_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m");
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;
352
}
353
354
return -1;
355
}
356
357
bool
358
do_pid1_crash_diagnosis_mode2(const char *msg)
359
{
360
if (basic_fork() == 0) {
361
/* Neuter our bootstrap port so that the shell doesn't try talking to us
362
* while we're blocked waiting on it.
363
*/
364
if (launchd_console) {
365
fflush(launchd_console);
366
}
367
368
task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL);
369
if (basic_fork() != 0) {
370
if (launchd_console) {
371
fflush(launchd_console);
372
}
373
374
return true;
375
}
376
} else {
377
return true;
378
}
379
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
}
388
389
setenv("TERM", "vt100", 1);
390
fprintf(stdout, "\n");
391
fprintf(stdout, "Entering launchd PID 1 debugging mode...\n");
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");
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);
401
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
407
void
408
fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
409
{
410
const char *doom_why = "at instruction";
411
char msg[128];
412
#if 0
413
char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL };
414
pid_t sample_p;
415
int wstatus;
416
#endif
417
418
crash_addr = si->si_addr;
419
crash_pid = si->si_pid;
420
#if 0
421
setenv("XPC_SERVICES_UNAVAILABLE", "1", 0);
422
unlink(PID1_CRASH_LOGFILE);
423
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;
434
}
435
#endif
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:
445
case SIGTRAP:
446
snprintf(msg, sizeof(msg), "%s: %p (%s sent by PID %u)", doom_why, crash_addr, strsignal(sig), crash_pid);
447
sync();
448
do_pid1_crash_diagnosis_mode(msg);
449
sleep(3);
450
reboot(0);
451
break;
452
}
453
}
454
455
void
456
pid1_magic_init(void)
457
{
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"));
467
468
#if !TARGET_OS_EMBEDDED
469
auditinfo_addr_t auinfo = {
470
.ai_termid = {
471
.at_type = AU_IPv4
472
},
473
.ai_asid = AU_ASSIGN_ASID,
474
.ai_auid = AU_DEFAUDITID,
475
.ai_flags = AU_SESSION_FLAG_IS_INITIAL,
476
};
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));
480
_exit(EXIT_FAILURE);
481
}
482
483
launchd_audit_session = auinfo.ai_asid;
484
launchd_syslog(LOG_DEBUG, "Audit Session ID: %i", launchd_audit_session);
485
486
launchd_audit_port = _audit_session_self();
487
#endif // !TARGET_OS_EMBEDDED
488
}
489
490
char *
491
launchd_copy_persistent_store(int type, const char *file)
492
{
493
char *result = NULL;
494
if (!file) {
495
file = "";
496
}
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;
507
}
508
509
return result;
510
}
511
512
int
513
_fd(int fd)
514
{
515
if (fd >= 0) {
516
(void)posix_assumes_zero(fcntl(fd, F_SETFD, 1));
517
}
518
return fd;
519
}
520
521
void
522
launchd_shutdown(void)
523
{
524
int64_t now;
525
526
if (launchd_shutting_down) {
527
return;
528
}
529
530
runtime_ktrace0(RTKT_LAUNCHD_EXITING);
531
532
launchd_shutting_down = true;
533
launchd_log_push();
534
535
now = runtime_get_wall_time();
536
537
char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for ";
538
launchd_syslog(LOG_INFO, "%s%s began", term_who, pid1_magic ? "" : launchd_username);
539
540
os_assert(jobmgr_shutdown(root_jobmgr) != NULL);
541
542
#if HAVE_LIBAUDITD
543
if (pid1_magic) {
544
(void)os_assumes_zero(audit_quick_stop());
545
}
546
#endif
547
}
548
549
void
550
launchd_SessionCreate(void)
551
{
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
};
559
if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
560
char session[16];
561
snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
562
setenv("SECURITYSESSIONID", session, 1);
563
} else {
564
launchd_syslog(LOG_WARNING, "Could not set audit session: %d: %s.", errno, strerror(errno));
565
}
566
#endif // !TARGET_OS_EMBEDDED
567
}
568
569
void
570
testfd_or_openfd(int fd, const char *path, int flags)
571
{
572
int tmpfd;
573
574
if (-1 != (tmpfd = dup(fd))) {
575
(void)posix_assumes_zero(runtime_close(tmpfd));
576
} else {
577
if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) {
578
launchd_syslog(LOG_ERR, "open(\"%s\", ...): %m", path);
579
} else if (tmpfd != fd) {
580
(void)posix_assumes_zero(dup2(tmpfd, fd));
581
(void)posix_assumes_zero(runtime_close(tmpfd));
582
}
583
}
584
}
585
586
bool
587
get_network_state(void)
588
{
589
struct ifaddrs *ifa, *ifai;
590
bool up = false;
591
int r;
592
593
/* Workaround 4978696: getifaddrs() reports false ENOMEM */
594
while ((r = getifaddrs(&ifa)) == -1 && errno == ENOMEM) {
595
launchd_syslog(LOG_DEBUG, "Worked around bug: 4978696");
596
(void)posix_assumes_zero(sched_yield());
597
}
598
599
if (posix_assumes_zero(r) == -1) {
600
return network_up;
601
}
602
603
for (ifai = ifa; ifai; ifai = ifai->ifa_next) {
604
if (!(ifai->ifa_flags & IFF_UP)) {
605
continue;
606
}
607
if (ifai->ifa_flags & IFF_LOOPBACK) {
608
continue;
609
}
610
if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) {
611
continue;
612
}
613
up = true;
614
break;
615
}
616
617
freeifaddrs(ifa);
618
619
return up;
620
}
621
622
void
623
monitor_networking_state(void)
624
{
625
int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT));
626
struct kev_request kev_req;
627
628
network_up = get_network_state();
629
630
if (pfs == -1) {
631
(void)os_assumes_zero(errno);
632
return;
633
}
634
635
memset(&kev_req, 0, sizeof(kev_req));
636
kev_req.vendor_code = KEV_VENDOR_APPLE;
637
kev_req.kev_class = KEV_NETWORK_CLASS;
638
639
if (posix_assumes_zero(ioctl(pfs, SIOCSKEVFILT, &kev_req)) == -1) {
640
runtime_close(pfs);
641
return;
642
}
643
644
(void)posix_assumes_zero(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback));
645
}
646
647
void
648
pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev)
649
{
650
bool new_networking_state;
651
char buf[1024];
652
653
(void)posix_assumes_zero(read((int)kev->ident, &buf, sizeof(buf)));
654
655
new_networking_state = get_network_state();
656
657
if (new_networking_state != network_up) {
658
network_up = new_networking_state;
659
jobmgr_dispatch_all_semaphores(root_jobmgr);
660
}
661
}
662
663