Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/openlaunchd
Path: blob/master/launchctl/launchctl.c
374 views
1
/*
2
* Copyright (c) 2005-2011 Apple 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 "launch.h"
23
#include "launch_priv.h"
24
#include "bootstrap.h"
25
#include "vproc.h"
26
#include "vproc_priv.h"
27
#include "vproc_internal.h"
28
#include "bootstrap_priv.h"
29
#include "launch_internal.h"
30
31
#include <CoreFoundation/CoreFoundation.h>
32
#include <CoreFoundation/CFPriv.h>
33
#include <CoreFoundation/CFLogUtilities.h>
34
#include <TargetConditionals.h>
35
#include <IOKit/IOKitLib.h>
36
#include <NSSystemDirectories.h>
37
#include <mach/mach.h>
38
#include <mach-o/getsect.h>
39
#include <sys/types.h>
40
#include <sys/sysctl.h>
41
#include <sys/time.h>
42
#include <sys/sysctl.h>
43
#include <sys/stat.h>
44
#include <sys/socket.h>
45
#include <sys/un.h>
46
#include <sys/fcntl.h>
47
#include <sys/event.h>
48
#include <sys/resource.h>
49
#include <sys/param.h>
50
#include <sys/mount.h>
51
#include <sys/reboot.h>
52
#include <net/if.h>
53
#include <netinet/in.h>
54
#include <netinet/in_var.h>
55
#include <netinet6/nd6.h>
56
#include <unistd.h>
57
#include <dirent.h>
58
#include <libgen.h>
59
#include <libinfo.h>
60
#include <pwd.h>
61
#include <stdio.h>
62
#include <stdlib.h>
63
#include <pwd.h>
64
#include <grp.h>
65
#include <netdb.h>
66
#include <syslog.h>
67
#include <glob.h>
68
#include <readline/readline.h>
69
#include <readline/history.h>
70
#include <dns_sd.h>
71
#include <paths.h>
72
#include <utmpx.h>
73
#include <bootfiles.h>
74
#include <sysexits.h>
75
#include <util.h>
76
#include <spawn.h>
77
#include <sys/syslimits.h>
78
#include <fnmatch.h>
79
#include <os/assumes.h>
80
#include <dlfcn.h>
81
#if HAVE_SYSTEMSTATS
82
#include <systemstats/systemstats.h>
83
#endif
84
85
#if HAVE_LIBAUDITD
86
#include <bsm/auditd_lib.h>
87
#ifndef AUDITD_PLIST_FILE
88
#define AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist"
89
#endif
90
#endif
91
92
extern char **environ;
93
94
#define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX"
95
#define LAUNCH_ENV_KEEPCONTEXT "LaunchKeepContext"
96
#define LAUNCH_ENV_BOOTSTRAPPINGSYSTEM "LaunchBootstrappingSystem"
97
98
#define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID())
99
#define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf);
100
101
#if TARGET_OS_EMBEDDED
102
#include <sys/kern_memorystatus.h>
103
104
#define XPC_PLIST_CACHE "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib"
105
#define XPC_PLIST_CACHE_KEY "LaunchDaemons"
106
107
#if JETSAM_PRIORITY_REVISION
108
#define READ_JETSAM_DEFAULTS 1
109
#define JETSAM_PROP_DIR "/System/Library/LaunchDaemons"
110
#define JETSAM_PROP_DIR_LENGTH (sizeof(JETSAM_PROP_DIR) - 1)
111
#define JETSAM_PROP_PREFIX "com.apple.jetsamproperties."
112
#define JETSAM_PROP_PREFIX_LENGTH (sizeof(JETSAM_PROP_PREFIX) - 1)
113
#define JETSAM_PROP_SUFFIX ".plist"
114
#define JETSAM_PROP_SUFFIX_LENGTH (sizeof(JETSAM_PROP_SUFFIX) - 1)
115
#endif
116
#endif
117
118
struct load_unload_state {
119
launch_data_t pass1;
120
char *session_type;
121
bool editondisk:1, load:1, forceload:1;
122
};
123
124
static void launchctl_log(int level, const char *fmt, ...);
125
static void launchctl_log_CFString(int level, CFStringRef string);
126
static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
127
static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj);
128
static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr);
129
static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict);
130
static bool launch_data_array_append(launch_data_t a, launch_data_t o);
131
static void insert_event(launch_data_t, const char *, const char *, launch_data_t);
132
static void distill_jobs(launch_data_t);
133
static void distill_config_file(launch_data_t);
134
static void distill_fsevents(launch_data_t);
135
static void sock_dict_cb(launch_data_t what, const char *key, void *context);
136
static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
137
static launch_data_t CF2launch_data(CFTypeRef);
138
static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
139
#if TARGET_OS_EMBEDDED
140
static CFPropertyListRef GetPropertyListFromCache(void);
141
static CFPropertyListRef CreateMyPropertyListFromCachedFile(const char *posixfile);
142
static bool require_jobs_from_cache(void);
143
#endif
144
static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
145
static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL);
146
static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
147
static bool path_goodness_check(const char *path, bool forceload);
148
static void readpath(const char *, struct load_unload_state *);
149
static void readfile(const char *, struct load_unload_state *);
150
static int _fd(int);
151
static int demux_cmd(int argc, char *const argv[]);
152
static void submit_job_pass(launch_data_t jobs);
153
static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
154
static mach_port_t str2bsport(const char *s);
155
static void print_jobs(launch_data_t j, const char *key, void *context);
156
static void print_obj(launch_data_t obj, const char *key, void *context);
157
static bool str2lim(const char *buf, rlim_t *res);
158
static const char *lim2str(rlim_t val, char *buf);
159
static const char *num2name(int n);
160
static ssize_t name2num(const char *n);
161
static void unloadjob(launch_data_t job);
162
static void print_key_value(launch_data_t obj, const char *key, void *context);
163
static void print_launchd_env(launch_data_t obj, const char *key, void *context);
164
static void loopback_setup_ipv4(void);
165
static void loopback_setup_ipv6(void);
166
static pid_t fwexec(const char *const *argv, int *wstatus);
167
static void do_potential_fsck(void);
168
static bool path_check(const char *path);
169
static bool is_safeboot(void);
170
static bool is_netboot(void);
171
static void apply_sysctls_from_file(const char *thefile);
172
static void empty_dir(const char *thedir, struct stat *psb);
173
static int touch_file(const char *path, mode_t m);
174
static void do_sysversion_sysctl(void);
175
static void do_application_firewall_magic(int sfd, launch_data_t thejob);
176
static void preheat_page_cache_hack(void);
177
static void do_bootroot_magic(void);
178
static void do_single_user_mode(bool);
179
static bool do_single_user_mode2(void);
180
static void do_crash_debug_mode(void);
181
static bool do_crash_debug_mode2(void);
182
static void read_launchd_conf(void);
183
static bool job_disabled_logic(launch_data_t obj);
184
static void fix_bogus_file_metadata(void);
185
static void do_file_init(void) __attribute__((constructor));
186
static void setup_system_context(void);
187
static void handle_system_bootstrapper_crashes_separately(void);
188
static void fatal_signal_handler(int sig, siginfo_t *si, void *uap);
189
190
typedef enum {
191
BOOTCACHE_START = 1,
192
BOOTCACHE_TAG,
193
BOOTCACHE_STOP,
194
} BootCache_action_t;
195
196
static void do_BootCache_magic(BootCache_action_t what);
197
198
static int bootstrap_cmd(int argc, char *const argv[]);
199
static int load_and_unload_cmd(int argc, char *const argv[]);
200
//static int reload_cmd(int argc, char *const argv[]);
201
static int start_stop_remove_cmd(int argc, char *const argv[]);
202
static int submit_cmd(int argc, char *const argv[]);
203
static int list_cmd(int argc, char *const argv[]);
204
205
static int setenv_cmd(int argc, char *const argv[]);
206
static int unsetenv_cmd(int argc, char *const argv[]);
207
static int getenv_and_export_cmd(int argc, char *const argv[]);
208
static int wait4debugger_cmd(int argc, char *const argv[]);
209
210
static int limit_cmd(int argc, char *const argv[]);
211
static int stdio_cmd(int argc, char *const argv[]);
212
static int fyi_cmd(int argc, char *const argv[]);
213
static int logupdate_cmd(int argc, char *const argv[]);
214
static int umask_cmd(int argc, char *const argv[]);
215
static int getrusage_cmd(int argc, char *const argv[]);
216
static int bsexec_cmd(int argc, char *const argv[]);
217
static int _bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only);
218
static int bslist_cmd(int argc, char *const argv[]);
219
static int _bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs);
220
static int bstree_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
221
static int managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
222
static int manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
223
static int managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
224
static int asuser_cmd(int argc, char * const argv[]);
225
static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn));
226
static int help_cmd(int argc, char *const argv[]);
227
228
static const struct {
229
const char *name;
230
int (*func)(int argc, char *const argv[]);
231
const char *desc;
232
} cmds[] = {
233
{ "load", load_and_unload_cmd, "Load configuration files and/or directories" },
234
{ "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
235
// { "reload", reload_cmd, "Reload configuration files and/or directories" },
236
{ "start", start_stop_remove_cmd, "Start specified job" },
237
{ "stop", start_stop_remove_cmd, "Stop specified job" },
238
{ "submit", submit_cmd, "Submit a job from the command line" },
239
{ "remove", start_stop_remove_cmd, "Remove specified job" },
240
{ "bootstrap", bootstrap_cmd, "Bootstrap launchd" },
241
{ "list", list_cmd, "List jobs and information about jobs" },
242
{ "setenv", setenv_cmd, "Set an environmental variable in launchd" },
243
{ "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
244
{ "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
245
{ "export", getenv_and_export_cmd, "Export shell settings from launchd" },
246
{ "debug", wait4debugger_cmd, "Set the WaitForDebugger flag for the target job to true." },
247
{ "limit", limit_cmd, "View and adjust launchd resource limits" },
248
{ "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
249
{ "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
250
{ "shutdown", fyi_cmd, "Prepare for system shutdown" },
251
{ "singleuser", fyi_cmd, "Switch to single-user mode" },
252
{ "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
253
{ "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
254
{ "umask", umask_cmd, "Change launchd's umask" },
255
{ "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" },
256
{ "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" },
257
{ "bstree", bstree_cmd, "Show the entire Mach bootstrap tree. Requires root privileges." },
258
{ "managerpid", managerpid_cmd, "Print the PID of the launchd managing this Mach bootstrap." },
259
{ "manageruid", manageruid_cmd, "Print the UID of the launchd managing this Mach bootstrap." },
260
{ "managername", managername_cmd, "Print the name of this Mach bootstrap." },
261
{ "asuser", asuser_cmd, "Execute a subcommand in the given user's context." },
262
{ "exit", exit_cmd, "Exit the interactive invocation of launchctl" },
263
{ "quit", exit_cmd, "Quit the interactive invocation of launchctl" },
264
{ "help", help_cmd, "This help output" },
265
};
266
267
static bool _launchctl_istty;
268
static bool _launchctl_verbose;
269
static bool _launchctl_is_managed;
270
static bool _launchctl_apple_internal;
271
static bool _launchctl_system_context;
272
static bool _launchctl_uid0_context;
273
static bool _launchctl_system_bootstrap;
274
static bool _launchctl_peruser_bootstrap;
275
static bool _launchctl_verbose_boot = false;
276
static bool _launchctl_startup_debugging = false;
277
278
static bool _launchctl_overrides_db_changed = false;
279
static CFMutableDictionaryRef _launchctl_overrides_db = NULL;
280
281
static char *_launchctl_job_overrides_db_path;
282
static char *_launchctl_managername = NULL;
283
284
#if READ_JETSAM_DEFAULTS
285
static CFDictionaryRef _launchctl_jetsam_defaults = NULL;
286
static CFDictionaryRef _launchctl_jetsam_defaults_cached = NULL;
287
#endif
288
289
int
290
main(int argc, char *const argv[])
291
{
292
char *l;
293
294
if (getenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM)) {
295
/* We're bootstrapping the install environment, so we can't talk to
296
* mDNSResponder or opendirectoryd.
297
*
298
* See <rdar://problem/9877230>.
299
*/
300
si_search_module_set_flags("mdns", 1);
301
si_search_module_set_flags("ds", 1);
302
}
303
304
int64_t is_managed = 0;
305
(void)vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed);
306
_launchctl_is_managed = is_managed;
307
308
_launchctl_istty = isatty(STDIN_FILENO);
309
argc--, argv++;
310
311
if (argc > 0 && argv[0][0] == '-') {
312
char *flago;
313
314
for (flago = argv[0] + 1; *flago; flago++) {
315
switch (*flago) {
316
case 'v':
317
_launchctl_verbose = true;
318
break;
319
case 'u':
320
if (argc > 1) {
321
if (strncmp(argv[1], "root", sizeof("root")) == 0) {
322
_launchctl_uid0_context = true;
323
} else {
324
launchctl_log(LOG_ERR, "Unknown user: %s", argv[1]);
325
exit(EXIT_FAILURE);
326
}
327
argc--, argv++;
328
} else {
329
launchctl_log(LOG_ERR, "-u option requires an argument.");
330
}
331
break;
332
case '1':
333
_launchctl_system_context = true;
334
break;
335
default:
336
launchctl_log(LOG_ERR, "Unknown argument: '-%c'", *flago);
337
break;
338
}
339
}
340
argc--, argv++;
341
}
342
343
/* Running in the context of the root user's per-user launchd is only from
344
* within that session.
345
*/
346
if (_launchctl_uid0_context) {
347
int64_t manager_uid = -1, manager_pid = -1;
348
(void)vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid);
349
(void)vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid);
350
if (manager_uid || manager_pid == 1) {
351
launchctl_log(LOG_ERR, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.");
352
exit(EXIT_FAILURE);
353
}
354
} else if (!(_launchctl_system_context || _launchctl_uid0_context)) {
355
/* Running in the system context is implied when we're running as root
356
* and not running as a bootstrapper.
357
*/
358
_launchctl_system_context = (!_launchctl_is_managed && getuid() == 0);
359
}
360
361
if (_launchctl_system_context) {
362
if (getuid() == 0) {
363
setup_system_context();
364
} else {
365
launchctl_log(LOG_ERR, "You must be root to run in the system context.");
366
exit(EXIT_FAILURE);
367
}
368
} else if (_launchctl_uid0_context) {
369
if (getuid() != 0) {
370
launchctl_log(LOG_ERR, "You must be root to run in the root user context.");
371
exit(EXIT_FAILURE);
372
}
373
}
374
375
if (!readline) {
376
launchctl_log(LOG_ERR, "missing library: readline");
377
exit(EXIT_FAILURE);
378
}
379
380
if (argc == 0) {
381
while ((l = readline(_launchctl_istty ? "launchd% " : NULL))) {
382
char *inputstring = l, *argv2[100], **ap = argv2;
383
int i = 0;
384
385
while ((*ap = strsep(&inputstring, " \t"))) {
386
if (**ap != '\0') {
387
ap++;
388
i++;
389
}
390
}
391
392
if (i > 0) {
393
demux_cmd(i, argv2);
394
}
395
396
free(l);
397
}
398
399
if (_launchctl_istty) {
400
fputc('\n', stdout);
401
}
402
}
403
404
if (argc > 0) {
405
exit(demux_cmd(argc, argv));
406
}
407
408
exit(EXIT_SUCCESS);
409
}
410
411
int
412
demux_cmd(int argc, char *const argv[])
413
{
414
size_t i;
415
416
optind = 1;
417
optreset = 1;
418
419
for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
420
if (!strcmp(cmds[i].name, argv[0])) {
421
return cmds[i].func(argc, argv);
422
}
423
}
424
425
launchctl_log(LOG_ERR, "%s: unknown subcommand \"%s\"", getprogname(), argv[0]);
426
return 1;
427
}
428
429
void
430
launchctl_log(int level, const char *fmt, ...)
431
{
432
va_list ap;
433
va_start(ap, fmt);
434
435
if (_launchctl_is_managed) {
436
vsyslog(level, fmt, ap);
437
} else {
438
char *buff = NULL;
439
(void)vasprintf(&buff, fmt, ap);
440
441
FILE *where = stdout;
442
if (level < LOG_NOTICE) {
443
where = stderr;
444
}
445
446
fprintf(where, "%s\n", buff);
447
free(buff);
448
}
449
450
va_end(ap);
451
}
452
453
void
454
launchctl_log_CFString(int level, CFStringRef string)
455
{
456
// Big enough. Don't feel like jumping through CF's hoops.
457
char *buff = malloc(4096);
458
(void)CFStringGetCString(string, buff, 4096, kCFStringEncodingUTF8);
459
launchctl_log(level, "%s", buff);
460
free(buff);
461
}
462
463
void
464
read_launchd_conf(void)
465
{
466
#if !TARGET_OS_EMBEDDED
467
char s[1000], *c, *av[100];
468
const char *file;
469
size_t len;
470
int i;
471
FILE *f;
472
473
if (getppid() == 1) {
474
file = "/etc/launchd.conf";
475
} else {
476
file = "/etc/launchd-user.conf";
477
}
478
479
if (!(f = fopen(file, "r"))) {
480
return;
481
}
482
483
while ((c = fgets(s, (int) sizeof s, f))) {
484
len = strlen(c);
485
if (len && c[len - 1] == '\n') {
486
c[len - 1] = '\0';
487
}
488
489
i = 0;
490
491
while ((av[i] = strsep(&c, " \t"))) {
492
if (*(av[i]) != '\0') {
493
i++;
494
}
495
}
496
497
if (i > 0) {
498
demux_cmd(i, av);
499
}
500
}
501
502
fclose(f);
503
#endif // !TARGET_OS_EMBEDDED
504
}
505
506
static CFPropertyListRef
507
CFPropertyListCreateFromFile(CFURLRef plistURL)
508
{
509
CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL);
510
511
CFErrorRef streamErr = NULL;
512
if (!CFReadStreamOpen(plistReadStream)) {
513
streamErr = CFReadStreamCopyError(plistReadStream);
514
CFStringRef errString = CFErrorCopyDescription(streamErr);
515
516
launchctl_log_CFString(LOG_ERR, errString);
517
518
CFRelease(errString);
519
CFRelease(streamErr);
520
}
521
522
CFPropertyListRef plist = NULL;
523
if (plistReadStream) {
524
CFStringRef errString = NULL;
525
CFPropertyListFormat plistFormat = 0;
526
plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString);
527
if (!plist) {
528
launchctl_log_CFString(LOG_ERR, errString);
529
CFRelease(errString);
530
}
531
}
532
533
CFReadStreamClose(plistReadStream);
534
CFRelease(plistReadStream);
535
536
return plist;
537
}
538
539
int
540
unsetenv_cmd(int argc, char *const argv[])
541
{
542
launch_data_t resp, tmp, msg;
543
544
if (argc != 2) {
545
launchctl_log(LOG_ERR, "%s usage: unsetenv <key>", getprogname());
546
return 1;
547
}
548
549
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
550
551
tmp = launch_data_new_string(argv[1]);
552
launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT);
553
554
resp = launch_msg(msg);
555
556
launch_data_free(msg);
557
558
if (resp) {
559
launch_data_free(resp);
560
} else {
561
launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno));
562
}
563
564
return 0;
565
}
566
567
int
568
setenv_cmd(int argc, char *const argv[])
569
{
570
launch_data_t resp, tmp, tmpv, msg;
571
572
if (argc != 3) {
573
launchctl_log(LOG_ERR, "%s usage: setenv <key> <value>", getprogname());
574
return 1;
575
}
576
577
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
578
tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
579
580
tmpv = launch_data_new_string(argv[2]);
581
launch_data_dict_insert(tmp, tmpv, argv[1]);
582
launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT);
583
584
resp = launch_msg(msg);
585
launch_data_free(msg);
586
587
if (resp) {
588
launch_data_free(resp);
589
} else {
590
launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno));
591
}
592
593
return 0;
594
}
595
596
void
597
print_launchd_env(launch_data_t obj, const char *key, void *context)
598
{
599
bool *is_csh = context;
600
601
/* XXX escape the double quotes */
602
if (*is_csh) {
603
launchctl_log(LOG_NOTICE, "setenv %s \"%s\";", key, launch_data_get_string(obj));
604
} else {
605
launchctl_log(LOG_NOTICE, "%s=\"%s\"; export %s;", key, launch_data_get_string(obj), key);
606
}
607
}
608
609
void
610
print_key_value(launch_data_t obj, const char *key, void *context)
611
{
612
const char *k = context;
613
614
if (!strcmp(key, k)) {
615
launchctl_log(LOG_NOTICE, "%s", launch_data_get_string(obj));
616
}
617
}
618
619
int
620
getenv_and_export_cmd(int argc, char *const argv[])
621
{
622
launch_data_t resp;
623
bool is_csh = false;
624
char *k;
625
626
if (!strcmp(argv[0], "export")) {
627
char *s = getenv("SHELL");
628
if (s) {
629
is_csh = strstr(s, "csh") ? true : false;
630
}
631
} else if (argc != 2) {
632
launchctl_log(LOG_ERR, "%s usage: getenv <key>", getprogname());
633
return 1;
634
}
635
636
k = argv[1];
637
638
if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &resp) == NULL) {
639
if (!strcmp(argv[0], "export")) {
640
launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
641
} else {
642
launch_data_dict_iterate(resp, print_key_value, k);
643
}
644
launch_data_free(resp);
645
return 0;
646
} else {
647
return 1;
648
}
649
650
return 0;
651
}
652
653
int
654
wait4debugger_cmd(int argc, char * const argv[])
655
{
656
if (argc != 3) {
657
launchctl_log(LOG_ERR, "%s usage: debug <label> <value>", argv[0]);
658
return 1;
659
}
660
661
int result = 1;
662
int64_t inval = 0;
663
if (strncmp(argv[2], "true", sizeof("true")) == 0) {
664
inval = 1;
665
} else if (strncmp(argv[2], "false", sizeof("false")) != 0) {
666
inval = atoi(argv[2]);
667
inval &= 1;
668
}
669
670
vproc_t vp = vprocmgr_lookup_vproc(argv[1]);
671
if (vp) {
672
vproc_err_t verr = vproc_swap_integer(vp, VPROC_GSK_WAITFORDEBUGGER, &inval, NULL);
673
if (verr) {
674
launchctl_log(LOG_ERR, "Failed to set WaitForDebugger flag on %s.", argv[1]);
675
} else {
676
result = 0;
677
}
678
vproc_release(vp);
679
}
680
681
return result;
682
}
683
684
void
685
unloadjob(launch_data_t job)
686
{
687
launch_data_t tmps;
688
689
tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
690
691
if (!tmps) {
692
launchctl_log(LOG_ERR, "%s: Error: Missing Key: %s", getprogname(), LAUNCH_JOBKEY_LABEL);
693
return;
694
}
695
696
if (_vproc_send_signal_by_label(launch_data_get_string(tmps), VPROC_MAGIC_UNLOAD_SIGNAL) != NULL) {
697
launchctl_log(LOG_ERR, "%s: Error unloading: %s", getprogname(), launch_data_get_string(tmps));
698
}
699
}
700
701
#if READ_JETSAM_DEFAULTS
702
703
static CFDictionaryRef
704
read_jetsam_defaults_from_cache(void) {
705
CFPropertyListRef cache = GetPropertyListFromCache();
706
CFPropertyListRef defaults = NULL;
707
const void **keys = 0;
708
CFIndex count, i;
709
710
if (!cache) {
711
return NULL;
712
}
713
714
CFPropertyListRef cachefiles = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
715
if (!cachefiles) {
716
return NULL;
717
}
718
719
count = CFDictionaryGetCount(cachefiles);
720
keys = (const void **)malloc(sizeof(void *) * count);
721
if (!keys) {
722
return NULL;
723
}
724
725
CFDictionaryGetKeysAndValues(cachefiles, keys, NULL);
726
for (i = 0; i < count; i++) {
727
CFStringRef key = (CFStringRef)keys[i];
728
CFIndex key_length = CFStringGetLength(key);
729
730
if (key_length <= (CFIndex)(JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH + 1)) {
731
continue;
732
}
733
734
if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_DIR "/" JETSAM_PROP_PREFIX),
735
CFRangeMake(0, JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + 1), 0)) {
736
continue;
737
}
738
739
if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_SUFFIX),
740
CFRangeMake(key_length - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX_LENGTH), 0)) {
741
continue;
742
}
743
744
defaults = CFDictionaryGetValue(cachefiles, key);
745
break;
746
}
747
748
free(keys);
749
750
return defaults;
751
}
752
753
static CFDictionaryRef
754
read_jetsam_defaults_from_file(void) {
755
DIR *dirp;
756
struct dirent *dp;
757
CFDictionaryRef defaults = NULL;
758
759
dirp = opendir(JETSAM_PROP_DIR);
760
while ((dp = readdir(dirp)) != NULL) {
761
char *fullpath;
762
763
if (dp->d_namlen <= (JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH)) {
764
continue;
765
}
766
767
if (strncmp(dp->d_name, JETSAM_PROP_PREFIX, JETSAM_PROP_PREFIX_LENGTH)) {
768
continue;
769
}
770
771
if (strncmp(dp->d_name + dp->d_namlen - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX, JETSAM_PROP_SUFFIX_LENGTH)) {
772
continue;
773
}
774
775
if (-1 != asprintf(&fullpath, "%s/%s", JETSAM_PROP_DIR, dp->d_name)) {
776
defaults = (CFDictionaryRef)CreateMyPropertyListFromFile(fullpath);
777
free(fullpath);
778
}
779
780
break;
781
}
782
783
if (dirp) {
784
closedir(dirp);
785
}
786
787
return defaults;
788
}
789
790
static bool
791
submit_cached_defaults(void) {
792
launch_data_t msg, resp;
793
const void **keys = NULL;
794
int i;
795
796
if (_launchctl_jetsam_defaults_cached == NULL) {
797
return false;
798
}
799
800
/* The dictionary to transmit */
801
CFMutableDictionaryRef payload_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
802
803
/* Add a key to indicate that this is a special job */
804
CFBooleanRef ID = kCFBooleanTrue;
805
CFDictionaryAddValue(payload_dict, CFSTR(LAUNCH_JOBKEY_DEFAULTS), ID);
806
807
CFMutableDictionaryRef defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
808
809
CFDictionaryAddValue(payload_dict, CFSTR(LAUNCHD_JOB_DEFAULTS), defaults_dict);
810
811
/* Compile appropriate launchd dictionary... */
812
CFIndex count = CFDictionaryGetCount(_launchctl_jetsam_defaults_cached);
813
keys = (const void **)malloc(sizeof(void *) * count);
814
if (!keys) {
815
goto exit;
816
}
817
818
CFDictionaryGetKeysAndValues(_launchctl_jetsam_defaults_cached, keys, NULL);
819
820
for (i = 0; i < count; i++) {
821
CFStringRef label = (CFStringRef)keys[i];
822
823
/* Get the defaults for the job */
824
CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults_cached, label);
825
if (!(job_defaults_dict && CFTypeCheck(job_defaults_dict, CFDictionary))) {
826
continue;
827
}
828
829
/* Create a new dictionary to represent the job */
830
CFMutableDictionaryRef job_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
831
832
/* Add the defaults */
833
CFDictionaryAddValue(job_dict, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
834
835
/* Finally, add the result to the main dictionary */
836
CFDictionaryAddValue(defaults_dict, label, job_dict);
837
838
/* Cleanup */
839
CFRelease(job_dict);
840
}
841
842
/* Send the payload */
843
launch_data_t ldp = CF2launch_data(payload_dict);
844
845
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
846
launch_data_dict_insert(msg, ldp, LAUNCH_KEY_SUBMITJOB);
847
848
resp = launch_msg(msg);
849
launch_data_free(msg);
850
851
launch_data_free(resp);
852
853
exit:
854
CFRelease(defaults_dict);
855
CFRelease(payload_dict);
856
857
free(keys);
858
859
return true;
860
}
861
862
static boolean_t
863
read_jetsam_defaults(void)
864
{
865
/* Current supported version */
866
const int v = 3;
867
868
CFDictionaryRef jetsam_defaults = NULL;
869
870
if (require_jobs_from_cache()) {
871
jetsam_defaults = read_jetsam_defaults_from_cache();
872
} else {
873
jetsam_defaults = read_jetsam_defaults_from_file();
874
}
875
876
if (NULL == jetsam_defaults) {
877
launchctl_log(LOG_NOTICE, "%s: no jetsam property file found", getprogname());
878
return false;
879
}
880
881
/* Validate the version */
882
CFNumberRef defaults_vers = CFDictionaryGetValue(jetsam_defaults, CFSTR("Version"));
883
if (!(defaults_vers && CFTypeCheck(defaults_vers, CFNumber))) {
884
return false;
885
}
886
887
CFNumberRef supported_vers = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &v);
888
if (!(kCFCompareEqualTo == CFNumberCompare(defaults_vers, supported_vers, NULL ))) {
889
return false;
890
}
891
892
/* These defaults are merged within launchctl prior to submitting the job */
893
_launchctl_jetsam_defaults = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS));
894
if (!(_launchctl_jetsam_defaults && CFTypeCheck(_launchctl_jetsam_defaults, CFDictionary))) {
895
_launchctl_jetsam_defaults = NULL;
896
return false;
897
}
898
899
/* Cached defaults (applied by launchd) - parse and submit immediately as a fake job */
900
_launchctl_jetsam_defaults_cached = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS_CACHED));
901
if (!(_launchctl_jetsam_defaults_cached && CFTypeCheck(_launchctl_jetsam_defaults_cached, CFDictionary))) {
902
_launchctl_jetsam_defaults_cached = NULL;
903
return false;
904
}
905
906
submit_cached_defaults();
907
908
return true;
909
}
910
911
#endif /* READ_JETSAM_DEFAULTS */
912
913
launch_data_t
914
read_plist_file(const char *file, bool editondisk, bool load)
915
{
916
CFPropertyListRef plist;
917
launch_data_t r = NULL;
918
#if TARGET_OS_EMBEDDED
919
if (require_jobs_from_cache()) {
920
plist = CreateMyPropertyListFromCachedFile(file);
921
} else {
922
plist = CreateMyPropertyListFromFile(file);
923
}
924
#else
925
plist = CreateMyPropertyListFromFile(file);
926
#endif
927
928
if (NULL == plist) {
929
launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), file);
930
return NULL;
931
}
932
933
CFStringRef label = CFDictionaryGetValue(plist, CFSTR(LAUNCH_JOBKEY_LABEL));
934
if (!(label && CFTypeCheck(label, CFString))) {
935
return NULL;
936
}
937
938
if (_launchctl_overrides_db) {
939
CFDictionaryRef overrides = CFDictionaryGetValue(_launchctl_overrides_db, label);
940
if (overrides && CFTypeCheck(overrides, CFDictionary)) {
941
CFBooleanRef disabled = CFDictionaryGetValue(overrides, CFSTR(LAUNCH_JOBKEY_DISABLED));
942
if (disabled && CFTypeCheck(disabled, CFBoolean)) {
943
CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), disabled);
944
}
945
}
946
}
947
948
if (editondisk) {
949
if (_launchctl_overrides_db) {
950
CFMutableDictionaryRef job = (CFMutableDictionaryRef)CFDictionaryGetValue(_launchctl_overrides_db, label);
951
if (!job || !CFTypeCheck(job, CFDictionary)) {
952
job = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
953
CFDictionarySetValue(_launchctl_overrides_db, label, job);
954
CFRelease(job);
955
}
956
957
CFDictionarySetValue(job, CFSTR(LAUNCH_JOBKEY_DISABLED), load ? kCFBooleanFalse : kCFBooleanTrue);
958
CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), load ? kCFBooleanFalse : kCFBooleanTrue);
959
_launchctl_overrides_db_changed = true;
960
} else {
961
if (load) {
962
CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED));
963
} else {
964
CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue);
965
}
966
WriteMyPropertyListToFile(plist, file);
967
}
968
}
969
970
#if READ_JETSAM_DEFAULTS
971
if (_launchctl_jetsam_defaults) {
972
CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults, label);
973
if (job_defaults_dict) {
974
CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
975
}
976
} else {
977
/* The plist is missing. Set a default memory limit, since the device will be otherwise unusable */
978
long default_limit = 0;
979
CFMutableDictionaryRef job_defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
980
CFNumberRef memory_limit = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &default_limit);
981
if (memory_limit) {
982
CFDictionaryAddValue(job_defaults_dict, CFSTR(LAUNCH_JOBKEY_JETSAMMEMORYLIMIT), memory_limit);
983
CFRelease(memory_limit);
984
}
985
CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
986
CFRelease(job_defaults_dict);
987
}
988
#endif /* READ_JETSAM_DEFAULTS */
989
990
r = CF2launch_data(plist);
991
992
CFRelease(plist);
993
994
return r;
995
}
996
997
static bool
998
sysctl_hw_streq(int mib_slot, const char *str)
999
{
1000
char buf[1000];
1001
size_t bufsz = sizeof(buf);
1002
int mib[] = { CTL_HW, mib_slot };
1003
1004
if (sysctl(mib, 2, buf, &bufsz, NULL, 0) != -1) {
1005
if (strcmp(buf, str) == 0) {
1006
return true;
1007
}
1008
}
1009
1010
return false;
1011
}
1012
1013
static void
1014
limitloadtohardware_iterator(launch_data_t val, const char *key, void *ctx)
1015
{
1016
bool *result = ctx;
1017
1018
char name[128];
1019
(void)snprintf(name, sizeof(name), "hw.%s", key);
1020
1021
int mib[2];
1022
size_t sz = 2;
1023
if (*result != true && os_assumes_zero(sysctlnametomib(name, mib, &sz)) == 0) {
1024
if (launch_data_get_type(val) == LAUNCH_DATA_ARRAY) {
1025
size_t c = launch_data_array_get_count(val);
1026
1027
size_t i = 0;
1028
for (i = 0; i < c; i++) {
1029
launch_data_t oai = launch_data_array_get_index(val, i);
1030
if (sysctl_hw_streq(mib[1], launch_data_get_string(oai))) {
1031
*result = true;
1032
i = c;
1033
}
1034
}
1035
}
1036
}
1037
}
1038
1039
void
1040
readfile(const char *what, struct load_unload_state *lus)
1041
{
1042
char ourhostname[1024];
1043
launch_data_t tmpd, tmps, thejob, tmpa;
1044
bool job_disabled = false;
1045
size_t i, c;
1046
1047
gethostname(ourhostname, sizeof(ourhostname));
1048
1049
if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) {
1050
launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), what);
1051
return;
1052
}
1053
1054
1055
if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
1056
launchctl_log(LOG_ERR, "%s: missing the Label key: %s", getprogname(), what);
1057
goto out_bad;
1058
}
1059
1060
if ((launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM) == NULL) &&
1061
(launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS) == NULL)) {
1062
launchctl_log(LOG_ERR, "%s: neither a Program nor a ProgramArguments key was specified: %s", getprogname(), what);
1063
goto out_bad;
1064
}
1065
1066
if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) {
1067
c = launch_data_array_get_count(tmpa);
1068
1069
for (i = 0; i < c; i++) {
1070
launch_data_t oai = launch_data_array_get_index(tmpa, i);
1071
if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
1072
goto out_bad;
1073
}
1074
}
1075
}
1076
1077
if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) {
1078
c = launch_data_array_get_count(tmpa);
1079
1080
for (i = 0; i < c; i++) {
1081
launch_data_t oai = launch_data_array_get_index(tmpa, i);
1082
if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
1083
break;
1084
}
1085
}
1086
1087
if (i == c) {
1088
goto out_bad;
1089
}
1090
}
1091
1092
if (NULL != (tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHARDWARE))) {
1093
bool result = false;
1094
launch_data_dict_iterate(tmpd, limitloadtohardware_iterator, &result);
1095
if (!result) {
1096
goto out_bad;
1097
}
1098
}
1099
1100
if (NULL != (tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE))) {
1101
bool result = false;
1102
launch_data_dict_iterate(tmpd, limitloadtohardware_iterator, &result);
1103
if (result) {
1104
goto out_bad;
1105
}
1106
}
1107
1108
/* If the manager is Aqua, the LimitLoadToSessionType should default to
1109
* "Aqua".
1110
*
1111
* <rdar://problem/8297909>
1112
*/
1113
if (!_launchctl_managername) {
1114
if (vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &_launchctl_managername)) {
1115
if (bootstrap_port) {
1116
/* This is only an error if we are running with a neutered
1117
* bootstrap port, otherwise we wouldn't expect this operating to
1118
* succeed.
1119
*
1120
* <rdar://problem/10514286>
1121
*/
1122
launchctl_log(LOG_ERR, "Could not obtain manager name: ppid/bootstrap: %d/0x%x", getppid(), bootstrap_port);
1123
}
1124
1125
_launchctl_managername = "";
1126
}
1127
}
1128
1129
if (!lus->session_type) {
1130
if (strcmp(_launchctl_managername, "Aqua") == 0) {
1131
lus->session_type = "Aqua";
1132
}
1133
}
1134
1135
if (lus->session_type && !(tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
1136
tmpa = launch_data_new_string("Aqua");
1137
launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
1138
}
1139
1140
if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
1141
const char *allowed_session;
1142
bool skipjob = true;
1143
1144
/* My sincere apologies to anyone who has to deal with this
1145
* LimitLoadToSessionType madness. It was like this when I got here, but
1146
* I've knowingly made it worse, hopefully to the benefit of the end
1147
* user.
1148
*
1149
* See <rdar://problem/8769211> and <rdar://problem/7114980>.
1150
*/
1151
if (!lus->session_type && launch_data_get_type(tmpa) == LAUNCH_DATA_STRING) {
1152
if (strcasecmp("System", _launchctl_managername) == 0 && strcasecmp("System", launch_data_get_string(tmpa)) == 0) {
1153
skipjob = false;
1154
}
1155
}
1156
1157
if (lus->session_type) switch (launch_data_get_type(tmpa)) {
1158
case LAUNCH_DATA_ARRAY:
1159
c = launch_data_array_get_count(tmpa);
1160
for (i = 0; i < c; i++) {
1161
tmps = launch_data_array_get_index(tmpa, i);
1162
allowed_session = launch_data_get_string(tmps);
1163
if (strcasecmp(lus->session_type, allowed_session) == 0) {
1164
skipjob = false;
1165
/* we have to do the following so job_reparent_hack() works within launchd */
1166
tmpa = launch_data_new_string(lus->session_type);
1167
launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
1168
break;
1169
}
1170
}
1171
break;
1172
case LAUNCH_DATA_STRING:
1173
allowed_session = launch_data_get_string(tmpa);
1174
if (strcasecmp(lus->session_type, allowed_session) == 0) {
1175
skipjob = false;
1176
}
1177
break;
1178
default:
1179
break;
1180
}
1181
1182
if (skipjob) {
1183
goto out_bad;
1184
}
1185
}
1186
1187
if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) {
1188
job_disabled = job_disabled_logic(tmpd);
1189
}
1190
1191
if (lus->forceload) {
1192
job_disabled = false;
1193
}
1194
1195
if (job_disabled && lus->load) {
1196
goto out_bad;
1197
}
1198
1199
if (_launchctl_system_bootstrap || _launchctl_peruser_bootstrap) {
1200
uuid_t uuid;
1201
uuid_clear(uuid);
1202
1203
launch_data_t lduuid = launch_data_new_opaque(uuid, sizeof(uuid_t));
1204
launch_data_dict_insert(thejob, lduuid, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1205
}
1206
1207
launch_data_array_append(lus->pass1, thejob);
1208
1209
if (_launchctl_verbose) {
1210
launchctl_log(LOG_NOTICE, "Will load: %s", what);
1211
}
1212
1213
return;
1214
out_bad:
1215
if (_launchctl_verbose) {
1216
launchctl_log(LOG_NOTICE, "Ignored: %s", what);
1217
}
1218
launch_data_free(thejob);
1219
}
1220
1221
static void
1222
job_disabled_dict_logic(launch_data_t obj, const char *key, void *context)
1223
{
1224
bool *r = context;
1225
1226
if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
1227
return;
1228
}
1229
1230
if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MACHINETYPE) == 0) {
1231
if (sysctl_hw_streq(HW_MACHINE, launch_data_get_string(obj))) {
1232
*r = true;
1233
}
1234
} else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MODELNAME) == 0) {
1235
if (sysctl_hw_streq(HW_MODEL, launch_data_get_string(obj))) {
1236
*r = true;
1237
}
1238
}
1239
}
1240
1241
bool
1242
job_disabled_logic(launch_data_t obj)
1243
{
1244
bool r = false;
1245
1246
switch (launch_data_get_type(obj)) {
1247
case LAUNCH_DATA_DICTIONARY:
1248
launch_data_dict_iterate(obj, job_disabled_dict_logic, &r);
1249
break;
1250
case LAUNCH_DATA_BOOL:
1251
r = launch_data_get_bool(obj);
1252
break;
1253
default:
1254
break;
1255
}
1256
1257
return r;
1258
}
1259
1260
bool
1261
path_goodness_check(const char *path, bool forceload)
1262
{
1263
struct stat sb;
1264
1265
if (stat(path, &sb) == -1) {
1266
launchctl_log(LOG_ERR, "%s: Couldn't stat(\"%s\"): %s", getprogname(), path, strerror(errno));
1267
return false;
1268
}
1269
1270
if (forceload) {
1271
return true;
1272
}
1273
1274
if (sb.st_mode & (S_IWOTH|S_IWGRP)) {
1275
launchctl_log(LOG_ERR, "%s: Dubious permissions on file (skipping): %s", getprogname(), path);
1276
return false;
1277
}
1278
1279
if (sb.st_uid != 0 && sb.st_uid != getuid()) {
1280
launchctl_log(LOG_ERR, "%s: Dubious ownership on file (skipping): %s", getprogname(), path);
1281
return false;
1282
}
1283
1284
if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) {
1285
launchctl_log(LOG_ERR, "%s: Dubious path. Not a regular file or directory (skipping): %s", getprogname(), path);
1286
return false;
1287
}
1288
1289
if ((!S_ISDIR(sb.st_mode)) && (fnmatch("*.plist", path, FNM_CASEFOLD) == FNM_NOMATCH)) {
1290
launchctl_log(LOG_ERR, "%s: Dubious file. Not of type .plist (skipping): %s", getprogname(), path);
1291
return false;
1292
}
1293
1294
return true;
1295
}
1296
1297
void
1298
readpath(const char *what, struct load_unload_state *lus)
1299
{
1300
char buf[MAXPATHLEN];
1301
struct stat sb;
1302
struct dirent *de;
1303
DIR *d;
1304
1305
if (!path_goodness_check(what, lus->forceload)) {
1306
return;
1307
}
1308
1309
if (stat(what, &sb) == -1) {
1310
return;
1311
}
1312
1313
if (S_ISREG(sb.st_mode)) {
1314
readfile(what, lus);
1315
} else if (S_ISDIR(sb.st_mode)) {
1316
if ((d = opendir(what)) == NULL) {
1317
launchctl_log(LOG_ERR, "%s: opendir() failed to open the directory", getprogname());
1318
return;
1319
}
1320
1321
while ((de = readdir(d))) {
1322
if (de->d_name[0] == '.') {
1323
continue;
1324
}
1325
snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
1326
1327
if (!path_goodness_check(buf, lus->forceload)) {
1328
continue;
1329
}
1330
1331
readfile(buf, lus);
1332
}
1333
closedir(d);
1334
}
1335
}
1336
1337
void
1338
insert_event(launch_data_t job, const char *stream, const char *key, launch_data_t event)
1339
{
1340
launch_data_t launchevents, streamdict;
1341
1342
launchevents = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LAUNCHEVENTS);
1343
if (launchevents == NULL) {
1344
launchevents = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1345
launch_data_dict_insert(job, launchevents, LAUNCH_JOBKEY_LAUNCHEVENTS);
1346
}
1347
1348
streamdict = launch_data_dict_lookup(launchevents, stream);
1349
if (streamdict == NULL) {
1350
streamdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1351
launch_data_dict_insert(launchevents, streamdict, stream);
1352
}
1353
1354
launch_data_dict_insert(streamdict, event, key);
1355
}
1356
1357
struct distill_context {
1358
launch_data_t base;
1359
launch_data_t newsockdict;
1360
};
1361
1362
void
1363
distill_jobs(launch_data_t jobs)
1364
{
1365
size_t i, c = launch_data_array_get_count(jobs);
1366
launch_data_t job;
1367
1368
for (i = 0; i < c; i++) {
1369
job = launch_data_array_get_index(jobs, i);
1370
distill_config_file(job);
1371
distill_fsevents(job);
1372
}
1373
}
1374
1375
void
1376
distill_config_file(launch_data_t id_plist)
1377
{
1378
struct distill_context dc = { id_plist, NULL };
1379
launch_data_t tmp;
1380
1381
if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) {
1382
dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1383
launch_data_dict_iterate(tmp, sock_dict_cb, &dc);
1384
launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS);
1385
}
1386
}
1387
1388
void
1389
sock_dict_cb(launch_data_t what, const char *key, void *context)
1390
{
1391
struct distill_context *dc = context;
1392
launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY);
1393
1394
launch_data_dict_insert(dc->newsockdict, fdarray, key);
1395
1396
if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) {
1397
sock_dict_edit_entry(what, key, fdarray, dc->base);
1398
} else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) {
1399
launch_data_t tmp;
1400
size_t i;
1401
1402
for (i = 0; i < launch_data_array_get_count(what); i++) {
1403
tmp = launch_data_array_get_index(what, i);
1404
sock_dict_edit_entry(tmp, key, fdarray, dc->base);
1405
}
1406
}
1407
}
1408
1409
void
1410
sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
1411
{
1412
launch_data_t a, val;
1413
int sfd, st = SOCK_STREAM;
1414
bool passive = true;
1415
1416
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
1417
if (!strcasecmp(launch_data_get_string(val), "stream")) {
1418
st = SOCK_STREAM;
1419
} else if (!strcasecmp(launch_data_get_string(val), "dgram")) {
1420
st = SOCK_DGRAM;
1421
} else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) {
1422
st = SOCK_SEQPACKET;
1423
}
1424
}
1425
1426
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) {
1427
passive = launch_data_get_bool(val);
1428
}
1429
1430
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) {
1431
char secdir[] = LAUNCH_SECDIR, buf[1024];
1432
launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
1433
1434
if (NULL == uenv) {
1435
uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1436
launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
1437
}
1438
1439
mkdtemp(secdir);
1440
1441
sprintf(buf, "%s/%s", secdir, key);
1442
1443
a = launch_data_new_string(buf);
1444
launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME);
1445
a = launch_data_new_string(buf);
1446
launch_data_dict_insert(uenv, a, launch_data_get_string(val));
1447
}
1448
1449
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
1450
struct sockaddr_un sun;
1451
mode_t sun_mode = 0;
1452
mode_t oldmask;
1453
bool setm = false;
1454
1455
memset(&sun, 0, sizeof(sun));
1456
1457
sun.sun_family = AF_UNIX;
1458
1459
strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path));
1460
1461
if (posix_assumes_zero(sfd = _fd(socket(AF_UNIX, st, 0))) == -1) {
1462
return;
1463
}
1464
1465
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
1466
sun_mode = (mode_t)launch_data_get_integer(val);
1467
setm = true;
1468
}
1469
1470
if (passive) {
1471
if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
1472
close(sfd);
1473
return;
1474
}
1475
oldmask = umask(S_IRWXG|S_IRWXO);
1476
if (bind(sfd, (struct sockaddr *)&sun, (socklen_t) sizeof sun) == -1) {
1477
close(sfd);
1478
umask(oldmask);
1479
return;
1480
}
1481
umask(oldmask);
1482
if (setm) {
1483
chmod(sun.sun_path, sun_mode);
1484
}
1485
if ((st == SOCK_STREAM || st == SOCK_SEQPACKET) && listen(sfd, -1) == -1) {
1486
close(sfd);
1487
return;
1488
}
1489
} else if (connect(sfd, (struct sockaddr *)&sun, (socklen_t) sizeof sun) == -1) {
1490
close(sfd);
1491
return;
1492
}
1493
1494
val = launch_data_new_fd(sfd);
1495
launch_data_array_append(fdarray, val);
1496
} else {
1497
launch_data_t rnames = NULL;
1498
const char *node = NULL, *serv = NULL, *mgroup = NULL;
1499
char servnbuf[50];
1500
struct addrinfo hints, *res0, *res;
1501
int gerr, sock_opt = 1;
1502
1503
memset(&hints, 0, sizeof(hints));
1504
1505
hints.ai_socktype = st;
1506
if (passive) {
1507
hints.ai_flags |= AI_PASSIVE;
1508
}
1509
1510
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME))) {
1511
node = launch_data_get_string(val);
1512
}
1513
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP))) {
1514
mgroup = launch_data_get_string(val);
1515
}
1516
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) {
1517
if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) {
1518
sprintf(servnbuf, "%lld", launch_data_get_integer(val));
1519
serv = servnbuf;
1520
} else {
1521
serv = launch_data_get_string(val);
1522
}
1523
}
1524
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) {
1525
if (!strcasecmp("IPv4", launch_data_get_string(val))) {
1526
hints.ai_family = AF_INET;
1527
} else if (!strcasecmp("IPv6", launch_data_get_string(val))) {
1528
hints.ai_family = AF_INET6;
1529
}
1530
}
1531
if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) {
1532
if (!strcasecmp("TCP", launch_data_get_string(val))) {
1533
hints.ai_protocol = IPPROTO_TCP;
1534
} else if (!strcasecmp("UDP", launch_data_get_string(val))) {
1535
hints.ai_protocol = IPPROTO_UDP;
1536
}
1537
}
1538
if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) {
1539
if (LAUNCH_DATA_BOOL != launch_data_get_type(rnames) || launch_data_get_bool(rnames)) {
1540
launch_data_t newevent;
1541
char eventkey[100];
1542
1543
newevent = launch_data_copy(tmp);
1544
snprintf(eventkey, sizeof(eventkey), "com.apple.launchd.%s", key);
1545
insert_event(thejob, "com.apple.bonjour.registration", eventkey, newevent);
1546
}
1547
}
1548
1549
if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) {
1550
launchctl_log(LOG_ERR, "getaddrinfo(): %s", gai_strerror(gerr));
1551
return;
1552
}
1553
1554
for (res = res0; res; res = res->ai_next) {
1555
if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
1556
launchctl_log(LOG_ERR, "socket(): %s", strerror(errno));
1557
return;
1558
}
1559
1560
do_application_firewall_magic(sfd, thejob);
1561
1562
if (hints.ai_flags & AI_PASSIVE) {
1563
if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY,
1564
(void *)&sock_opt, (socklen_t) sizeof sock_opt)) {
1565
launchctl_log(LOG_ERR, "setsockopt(IPV6_V6ONLY): %m");
1566
return;
1567
}
1568
if (mgroup) {
1569
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, (socklen_t) sizeof sock_opt) == -1) {
1570
launchctl_log(LOG_ERR, "setsockopt(SO_REUSEPORT): %s", strerror(errno));
1571
return;
1572
}
1573
} else {
1574
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, (socklen_t) sizeof sock_opt) == -1) {
1575
launchctl_log(LOG_ERR, "setsockopt(SO_REUSEADDR): %s", strerror(errno));
1576
return;
1577
}
1578
}
1579
if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1580
launchctl_log(LOG_ERR, "bind(): %s", strerror(errno));
1581
return;
1582
}
1583
/* The kernel may have dynamically assigned some part of the
1584
* address. (The port being a common example.)
1585
*/
1586
if (getsockname(sfd, res->ai_addr, &res->ai_addrlen) == -1) {
1587
launchctl_log(LOG_ERR, "getsockname(): %s", strerror(errno));
1588
return;
1589
}
1590
1591
if (mgroup) {
1592
do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
1593
}
1594
if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET) && listen(sfd, -1) == -1) {
1595
launchctl_log(LOG_ERR, "listen(): %s", strerror(errno));
1596
return;
1597
}
1598
} else {
1599
if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1600
launchctl_log(LOG_ERR, "connect(): %s", strerror(errno));
1601
return;
1602
}
1603
}
1604
val = launch_data_new_fd(sfd);
1605
launch_data_array_append(fdarray, val);
1606
}
1607
}
1608
}
1609
1610
void
1611
distill_fsevents(launch_data_t id_plist)
1612
{
1613
launch_data_t copy, newevent;
1614
launch_data_t tmp, tmp2;
1615
1616
if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_QUEUEDIRECTORIES))) {
1617
copy = launch_data_copy(tmp);
1618
(void)launch_data_dict_remove(id_plist, LAUNCH_JOBKEY_QUEUEDIRECTORIES);
1619
1620
newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1621
launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_QUEUEDIRECTORIES);
1622
insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_QUEUEDIRECTORIES, newevent);
1623
}
1624
1625
if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_WATCHPATHS))) {
1626
copy = launch_data_copy(tmp);
1627
(void)launch_data_dict_remove(id_plist, LAUNCH_JOBKEY_WATCHPATHS);
1628
1629
newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1630
launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_WATCHPATHS);
1631
insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_WATCHPATHS, newevent);
1632
}
1633
1634
if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_KEEPALIVE))) {
1635
if ((tmp2 = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE))) {
1636
copy = launch_data_copy(tmp2);
1637
(void)launch_data_dict_remove(tmp, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE);
1638
1639
newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1640
launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE);
1641
insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE, newevent);
1642
}
1643
}
1644
}
1645
1646
void
1647
do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup)
1648
{
1649
struct addrinfo hints, *res0, *res;
1650
struct ip_mreq mreq;
1651
struct ipv6_mreq m6req;
1652
int gerr;
1653
1654
memset(&hints, 0, sizeof(hints));
1655
1656
hints.ai_flags |= AI_PASSIVE;
1657
hints.ai_family = family;
1658
hints.ai_socktype = socktype;
1659
hints.ai_protocol = protocol;
1660
1661
if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) {
1662
launchctl_log(LOG_ERR, "getaddrinfo(): %s", gai_strerror(gerr));
1663
return;
1664
}
1665
1666
for (res = res0; res; res = res->ai_next) {
1667
if (AF_INET == family) {
1668
memset(&mreq, 0, sizeof(mreq));
1669
mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
1670
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, (socklen_t) sizeof mreq) == -1) {
1671
launchctl_log(LOG_ERR, "setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno));
1672
continue;
1673
}
1674
break;
1675
} else if (AF_INET6 == family) {
1676
memset(&m6req, 0, sizeof(m6req));
1677
m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1678
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, (socklen_t) sizeof m6req) == -1) {
1679
launchctl_log(LOG_ERR, "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
1680
continue;
1681
}
1682
break;
1683
} else {
1684
launchctl_log(LOG_ERR, "unknown family during multicast group bind!");
1685
break;
1686
}
1687
}
1688
1689
freeaddrinfo(res0);
1690
}
1691
1692
#pragma mark XPC Cache
1693
1694
#if TARGET_OS_EMBEDDED
1695
1696
CFPropertyListRef
1697
GetPropertyListFromCache(void)
1698
{
1699
static CFPropertyListRef propertyList;
1700
CFDataRef cacheData;
1701
CFErrorRef error;
1702
1703
if (!propertyList) {
1704
uint8_t *data = NULL;
1705
unsigned long sz = 0;
1706
1707
void *handle = dlopen(XPC_PLIST_CACHE, RTLD_NOW);
1708
1709
if (handle) {
1710
void *fnptr = dlsym(handle, "__xpcd_cache");
1711
1712
if (fnptr) {
1713
Dl_info image_info;
1714
1715
int rv = dladdr(fnptr, &image_info);
1716
if (rv != 0) {
1717
data = getsectiondata(image_info.dli_fbase, "__TEXT", "__xpcd_cache", &sz);
1718
} else {
1719
launchctl_log(LOG_ERR, "cache loading failed: failed to find address of __xpcd_cache symbol.");
1720
}
1721
} else {
1722
launchctl_log(LOG_ERR, "cache loading failed: failed to find __xpcd_cache symbol in cache.");
1723
}
1724
} else {
1725
launchctl_log(LOG_ERR, "cache loading failed: dlopen returned %s.", dlerror());
1726
}
1727
1728
if (data) {
1729
cacheData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, data, sz, kCFAllocatorNull);
1730
if (cacheData) {
1731
propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, cacheData, kCFPropertyListMutableContainersAndLeaves, NULL, &error);
1732
CFRelease(cacheData);
1733
} else {
1734
launchctl_log(LOG_ERR, "cache loading failed: unable to create data out of memory region.");
1735
}
1736
} else {
1737
launchctl_log(LOG_ERR, "cache loading failed: no cache data found in __TEXT,__xpcd_cache segment.");
1738
}
1739
}
1740
1741
return propertyList;
1742
}
1743
1744
CFPropertyListRef
1745
CreateMyPropertyListFromCachedFile(const char *posixfile)
1746
{
1747
CFPropertyListRef cache = GetPropertyListFromCache();
1748
CFPropertyListRef job = NULL;
1749
1750
if (cache) {
1751
CFPropertyListRef jobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
1752
1753
if (jobs) {
1754
CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, posixfile, kCFStringEncodingUTF8, kCFAllocatorNull);
1755
1756
if (key) {
1757
job = CFDictionaryGetValue(jobs, key);
1758
CFRelease(key);
1759
}
1760
}
1761
}
1762
1763
if (job) {
1764
CFRetain(job);
1765
}
1766
return job;
1767
}
1768
1769
bool
1770
require_jobs_from_cache(void)
1771
{
1772
char buf[1024];
1773
size_t len;
1774
char *ptr;
1775
unsigned long val;
1776
bool cs_disabled = false;
1777
len = sizeof(buf);
1778
1779
if (sysctlbyname("kern.bootargs", buf, &len, NULL, 0) == 0) {
1780
ptr = strnstr(buf, "cs_enforcement_disable=", len);
1781
if (ptr != NULL) {
1782
val = strtoul(ptr + strlen("cs_enforcement_disable="), NULL, 10);
1783
cs_disabled = (val != 0);
1784
}
1785
ptr = strnstr(buf, "launchctl_enforce_codesign=", len);
1786
if (ptr != NULL) {
1787
char *endptr = NULL;
1788
char *startptr = ptr + strlen("launchctl_enforce_codesign=");
1789
val = strtoul(startptr, &endptr, 10);
1790
cs_disabled = (val == 0 && startptr != endptr);
1791
}
1792
}
1793
1794
return !cs_disabled;
1795
}
1796
1797
#endif
1798
1799
#pragma mark File-based Property Lists
1800
1801
CFPropertyListRef
1802
CreateMyPropertyListFromFile(const char *posixfile)
1803
{
1804
CFPropertyListRef propertyList;
1805
CFStringRef errorString;
1806
CFDataRef resourceData;
1807
SInt32 errorCode;
1808
CFURLRef fileURL;
1809
1810
fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1811
if (!fileURL) {
1812
launchctl_log(LOG_ERR, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed", getprogname(), posixfile);
1813
}
1814
if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
1815
launchctl_log(LOG_ERR, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d", getprogname(), posixfile, (int)errorCode);
1816
}
1817
1818
propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainersAndLeaves, &errorString);
1819
if (fileURL) {
1820
CFRelease(fileURL);
1821
}
1822
1823
if (resourceData) {
1824
CFRelease(resourceData);
1825
}
1826
1827
return propertyList;
1828
}
1829
1830
void
1831
WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
1832
{
1833
CFDataRef resourceData;
1834
CFURLRef fileURL;
1835
SInt32 errorCode;
1836
1837
fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1838
if (!fileURL) {
1839
launchctl_log(LOG_ERR, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed", getprogname(), posixfile);
1840
}
1841
resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
1842
if (resourceData == NULL) {
1843
launchctl_log(LOG_ERR, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile);
1844
}
1845
if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) {
1846
launchctl_log(LOG_ERR, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d", getprogname(), posixfile, (int)errorCode);
1847
}
1848
1849
if (resourceData) {
1850
CFRelease(resourceData);
1851
}
1852
}
1853
1854
static inline Boolean
1855
_is_launch_data_t(launch_data_t obj)
1856
{
1857
Boolean result = true;
1858
1859
switch (launch_data_get_type(obj)) {
1860
case LAUNCH_DATA_STRING : break;
1861
case LAUNCH_DATA_INTEGER : break;
1862
case LAUNCH_DATA_REAL : break;
1863
case LAUNCH_DATA_BOOL : break;
1864
case LAUNCH_DATA_ARRAY : break;
1865
case LAUNCH_DATA_DICTIONARY : break;
1866
case LAUNCH_DATA_FD : break;
1867
case LAUNCH_DATA_MACHPORT : break;
1868
default : result = false;
1869
}
1870
1871
return result;
1872
}
1873
1874
static void
1875
_launch_data_iterate(launch_data_t obj, const char *key, CFMutableDictionaryRef dict)
1876
{
1877
if (obj && _is_launch_data_t(obj)) {
1878
CFStringRef cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
1879
CFTypeRef cfVal = CFTypeCreateFromLaunchData(obj);
1880
1881
if (cfVal) {
1882
CFDictionarySetValue(dict, cfKey, cfVal);
1883
CFRelease(cfVal);
1884
}
1885
CFRelease(cfKey);
1886
}
1887
}
1888
1889
static CFTypeRef
1890
CFTypeCreateFromLaunchData(launch_data_t obj)
1891
{
1892
CFTypeRef cfObj = NULL;
1893
1894
switch (launch_data_get_type(obj)) {
1895
case LAUNCH_DATA_STRING: {
1896
const char *str = launch_data_get_string(obj);
1897
cfObj = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
1898
break;
1899
}
1900
case LAUNCH_DATA_INTEGER: {
1901
long long integer = launch_data_get_integer(obj);
1902
cfObj = CFNumberCreate(NULL, kCFNumberLongLongType, &integer);
1903
break;
1904
}
1905
case LAUNCH_DATA_REAL: {
1906
double real = launch_data_get_real(obj);
1907
cfObj = CFNumberCreate(NULL, kCFNumberDoubleType, &real);
1908
break;
1909
}
1910
case LAUNCH_DATA_BOOL: {
1911
bool yesno = launch_data_get_bool(obj);
1912
cfObj = yesno ? kCFBooleanTrue : kCFBooleanFalse;
1913
break;
1914
}
1915
case LAUNCH_DATA_ARRAY: {
1916
cfObj = (CFTypeRef)CFArrayCreateFromLaunchArray(obj);
1917
break;
1918
}
1919
case LAUNCH_DATA_DICTIONARY: {
1920
cfObj = (CFTypeRef)CFDictionaryCreateFromLaunchDictionary(obj);
1921
break;
1922
}
1923
case LAUNCH_DATA_FD: {
1924
int fd = launch_data_get_fd(obj);
1925
cfObj = CFNumberCreate(NULL, kCFNumberIntType, &fd);
1926
break;
1927
}
1928
case LAUNCH_DATA_MACHPORT: {
1929
mach_port_t port = launch_data_get_machport(obj);
1930
cfObj = CFNumberCreate(NULL, kCFNumberIntType, &port);
1931
break;
1932
}
1933
default:
1934
break;
1935
}
1936
1937
return cfObj;
1938
}
1939
1940
#pragma mark CFArray
1941
static CFArrayRef
1942
CFArrayCreateFromLaunchArray(launch_data_t arr)
1943
{
1944
CFArrayRef result = NULL;
1945
CFMutableArrayRef mutResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1946
1947
if (launch_data_get_type(arr) == LAUNCH_DATA_ARRAY) {
1948
unsigned int count = launch_data_array_get_count(arr);
1949
unsigned int i = 0;
1950
1951
for (i = 0; i < count; i++) {
1952
launch_data_t launch_obj = launch_data_array_get_index(arr, i);
1953
CFTypeRef obj = CFTypeCreateFromLaunchData(launch_obj);
1954
1955
if (obj) {
1956
CFArrayAppendValue(mutResult, obj);
1957
CFRelease(obj);
1958
}
1959
}
1960
1961
result = CFArrayCreateCopy(NULL, mutResult);
1962
}
1963
1964
if (mutResult) {
1965
CFRelease(mutResult);
1966
}
1967
return result;
1968
}
1969
1970
#pragma mark CFDictionary / CFPropertyList
1971
static CFDictionaryRef
1972
CFDictionaryCreateFromLaunchDictionary(launch_data_t dict)
1973
{
1974
CFDictionaryRef result = NULL;
1975
1976
if (launch_data_get_type(dict) == LAUNCH_DATA_DICTIONARY) {
1977
CFMutableDictionaryRef mutResult = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1978
1979
launch_data_dict_iterate(dict, (void (*)(launch_data_t, const char *, void *))_launch_data_iterate, mutResult);
1980
1981
result = CFDictionaryCreateCopy(NULL, mutResult);
1982
CFRelease(mutResult);
1983
}
1984
1985
return result;
1986
}
1987
1988
void
1989
myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
1990
{
1991
launch_data_t ik, iw, where = context;
1992
1993
ik = CF2launch_data(key);
1994
iw = CF2launch_data(value);
1995
1996
launch_data_dict_insert(where, iw, launch_data_get_string(ik));
1997
launch_data_free(ik);
1998
}
1999
2000
launch_data_t
2001
CF2launch_data(CFTypeRef cfr)
2002
{
2003
launch_data_t r;
2004
CFTypeID cft = CFGetTypeID(cfr);
2005
2006
if (cft == CFStringGetTypeID()) {
2007
char buf[4096];
2008
CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
2009
r = launch_data_alloc(LAUNCH_DATA_STRING);
2010
launch_data_set_string(r, buf);
2011
} else if (cft == CFBooleanGetTypeID()) {
2012
r = launch_data_alloc(LAUNCH_DATA_BOOL);
2013
launch_data_set_bool(r, CFBooleanGetValue(cfr));
2014
} else if (cft == CFArrayGetTypeID()) {
2015
CFIndex i, ac = CFArrayGetCount(cfr);
2016
r = launch_data_alloc(LAUNCH_DATA_ARRAY);
2017
for (i = 0; i < ac; i++) {
2018
CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
2019
if (v) {
2020
launch_data_t iv = CF2launch_data(v);
2021
launch_data_array_set_index(r, iv, i);
2022
}
2023
}
2024
} else if (cft == CFDictionaryGetTypeID()) {
2025
r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2026
CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
2027
} else if (cft == CFDataGetTypeID()) {
2028
r = launch_data_alloc(LAUNCH_DATA_OPAQUE);
2029
launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
2030
} else if (cft == CFNumberGetTypeID()) {
2031
long long n;
2032
double d;
2033
CFNumberType cfnt = CFNumberGetType(cfr);
2034
switch (cfnt) {
2035
case kCFNumberSInt8Type:
2036
case kCFNumberSInt16Type:
2037
case kCFNumberSInt32Type:
2038
case kCFNumberSInt64Type:
2039
case kCFNumberCharType:
2040
case kCFNumberShortType:
2041
case kCFNumberIntType:
2042
case kCFNumberLongType:
2043
case kCFNumberLongLongType:
2044
CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
2045
r = launch_data_alloc(LAUNCH_DATA_INTEGER);
2046
launch_data_set_integer(r, n);
2047
break;
2048
case kCFNumberFloat32Type:
2049
case kCFNumberFloat64Type:
2050
case kCFNumberFloatType:
2051
case kCFNumberDoubleType:
2052
CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
2053
r = launch_data_alloc(LAUNCH_DATA_REAL);
2054
launch_data_set_real(r, d);
2055
break;
2056
default:
2057
r = NULL;
2058
break;
2059
}
2060
} else {
2061
r = NULL;
2062
}
2063
return r;
2064
}
2065
2066
int
2067
help_cmd(int argc, char *const argv[])
2068
{
2069
size_t i, l, cmdwidth = 0;
2070
2071
int level = LOG_NOTICE;
2072
if (argc == 0 || argv == NULL) {
2073
level = LOG_ERR;
2074
}
2075
2076
launchctl_log(level, "usage: %s <subcommand>", getprogname());
2077
2078
for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
2079
l = strlen(cmds[i].name);
2080
if (l > cmdwidth) {
2081
cmdwidth = l;
2082
}
2083
}
2084
2085
for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
2086
launchctl_log(level, "\t%-*s\t%s", (int)cmdwidth, cmds[i].name, cmds[i].desc);
2087
}
2088
2089
return 0;
2090
}
2091
2092
int
2093
exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused)))
2094
{
2095
exit(0);
2096
}
2097
2098
int
2099
_fd(int fd)
2100
{
2101
if (fd >= 0)
2102
fcntl(fd, F_SETFD, 1);
2103
return fd;
2104
}
2105
2106
void
2107
do_single_user_mode(bool sflag)
2108
{
2109
if (sflag) {
2110
while (!do_single_user_mode2()) {
2111
sleep(1);
2112
}
2113
}
2114
}
2115
2116
bool
2117
do_single_user_mode2(void)
2118
{
2119
bool runcom_fsck = true; /* should_fsck(); */
2120
int wstatus;
2121
int fd;
2122
pid_t p;
2123
2124
switch ((p = fork())) {
2125
case -1:
2126
syslog(LOG_ERR, "can't fork single-user shell, trying again: %m");
2127
return false;
2128
case 0:
2129
break;
2130
default:
2131
(void)os_assumes_zero(waitpid(p, &wstatus, 0));
2132
if (WIFEXITED(wstatus)) {
2133
if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
2134
return true;
2135
} else {
2136
launchctl_log(LOG_NOTICE, "single user mode: exit status: %d", WEXITSTATUS(wstatus));
2137
}
2138
} else {
2139
launchctl_log(LOG_NOTICE, "single user mode shell: %s", strsignal(WTERMSIG(wstatus)));
2140
}
2141
return false;
2142
}
2143
2144
revoke(_PATH_CONSOLE);
2145
if (posix_assumes_zero((fd = open(_PATH_CONSOLE, O_RDWR))) == -1) {
2146
_exit(EXIT_FAILURE);
2147
}
2148
if (posix_assumes_zero(login_tty(fd)) == -1) {
2149
_exit(EXIT_FAILURE);
2150
}
2151
2152
mach_timespec_t wt = { 5, 0 };
2153
IOKitWaitQuiet(kIOMasterPortDefault, &wt); /* This will hopefully return after all the kexts have shut up. */
2154
2155
setenv("TERM", "vt100", 1);
2156
if (runcom_fsck) {
2157
fprintf(stdout, "Singleuser boot -- fsck not done\n");
2158
fprintf(stdout, "Root device is mounted read-only\n");
2159
fprintf(stdout, "If you want to make modifications to files:\n");
2160
fprintf(stdout, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n");
2161
fprintf(stdout, "If you wish to boot the system:\n");
2162
fprintf(stdout, "\texit\n");
2163
fflush(stdout);
2164
}
2165
2166
execl(_PATH_BSHELL, "-sh", NULL);
2167
fprintf(stderr, "can't exec %s for single user: %m\n", _PATH_BSHELL);
2168
_exit(EXIT_FAILURE);
2169
}
2170
2171
void
2172
do_crash_debug_mode(void)
2173
{
2174
while (!do_crash_debug_mode2()) {
2175
sleep(1);
2176
}
2177
}
2178
2179
bool
2180
do_crash_debug_mode2(void)
2181
{
2182
int wstatus;
2183
int fd;
2184
pid_t p;
2185
2186
switch ((p = fork())) {
2187
case -1:
2188
syslog(LOG_ERR, "can't fork crash debug shell, trying again: %m");
2189
return false;
2190
case 0:
2191
break;
2192
default:
2193
(void)os_assumes_zero(waitpid(p, &wstatus, 0));
2194
if (WIFEXITED(wstatus)) {
2195
if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
2196
return true;
2197
} else {
2198
launchctl_log(LOG_NOTICE, "crash debug mode: exit status: %d", WEXITSTATUS(wstatus));
2199
}
2200
} else {
2201
launchctl_log(LOG_NOTICE, "crash debug mode shell: %s", strsignal(WTERMSIG(wstatus)));
2202
}
2203
return false;
2204
}
2205
2206
revoke(_PATH_CONSOLE);
2207
if (posix_assumes_zero((fd = open(_PATH_CONSOLE, O_RDWR))) == -1) {
2208
_exit(EXIT_FAILURE);
2209
}
2210
if (posix_assumes_zero(login_tty(fd)) == -1) {
2211
_exit(EXIT_FAILURE);
2212
}
2213
2214
/* The idea is to wait until all the kexts have quiesced to prevent a bunch
2215
* of log messages from being slammed onto the console prompt. It mostly
2216
* works.
2217
*/
2218
mach_timespec_t wt = { 5, 0 };
2219
IOKitWaitQuiet(kIOMasterPortDefault, &wt);
2220
2221
setenv("TERM", "vt100", 1);
2222
fprintf(stdout, "Entering boot-time debugging mode...\n");
2223
fprintf(stdout, "The system bootstrapper process has crashed. To debug:\n");
2224
fprintf(stdout, "\tgdb attach %i\n", getppid());
2225
fprintf(stdout, "You can try booting the system with:\n");
2226
fprintf(stdout, "\tlaunchctl load -S System -D All\n");
2227
2228
execl(_PATH_BSHELL, "-sh", NULL);
2229
fprintf(stderr, "can't exec %s for crash debug: %m\n", _PATH_BSHELL);
2230
_exit(EXIT_FAILURE);
2231
}
2232
2233
static void
2234
exit_at_sigterm(int sig)
2235
{
2236
if (sig == SIGTERM) {
2237
_exit(EXIT_SUCCESS);
2238
}
2239
}
2240
2241
void
2242
fatal_signal_handler(int sig __attribute__((unused)), siginfo_t *si __attribute__((unused)), void *uap __attribute__((unused)))
2243
{
2244
do_crash_debug_mode();
2245
}
2246
2247
void
2248
handle_system_bootstrapper_crashes_separately(void)
2249
{
2250
if (!_launchctl_startup_debugging) {
2251
return;
2252
}
2253
2254
fprintf(stdout, "com.apple.launchctl.System\t\t\t*** Handling system bootstrapper crashes separately. ***\n");
2255
struct sigaction fsa;
2256
2257
fsa.sa_sigaction = fatal_signal_handler;
2258
fsa.sa_flags = SA_SIGINFO;
2259
sigemptyset(&fsa.sa_mask);
2260
2261
(void)posix_assumes_zero(sigaction(SIGILL, &fsa, NULL));
2262
(void)posix_assumes_zero(sigaction(SIGFPE, &fsa, NULL));
2263
(void)posix_assumes_zero(sigaction(SIGBUS, &fsa, NULL));
2264
(void)posix_assumes_zero(sigaction(SIGTRAP, &fsa, NULL));
2265
(void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL));
2266
}
2267
2268
#if TARGET_OS_EMBEDDED
2269
static void
2270
init_data_protection(void)
2271
{
2272
if (path_check("/usr/libexec/init_data_protection")) {
2273
const char *init_cp[] = { "/usr/libexec/init_data_protection", NULL };
2274
if (fwexec(init_cp, NULL) == -1) {
2275
launchctl_log(LOG_ERR, "Couldn't init content protection: %d: %s", errno, strerror(errno));
2276
(void)reboot(RB_HALT);
2277
2278
_exit(EXIT_FAILURE);
2279
}
2280
}
2281
}
2282
#endif
2283
2284
static void
2285
system_specific_bootstrap(bool sflag)
2286
{
2287
int hnmib[] = { CTL_KERN, KERN_HOSTNAME };
2288
struct kevent kev;
2289
int kq;
2290
#if HAVE_LIBAUDITD
2291
launch_data_t lda, ldb;
2292
#endif
2293
2294
handle_system_bootstrapper_crashes_separately();
2295
2296
// Disable Libinfo lookups to mdns and ds while bootstrapping (8698260)
2297
si_search_module_set_flags("mdns", 1);
2298
si_search_module_set_flags("ds", 1);
2299
2300
/* rc.cdrom's hack to load the system means that we're not the real system
2301
* bootstrapper. So we set this environment variable, and if the real
2302
* bootstrapper detects it, it will disable lookups to mDNSResponder and
2303
* opendirectoryd to prevent deadlocks at boot.
2304
*
2305
* See <rdar://problem/9877230>.
2306
*/
2307
(void)setenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM, "1", 1);
2308
2309
do_sysversion_sysctl();
2310
2311
do_single_user_mode(sflag);
2312
2313
(void)posix_assumes_zero(kq = kqueue());
2314
EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 60, 0);
2315
(void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL));
2316
2317
__OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
2318
EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
2319
(void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL));
2320
(void)posix_assumes_zero(signal(SIGTERM, SIG_IGN));
2321
(void)posix_assumes_zero(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")));
2322
2323
loopback_setup_ipv4();
2324
loopback_setup_ipv6();
2325
2326
apply_sysctls_from_file("/etc/sysctl.conf");
2327
2328
#if TARGET_OS_EMBEDDED
2329
if (path_check("/etc/rc.boot")) {
2330
const char *rcboot_tool[] = { "/etc/rc.boot", NULL };
2331
2332
(void)posix_assumes_zero(signal(SIGTERM, exit_at_sigterm));
2333
(void)posix_assumes_zero(fwexec(rcboot_tool, NULL));
2334
}
2335
#endif
2336
2337
if (path_check("/etc/rc.cdrom")) {
2338
const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL };
2339
2340
/* The bootstrapper should always be killable during install-time. This
2341
* is a special case for /etc/rc.cdrom, which runs a process and never
2342
* exits.
2343
*
2344
* <rdar://problem/6103485>
2345
*/
2346
(void)posix_assumes_zero(signal(SIGTERM, exit_at_sigterm));
2347
(void)posix_assumes_zero(fwexec(rccdrom_tool, NULL));
2348
(void)reboot(RB_HALT);
2349
_exit(EXIT_FAILURE);
2350
} else if (is_netboot()) {
2351
const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL };
2352
if (posix_assumes_zero(fwexec(rcnetboot_tool, NULL)) == -1) {
2353
(void)reboot(RB_HALT);
2354
_exit(EXIT_FAILURE);
2355
}
2356
} else {
2357
do_potential_fsck();
2358
}
2359
2360
#if TARGET_OS_EMBEDDED
2361
if (path_check("/usr/libexec/tzinit")) {
2362
const char *tzinit_tool[] = { "/usr/libexec/tzinit", NULL };
2363
(void)posix_assumes_zero(fwexec(tzinit_tool, NULL));
2364
}
2365
#endif
2366
2367
#if TARGET_OS_EMBEDDED
2368
if (path_check("/usr/libexec/FinishRestoreFromBackup")) {
2369
const char *finish_restore[] = { "/usr/libexec/FinishRestoreFromBackup", NULL };
2370
if (fwexec(finish_restore, NULL) == -1) {
2371
launchctl_log(LOG_ERR, "Couldn't finish restore: %d: %s", errno, strerror(errno));
2372
(void)reboot(RB_HALT);
2373
2374
_exit(EXIT_FAILURE);
2375
}
2376
}
2377
#endif
2378
2379
if (path_check("/usr/libexec/cc_fips_test")) {
2380
const char *fips_tool[] = { "/usr/libexec/cc_fips_test", "-P", NULL };
2381
if (fwexec(fips_tool, NULL) == -1) {
2382
launchctl_log(LOG_ERR, "FIPS self check failure: %d: %s", errno, strerror(errno));
2383
(void)reboot(RB_HALT);
2384
2385
_exit(EXIT_FAILURE);
2386
}
2387
}
2388
2389
if (path_check("/etc/rc.server")) {
2390
const char *rcserver_tool[] = { _PATH_BSHELL, "/etc/rc.server", NULL };
2391
(void)posix_assumes_zero(fwexec(rcserver_tool, NULL));
2392
}
2393
2394
read_launchd_conf();
2395
2396
if (path_check("/var/account/acct")) {
2397
(void)posix_assumes_zero(acct("/var/account/acct"));
2398
}
2399
2400
#if !TARGET_OS_EMBEDDED
2401
if (path_check("/etc/fstab")) {
2402
const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
2403
(void)posix_assumes_zero(fwexec(mount_tool, NULL));
2404
}
2405
#endif
2406
2407
if (path_check("/etc/rc.installer_cleanup")) {
2408
const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL };
2409
(void)posix_assumes_zero(fwexec(rccleanup_tool, NULL));
2410
}
2411
2412
if (path_check("/etc/rc.deferred_install")) {
2413
int status = 0;
2414
const char *deferredinstall_tool[] = { _PATH_BSHELL, "/etc/rc.deferred_install", NULL };
2415
if (posix_assumes_zero(fwexec(deferredinstall_tool, &status)) == 0) {
2416
if (WEXITSTATUS(status) == EXIT_SUCCESS) {
2417
if (_launchctl_apple_internal) {
2418
launchctl_log(LOG_NOTICE, "Deferred install script completed successfully. Rebooting in 3 seconds...");
2419
sleep(3);
2420
}
2421
2422
(void)remove(deferredinstall_tool[1]);
2423
(void)reboot(RB_AUTOBOOT);
2424
exit(EXIT_FAILURE);
2425
} else {
2426
launchctl_log(LOG_NOTICE, "Deferred install script exited with status %i. Continuing boot and hoping it'll work...", WEXITSTATUS(status));
2427
(void)remove(deferredinstall_tool[1]);
2428
}
2429
}
2430
}
2431
2432
empty_dir(_PATH_VARRUN, NULL);
2433
empty_dir(_PATH_TMP, NULL);
2434
(void)remove(_PATH_NOLOGIN);
2435
2436
if (path_check("/usr/libexec/dirhelper")) {
2437
const char *dirhelper_tool[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL };
2438
(void)posix_assumes_zero(fwexec(dirhelper_tool, NULL));
2439
}
2440
2441
(void)posix_assumes_zero(touch_file(_PATH_UTMPX, DEFFILEMODE));
2442
#if !TARGET_OS_EMBEDDED
2443
(void)posix_assumes_zero(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE));
2444
#endif
2445
2446
#if HAVE_LIBAUDITD
2447
/* Only start auditing if not "Disabled" in auditd plist. */
2448
if ((lda = read_plist_file(AUDITD_PLIST_FILE, false, false)) != NULL && ((ldb = launch_data_dict_lookup(lda, LAUNCH_JOBKEY_DISABLED)) == NULL || job_disabled_logic(ldb) == false)) {
2449
(void)os_assumes_zero(audit_quick_start());
2450
launch_data_free(lda);
2451
}
2452
#else
2453
if (path_check("/etc/security/rc.audit")) {
2454
const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
2455
(void)posix_assumes_zero(fwexec(audit_tool, NULL));
2456
}
2457
#endif
2458
2459
#if HAVE_SYSTEMSTATS
2460
systemstats_boot();
2461
#endif
2462
2463
do_BootCache_magic(BOOTCACHE_START);
2464
2465
preheat_page_cache_hack();
2466
2467
_vproc_set_global_on_demand(true);
2468
2469
char *load_launchd_items[] = { "load", "-D", "all", NULL };
2470
int load_launchd_items_cnt = 3;
2471
2472
if (is_safeboot()) {
2473
load_launchd_items[2] = "system";
2474
}
2475
2476
(void)posix_assumes_zero(load_and_unload_cmd(load_launchd_items_cnt, load_launchd_items));
2477
2478
/* See <rdar://problem/5066316>. */
2479
if (!_launchctl_apple_internal) {
2480
mach_timespec_t w = { 5, 0 };
2481
IOKitWaitQuiet(kIOMasterPortDefault, &w);
2482
}
2483
2484
do_BootCache_magic(BOOTCACHE_TAG);
2485
2486
do_bootroot_magic();
2487
2488
_vproc_set_global_on_demand(false);
2489
2490
(void)posix_assumes_zero(kevent(kq, NULL, 0, &kev, 1, NULL));
2491
2492
/* warmd now handles cutting off the BootCache. We just kick it off. */
2493
(void)close(kq);
2494
}
2495
2496
void
2497
do_BootCache_magic(BootCache_action_t what)
2498
{
2499
const char *bcc_tool[] = { "/usr/sbin/BootCacheControl", NULL, NULL };
2500
2501
if (is_safeboot() || !path_check(bcc_tool[0])) {
2502
return;
2503
}
2504
2505
switch (what) {
2506
case BOOTCACHE_START:
2507
bcc_tool[1] = "start";
2508
break;
2509
case BOOTCACHE_TAG:
2510
bcc_tool[1] = "tag";
2511
break;
2512
case BOOTCACHE_STOP:
2513
bcc_tool[1] = "stop";
2514
break;
2515
}
2516
2517
fwexec(bcc_tool, NULL);
2518
}
2519
2520
int
2521
bootstrap_cmd(int argc, char *const argv[])
2522
{
2523
char *session = NULL;
2524
bool sflag = false;
2525
int ch;
2526
2527
while ((ch = getopt(argc, argv, "sS:")) != -1) {
2528
switch (ch) {
2529
case 's':
2530
sflag = true;
2531
break;
2532
case 'S':
2533
session = optarg;
2534
break;
2535
case '?':
2536
default:
2537
break;
2538
}
2539
}
2540
2541
optind = 1;
2542
optreset = 1;
2543
2544
if (!session) {
2545
launchctl_log(LOG_ERR, "usage: %s bootstrap [-s] -S <session-type>", getprogname());
2546
return 1;
2547
}
2548
2549
if (strcasecmp(session, "System") == 0) {
2550
_launchctl_system_bootstrap = true;
2551
system_specific_bootstrap(sflag);
2552
} else {
2553
char *load_launchd_items[] = {
2554
"load",
2555
"-S",
2556
session,
2557
"-D",
2558
"all",
2559
NULL,
2560
NULL,
2561
NULL,
2562
};
2563
size_t the_argc = 5;
2564
2565
bool bootstrap_login_items = false;
2566
if (strcasecmp(session, VPROCMGR_SESSION_AQUA) == 0) {
2567
bootstrap_login_items = true;
2568
} else if (strcasecmp(session, VPROCMGR_SESSION_BACKGROUND) == 0
2569
|| strcasecmp(session, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
2570
/* If we're bootstrapping either the LoginWindow or Background
2571
* sessions, then we only load items from /System and /Library. We
2572
* do not attempt to load anything from a user's home directory, as
2573
* it might not be available at this time.
2574
*/
2575
load_launchd_items[4] = "system";
2576
if (!is_safeboot()) {
2577
load_launchd_items[5] = "-D";
2578
load_launchd_items[6] = "local";
2579
the_argc += 2;
2580
}
2581
2582
if (strcasecmp(session, VPROCMGR_SESSION_BACKGROUND) == 0) {
2583
/* This is to force a bootstrapped job to inherit its security
2584
* session from the launchd that it resides in.
2585
*/
2586
_launchctl_peruser_bootstrap = true;
2587
read_launchd_conf();
2588
}
2589
}
2590
2591
if (is_safeboot()) {
2592
load_launchd_items[4] = "system";
2593
}
2594
2595
int result = load_and_unload_cmd(the_argc, load_launchd_items);
2596
if (result) {
2597
syslog(LOG_ERR, "Could not bootstrap session: %s", session);
2598
return 1;
2599
}
2600
2601
/* This will tell launchd to start listening on MachServices again. When
2602
* bootstrapping, launchd ignores requests from everyone but the
2603
* bootstrapper (us), so this unsets the "weird bootstrap" mode.
2604
*/
2605
int64_t junk = 0;
2606
vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_WEIRD_BOOTSTRAP, &junk, NULL);
2607
if (!verr) {
2608
#if !TARGET_OS_EMBEDDED
2609
if (bootstrap_login_items) {
2610
void *smf = dlopen("/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement", 0);
2611
if (smf) {
2612
void (*_SMLoginItemBootstrapItemsFunc)(void) = dlsym(smf, "_SMLoginItemBootstrapItems");
2613
if (_SMLoginItemBootstrapItemsFunc) {
2614
_SMLoginItemBootstrapItemsFunc();
2615
} else {
2616
launchctl_log(LOG_ERR, "Could not find login item bootstrap function. LoginItems will be unavailable.");
2617
}
2618
} else {
2619
launchctl_log(LOG_ERR, "Failed to open ServiceManagement framework. LoginItems will be unavailable.");
2620
}
2621
}
2622
#endif
2623
} else if (bootstrap_login_items) {
2624
launchctl_log(LOG_ERR, "Failed to unset weird bootstrap. LoginItems will be unavailable.");
2625
}
2626
}
2627
2628
return 0;
2629
}
2630
2631
int
2632
load_and_unload_cmd(int argc, char *const argv[])
2633
{
2634
NSSearchPathEnumerationState es = 0;
2635
char nspath[PATH_MAX * 2]; /* safe side, we need to append */
2636
bool badopts = false;
2637
struct load_unload_state lus;
2638
size_t i;
2639
int ch;
2640
2641
memset(&lus, 0, sizeof(lus));
2642
2643
if (strcmp(argv[0], "load") == 0) {
2644
lus.load = true;
2645
}
2646
2647
while ((ch = getopt(argc, argv, "wFS:D:")) != -1) {
2648
switch (ch) {
2649
case 'w':
2650
lus.editondisk = true;
2651
break;
2652
case 'F':
2653
lus.forceload = true;
2654
break;
2655
case 'S':
2656
lus.session_type = optarg;
2657
break;
2658
case 'D':
2659
if (strcasecmp(optarg, "all") == 0) {
2660
es |= NSAllDomainsMask;
2661
} else if (strcasecmp(optarg, "user") == 0) {
2662
es |= NSUserDomainMask;
2663
} else if (strcasecmp(optarg, "local") == 0) {
2664
es |= NSLocalDomainMask;
2665
} else if (strcasecmp(optarg, "network") == 0) {
2666
es |= NSNetworkDomainMask;
2667
} else if (strcasecmp(optarg, "system") == 0) {
2668
es |= NSSystemDomainMask;
2669
} else {
2670
badopts = true;
2671
}
2672
break;
2673
case '?':
2674
default:
2675
badopts = true;
2676
break;
2677
}
2678
}
2679
argc -= optind;
2680
argv += optind;
2681
2682
if (lus.session_type == NULL) {
2683
es &= ~NSUserDomainMask;
2684
}
2685
2686
if (argc == 0 && es == 0) {
2687
badopts = true;
2688
}
2689
2690
if (badopts) {
2691
launchctl_log(LOG_ERR, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...", getprogname());
2692
return 1;
2693
}
2694
2695
int dbfd = -1;
2696
vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_JOB_OVERRIDES_DB, NULL, &_launchctl_job_overrides_db_path);
2697
if (verr) {
2698
if (bootstrap_port) {
2699
launchctl_log(LOG_ERR, "Could not get location of job overrides database: ppid/bootstrap: %d/0x%x", getppid(), bootstrap_port);
2700
}
2701
} else {
2702
dbfd = open(_launchctl_job_overrides_db_path, O_RDONLY | O_EXLOCK | O_CREAT, S_IRUSR | S_IWUSR);
2703
if (dbfd != -1) {
2704
_launchctl_overrides_db = (CFMutableDictionaryRef)CreateMyPropertyListFromFile(_launchctl_job_overrides_db_path);
2705
if (!_launchctl_overrides_db) {
2706
_launchctl_overrides_db = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2707
}
2708
} else if (errno != EROFS) {
2709
launchctl_log(LOG_ERR, "Could not open job overrides database at: %s: %d: %s", _launchctl_job_overrides_db_path, errno, strerror(errno));
2710
}
2711
}
2712
2713
#if READ_JETSAM_DEFAULTS
2714
if (!read_jetsam_defaults()) {
2715
launchctl_log(LOG_NOTICE, "Failed to read jetsam defaults; no process limits applied");
2716
}
2717
#endif
2718
2719
/* Only one pass! */
2720
lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
2721
2722
es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
2723
2724
while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
2725
if (lus.session_type) {
2726
strcat(nspath, "/LaunchAgents");
2727
} else {
2728
strcat(nspath, "/LaunchDaemons");
2729
}
2730
2731
bool should_glob = true;
2732
2733
#if TARGET_OS_EMBEDDED
2734
if (require_jobs_from_cache()) {
2735
CFDictionaryRef cache = GetPropertyListFromCache();
2736
if (cache) {
2737
CFDictionaryRef launchdJobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
2738
if (launchdJobs) {
2739
CFIndex sz = CFDictionaryGetCount(launchdJobs);
2740
2741
CFStringRef *keys = malloc(sz * sizeof(CFStringRef));
2742
CFDictionaryGetKeysAndValues(launchdJobs, (const void**)keys, NULL);
2743
2744
for (i=0; i < (size_t)sz; i++) {
2745
char path[PATH_MAX];
2746
if (CFStringGetCString(keys[i], path, PATH_MAX, kCFStringEncodingUTF8) && (strncmp(path, nspath, strlen(nspath)) == 0)) {
2747
readpath(path, &lus);
2748
}
2749
}
2750
}
2751
}
2752
2753
should_glob = false;
2754
}
2755
#endif
2756
2757
if (should_glob) {
2758
glob_t g;
2759
2760
if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
2761
for (i = 0; i < g.gl_pathc; i++) {
2762
readpath(g.gl_pathv[i], &lus);
2763
}
2764
globfree(&g);
2765
}
2766
}
2767
}
2768
2769
for (i = 0; i < (size_t)argc; i++) {
2770
readpath(argv[i], &lus);
2771
}
2772
2773
if (launch_data_array_get_count(lus.pass1) == 0) {
2774
if (!_launchctl_is_managed) {
2775
launchctl_log(LOG_ERR, "nothing found to %s", lus.load ? "load" : "unload");
2776
}
2777
launch_data_free(lus.pass1);
2778
return _launchctl_is_managed ? 0 : 1;
2779
}
2780
2781
if (lus.load) {
2782
distill_jobs(lus.pass1);
2783
submit_job_pass(lus.pass1);
2784
} else {
2785
for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) {
2786
unloadjob(launch_data_array_get_index(lus.pass1, i));
2787
}
2788
}
2789
2790
if (_launchctl_overrides_db_changed) {
2791
WriteMyPropertyListToFile(_launchctl_overrides_db, _launchctl_job_overrides_db_path);
2792
}
2793
2794
flock(dbfd, LOCK_UN);
2795
close(dbfd);
2796
return 0;
2797
}
2798
2799
void
2800
submit_job_pass(launch_data_t jobs)
2801
{
2802
launch_data_t msg, resp;
2803
size_t i;
2804
int e;
2805
2806
if (launch_data_array_get_count(jobs) == 0)
2807
return;
2808
2809
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2810
2811
launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
2812
2813
resp = launch_msg(msg);
2814
2815
if (resp) {
2816
switch (launch_data_get_type(resp)) {
2817
case LAUNCH_DATA_ERRNO:
2818
if ((e = launch_data_get_errno(resp)))
2819
launchctl_log(LOG_ERR, "%s", strerror(e));
2820
break;
2821
case LAUNCH_DATA_ARRAY:
2822
for (i = 0; i < launch_data_array_get_count(jobs); i++) {
2823
launch_data_t obatind = launch_data_array_get_index(resp, i);
2824
launch_data_t jatind = launch_data_array_get_index(jobs, i);
2825
const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
2826
if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
2827
e = launch_data_get_errno(obatind);
2828
switch (e) {
2829
case EEXIST:
2830
launchctl_log(LOG_ERR, "%s: %s", lab4job, "Already loaded");
2831
break;
2832
case ESRCH:
2833
launchctl_log(LOG_ERR, "%s: %s", lab4job, "Not loaded");
2834
break;
2835
case ENEEDAUTH:
2836
launchctl_log(LOG_ERR, "%s: %s", lab4job, "Could not set security session");
2837
default:
2838
launchctl_log(LOG_ERR, "%s: %s", lab4job, strerror(e));
2839
case 0:
2840
break;
2841
}
2842
}
2843
}
2844
break;
2845
default:
2846
launchctl_log(LOG_ERR, "unknown respose from launchd!");
2847
break;
2848
}
2849
launch_data_free(resp);
2850
} else {
2851
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
2852
}
2853
2854
launch_data_free(msg);
2855
}
2856
2857
int
2858
start_stop_remove_cmd(int argc, char *const argv[])
2859
{
2860
launch_data_t resp, msg;
2861
const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
2862
int e, r = 0;
2863
2864
if (0 == strcmp(argv[0], "start"))
2865
lmsgcmd = LAUNCH_KEY_STARTJOB;
2866
2867
if (0 == strcmp(argv[0], "remove"))
2868
lmsgcmd = LAUNCH_KEY_REMOVEJOB;
2869
2870
if (argc != 2) {
2871
launchctl_log(LOG_ERR, "usage: %s %s <job label>", getprogname(), argv[0]);
2872
return 1;
2873
}
2874
2875
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2876
launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
2877
2878
resp = launch_msg(msg);
2879
launch_data_free(msg);
2880
2881
if (resp == NULL) {
2882
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
2883
return 1;
2884
} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2885
if ((e = launch_data_get_errno(resp))) {
2886
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(e));
2887
r = 1;
2888
}
2889
} else {
2890
launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
2891
r = 1;
2892
}
2893
2894
launch_data_free(resp);
2895
return r;
2896
}
2897
2898
void
2899
print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused)))
2900
{
2901
static size_t depth = 0;
2902
launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL);
2903
launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID);
2904
launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS);
2905
const char *label = launch_data_get_string(lo);
2906
size_t i;
2907
2908
if (pido) {
2909
fprintf(stdout, "%lld\t-\t%s\n", launch_data_get_integer(pido), label);
2910
} else if (stato) {
2911
int wstatus = (int)launch_data_get_integer(stato);
2912
if (WIFEXITED(wstatus)) {
2913
fprintf(stdout, "-\t%d\t%s\n", WEXITSTATUS(wstatus), label);
2914
} else if (WIFSIGNALED(wstatus)) {
2915
fprintf(stdout, "-\t-%d\t%s\n", WTERMSIG(wstatus), label);
2916
} else {
2917
fprintf(stdout, "-\t???\t%s\n", label);
2918
}
2919
} else {
2920
fprintf(stdout, "-\t-\t%s\n", label);
2921
}
2922
for (i = 0; i < depth; i++) {
2923
fprintf(stdout, "\t");
2924
}
2925
}
2926
2927
void
2928
print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused)))
2929
{
2930
static size_t indent = 0;
2931
size_t i, c;
2932
2933
for (i = 0; i < indent; i++) {
2934
fprintf(stdout, "\t");
2935
}
2936
2937
if (key) {
2938
fprintf(stdout, "\"%s\" = ", key);
2939
}
2940
2941
switch (launch_data_get_type(obj)) {
2942
case LAUNCH_DATA_STRING:
2943
fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj));
2944
break;
2945
case LAUNCH_DATA_INTEGER:
2946
fprintf(stdout, "%lld;\n", launch_data_get_integer(obj));
2947
break;
2948
case LAUNCH_DATA_REAL:
2949
fprintf(stdout, "%f;\n", launch_data_get_real(obj));
2950
break;
2951
case LAUNCH_DATA_BOOL:
2952
fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false");
2953
break;
2954
case LAUNCH_DATA_ARRAY:
2955
c = launch_data_array_get_count(obj);
2956
fprintf(stdout, "(\n");
2957
indent++;
2958
for (i = 0; i < c; i++) {
2959
print_obj(launch_data_array_get_index(obj, i), NULL, NULL);
2960
}
2961
indent--;
2962
for (i = 0; i < indent; i++) {
2963
fprintf(stdout, "\t");
2964
}
2965
fprintf(stdout, ");\n");
2966
break;
2967
case LAUNCH_DATA_DICTIONARY:
2968
fprintf(stdout, "{\n");
2969
indent++;
2970
launch_data_dict_iterate(obj, print_obj, NULL);
2971
indent--;
2972
for (i = 0; i < indent; i++) {
2973
fprintf(stdout, "\t");
2974
}
2975
fprintf(stdout, "};\n");
2976
break;
2977
case LAUNCH_DATA_FD:
2978
fprintf(stdout, "file-descriptor-object;\n");
2979
break;
2980
case LAUNCH_DATA_MACHPORT:
2981
fprintf(stdout, "mach-port-object;\n");
2982
break;
2983
default:
2984
fprintf(stdout, "???;\n");
2985
break;
2986
}
2987
}
2988
2989
int
2990
list_cmd(int argc, char *const argv[])
2991
{
2992
if (_launchctl_is_managed) {
2993
/* This output is meant for a command line, so don't print anything if
2994
* we're managed by launchd.
2995
*/
2996
return 1;
2997
}
2998
2999
launch_data_t resp, msg = NULL;
3000
int r = 0;
3001
3002
bool plist_output = false;
3003
char *label = NULL;
3004
if (argc > 3) {
3005
launchctl_log(LOG_ERR, "usage: %s list [-x] [label]", getprogname());
3006
return 1;
3007
} else if (argc >= 2) {
3008
plist_output = (strncmp(argv[1], "-x", sizeof("-x")) == 0);
3009
label = plist_output ? argv[2] : argv[1];
3010
}
3011
3012
if (label) {
3013
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3014
launch_data_dict_insert(msg, launch_data_new_string(label), LAUNCH_KEY_GETJOB);
3015
3016
resp = launch_msg(msg);
3017
launch_data_free(msg);
3018
3019
if (resp == NULL) {
3020
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3021
r = 1;
3022
} else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
3023
if (plist_output) {
3024
CFDictionaryRef respDict = CFDictionaryCreateFromLaunchDictionary(resp);
3025
CFStringRef plistStr = NULL;
3026
if (respDict) {
3027
CFDataRef plistData = CFPropertyListCreateXMLData(NULL, (CFPropertyListRef)respDict);
3028
CFRelease(respDict);
3029
if (plistData) {
3030
plistStr = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(plistData), CFDataGetLength(plistData), kCFStringEncodingUTF8, false);
3031
CFRelease(plistData);
3032
} else {
3033
r = 1;
3034
}
3035
} else {
3036
r = 1;
3037
}
3038
3039
if (plistStr) {
3040
launchctl_log_CFString(LOG_NOTICE, plistStr);
3041
CFRelease(plistStr);
3042
r = 0;
3043
}
3044
} else {
3045
print_obj(resp, NULL, NULL);
3046
r = 0;
3047
}
3048
launch_data_free(resp);
3049
} else {
3050
launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3051
r = 1;
3052
launch_data_free(resp);
3053
}
3054
} else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
3055
fprintf(stdout, "PID\tStatus\tLabel\n");
3056
launch_data_dict_iterate(resp, print_jobs, NULL);
3057
launch_data_free(resp);
3058
3059
r = 0;
3060
}
3061
3062
return r;
3063
}
3064
3065
int
3066
stdio_cmd(int argc __attribute__((unused)), char *const argv[])
3067
{
3068
launchctl_log(LOG_ERR, "%s %s: This sub-command no longer does anything", getprogname(), argv[0]);
3069
return 1;
3070
}
3071
3072
int
3073
fyi_cmd(int argc, char *const argv[])
3074
{
3075
launch_data_t resp, msg;
3076
const char *lmsgk = NULL;
3077
int e, r = 0;
3078
3079
if (argc != 1) {
3080
launchctl_log(LOG_ERR, "usage: %s %s", getprogname(), argv[0]);
3081
return 1;
3082
}
3083
3084
if (!strcmp(argv[0], "shutdown")) {
3085
lmsgk = LAUNCH_KEY_SHUTDOWN;
3086
} else if (!strcmp(argv[0], "singleuser")) {
3087
lmsgk = LAUNCH_KEY_SINGLEUSER;
3088
} else {
3089
return 1;
3090
}
3091
3092
msg = launch_data_new_string(lmsgk);
3093
resp = launch_msg(msg);
3094
launch_data_free(msg);
3095
3096
if (resp == NULL) {
3097
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3098
return 1;
3099
} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
3100
if ((e = launch_data_get_errno(resp))) {
3101
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(e));
3102
r = 1;
3103
}
3104
} else {
3105
launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3106
r = 1;
3107
}
3108
3109
launch_data_free(resp);
3110
3111
return r;
3112
}
3113
3114
int
3115
logupdate_cmd(int argc, char *const argv[])
3116
{
3117
int64_t inval, outval;
3118
bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
3119
static const struct {
3120
const char *name;
3121
int level;
3122
} logtbl[] = {
3123
{ "debug", LOG_DEBUG },
3124
{ "info", LOG_INFO },
3125
{ "notice", LOG_NOTICE },
3126
{ "warning", LOG_WARNING },
3127
{ "error", LOG_ERR },
3128
{ "critical", LOG_CRIT },
3129
{ "alert", LOG_ALERT },
3130
{ "emergency", LOG_EMERG },
3131
};
3132
size_t i, j, logtblsz = sizeof logtbl / sizeof logtbl[0];
3133
int m = 0;
3134
3135
if (argc >= 2) {
3136
if (!strcmp(argv[1], "mask"))
3137
maskmode = true;
3138
else if (!strcmp(argv[1], "only"))
3139
onlymode = true;
3140
else if (!strcmp(argv[1], "level"))
3141
levelmode = true;
3142
else
3143
badargs = true;
3144
}
3145
3146
if (maskmode)
3147
m = LOG_UPTO(LOG_DEBUG);
3148
3149
if (argc > 2 && (maskmode || onlymode)) {
3150
for (i = 2; i < (size_t)argc; i++) {
3151
for (j = 0; j < logtblsz; j++) {
3152
if (!strcmp(argv[i], logtbl[j].name)) {
3153
if (maskmode)
3154
m &= ~(LOG_MASK(logtbl[j].level));
3155
else
3156
m |= LOG_MASK(logtbl[j].level);
3157
break;
3158
}
3159
}
3160
if (j == logtblsz) {
3161
badargs = true;
3162
break;
3163
}
3164
}
3165
} else if (argc > 2 && levelmode) {
3166
for (j = 0; j < logtblsz; j++) {
3167
if (!strcmp(argv[2], logtbl[j].name)) {
3168
m = LOG_UPTO(logtbl[j].level);
3169
break;
3170
}
3171
}
3172
if (j == logtblsz)
3173
badargs = true;
3174
} else if (argc != 1) {
3175
badargs = true;
3176
}
3177
3178
if (badargs) {
3179
launchctl_log(LOG_ERR, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]", getprogname());
3180
return 1;
3181
}
3182
3183
inval = m;
3184
3185
if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_LOG_MASK, argc != 1 ? &inval : NULL, &outval) == NULL) {
3186
if (argc == 1) {
3187
for (j = 0; j < logtblsz; j++) {
3188
if (outval & LOG_MASK(logtbl[j].level)) {
3189
launchctl_log(LOG_NOTICE, "%s ", logtbl[j].name);
3190
}
3191
}
3192
launchctl_log(LOG_NOTICE, "");
3193
}
3194
return 0;
3195
} else {
3196
return 1;
3197
}
3198
}
3199
3200
static const struct {
3201
const char *name;
3202
int lim;
3203
} limlookup[] = {
3204
{ "cpu", RLIMIT_CPU },
3205
{ "filesize", RLIMIT_FSIZE },
3206
{ "data", RLIMIT_DATA },
3207
{ "stack", RLIMIT_STACK },
3208
{ "core", RLIMIT_CORE },
3209
{ "rss", RLIMIT_RSS },
3210
{ "memlock", RLIMIT_MEMLOCK },
3211
{ "maxproc", RLIMIT_NPROC },
3212
{ "maxfiles", RLIMIT_NOFILE }
3213
};
3214
3215
static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
3216
3217
ssize_t
3218
name2num(const char *n)
3219
{
3220
size_t i;
3221
3222
for (i = 0; i < limlookupcnt; i++) {
3223
if (!strcmp(limlookup[i].name, n)) {
3224
return limlookup[i].lim;
3225
}
3226
}
3227
return -1;
3228
}
3229
3230
const char *
3231
num2name(int n)
3232
{
3233
size_t i;
3234
3235
for (i = 0; i < limlookupcnt; i++) {
3236
if (limlookup[i].lim == n)
3237
return limlookup[i].name;
3238
}
3239
return NULL;
3240
}
3241
3242
const char *
3243
lim2str(rlim_t val, char *buf)
3244
{
3245
if (val == RLIM_INFINITY)
3246
strcpy(buf, "unlimited");
3247
else
3248
sprintf(buf, "%lld", val);
3249
return buf;
3250
}
3251
3252
bool
3253
str2lim(const char *buf, rlim_t *res)
3254
{
3255
char *endptr;
3256
*res = strtoll(buf, &endptr, 10);
3257
if (!strcmp(buf, "unlimited")) {
3258
*res = RLIM_INFINITY;
3259
return false;
3260
} else if (*endptr == '\0') {
3261
return false;
3262
}
3263
return true;
3264
}
3265
3266
int
3267
limit_cmd(int argc, char *const argv[])
3268
{
3269
char slimstr[100];
3270
char hlimstr[100];
3271
struct rlimit *lmts = NULL;
3272
launch_data_t resp, resp1 = NULL, msg, tmp;
3273
int r = 0;
3274
size_t i, lsz = -1;
3275
ssize_t which = 0;
3276
rlim_t slim = -1, hlim = -1;
3277
bool badargs = false;
3278
3279
if (argc > 4)
3280
badargs = true;
3281
3282
if (argc >= 3 && str2lim(argv[2], &slim))
3283
badargs = true;
3284
else
3285
hlim = slim;
3286
3287
if (argc == 4 && str2lim(argv[3], &hlim))
3288
badargs = true;
3289
3290
if (argc >= 2 && -1 == (which = name2num(argv[1])))
3291
badargs = true;
3292
3293
if (badargs) {
3294
launchctl_log(LOG_ERR, "usage: %s %s [", getprogname(), argv[0]);
3295
for (i = 0; i < limlookupcnt; i++)
3296
launchctl_log(LOG_ERR, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
3297
launchctl_log(LOG_ERR, "[both | soft hard]]");
3298
return 1;
3299
}
3300
3301
msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
3302
resp = launch_msg(msg);
3303
launch_data_free(msg);
3304
3305
if (resp == NULL) {
3306
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3307
return 1;
3308
} else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
3309
lmts = launch_data_get_opaque(resp);
3310
lsz = launch_data_get_opaque_size(resp);
3311
if (argc <= 2) {
3312
for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
3313
if (argc == 2 && (size_t)which != i)
3314
continue;
3315
launchctl_log(LOG_NOTICE, "\t%-12s%-15s%-15s", num2name((int)i),
3316
lim2str(lmts[i].rlim_cur, slimstr),
3317
lim2str(lmts[i].rlim_max, hlimstr));
3318
}
3319
}
3320
} else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
3321
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], launch_data_get_string(resp));
3322
r = 1;
3323
} else {
3324
launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3325
r = 1;
3326
}
3327
3328
if (argc <= 2 || r != 0) {
3329
launch_data_free(resp);
3330
return r;
3331
} else {
3332
resp1 = resp;
3333
}
3334
3335
lmts[which].rlim_cur = slim;
3336
lmts[which].rlim_max = hlim;
3337
3338
bool maxfiles_exceeded = false;
3339
if (strncmp(argv[1], "maxfiles", sizeof("maxfiles")) == 0) {
3340
if (argc > 2) {
3341
maxfiles_exceeded = (strncmp(argv[2], "unlimited", sizeof("unlimited")) == 0);
3342
}
3343
3344
if (argc > 3) {
3345
maxfiles_exceeded = (maxfiles_exceeded || strncmp(argv[3], "unlimited", sizeof("unlimited")) == 0);
3346
}
3347
3348
if (maxfiles_exceeded) {
3349
launchctl_log(LOG_ERR, "Neither the hard nor soft limit for \"maxfiles\" can be unlimited. Please use a numeric parameter for both.");
3350
return 1;
3351
}
3352
}
3353
3354
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3355
tmp = launch_data_new_opaque(lmts, lsz);
3356
launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
3357
resp = launch_msg(msg);
3358
launch_data_free(msg);
3359
3360
if (resp == NULL) {
3361
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3362
return 1;
3363
} else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
3364
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], launch_data_get_string(resp));
3365
r = 1;
3366
} else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
3367
launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3368
r = 1;
3369
}
3370
3371
launch_data_free(resp);
3372
launch_data_free(resp1);
3373
3374
return r;
3375
}
3376
3377
int
3378
umask_cmd(int argc, char *const argv[])
3379
{
3380
bool badargs = false;
3381
char *endptr;
3382
long m = 0;
3383
int64_t inval, outval;
3384
3385
if (argc == 2) {
3386
m = strtol(argv[1], &endptr, 8);
3387
if (*endptr != '\0' || m > 0777)
3388
badargs = true;
3389
}
3390
3391
if (argc > 2 || badargs) {
3392
launchctl_log(LOG_ERR, "usage: %s %s <mask>", getprogname(), argv[0]);
3393
return 1;
3394
}
3395
3396
inval = m;
3397
3398
if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_UMASK, argc == 2 ? &inval : NULL, &outval) == NULL) {
3399
if (argc == 1) {
3400
launchctl_log(LOG_NOTICE, "%o", (unsigned int)outval);
3401
}
3402
return 0;
3403
} else {
3404
return 1;
3405
}
3406
}
3407
3408
void
3409
setup_system_context(void)
3410
{
3411
if (getenv(LAUNCHD_SOCKET_ENV)) {
3412
return;
3413
}
3414
3415
if (getenv(LAUNCH_ENV_KEEPCONTEXT)) {
3416
return;
3417
}
3418
3419
if (geteuid() != 0) {
3420
launchctl_log(LOG_ERR, "You must be the root user to perform this operation.");
3421
return;
3422
}
3423
3424
/* Use the system launchd's socket. */
3425
setenv("__USE_SYSTEM_LAUNCHD", "1", 0);
3426
3427
/* Put ourselves in the system launchd's bootstrap. */
3428
mach_port_t rootbs = str2bsport("/");
3429
mach_port_deallocate(mach_task_self(), bootstrap_port);
3430
task_set_bootstrap_port(mach_task_self(), rootbs);
3431
bootstrap_port = rootbs;
3432
}
3433
3434
int
3435
submit_cmd(int argc, char *const argv[])
3436
{
3437
launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3438
launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3439
launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY);
3440
int ch, i, r = 0;
3441
3442
launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND);
3443
3444
while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) {
3445
switch (ch) {
3446
case 'l':
3447
launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL);
3448
break;
3449
case 'p':
3450
launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM);
3451
break;
3452
case 'o':
3453
launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH);
3454
break;
3455
case 'e':
3456
launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH);
3457
break;
3458
default:
3459
launchctl_log(LOG_ERR, "usage: %s submit ...", getprogname());
3460
return 1;
3461
}
3462
}
3463
argc -= optind;
3464
argv += optind;
3465
3466
for (i = 0; argv[i]; i++) {
3467
launch_data_array_append(largv, launch_data_new_string(argv[i]));
3468
}
3469
3470
launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
3471
3472
launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
3473
3474
resp = launch_msg(msg);
3475
launch_data_free(msg);
3476
3477
if (resp == NULL) {
3478
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3479
return 1;
3480
} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
3481
errno = launch_data_get_errno(resp);
3482
if (errno) {
3483
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(errno));
3484
r = 1;
3485
}
3486
} else {
3487
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], "unknown response");
3488
}
3489
3490
launch_data_free(resp);
3491
3492
return r;
3493
}
3494
3495
int
3496
getrusage_cmd(int argc, char *const argv[])
3497
{
3498
launch_data_t resp, msg;
3499
bool badargs = false;
3500
int r = 0;
3501
3502
if (argc != 2)
3503
badargs = true;
3504
else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
3505
badargs = true;
3506
3507
if (badargs) {
3508
launchctl_log(LOG_ERR, "usage: %s %s self | children", getprogname(), argv[0]);
3509
return 1;
3510
}
3511
3512
if (!strcmp(argv[1], "self")) {
3513
msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
3514
} else {
3515
msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
3516
}
3517
3518
resp = launch_msg(msg);
3519
launch_data_free(msg);
3520
3521
if (resp == NULL) {
3522
launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3523
return 1;
3524
} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
3525
launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
3526
r = 1;
3527
} else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
3528
struct rusage *rusage = launch_data_get_opaque(resp);
3529
launchctl_log(LOG_NOTICE, "\t%-10f\tuser time used",
3530
(double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
3531
launchctl_log(LOG_NOTICE, "\t%-10f\tsystem time used",
3532
(double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
3533
launchctl_log(LOG_NOTICE, "\t%-10ld\tmax resident set size", rusage->ru_maxrss);
3534
launchctl_log(LOG_NOTICE, "\t%-10ld\tshared text memory size", rusage->ru_ixrss);
3535
launchctl_log(LOG_NOTICE, "\t%-10ld\tunshared data size", rusage->ru_idrss);
3536
launchctl_log(LOG_NOTICE, "\t%-10ld\tunshared stack size", rusage->ru_isrss);
3537
launchctl_log(LOG_NOTICE, "\t%-10ld\tpage reclaims", rusage->ru_minflt);
3538
launchctl_log(LOG_NOTICE, "\t%-10ld\tpage faults", rusage->ru_majflt);
3539
launchctl_log(LOG_NOTICE, "\t%-10ld\tswaps", rusage->ru_nswap);
3540
launchctl_log(LOG_NOTICE, "\t%-10ld\tblock input operations", rusage->ru_inblock);
3541
launchctl_log(LOG_NOTICE, "\t%-10ld\tblock output operations", rusage->ru_oublock);
3542
launchctl_log(LOG_NOTICE, "\t%-10ld\tmessages sent", rusage->ru_msgsnd);
3543
launchctl_log(LOG_NOTICE, "\t%-10ld\tmessages received", rusage->ru_msgrcv);
3544
launchctl_log(LOG_NOTICE, "\t%-10ld\tsignals received", rusage->ru_nsignals);
3545
launchctl_log(LOG_NOTICE, "\t%-10ld\tvoluntary context switches", rusage->ru_nvcsw);
3546
launchctl_log(LOG_NOTICE, "\t%-10ld\tinvoluntary context switches", rusage->ru_nivcsw);
3547
} else {
3548
launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3549
r = 1;
3550
}
3551
3552
launch_data_free(resp);
3553
3554
return r;
3555
}
3556
3557
bool
3558
launch_data_array_append(launch_data_t a, launch_data_t o)
3559
{
3560
size_t offt = launch_data_array_get_count(a);
3561
3562
return launch_data_array_set_index(a, o, offt);
3563
}
3564
3565
mach_port_t
3566
str2bsport(const char *s)
3567
{
3568
bool getrootbs = strcmp(s, "/") == 0;
3569
mach_port_t last_bport, bport = bootstrap_port;
3570
task_t task = mach_task_self();
3571
kern_return_t result;
3572
3573
if (strcmp(s, "..") == 0 || getrootbs) {
3574
do {
3575
last_bport = bport;
3576
result = bootstrap_parent(last_bport, &bport);
3577
3578
if (result == BOOTSTRAP_NOT_PRIVILEGED) {
3579
launchctl_log(LOG_ERR, "Permission denied");
3580
return 1;
3581
} else if (result != BOOTSTRAP_SUCCESS) {
3582
launchctl_log(LOG_ERR, "bootstrap_parent() %d", result);
3583
return 1;
3584
}
3585
} while (getrootbs && last_bport != bport);
3586
} else if (strcmp(s, "0") == 0 || strcmp(s, "NULL") == 0) {
3587
bport = MACH_PORT_NULL;
3588
} else {
3589
int pid = atoi(s);
3590
3591
result = task_for_pid(mach_task_self(), pid, &task);
3592
3593
if (result != KERN_SUCCESS) {
3594
launchctl_log(LOG_ERR, "task_for_pid() %s", mach_error_string(result));
3595
return 1;
3596
}
3597
3598
result = task_get_bootstrap_port(task, &bport);
3599
3600
if (result != KERN_SUCCESS) {
3601
launchctl_log(LOG_ERR, "Couldn't get bootstrap port: %s", mach_error_string(result));
3602
return 1;
3603
}
3604
}
3605
3606
return bport;
3607
}
3608
3609
int
3610
bsexec_cmd(int argc, char *const argv[])
3611
{
3612
kern_return_t result;
3613
mach_port_t bport;
3614
3615
if (argc < 3) {
3616
launchctl_log(LOG_ERR, "usage: %s bsexec <PID> prog...", getprogname());
3617
return 1;
3618
}
3619
3620
bport = str2bsport(argv[1]);
3621
3622
result = task_set_bootstrap_port(mach_task_self(), bport);
3623
3624
if (result != KERN_SUCCESS) {
3625
launchctl_log(LOG_ERR, "Couldn't switch to new bootstrap port: %s", mach_error_string(result));
3626
return 1;
3627
}
3628
3629
setgid(getgid());
3630
setuid(getuid());
3631
3632
setenv(LAUNCH_ENV_KEEPCONTEXT, "1", 1);
3633
if (fwexec((const char *const *)argv + 2, NULL) == -1) {
3634
launchctl_log(LOG_ERR, "%s bsexec failed: %s", getprogname(), strerror(errno));
3635
return 1;
3636
}
3637
3638
return 0;
3639
}
3640
3641
int
3642
_bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only)
3643
{
3644
kern_return_t result;
3645
name_array_t service_names;
3646
name_array_t service_jobs;
3647
mach_msg_type_number_t service_cnt, service_jobs_cnt, service_active_cnt;
3648
bootstrap_status_array_t service_actives;
3649
unsigned int i;
3650
3651
if (bport == MACH_PORT_NULL) {
3652
launchctl_log(LOG_ERR, "Invalid bootstrap port");
3653
return 1;
3654
}
3655
3656
uint64_t flags = 0;
3657
flags |= local_only ? BOOTSTRAP_FORCE_LOCAL : 0;
3658
result = bootstrap_info(bport, &service_names, &service_cnt, &service_jobs, &service_jobs_cnt, &service_actives, &service_active_cnt, flags);
3659
if (result != BOOTSTRAP_SUCCESS) {
3660
launchctl_log(LOG_ERR, "bootstrap_info(): %d", result);
3661
return 1;
3662
}
3663
3664
#define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
3665
3666
for (i = 0; i < service_cnt ; i++) {
3667
if (!show_job) {
3668
fprintf(stdout, "%*s%-3s%s\n", depth, "", bport_state((service_actives[i])), service_names[i]);
3669
} else {
3670
fprintf(stdout, "%*s%-3s%s (%s)\n", depth, "", bport_state((service_actives[i])), service_names[i], service_jobs[i]);
3671
}
3672
}
3673
3674
return 0;
3675
}
3676
3677
int
3678
bslist_cmd(int argc, char *const argv[])
3679
{
3680
if (_launchctl_is_managed) {
3681
/* This output is meant for a command line, so don't print anything if
3682
* we're managed by launchd.
3683
*/
3684
return 1;
3685
}
3686
3687
mach_port_t bport = bootstrap_port;
3688
bool show_jobs = false;
3689
if (argc > 2 && strcmp(argv[2], "-j") == 0) {
3690
show_jobs = true;
3691
}
3692
3693
if (argc > 1) {
3694
if (show_jobs) {
3695
bport = str2bsport(argv[1]);
3696
} else if (strcmp(argv[1], "-j") == 0) {
3697
show_jobs = true;
3698
}
3699
}
3700
3701
if (bport == MACH_PORT_NULL) {
3702
launchctl_log(LOG_ERR, "Invalid bootstrap port");
3703
return 1;
3704
}
3705
3706
return _bslist_cmd(bport, 0, show_jobs, false);
3707
}
3708
3709
int
3710
_bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs)
3711
{
3712
if (bsport == MACH_PORT_NULL) {
3713
launchctl_log(LOG_ERR, "No root port!");
3714
return 1;
3715
}
3716
3717
mach_port_array_t child_ports = NULL;
3718
name_array_t child_names = NULL;
3719
bootstrap_property_array_t child_props = NULL;
3720
unsigned int cnt = 0;
3721
3722
kern_return_t kr = bootstrap_lookup_children(bsport, &child_ports, &child_names, &child_props, (mach_msg_type_number_t *)&cnt);
3723
if (kr != BOOTSTRAP_SUCCESS && kr != BOOTSTRAP_NO_CHILDREN) {
3724
if (kr == BOOTSTRAP_NOT_PRIVILEGED) {
3725
launchctl_log(LOG_ERR, "You must be root to perform this operation.");
3726
} else {
3727
launchctl_log(LOG_ERR, "bootstrap_lookup_children(): %d", kr);
3728
}
3729
3730
return 1;
3731
}
3732
3733
unsigned int i = 0;
3734
_bslist_cmd(bsport, depth, show_jobs, true);
3735
3736
for (i = 0; i < cnt; i++) {
3737
char *type = NULL;
3738
if (child_props[i] & BOOTSTRAP_PROPERTY_PERUSER) {
3739
type = "Per-user";
3740
} else if (child_props[i] & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) {
3741
type = "Explicit Subset";
3742
} else if (child_props[i] & BOOTSTRAP_PROPERTY_IMPLICITSUBSET) {
3743
type = "Implicit Subset";
3744
} else if (child_props[i] & BOOTSTRAP_PROPERTY_MOVEDSUBSET) {
3745
type = "Moved Subset";
3746
} else if (child_props[i] & BOOTSTRAP_PROPERTY_XPC_SINGLETON) {
3747
type = "XPC Singleton Domain";
3748
} else if (child_props[i] & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
3749
type = "XPC Private Domain";
3750
} else {
3751
type = "Unknown";
3752
}
3753
3754
fprintf(stdout, "%*s%s (%s)/\n", depth, "", child_names[i], type);
3755
if (child_ports[i] != MACH_PORT_NULL) {
3756
_bstree_cmd(child_ports[i], depth + 4, show_jobs);
3757
}
3758
}
3759
3760
return 0;
3761
}
3762
3763
int
3764
bstree_cmd(int argc, char * const argv[])
3765
{
3766
if (_launchctl_is_managed) {
3767
/* This output is meant for a command line, so don't print anything if
3768
* we're managed by launchd.
3769
*/
3770
return 1;
3771
}
3772
3773
bool show_jobs = false;
3774
if (geteuid() != 0) {
3775
launchctl_log(LOG_ERR, "You must be root to perform this operation.");
3776
return 1;
3777
} else {
3778
if (argc == 2 && strcmp(argv[1], "-j") == 0) {
3779
show_jobs = true;
3780
}
3781
fprintf(stdout, "System/\n");
3782
}
3783
3784
return _bstree_cmd(str2bsport("/"), 4, show_jobs);
3785
}
3786
3787
int
3788
managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
3789
{
3790
int64_t manager_pid = 0;
3791
vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, (int64_t *)&manager_pid);
3792
if (verr) {
3793
launchctl_log(LOG_NOTICE, "Unknown job manager!");
3794
return 1;
3795
}
3796
3797
launchctl_log(LOG_NOTICE, "%d", (pid_t)manager_pid);
3798
return 0;
3799
}
3800
3801
int
3802
manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
3803
{
3804
int64_t manager_uid = 0;
3805
vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, (int64_t *)&manager_uid);
3806
if (verr) {
3807
launchctl_log(LOG_NOTICE, "Unknown job manager!");
3808
return 1;
3809
}
3810
3811
launchctl_log(LOG_NOTICE, "%lli", manager_uid);
3812
return 0;
3813
}
3814
3815
int
3816
managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
3817
{
3818
char *manager_name = NULL;
3819
vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &manager_name);
3820
if (verr) {
3821
launchctl_log(LOG_NOTICE, "Unknown job manager!");
3822
return 1;
3823
}
3824
3825
launchctl_log(LOG_NOTICE, "%s", manager_name);
3826
free(manager_name);
3827
3828
return 0;
3829
}
3830
3831
int
3832
asuser_cmd(int argc, char * const argv[])
3833
{
3834
/* This code plays fast and loose with Mach ports. Do NOT use it as any sort
3835
* of reference for port handling. Or really anything else in this file.
3836
*/
3837
uid_t req_uid = (uid_t)-2;
3838
if (argc > 2) {
3839
req_uid = atoi(argv[1]);
3840
if (req_uid == (uid_t)-2) {
3841
launchctl_log(LOG_ERR, "You cannot run a command nobody.");
3842
return 1;
3843
}
3844
} else {
3845
launchctl_log(LOG_ERR, "Usage: launchctl asuser <UID> <command> [arguments...].");
3846
return 1;
3847
}
3848
3849
if (geteuid() != 0) {
3850
launchctl_log(LOG_ERR, "You must be root to run a command as another user.");
3851
return 1;
3852
}
3853
3854
mach_port_t rbs = MACH_PORT_NULL;
3855
kern_return_t kr = bootstrap_get_root(bootstrap_port, &rbs);
3856
if (kr != BOOTSTRAP_SUCCESS) {
3857
launchctl_log(LOG_ERR, "bootstrap_get_root(): %u", kr);
3858
return 1;
3859
}
3860
3861
mach_port_t bp = MACH_PORT_NULL;
3862
kr = bootstrap_look_up_per_user(rbs, NULL, req_uid, &bp);
3863
if (kr != BOOTSTRAP_SUCCESS) {
3864
launchctl_log(LOG_ERR, "bootstrap_look_up_per_user(): %u", kr);
3865
return 1;
3866
}
3867
3868
bootstrap_port = bp;
3869
kr = task_set_bootstrap_port(mach_task_self(), bp);
3870
if (kr != KERN_SUCCESS) {
3871
launchctl_log(LOG_ERR, "task_set_bootstrap_port(): 0x%x: %s", kr, mach_error_string(kr));
3872
return 1;
3873
}
3874
3875
name_t sockpath;
3876
sockpath[0] = 0;
3877
kr = _vprocmgr_getsocket(sockpath);
3878
if (kr != BOOTSTRAP_SUCCESS) {
3879
launchctl_log(LOG_ERR, "_vprocmgr_getsocket(): %u", kr);
3880
return 1;
3881
}
3882
3883
setenv(LAUNCHD_SOCKET_ENV, sockpath, 1);
3884
setenv(LAUNCH_ENV_KEEPCONTEXT, "1", 1);
3885
if (fwexec((const char *const *)argv + 2, NULL) == -1) {
3886
launchctl_log(LOG_ERR, "Couldn't spawn command: %s", argv[2]);
3887
return 1;
3888
}
3889
3890
return 0;
3891
}
3892
3893
void
3894
loopback_setup_ipv4(void)
3895
{
3896
struct ifaliasreq ifra;
3897
struct ifreq ifr;
3898
int s;
3899
3900
memset(&ifr, 0, sizeof(ifr));
3901
strcpy(ifr.ifr_name, "lo0");
3902
3903
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
3904
return;
3905
3906
if (posix_assumes_zero(ioctl(s, SIOCGIFFLAGS, &ifr)) != -1) {
3907
ifr.ifr_flags |= IFF_UP;
3908
(void)posix_assumes_zero(ioctl(s, SIOCSIFFLAGS, &ifr));
3909
}
3910
3911
memset(&ifra, 0, sizeof(ifra));
3912
strcpy(ifra.ifra_name, "lo0");
3913
((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
3914
((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3915
((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
3916
((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
3917
((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
3918
((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
3919
3920
(void)posix_assumes_zero(ioctl(s, SIOCAIFADDR, &ifra));
3921
(void)close(s);
3922
}
3923
3924
void
3925
loopback_setup_ipv6(void)
3926
{
3927
struct in6_aliasreq ifra6;
3928
struct ifreq ifr;
3929
int s6;
3930
3931
memset(&ifr, 0, sizeof(ifr));
3932
strcpy(ifr.ifr_name, "lo0");
3933
3934
if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
3935
return;
3936
3937
memset(&ifr, 0, sizeof(ifr));
3938
strcpy(ifr.ifr_name, "lo0");
3939
3940
if (posix_assumes_zero(ioctl(s6, SIOCGIFFLAGS, &ifr)) != -1) {
3941
ifr.ifr_flags |= IFF_UP;
3942
(void)posix_assumes_zero(ioctl(s6, SIOCSIFFLAGS, &ifr));
3943
}
3944
3945
memset(&ifra6, 0, sizeof(ifra6));
3946
strcpy(ifra6.ifra_name, "lo0");
3947
3948
ifra6.ifra_addr.sin6_family = AF_INET6;
3949
ifra6.ifra_addr.sin6_addr = in6addr_loopback;
3950
ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
3951
ifra6.ifra_prefixmask.sin6_family = AF_INET6;
3952
memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
3953
ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
3954
ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
3955
ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
3956
3957
if (ioctl(s6, SIOCAIFADDR_IN6, &ifra6) == -1 && errno != EEXIST) {
3958
(void)os_assumes_zero(errno);
3959
}
3960
3961
(void)close(s6);
3962
}
3963
3964
pid_t
3965
fwexec(const char *const *argv, int *wstatus)
3966
{
3967
int wstatus2;
3968
pid_t p;
3969
3970
/* We'd use posix_spawnp(), but we want to workaround: 6288899 */
3971
if ((p = vfork()) == -1) {
3972
return -1;
3973
} else if (p == 0) {
3974
execvp(argv[0], (char *const *)argv);
3975
_exit(EXIT_FAILURE);
3976
}
3977
3978
if (waitpid(p, wstatus ? wstatus : &wstatus2, 0) == -1) {
3979
return -1;
3980
}
3981
3982
if (wstatus) {
3983
return p;
3984
} else if (WIFEXITED(wstatus2) && WEXITSTATUS(wstatus2) == EXIT_SUCCESS) {
3985
return p;
3986
}
3987
3988
return -1;
3989
}
3990
3991
void
3992
do_potential_fsck(void)
3993
{
3994
/* XXX: This whole function's logic needs to be redone. */
3995
3996
const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
3997
const char *fsck_tool[] = { "fsck", "-q", NULL };
3998
const char *remount_tool[] = { "mount", "-uw", "/", NULL };
3999
#if TARGET_OS_EMBEDDED
4000
const char *nvram_tool[] = { "/usr/sbin/nvram", "auto-boot=false", NULL };
4001
#endif /* TARGET_OS_EMBEDDED */
4002
struct statfs sfs;
4003
int status = 0;
4004
4005
if (posix_assumes_zero(statfs("/", &sfs)) == -1) {
4006
return;
4007
}
4008
4009
if (!(sfs.f_flags & MNT_RDONLY)) {
4010
return;
4011
}
4012
4013
if (!is_safeboot()) {
4014
#if 0
4015
/* We have disabled this block for now. We need to revisit this optimization after Leopard. */
4016
if (sfs.f_flags & MNT_JOURNALED) {
4017
goto out;
4018
}
4019
#endif
4020
launchctl_log(LOG_NOTICE, "Running fsck on the boot volume...");
4021
if (fwexec(fsck_tool, &status) != -1) {
4022
if (WEXITSTATUS(status) != 0) {
4023
launchctl_log(LOG_NOTICE, "fsck exited with status: %d", WEXITSTATUS(status));
4024
} else {
4025
goto out;
4026
}
4027
} else {
4028
launchctl_log(LOG_NOTICE, "fwexec(): %d: %s", errno, strerror(errno));
4029
}
4030
}
4031
4032
launchctl_log(LOG_NOTICE, "Running safe fsck on the boot volume...");
4033
if (fwexec(safe_fsck_tool, &status) != -1) {
4034
if (WEXITSTATUS(status) != 0) {
4035
launchctl_log(LOG_NOTICE, "Safe fsck exited with status: %d", WEXITSTATUS(status));
4036
} else {
4037
goto out;
4038
}
4039
} else {
4040
launchctl_log(LOG_NOTICE, "fwexec(): %d: %s", errno, strerror(errno));
4041
}
4042
4043
/* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
4044
#if TARGET_OS_EMBEDDED
4045
launchctl_log(LOG_NOTICE, "fsck failed! Booting into restore mode...");
4046
(void)posix_assumes_zero(fwexec(nvram_tool, NULL));
4047
(void)reboot(RB_AUTOBOOT);
4048
#else
4049
launchctl_log(LOG_NOTICE, "fsck failed! Shutting down in 3 seconds.");
4050
sleep(3);
4051
(void)reboot(RB_HALT);
4052
#endif
4053
4054
return;
4055
out:
4056
4057
#if TARGET_OS_EMBEDDED
4058
/* Once we've validated the root filesystem, kick off any
4059
* tasks needed for data protection before we mount other file
4060
* systems.
4061
*/
4062
init_data_protection();
4063
#endif
4064
4065
/*
4066
* Once this is fixed:
4067
*
4068
* <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount()
4069
*
4070
* We can then do this one system call instead of calling out a full blown process.
4071
*
4072
* assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1);
4073
*/
4074
#if TARGET_OS_EMBEDDED
4075
if (path_check("/etc/fstab")) {
4076
const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
4077
if (posix_assumes_zero(fwexec(mount_tool, NULL)) == -1) {
4078
(void)fwexec(nvram_tool, NULL);
4079
(void)reboot(RB_AUTOBOOT);
4080
}
4081
} else
4082
#endif
4083
{
4084
(void)posix_assumes_zero(fwexec(remount_tool, NULL));
4085
}
4086
4087
fix_bogus_file_metadata();
4088
}
4089
4090
void
4091
fix_bogus_file_metadata(void)
4092
{
4093
// Don't do any of this on embedded: <rdar://problem/13212363>
4094
#if !TARGET_OS_EMBEDDED
4095
static const struct {
4096
const char *path;
4097
const uid_t owner;
4098
const gid_t group;
4099
const mode_t needed_bits;
4100
const mode_t bad_bits;
4101
const bool create;
4102
} f[] = {
4103
{ "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH, false },
4104
{ _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true },
4105
{ _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true },
4106
{ "/var/folders", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_ISUID | S_ISGID, true },
4107
{ LAUNCHD_DB_PREFIX, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true },
4108
{ LAUNCHD_DB_PREFIX "/com.apple.launchd", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true },
4109
// Fixing <rdar://problem/7571633>.
4110
{ _PATH_VARDB, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
4111
{ _PATH_VARDB "mds/", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
4112
// Similar fix for <rdar://problem/6550172>.
4113
{ "/Library/StartupItems", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
4114
};
4115
struct stat sb;
4116
size_t i;
4117
4118
for (i = 0; i < (sizeof(f) / sizeof(f[0])); i++) {
4119
mode_t i_needed_bits;
4120
mode_t i_bad_bits;
4121
bool fix_mode = false;
4122
bool fix_id = false;
4123
4124
if (stat(f[i].path, &sb) == -1) {
4125
launchctl_log(LOG_NOTICE, "Crucial filesystem check: Path not present: %s. %s", f[i].path, f[i].create ? "Will create." : "");
4126
if (f[i].create) {
4127
if (posix_assumes_zero(mkdir(f[i].path, f[i].needed_bits)) == -1) {
4128
continue;
4129
} else if (posix_assumes_zero(stat(f[i].path, &sb)) == -1) {
4130
continue;
4131
}
4132
} else {
4133
continue;
4134
}
4135
}
4136
4137
i_needed_bits = ~sb.st_mode & f[i].needed_bits;
4138
i_bad_bits = sb.st_mode & f[i].bad_bits;
4139
4140
if (i_bad_bits) {
4141
launchctl_log(LOG_ERR, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s", i_bad_bits, f[i].path);
4142
fix_mode = true;
4143
}
4144
if (i_needed_bits) {
4145
launchctl_log(LOG_ERR, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s", i_needed_bits, f[i].path);
4146
fix_mode = true;
4147
}
4148
if (sb.st_uid != f[i].owner) {
4149
launchctl_log(LOG_ERR, "Crucial filesystem check: Fixing bogus UID %u on path: %s", sb.st_uid, f[i].path);
4150
fix_id = true;
4151
}
4152
if (sb.st_gid != f[i].group) {
4153
launchctl_log(LOG_ERR, "Crucial filesystem check: Fixing bogus GID %u on path: %s", sb.st_gid, f[i].path);
4154
fix_id = true;
4155
}
4156
4157
if (fix_mode) {
4158
(void)posix_assumes_zero(chmod(f[i].path, (sb.st_mode & ~i_bad_bits) | i_needed_bits));
4159
}
4160
if (fix_id) {
4161
(void)posix_assumes_zero(chown(f[i].path, f[i].owner, f[i].group));
4162
}
4163
}
4164
#endif
4165
}
4166
4167
4168
bool
4169
path_check(const char *path)
4170
{
4171
struct stat sb;
4172
4173
if (stat(path, &sb) == 0)
4174
return true;
4175
return false;
4176
}
4177
4178
bool
4179
is_safeboot(void)
4180
{
4181
int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
4182
uint32_t sb = 0;
4183
size_t sbsz = sizeof(sb);
4184
4185
if (posix_assumes_zero(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0)) == -1) {
4186
return false;
4187
}
4188
4189
return (bool)sb;
4190
}
4191
4192
bool
4193
is_netboot(void)
4194
{
4195
int nbmib[] = { CTL_KERN, KERN_NETBOOT };
4196
uint32_t nb = 0;
4197
size_t nbsz = sizeof(nb);
4198
4199
if (posix_assumes_zero(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0)) == -1) {
4200
return false;
4201
}
4202
4203
return (bool)nb;
4204
}
4205
4206
void
4207
empty_dir(const char *thedir, struct stat *psb)
4208
{
4209
struct dirent *de;
4210
struct stat psb2;
4211
DIR *od;
4212
int currend_dir_fd;
4213
4214
if (!psb) {
4215
psb = &psb2;
4216
if (posix_assumes_zero(lstat(thedir, psb)) == -1) {
4217
return;
4218
}
4219
}
4220
4221
if (posix_assumes_zero(currend_dir_fd = open(".", 0)) == -1) {
4222
return;
4223
}
4224
4225
if (posix_assumes_zero(chdir(thedir)) == -1) {
4226
goto out;
4227
}
4228
4229
if (!(od = opendir("."))) {
4230
(void)os_assumes_zero(errno);
4231
goto out;
4232
}
4233
4234
while ((de = readdir(od))) {
4235
struct stat sb;
4236
4237
if (strcmp(de->d_name, ".") == 0) {
4238
continue;
4239
}
4240
4241
if (strcmp(de->d_name, "..") == 0) {
4242
continue;
4243
}
4244
4245
if (posix_assumes_zero(lstat(de->d_name, &sb)) == -1) {
4246
continue;
4247
}
4248
4249
if (psb->st_dev != sb.st_dev) {
4250
(void)posix_assumes_zero(unmount(de->d_name, MNT_FORCE));
4251
4252
/* Let's lstat() again to see if the unmount() worked and what was
4253
* under it.
4254
*/
4255
if (posix_assumes_zero(lstat(de->d_name, &sb)) == -1) {
4256
continue;
4257
}
4258
4259
if (os_assumes(psb->st_dev == sb.st_dev)) {
4260
continue;
4261
}
4262
}
4263
4264
if (S_ISDIR(sb.st_mode)) {
4265
empty_dir(de->d_name, &sb);
4266
}
4267
4268
(void)posix_assumes_zero(lchflags(de->d_name, 0));
4269
(void)posix_assumes_zero(remove(de->d_name));
4270
}
4271
4272
(void)closedir(od);
4273
4274
out:
4275
(void)posix_assumes_zero(fchdir(currend_dir_fd));
4276
(void)posix_assumes_zero(close(currend_dir_fd));
4277
}
4278
4279
int
4280
touch_file(const char *path, mode_t m)
4281
{
4282
int fd = open(path, O_CREAT, m);
4283
4284
if (fd == -1)
4285
return -1;
4286
4287
return close(fd);
4288
}
4289
4290
void
4291
apply_sysctls_from_file(const char *thefile)
4292
{
4293
const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
4294
size_t ln_len = 0;
4295
char *val, *tmpstr;
4296
FILE *sf;
4297
4298
if (!(sf = fopen(thefile, "r")))
4299
return;
4300
4301
while ((val = fgetln(sf, &ln_len))) {
4302
if (ln_len == 0) {
4303
continue;
4304
}
4305
if (!(tmpstr = malloc(ln_len + 1))) {
4306
(void)os_assumes_zero(errno);
4307
continue;
4308
}
4309
memcpy(tmpstr, val, ln_len);
4310
tmpstr[ln_len] = 0;
4311
val = tmpstr;
4312
4313
if (val[ln_len - 1] == '\n' || val[ln_len - 1] == '\r') {
4314
val[ln_len - 1] = '\0';
4315
}
4316
4317
while (*val && isspace(*val))
4318
val++;
4319
if (*val == '\0' || *val == '#') {
4320
goto skip_sysctl_tool;
4321
}
4322
sysctl_tool[2] = val;
4323
(void)posix_assumes_zero(fwexec(sysctl_tool, NULL));
4324
skip_sysctl_tool:
4325
free(tmpstr);
4326
}
4327
4328
(void)fclose(sf);
4329
}
4330
4331
static CFStringRef
4332
copySystemBuildVersion(void)
4333
{
4334
CFStringRef build = NULL;
4335
const char path[] = "/System/Library/CoreServices/SystemVersion.plist";
4336
CFURLRef plistURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (const uint8_t *)path, sizeof(path) - 1, false);
4337
4338
CFPropertyListRef plist = NULL;
4339
if (plistURL && (plist = CFPropertyListCreateFromFile(plistURL))) {
4340
if (CFTypeCheck(plist, CFDictionary)) {
4341
build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey);
4342
if (build && CFTypeCheck(build, CFString)) {
4343
CFRetain(build);
4344
} else {
4345
build = CFSTR("99Z999");
4346
}
4347
}
4348
4349
CFRelease(plist);
4350
} else {
4351
build = CFSTR("99Z999");
4352
}
4353
4354
if (plistURL) {
4355
CFRelease(plistURL);
4356
}
4357
4358
return build;
4359
}
4360
4361
void
4362
do_sysversion_sysctl(void)
4363
{
4364
int mib[] = { CTL_KERN, KERN_OSVERSION };
4365
CFStringRef buildvers;
4366
char buf[1024];
4367
size_t bufsz = sizeof(buf);
4368
4369
/* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
4370
4371
if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
4372
launchctl_log(LOG_ERR, "sysctl(): %s", strerror(errno));
4373
return;
4374
}
4375
4376
if (buf[0] != '\0') {
4377
return;
4378
}
4379
4380
buildvers = copySystemBuildVersion();
4381
if (buildvers) {
4382
CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
4383
(void)posix_assumes_zero(sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1));
4384
}
4385
4386
CFRelease(buildvers);
4387
}
4388
4389
void
4390
do_application_firewall_magic(int sfd, launch_data_t thejob)
4391
{
4392
const char *prog = NULL, *partialprog = NULL;
4393
char *path, *pathtmp, **pathstmp;
4394
char *paths[100];
4395
launch_data_t tmp;
4396
4397
/*
4398
* Sigh...
4399
* <rdar://problem/4684434> setsockopt() with the executable path as the argument
4400
*/
4401
4402
if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM))) {
4403
prog = launch_data_get_string(tmp);
4404
}
4405
4406
if (!prog) {
4407
if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
4408
if ((tmp = launch_data_array_get_index(tmp, 0))) {
4409
if ((partialprog = launch_data_get_string(tmp))) {
4410
if (partialprog[0] == '/') {
4411
prog = partialprog;
4412
}
4413
}
4414
}
4415
}
4416
}
4417
4418
if (!prog) {
4419
pathtmp = path = strdup(getenv("PATH"));
4420
4421
pathstmp = paths;
4422
4423
while ((*pathstmp = strsep(&pathtmp, ":"))) {
4424
if (**pathstmp != '\0') {
4425
pathstmp++;
4426
}
4427
}
4428
4429
free(path);
4430
pathtmp = alloca(MAXPATHLEN);
4431
4432
pathstmp = paths;
4433
4434
for (; *pathstmp; pathstmp++) {
4435
snprintf(pathtmp, MAXPATHLEN, "%s/%s", *pathstmp, partialprog);
4436
if (path_check(pathtmp)) {
4437
prog = pathtmp;
4438
break;
4439
}
4440
}
4441
}
4442
4443
if (prog != NULL) {
4444
/* The networking team has asked us to ignore the failure of this API if
4445
* errno == ENOPROTOOPT.
4446
*/
4447
if (setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, (socklen_t)(strlen(prog) + 1)) == -1 && errno != ENOPROTOOPT) {
4448
(void)os_assumes_zero(errno);
4449
}
4450
}
4451
}
4452
4453
4454
void
4455
preheat_page_cache_hack(void)
4456
{
4457
struct dirent *de;
4458
DIR *thedir;
4459
4460
/* Disable this hack for now */
4461
return;
4462
4463
if ((thedir = opendir("/etc/preheat_at_boot")) == NULL) {
4464
return;
4465
}
4466
4467
while ((de = readdir(thedir))) {
4468
struct stat sb;
4469
void *junkbuf;
4470
int fd;
4471
4472
if (de->d_name[0] == '.') {
4473
continue;
4474
}
4475
4476
if ((fd = open(de->d_name, O_RDONLY)) == -1) {
4477
continue;
4478
}
4479
4480
if (fstat(fd, &sb) != -1) {
4481
if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) {
4482
ssize_t n = read(fd, junkbuf, (size_t)sb.st_size);
4483
if (posix_assumes_zero(n) != -1 && n != (ssize_t)sb.st_size) {
4484
(void)os_assumes_zero(n);
4485
}
4486
free(junkbuf);
4487
}
4488
}
4489
4490
close(fd);
4491
}
4492
4493
closedir(thedir);
4494
}
4495
4496
void
4497
do_bootroot_magic(void)
4498
{
4499
const char *kextcache_tool[] = { "kextcache", "-U", "/", NULL };
4500
CFTypeRef bootrootProp;
4501
io_service_t chosen;
4502
int wstatus;
4503
pid_t p;
4504
4505
chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
4506
4507
if (!os_assumes(chosen)) {
4508
return;
4509
}
4510
4511
bootrootProp = IORegistryEntryCreateCFProperty(chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault, 0);
4512
4513
IOObjectRelease(chosen);
4514
4515
if (!bootrootProp) {
4516
return;
4517
}
4518
4519
CFRelease(bootrootProp);
4520
4521
if (posix_assumes_zero(p = fwexec(kextcache_tool, &wstatus)) == -1) {
4522
return;
4523
}
4524
4525
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EX_OSFILE) {
4526
(void)reboot(RB_AUTOBOOT);
4527
}
4528
}
4529
4530
void
4531
do_file_init(void)
4532
{
4533
struct stat sb;
4534
4535
if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) {
4536
_launchctl_apple_internal = true;
4537
}
4538
4539
char bootargs[128];
4540
size_t len = sizeof(bootargs);
4541
int r = sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0);
4542
if (r == 0 && (strnstr(bootargs, "-v", len) != NULL || strnstr(bootargs, "-s", len))) {
4543
_launchctl_verbose_boot = true;
4544
}
4545
4546
if (stat("/var/db/.launchd_shutdown_debugging", &sb) == 0 && _launchctl_verbose_boot) {
4547
_launchctl_startup_debugging = true;
4548
}
4549
}
4550
4551