2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
21 static const char *const __rcs_file_version__
= "$Revision: 1.88 $";
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <CoreFoundation/CFPriv.h>
25 #include <NSSystemDirectories.h>
26 #include <mach/mach.h>
27 #include <sys/types.h>
28 #include <sys/sysctl.h>
30 #include <sys/sysctl.h>
32 #include <sys/socket.h>
34 #include <sys/fcntl.h>
35 #include <sys/event.h>
36 #include <sys/resource.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/reboot.h>
41 #include <netinet/in.h>
42 #include <netinet/in_var.h>
43 #include <netinet6/nd6.h>
55 #include <readline/readline.h>
56 #include <readline/history.h>
62 #include "bootstrap_public.h"
63 #include "bootstrap_private.h"
65 #include "launch_priv.h"
67 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
69 #define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
70 #define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
71 #define MACHINIT_JOBKEY_COMMAND "Command"
72 #define MACHINIT_JOBKEY_SERVERPORT "ServerPort"
73 #define MACHINIT_JOBKEY_SERVICEPORT "ServicePort"
76 (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true)
79 struct load_unload_state
{
84 unsigned int editondisk
:1, load
:1, forceload
:1, __pad
:29;
87 static void myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
);
88 static bool launch_data_array_append(launch_data_t a
, launch_data_t o
);
89 static void distill_jobs(launch_data_t
);
90 static void distill_config_file(launch_data_t
);
91 static void sock_dict_cb(launch_data_t what
, const char *key
, void *context
);
92 static void sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
);
93 static launch_data_t
CF2launch_data(CFTypeRef
);
94 static launch_data_t
read_plist_file(const char *file
, bool editondisk
, bool load
);
95 static CFPropertyListRef
CreateMyPropertyListFromFile(const char *);
96 static void WriteMyPropertyListToFile(CFPropertyListRef
, const char *);
97 static bool path_goodness_check(const char *path
, bool forceload
);
98 static void readpath(const char *, struct load_unload_state
*);
99 static void readfile(const char *, struct load_unload_state
*);
101 static int demux_cmd(int argc
, char *const argv
[]);
102 static launch_data_t
do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
);
103 static void submit_job_pass(launch_data_t jobs
);
104 static void submit_mach_jobs(launch_data_t jobs
);
105 static void let_go_of_mach_jobs(launch_data_t jobs
);
106 static void do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
);
107 static mach_port_t
str2bsport(const char *s
);
108 static void print_jobs(launch_data_t j
);
109 static void print_obj(launch_data_t obj
, const char *key
, void *context
);
110 static bool is_legacy_mach_job(launch_data_t obj
);
111 static bool delay_to_second_pass(launch_data_t o
);
112 static void delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
);
113 static bool str2lim(const char *buf
, rlim_t
*res
);
114 static const char *lim2str(rlim_t val
, char *buf
);
115 static const char *num2name(int n
);
116 static ssize_t
name2num(const char *n
);
117 static void unloadjob(launch_data_t job
);
118 static void print_key_value(launch_data_t obj
, const char *key
, void *context
);
119 static void print_launchd_env(launch_data_t obj
, const char *key
, void *context
);
120 static void _log_launchctl_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
);
121 static void loopback_setup_ipv4(void);
122 static void loopback_setup_ipv6(void);
123 static pid_t
fwexec(const char *const *argv
, bool _wait
);
124 static void do_potential_fsck(void);
125 static bool path_check(const char *path
);
126 static bool is_safeboot(void);
127 static bool is_netboot(void);
128 static void apply_func_to_dir(const char *thedir
, void (*thefunc
)(const char *));
129 static void apply_sysctls_from_file(const char *thefile
);
130 static void empty_dir(const char *path
);
131 static int touch_file(const char *path
, mode_t m
);
132 static void do_sysversion_sysctl(void);
133 static void workaround4465949(void);
135 static int bootstrap_cmd(int argc
, char *const argv
[]);
136 static int load_and_unload_cmd(int argc
, char *const argv
[]);
137 //static int reload_cmd(int argc, char *const argv[]);
138 static int start_stop_remove_cmd(int argc
, char *const argv
[]);
139 static int submit_cmd(int argc
, char *const argv
[]);
140 static int list_cmd(int argc
, char *const argv
[]);
142 static int setenv_cmd(int argc
, char *const argv
[]);
143 static int unsetenv_cmd(int argc
, char *const argv
[]);
144 static int getenv_and_export_cmd(int argc
, char *const argv
[]);
146 static int limit_cmd(int argc
, char *const argv
[]);
147 static int stdio_cmd(int argc
, char *const argv
[]);
148 static int fyi_cmd(int argc
, char *const argv
[]);
149 static int logupdate_cmd(int argc
, char *const argv
[]);
150 static int umask_cmd(int argc
, char *const argv
[]);
151 static int getrusage_cmd(int argc
, char *const argv
[]);
152 static int bsexec_cmd(int argc
, char *const argv
[]);
153 static int bslist_cmd(int argc
, char *const argv
[]);
155 static int exit_cmd(int argc
, char *const argv
[]) __attribute__((noreturn
));
156 static int help_cmd(int argc
, char *const argv
[]);
158 static const struct {
160 int (*func
)(int argc
, char *const argv
[]);
163 { "load", load_and_unload_cmd
, "Load configuration files and/or directories" },
164 { "unload", load_and_unload_cmd
, "Unload configuration files and/or directories" },
165 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
166 { "start", start_stop_remove_cmd
, "Start specified job" },
167 { "stop", start_stop_remove_cmd
, "Stop specified job" },
168 { "submit", submit_cmd
, "Submit a job from the command line" },
169 { "remove", start_stop_remove_cmd
, "Remove specified job" },
170 { "bootstrap", bootstrap_cmd
, "Bootstrap launchd" },
171 { "list", list_cmd
, "List jobs and information about jobs" },
172 { "setenv", setenv_cmd
, "Set an environmental variable in launchd" },
173 { "unsetenv", unsetenv_cmd
, "Unset an environmental variable in launchd" },
174 { "getenv", getenv_and_export_cmd
, "Get an environmental variable from launchd" },
175 { "export", getenv_and_export_cmd
, "Export shell settings from launchd" },
176 { "limit", limit_cmd
, "View and adjust launchd resource limits" },
177 { "stdout", stdio_cmd
, "Redirect launchd's standard out to the given path" },
178 { "stderr", stdio_cmd
, "Redirect launchd's standard error to the given path" },
179 { "shutdown", fyi_cmd
, "Prepare for system shutdown" },
180 { "singleuser", fyi_cmd
, "Switch to single-user mode" },
181 { "reloadttys", fyi_cmd
, "Reload /etc/ttys" },
182 { "getrusage", getrusage_cmd
, "Get resource usage statistics from launchd" },
183 { "log", logupdate_cmd
, "Adjust the logging level or mask of launchd" },
184 { "umask", umask_cmd
, "Change launchd's umask" },
185 { "bsexec", bsexec_cmd
, "Execute a process within a different Mach bootstrap subset" },
186 { "bslist", bslist_cmd
, "List Mach bootstrap services and optional servers" },
187 { "exit", exit_cmd
, "Exit the interactive invocation of launchctl" },
188 { "quit", exit_cmd
, "Quit the interactive invocation of launchctl" },
189 { "help", help_cmd
, "This help output" },
192 static bool istty
= false;
193 static bool verbose
= false;
196 main(int argc
, char *const argv
[])
200 do_sysversion_sysctl();
202 istty
= isatty(STDIN_FILENO
);
206 if (argc
> 0 && argv
[0][0] == '-') {
209 for (flago
= argv
[0] + 1; *flago
; flago
++) {
215 fprintf(stderr
, "Unknown argument: '-%c'\n", *flago
);
222 if (NULL
== readline
) {
223 fprintf(stderr
, "missing library: readline\n");
228 while ((l
= readline(istty
? "launchd% " : NULL
))) {
229 char *inputstring
= l
, *argv2
[100], **ap
= argv2
;
232 while ((*ap
= strsep(&inputstring
, " \t"))) {
251 exit(demux_cmd(argc
, argv
));
258 demux_cmd(int argc
, char *const argv
[])
265 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
266 if (!strcmp(cmds
[i
].name
, argv
[0]))
267 return cmds
[i
].func(argc
, argv
);
270 fprintf(stderr
, "%s: unknown subcommand \"%s\"\n", getprogname(), argv
[0]);
275 unsetenv_cmd(int argc
, char *const argv
[])
277 launch_data_t resp
, tmp
, msg
;
280 fprintf(stderr
, "%s usage: unsetenv <key>\n", getprogname());
284 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
286 tmp
= launch_data_new_string(argv
[1]);
287 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_UNSETUSERENVIRONMENT
);
289 resp
= launch_msg(msg
);
291 launch_data_free(msg
);
294 launch_data_free(resp
);
296 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT
, strerror(errno
));
303 setenv_cmd(int argc
, char *const argv
[])
305 launch_data_t resp
, tmp
, tmpv
, msg
;
308 fprintf(stderr
, "%s usage: setenv <key> <value>\n", getprogname());
312 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
313 tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
315 tmpv
= launch_data_new_string(argv
[2]);
316 launch_data_dict_insert(tmp
, tmpv
, argv
[1]);
317 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETUSERENVIRONMENT
);
319 resp
= launch_msg(msg
);
320 launch_data_free(msg
);
323 launch_data_free(resp
);
325 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT
, strerror(errno
));
332 print_launchd_env(launch_data_t obj
, const char *key
, void *context
)
334 bool *is_csh
= context
;
336 /* XXX escape the double quotes */
338 fprintf(stdout
, "setenv %s \"%s\";\n", key
, launch_data_get_string(obj
));
340 fprintf(stdout
, "%s=\"%s\"; export %s;\n", key
, launch_data_get_string(obj
), key
);
344 print_key_value(launch_data_t obj
, const char *key
, void *context
)
346 const char *k
= context
;
349 fprintf(stdout
, "%s\n", launch_data_get_string(obj
));
353 getenv_and_export_cmd(int argc
, char *const argv
[] __attribute__((unused
)))
355 launch_data_t resp
, msg
;
359 if (!strcmp(argv
[0], "export")) {
360 char *s
= getenv("SHELL");
362 is_csh
= strstr(s
, "csh") ? true : false;
363 } else if (argc
!= 2) {
364 fprintf(stderr
, "%s usage: getenv <key>\n", getprogname());
370 msg
= launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT
);
372 resp
= launch_msg(msg
);
373 launch_data_free(msg
);
376 if (!strcmp(argv
[0], "export"))
377 launch_data_dict_iterate(resp
, print_launchd_env
, &is_csh
);
379 launch_data_dict_iterate(resp
, print_key_value
, k
);
380 launch_data_free(resp
);
382 fprintf(stderr
, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT
"\"): %s\n", strerror(errno
));
388 unloadjob(launch_data_t job
)
390 launch_data_t resp
, tmp
, tmps
, msg
;
393 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
394 tmp
= launch_data_alloc(LAUNCH_DATA_STRING
);
395 tmps
= launch_data_dict_lookup(job
, LAUNCH_JOBKEY_LABEL
);
398 fprintf(stderr
, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL
);
402 launch_data_set_string(tmp
, launch_data_get_string(tmps
));
403 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_REMOVEJOB
);
404 resp
= launch_msg(msg
);
405 launch_data_free(msg
);
407 fprintf(stderr
, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno
));
410 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(resp
)) {
411 if ((e
= launch_data_get_errno(resp
)))
412 fprintf(stderr
, "%s\n", strerror(e
));
414 launch_data_free(resp
);
418 read_plist_file(const char *file
, bool editondisk
, bool load
)
420 CFPropertyListRef plist
= CreateMyPropertyListFromFile(file
);
421 launch_data_t r
= NULL
;
424 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), file
);
430 CFDictionaryRemoveValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
));
432 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
), kCFBooleanTrue
);
433 WriteMyPropertyListToFile(plist
, file
);
436 r
= CF2launch_data(plist
);
444 delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
)
449 if (key
&& 0 == strcmp(key
, LAUNCH_JOBSOCKETKEY_BONJOUR
)) {
454 switch (launch_data_get_type(o
)) {
455 case LAUNCH_DATA_DICTIONARY
:
456 launch_data_dict_iterate(o
, delay_to_second_pass2
, context
);
458 case LAUNCH_DATA_ARRAY
:
459 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
460 delay_to_second_pass2(launch_data_array_get_index(o
, i
), NULL
, context
);
468 delay_to_second_pass(launch_data_t o
)
472 launch_data_t socks
= launch_data_dict_lookup(o
, LAUNCH_JOBKEY_SOCKETS
);
477 delay_to_second_pass2(socks
, NULL
, &res
);
483 readfile(const char *what
, struct load_unload_state
*lus
)
485 char ourhostname
[1024];
486 launch_data_t tmpd
, tmps
, thejob
, tmpa
;
487 bool job_disabled
= false;
490 gethostname(ourhostname
, sizeof(ourhostname
));
492 if (NULL
== (thejob
= read_plist_file(what
, lus
->editondisk
, lus
->load
))) {
493 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), what
);
497 if (is_legacy_mach_job(thejob
)) {
498 fprintf(stderr
, "%s: Please convert the following to launchd: %s\n", getprogname(), what
);
499 launch_data_array_append(lus
->pass0
, thejob
);
503 if (NULL
== launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LABEL
)) {
504 fprintf(stderr
, "%s: missing the Label key: %s\n", getprogname(), what
);
508 if (NULL
!= (tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS
))) {
509 c
= launch_data_array_get_count(tmpa
);
511 for (i
= 0; i
< c
; i
++) {
512 launch_data_t oai
= launch_data_array_get_index(tmpa
, i
);
513 if (!strcasecmp(ourhostname
, launch_data_get_string(oai
)))
518 if (NULL
!= (tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADTOHOSTS
))) {
519 c
= launch_data_array_get_count(tmpa
);
521 for (i
= 0; i
< c
; i
++) {
522 launch_data_t oai
= launch_data_array_get_index(tmpa
, i
);
523 if (!strcasecmp(ourhostname
, launch_data_get_string(oai
)))
531 if ((tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
))) {
532 const char *allowed_session
;
535 if (lus
->session_type
) switch (launch_data_get_type(tmpa
)) {
536 case LAUNCH_DATA_ARRAY
:
537 c
= launch_data_array_get_count(tmpa
);
538 for (i
= 0; i
< c
; i
++) {
539 tmps
= launch_data_array_get_index(tmpa
, i
);
540 allowed_session
= launch_data_get_string(tmps
);
541 if (strcasecmp(lus
->session_type
, allowed_session
) == 0) {
547 case LAUNCH_DATA_STRING
:
548 allowed_session
= launch_data_get_string(tmpa
);
549 if (strcasecmp(lus
->session_type
, allowed_session
) == 0) {
562 if ((tmpd
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_DISABLED
)))
563 job_disabled
= launch_data_get_bool(tmpd
);
566 job_disabled
= false;
568 if (job_disabled
&& lus
->load
)
571 if (delay_to_second_pass(thejob
))
572 launch_data_array_append(lus
->pass2
, thejob
);
574 launch_data_array_append(lus
->pass1
, thejob
);
577 fprintf(stdout
, "Will load: %s\n", what
);
582 fprintf(stdout
, "Ignored: %s\n", what
);
583 launch_data_free(thejob
);
587 path_goodness_check(const char *path
, bool forceload
)
591 if (stat(path
, &sb
) == -1) {
592 fprintf(stderr
, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path
, strerror(errno
));
599 if (sb
.st_mode
& (S_IWOTH
|S_IWGRP
)) {
600 fprintf(stderr
, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path
);
604 if (sb
.st_uid
!= 0 && sb
.st_uid
!= getuid()) {
605 fprintf(stderr
, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path
);
609 if (!(S_ISREG(sb
.st_mode
) || S_ISDIR(sb
.st_mode
))) {
610 fprintf(stderr
, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path
);
618 readpath(const char *what
, struct load_unload_state
*lus
)
620 char buf
[MAXPATHLEN
];
625 if (!path_goodness_check(what
, lus
->forceload
))
628 if (stat(what
, &sb
) == -1)
631 if (S_ISREG(sb
.st_mode
)) {
633 } else if (S_ISDIR(sb
.st_mode
)) {
634 if ((d
= opendir(what
)) == NULL
) {
635 fprintf(stderr
, "%s: opendir() failed to open the directory\n", getprogname());
639 while ((de
= readdir(d
))) {
640 if ((de
->d_name
[0] == '.'))
642 snprintf(buf
, sizeof(buf
), "%s/%s", what
, de
->d_name
);
644 if (!path_goodness_check(buf
, lus
->forceload
))
653 struct distill_context
{
655 launch_data_t newsockdict
;
659 distill_jobs(launch_data_t jobs
)
661 size_t i
, c
= launch_data_array_get_count(jobs
);
663 for (i
= 0; i
< c
; i
++)
664 distill_config_file(launch_data_array_get_index(jobs
, i
));
668 distill_config_file(launch_data_t id_plist
)
670 struct distill_context dc
= { id_plist
, NULL
};
673 if ((tmp
= launch_data_dict_lookup(dc
.base
, LAUNCH_JOBKEY_SOCKETS
))) {
674 dc
.newsockdict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
675 launch_data_dict_iterate(tmp
, sock_dict_cb
, &dc
);
676 launch_data_dict_insert(dc
.base
, dc
.newsockdict
, LAUNCH_JOBKEY_SOCKETS
);
681 sock_dict_cb(launch_data_t what
, const char *key
, void *context
)
683 struct distill_context
*dc
= context
;
684 launch_data_t fdarray
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
686 launch_data_dict_insert(dc
->newsockdict
, fdarray
, key
);
688 if (launch_data_get_type(what
) == LAUNCH_DATA_DICTIONARY
) {
689 sock_dict_edit_entry(what
, key
, fdarray
, dc
->base
);
690 } else if (launch_data_get_type(what
) == LAUNCH_DATA_ARRAY
) {
694 for (i
= 0; i
< launch_data_array_get_count(what
); i
++) {
695 tmp
= launch_data_array_get_index(what
, i
);
696 sock_dict_edit_entry(tmp
, key
, fdarray
, dc
->base
);
702 sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
)
704 launch_data_t a
, val
;
705 int sfd
, st
= SOCK_STREAM
;
708 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_TYPE
))) {
709 if (!strcasecmp(launch_data_get_string(val
), "stream")) {
711 } else if (!strcasecmp(launch_data_get_string(val
), "dgram")) {
713 } else if (!strcasecmp(launch_data_get_string(val
), "seqpacket")) {
718 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PASSIVE
)))
719 passive
= launch_data_get_bool(val
);
721 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY
))) {
722 char secdir
[] = LAUNCH_SECDIR
, buf
[1024];
723 launch_data_t uenv
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
726 uenv
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
727 launch_data_dict_insert(thejob
, uenv
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
732 sprintf(buf
, "%s/%s", secdir
, key
);
734 a
= launch_data_new_string(buf
);
735 launch_data_dict_insert(tmp
, a
, LAUNCH_JOBSOCKETKEY_PATHNAME
);
736 a
= launch_data_new_string(buf
);
737 launch_data_dict_insert(uenv
, a
, launch_data_get_string(val
));
740 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHNAME
))) {
741 struct sockaddr_un sun
;
746 memset(&sun
, 0, sizeof(sun
));
748 sun
.sun_family
= AF_UNIX
;
750 strncpy(sun
.sun_path
, launch_data_get_string(val
), sizeof(sun
.sun_path
));
752 if ((sfd
= _fd(socket(AF_UNIX
, st
, 0))) == -1)
755 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHMODE
))) {
756 sun_mode
= (mode_t
)launch_data_get_integer(val
);
761 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
765 oldmask
= umask(S_IRWXG
|S_IRWXO
);
766 if (bind(sfd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
773 chmod(sun
.sun_path
, sun_mode
);
775 if ((st
== SOCK_STREAM
|| st
== SOCK_SEQPACKET
)
776 && listen(sfd
, SOMAXCONN
) == -1) {
780 } else if (connect(sfd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
785 val
= launch_data_new_fd(sfd
);
786 launch_data_array_append(fdarray
, val
);
788 launch_data_t rnames
= NULL
;
789 const char *node
= NULL
, *serv
= NULL
, *mgroup
= NULL
;
791 struct addrinfo hints
, *res0
, *res
;
792 int gerr
, sock_opt
= 1;
793 bool rendezvous
= false;
795 memset(&hints
, 0, sizeof(hints
));
797 hints
.ai_socktype
= st
;
799 hints
.ai_flags
|= AI_PASSIVE
;
801 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_NODENAME
)))
802 node
= launch_data_get_string(val
);
803 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP
)))
804 mgroup
= launch_data_get_string(val
);
805 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SERVICENAME
))) {
806 if (LAUNCH_DATA_INTEGER
== launch_data_get_type(val
)) {
807 sprintf(servnbuf
, "%lld", launch_data_get_integer(val
));
810 serv
= launch_data_get_string(val
);
813 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_FAMILY
))) {
814 if (!strcasecmp("IPv4", launch_data_get_string(val
)))
815 hints
.ai_family
= AF_INET
;
816 else if (!strcasecmp("IPv6", launch_data_get_string(val
)))
817 hints
.ai_family
= AF_INET6
;
819 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PROTOCOL
))) {
820 if (!strcasecmp("TCP", launch_data_get_string(val
)))
821 hints
.ai_protocol
= IPPROTO_TCP
;
823 if ((rnames
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_BONJOUR
))) {
825 if (LAUNCH_DATA_BOOL
== launch_data_get_type(rnames
)) {
826 rendezvous
= launch_data_get_bool(rnames
);
831 if ((gerr
= getaddrinfo(node
, serv
, &hints
, &res0
)) != 0) {
832 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
836 for (res
= res0
; res
; res
= res
->ai_next
) {
837 launch_data_t rvs_fd
= NULL
;
838 if ((sfd
= _fd(socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
))) == -1) {
839 fprintf(stderr
, "socket(): %s\n", strerror(errno
));
842 if (hints
.ai_flags
& AI_PASSIVE
) {
843 if (AF_INET6
== res
->ai_family
&& -1 == setsockopt(sfd
, IPPROTO_IPV6
, IPV6_V6ONLY
,
844 (void *)&sock_opt
, sizeof(sock_opt
))) {
845 fprintf(stderr
, "setsockopt(IPV6_V6ONLY): %m");
849 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEPORT
, (void *)&sock_opt
, sizeof(sock_opt
)) == -1) {
850 fprintf(stderr
, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno
));
854 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&sock_opt
, sizeof(sock_opt
)) == -1) {
855 fprintf(stderr
, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno
));
859 if (bind(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
860 fprintf(stderr
, "bind(): %s\n", strerror(errno
));
865 do_mgroup_join(sfd
, res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
, mgroup
);
867 if ((res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_SEQPACKET
)
868 && listen(sfd
, SOMAXCONN
) == -1) {
869 fprintf(stderr
, "listen(): %s\n", strerror(errno
));
872 if (rendezvous
&& (res
->ai_family
== AF_INET
|| res
->ai_family
== AF_INET6
) &&
873 (res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_DGRAM
)) {
874 launch_data_t rvs_fds
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_BONJOURFDS
);
875 if (NULL
== rvs_fds
) {
876 rvs_fds
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
877 launch_data_dict_insert(thejob
, rvs_fds
, LAUNCH_JOBKEY_BONJOURFDS
);
879 if (NULL
== rnames
) {
880 rvs_fd
= do_rendezvous_magic(res
, serv
);
882 launch_data_array_append(rvs_fds
, rvs_fd
);
883 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(rnames
)) {
884 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rnames
));
886 launch_data_array_append(rvs_fds
, rvs_fd
);
887 } else if (LAUNCH_DATA_ARRAY
== launch_data_get_type(rnames
)) {
888 size_t rn_i
, rn_ac
= launch_data_array_get_count(rnames
);
890 for (rn_i
= 0; rn_i
< rn_ac
; rn_i
++) {
891 launch_data_t rn_tmp
= launch_data_array_get_index(rnames
, rn_i
);
893 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rn_tmp
));
895 launch_data_array_append(rvs_fds
, rvs_fd
);
900 if (connect(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
901 fprintf(stderr
, "connect(): %s\n", strerror(errno
));
905 val
= launch_data_new_fd(sfd
);
907 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
908 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
911 launch_data_array_append(fdarray
, val
);
917 do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
)
919 struct addrinfo hints
, *res0
, *res
;
921 struct ipv6_mreq m6req
;
924 memset(&hints
, 0, sizeof(hints
));
926 hints
.ai_flags
|= AI_PASSIVE
;
927 hints
.ai_family
= family
;
928 hints
.ai_socktype
= socktype
;
929 hints
.ai_protocol
= protocol
;
931 if ((gerr
= getaddrinfo(mgroup
, NULL
, &hints
, &res0
)) != 0) {
932 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
936 for (res
= res0
; res
; res
= res
->ai_next
) {
937 if (AF_INET
== family
) {
938 memset(&mreq
, 0, sizeof(mreq
));
939 mreq
.imr_multiaddr
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_addr
;
940 if (setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
)) == -1) {
941 fprintf(stderr
, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
945 } else if (AF_INET6
== family
) {
946 memset(&m6req
, 0, sizeof(m6req
));
947 m6req
.ipv6mr_multiaddr
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
;
948 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &m6req
, sizeof(m6req
)) == -1) {
949 fprintf(stderr
, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno
));
954 fprintf(stderr
, "unknown family during multicast group bind!\n");
964 do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
)
967 DNSServiceRef service
;
968 DNSServiceErrorType error
;
971 static int statres
= 1;
974 statres
= stat("/usr/sbin/mDNSResponder", &sb
);
979 sprintf(rvs_buf
, "_%s._%s.", serv
, res
->ai_socktype
== SOCK_STREAM
? "tcp" : "udp");
981 if (res
->ai_family
== AF_INET
)
982 port
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_port
;
984 port
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_port
;
986 error
= DNSServiceRegister(&service
, 0, 0, NULL
, rvs_buf
, NULL
, NULL
, port
, 0, NULL
, NULL
, NULL
);
988 if (error
== kDNSServiceErr_NoError
)
989 return launch_data_new_fd(DNSServiceRefSockFD(service
));
991 fprintf(stderr
, "DNSServiceRegister(\"%s\"): %d\n", serv
, error
);
996 CreateMyPropertyListFromFile(const char *posixfile
)
998 CFPropertyListRef propertyList
;
999 CFStringRef errorString
;
1000 CFDataRef resourceData
;
1004 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)posixfile
, strlen(posixfile
), false);
1006 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
1007 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, fileURL
, &resourceData
, NULL
, NULL
, &errorCode
))
1008 fprintf(stderr
, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
1009 propertyList
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, resourceData
, kCFPropertyListMutableContainers
, &errorString
);
1011 fprintf(stderr
, "%s: propertyList is NULL\n", getprogname());
1013 return propertyList
;
1017 WriteMyPropertyListToFile(CFPropertyListRef plist
, const char *posixfile
)
1019 CFDataRef resourceData
;
1023 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)posixfile
, strlen(posixfile
), false);
1025 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
1026 resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, plist
);
1027 if (resourceData
== NULL
)
1028 fprintf(stderr
, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile
);
1029 if (!CFURLWriteDataAndPropertiesToResource(fileURL
, resourceData
, NULL
, &errorCode
))
1030 fprintf(stderr
, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
1034 myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
)
1036 launch_data_t ik
, iw
, where
= context
;
1038 ik
= CF2launch_data(key
);
1039 iw
= CF2launch_data(value
);
1041 launch_data_dict_insert(where
, iw
, launch_data_get_string(ik
));
1042 launch_data_free(ik
);
1046 CF2launch_data(CFTypeRef cfr
)
1049 CFTypeID cft
= CFGetTypeID(cfr
);
1051 if (cft
== CFStringGetTypeID()) {
1053 CFStringGetCString(cfr
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
1054 r
= launch_data_alloc(LAUNCH_DATA_STRING
);
1055 launch_data_set_string(r
, buf
);
1056 } else if (cft
== CFBooleanGetTypeID()) {
1057 r
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1058 launch_data_set_bool(r
, CFBooleanGetValue(cfr
));
1059 } else if (cft
== CFArrayGetTypeID()) {
1060 CFIndex i
, ac
= CFArrayGetCount(cfr
);
1061 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1062 for (i
= 0; i
< ac
; i
++) {
1063 CFTypeRef v
= CFArrayGetValueAtIndex(cfr
, i
);
1065 launch_data_t iv
= CF2launch_data(v
);
1066 launch_data_array_set_index(r
, iv
, i
);
1069 } else if (cft
== CFDictionaryGetTypeID()) {
1070 r
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1071 CFDictionaryApplyFunction(cfr
, myCFDictionaryApplyFunction
, r
);
1072 } else if (cft
== CFDataGetTypeID()) {
1073 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1074 launch_data_set_opaque(r
, CFDataGetBytePtr(cfr
), CFDataGetLength(cfr
));
1075 } else if (cft
== CFNumberGetTypeID()) {
1078 CFNumberType cfnt
= CFNumberGetType(cfr
);
1080 case kCFNumberSInt8Type
:
1081 case kCFNumberSInt16Type
:
1082 case kCFNumberSInt32Type
:
1083 case kCFNumberSInt64Type
:
1084 case kCFNumberCharType
:
1085 case kCFNumberShortType
:
1086 case kCFNumberIntType
:
1087 case kCFNumberLongType
:
1088 case kCFNumberLongLongType
:
1089 CFNumberGetValue(cfr
, kCFNumberLongLongType
, &n
);
1090 r
= launch_data_alloc(LAUNCH_DATA_INTEGER
);
1091 launch_data_set_integer(r
, n
);
1093 case kCFNumberFloat32Type
:
1094 case kCFNumberFloat64Type
:
1095 case kCFNumberFloatType
:
1096 case kCFNumberDoubleType
:
1097 CFNumberGetValue(cfr
, kCFNumberDoubleType
, &d
);
1098 r
= launch_data_alloc(LAUNCH_DATA_REAL
);
1099 launch_data_set_real(r
, d
);
1112 help_cmd(int argc
, char *const argv
[])
1114 FILE *where
= stdout
;
1115 int l
, cmdwidth
= 0;
1118 if (argc
== 0 || argv
== NULL
)
1121 fprintf(where
, "usage: %s <subcommand>\n", getprogname());
1123 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
1124 l
= strlen(cmds
[i
].name
);
1129 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++)
1130 fprintf(where
, "\t%-*s\t%s\n", cmdwidth
, cmds
[i
].name
, cmds
[i
].desc
);
1136 exit_cmd(int argc
__attribute__((unused
)), char *const argv
[] __attribute__((unused
)))
1145 fcntl(fd
, F_SETFD
, 1);
1150 bootstrap_cmd(int argc
__attribute__((unused
)), char *const argv
[] __attribute__((unused
)))
1152 int memmib
[] = { CTL_HW
, HW_MEMSIZE
};
1153 int mvnmib
[] = { CTL_KERN
, KERN_MAXVNODES
};
1154 int hnmib
[] = { CTL_KERN
, KERN_HOSTNAME
};
1157 size_t memsz
= sizeof(mem
);
1158 struct group
*tfp_gr
;
1160 if (assumes((tfp_gr
= getgrnam("procview")) != NULL
)) {
1161 int tfp_r_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_READ_GROUP
};
1162 gid_t tfp_r_gid
= tfp_gr
->gr_gid
;
1163 assumes(sysctl(tfp_r_mib
, 3, NULL
, NULL
, &tfp_r_gid
, sizeof(tfp_r_gid
)) != -1);
1166 if (assumes((tfp_gr
= getgrnam("procmod")) != NULL
)) {
1167 int tfp_rw_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_RW_GROUP
};
1168 gid_t tfp_rw_gid
= tfp_gr
->gr_gid
;
1169 assumes(sysctl(tfp_rw_mib
, 3, NULL
, NULL
, &tfp_rw_gid
, sizeof(tfp_rw_gid
)) != -1);
1172 if (assumes(sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) != -1)) {
1173 mvn
= mem
/ (64 * 1024) + 1024;
1174 assumes(sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) != -1);
1176 assumes(sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) != -1);
1178 loopback_setup_ipv4();
1179 loopback_setup_ipv6();
1181 apply_sysctls_from_file("/etc/sysctl-macosxserver.conf");
1182 apply_sysctls_from_file("/etc/sysctl.conf");
1184 if (path_check("/System/Installation") && path_check("/etc/rc.cdrom")) {
1185 const char *rccdrom_tool
[] = { _PATH_BSHELL
, "/etc/rc.cdrom", "multiuser", NULL
};
1186 assumes(fwexec(rccdrom_tool
, true) != -1);
1187 assumes(reboot(RB_HALT
) != -1);
1188 _exit(EXIT_FAILURE
);
1189 } else if (is_netboot()) {
1190 const char *rcnetboot_tool
[] = { _PATH_BSHELL
, "/etc/rc.netboot", "init", NULL
};
1191 if (!assumes(fwexec(rcnetboot_tool
, true) != -1)) {
1192 assumes(reboot(RB_HALT
) != -1);
1193 _exit(EXIT_FAILURE
);
1196 do_potential_fsck();
1199 if (path_check("/var/account/acct")) {
1200 assumes(acct("/var/account/acct") != -1);
1203 if (path_check("/etc/fstab")) {
1204 const char *mount_tool
[] = { "mount", "-vat", "nonfs", NULL
};
1205 assumes(fwexec(mount_tool
, true) != -1);
1208 if (path_check("/etc/rc.installer_cleanup")) {
1209 const char *rccleanup_tool
[] = { _PATH_BSHELL
, "/etc/rc.installer_cleanup", "multiuser", NULL
};
1210 assumes(fwexec(rccleanup_tool
, true) != -1);
1213 apply_func_to_dir(_PATH_VARRUN
, empty_dir
);
1214 apply_func_to_dir(_PATH_TMP
, empty_dir
);
1215 remove(_PATH_NOLOGIN
);
1217 // XXX --> RMRF_ITEMS="/var/tmp/folders.*
1219 // 775 www:www /var/run/davlocks 4489695
1220 // 775 root:daemon /var/run/StartupItems
1222 assumes(touch_file(_PATH_UTMP
, DEFFILEMODE
) != -1);
1223 assumes(touch_file(_PATH_UTMPX
, DEFFILEMODE
) != -1);
1224 assumes(touch_file(_PATH_VARRUN
"/.systemStarterRunning", DEFFILEMODE
) != -1);
1226 if (!path_check("/var/db/netinfo/local.nidb.migrated") && !path_check("/var/db/netinfo/local.nidb")) {
1227 const char *create_nidb_tool
[] = { "/usr/libexec/create_nidb", NULL
};
1229 fprintf(stderr
, "NetInfo database missing. Creating...\n");
1231 mkdir("/var/db/netinfo", ACCESSPERMS
);
1232 remove("/var/db/.AppleSetupDone");
1233 assumes(fwexec(create_nidb_tool
, true) != -1);
1236 if (path_check("/etc/security/rc.audit")) {
1237 const char *audit_tool
[] = { _PATH_BSHELL
, "/etc/security/rc.audit", NULL
};
1238 assumes(fwexec(audit_tool
, true) != -1);
1241 if (path_check("/Library/Preferences/com.apple.sharing.firewall.plist")) {
1242 const char *fw_tool
[] = { "/usr/libexec/FirewallTool", NULL
};
1243 assumes(fwexec(fw_tool
, true) != -1);
1246 const char *bcc_tool
[] = { "BootCacheControl", "start", NULL
};
1247 assumes(fwexec(bcc_tool
, true) != -1);
1249 char *load_launchd_items
[] = { "load", "-D", "all", "/etc/mach_init.d", NULL
};
1251 load_launchd_items
[2] = "system";
1252 assumes(load_and_unload_cmd(4, load_launchd_items
) == 0);
1254 const char *bcc_tag_tool
[] = { "BootCacheControl", "tag", NULL
};
1255 assumes(fwexec(bcc_tag_tool
, true) != -1);
1257 const char *SystemStarter_tool
[] = { "SystemStarter", NULL
};
1258 assumes(fwexec(SystemStarter_tool
, false) != -1);
1260 workaround4465949();
1262 if (path_check("/etc/rc.local")) {
1263 const char *rc_local_tool
[] = { _PATH_BSHELL
, "/etc/rc.local", NULL
};
1264 assumes(fwexec(rc_local_tool
, false) != -1);
1271 workaround4465949(void)
1273 const char *pbs_tool
[] = { "/System/Library/CoreServices/pbs", NULL
};
1274 const char *lca_tool
[] = { "/System/Library/CoreServices/Language Chooser.app/Contents/MacOS/Language Chooser", NULL
};
1275 char *const reloadttys_argv
[] = { "reloadttys", NULL
};
1279 if (path_check("/System/Library/LaunchDaemons/com.apple.loginwindow.plist")) {
1283 if (path_check(pbs_tool
[0]) && path_check(lca_tool
[0]) &&
1284 !path_check("/var/db/.AppleSetupDone") &&
1285 path_check("/var/db/.RunLanguageChooserToo")) {
1286 if (assumes((pbs_p
= fwexec(pbs_tool
, false)) != -1)) {
1287 assumes(fwexec(lca_tool
, true) != -1);
1288 assumes(kill(pbs_p
, SIGTERM
) != -1);
1289 assumes(waitpid(pbs_p
, &wstatus
, 0) != -1);
1293 assumes(fyi_cmd(1, reloadttys_argv
) == 0);
1297 load_and_unload_cmd(int argc
, char *const argv
[])
1299 NSSearchPathEnumerationState es
= 0;
1300 char nspath
[PATH_MAX
* 2]; /* safe side, we need to append */
1301 bool badopts
= false;
1302 struct load_unload_state lus
;
1306 memset(&lus
, 0, sizeof(lus
));
1308 if (!strcmp(argv
[0], "load"))
1311 while ((ch
= getopt(argc
, argv
, "wFS:D:")) != -1) {
1314 lus
.editondisk
= true;
1317 lus
.forceload
= true;
1320 lus
.session_type
= optarg
;
1323 if (strcasecmp(optarg
, "all") == 0) {
1324 es
|= NSAllDomainsMask
;
1325 } else if (strcasecmp(optarg
, "user") == 0) {
1326 es
|= NSUserDomainMask
;
1327 } else if (strcasecmp(optarg
, "local") == 0) {
1328 es
|= NSLocalDomainMask
;
1329 } else if (strcasecmp(optarg
, "network") == 0) {
1330 es
|= NSNetworkDomainMask
;
1331 } else if (strcasecmp(optarg
, "system") == 0) {
1332 es
|= NSSystemDomainMask
;
1346 if (lus
.session_type
== NULL
)
1347 es
&= ~NSUserDomainMask
;
1349 if (argc
== 0 && es
== 0)
1353 fprintf(stderr
, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname());
1357 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
1358 * And loading legacy mach init jobs is extra fun.
1360 * In later versions of launchd, I hope to load everything in the first pass,
1361 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
1362 * I haven't thought through the various complexities of reloading jobs, and therefore
1363 * launchd doesn't have reload support right now.
1366 lus
.pass0
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1367 lus
.pass1
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1368 lus
.pass2
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1370 es
= NSStartSearchPathEnumeration(NSLibraryDirectory
, es
);
1372 while ((es
= NSGetNextSearchPathEnumeration(es
, nspath
))) {
1375 if (lus
.session_type
) {
1376 strcat(nspath
, "/LaunchAgents");
1378 strcat(nspath
, "/LaunchDaemons");
1381 if (glob(nspath
, GLOB_TILDE
|GLOB_NOSORT
, NULL
, &g
) == 0) {
1382 for (i
= 0; i
< g
.gl_pathc
; i
++) {
1383 readpath(g
.gl_pathv
[i
], &lus
);
1389 for (i
= 0; i
< (size_t)argc
; i
++)
1390 readpath(argv
[i
], &lus
);
1392 if (launch_data_array_get_count(lus
.pass0
) == 0 &&
1393 launch_data_array_get_count(lus
.pass1
) == 0 &&
1394 launch_data_array_get_count(lus
.pass2
) == 0) {
1395 fprintf(stderr
, "nothing found to %s\n", lus
.load
? "load" : "unload");
1396 launch_data_free(lus
.pass0
);
1397 launch_data_free(lus
.pass1
);
1398 launch_data_free(lus
.pass2
);
1403 distill_jobs(lus
.pass1
);
1404 submit_mach_jobs(lus
.pass0
);
1405 submit_job_pass(lus
.pass1
);
1406 let_go_of_mach_jobs(lus
.pass0
);
1407 distill_jobs(lus
.pass2
);
1408 submit_job_pass(lus
.pass2
);
1410 for (i
= 0; i
< launch_data_array_get_count(lus
.pass1
); i
++)
1411 unloadjob(launch_data_array_get_index(lus
.pass1
, i
));
1412 for (i
= 0; i
< launch_data_array_get_count(lus
.pass2
); i
++)
1413 unloadjob(launch_data_array_get_index(lus
.pass2
, i
));
1420 submit_mach_jobs(launch_data_t jobs
)
1424 c
= launch_data_array_get_count(jobs
);
1426 for (i
= 0; i
< c
; i
++) {
1427 launch_data_t tmp
, oai
= launch_data_array_get_index(jobs
, i
);
1428 const char *sn
= NULL
, *cmd
= NULL
;
1430 mach_port_t msr
, msv
;
1434 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_ONDEMAND
)))
1435 d
= launch_data_get_bool(tmp
);
1436 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_SERVICENAME
)))
1437 sn
= launch_data_get_string(tmp
);
1438 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_COMMAND
)))
1439 cmd
= launch_data_get_string(tmp
);
1441 if ((kr
= bootstrap_create_server(bootstrap_port
, (char *)cmd
, u
, d
, &msr
)) != KERN_SUCCESS
) {
1442 fprintf(stderr
, "%s: bootstrap_create_server(): %d\n", getprogname(), kr
);
1445 if ((kr
= bootstrap_create_service(msr
, (char*)sn
, &msv
)) != KERN_SUCCESS
) {
1446 fprintf(stderr
, "%s: bootstrap_create_service(): %d\n", getprogname(), kr
);
1447 mach_port_destroy(mach_task_self(), msr
);
1450 launch_data_dict_insert(oai
, launch_data_new_machport(msr
), MACHINIT_JOBKEY_SERVERPORT
);
1451 launch_data_dict_insert(oai
, launch_data_new_machport(msv
), MACHINIT_JOBKEY_SERVICEPORT
);
1456 let_go_of_mach_jobs(launch_data_t jobs
)
1458 size_t i
, c
= launch_data_array_get_count(jobs
);
1460 for (i
= 0; i
< c
; i
++) {
1461 launch_data_t tmp
, oai
= launch_data_array_get_index(jobs
, i
);
1462 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_SERVICEPORT
))) {
1463 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp
));
1465 fprintf(stderr
, "%s: ack! missing service port!\n", getprogname());
1467 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_SERVERPORT
))) {
1468 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp
));
1470 fprintf(stderr
, "%s: ack! missing server port!\n", getprogname());
1476 submit_job_pass(launch_data_t jobs
)
1478 launch_data_t msg
, resp
;
1482 if (launch_data_array_get_count(jobs
) == 0)
1485 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1487 launch_data_dict_insert(msg
, jobs
, LAUNCH_KEY_SUBMITJOB
);
1489 resp
= launch_msg(msg
);
1492 switch (launch_data_get_type(resp
)) {
1493 case LAUNCH_DATA_ERRNO
:
1494 if ((e
= launch_data_get_errno(resp
)))
1495 fprintf(stderr
, "%s\n", strerror(e
));
1497 case LAUNCH_DATA_ARRAY
:
1498 for (i
= 0; i
< launch_data_array_get_count(jobs
); i
++) {
1499 launch_data_t obatind
= launch_data_array_get_index(resp
, i
);
1500 launch_data_t jatind
= launch_data_array_get_index(jobs
, i
);
1501 const char *lab4job
= launch_data_get_string(launch_data_dict_lookup(jatind
, LAUNCH_JOBKEY_LABEL
));
1502 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(obatind
)) {
1503 e
= launch_data_get_errno(obatind
);
1506 fprintf(stderr
, "%s: %s\n", lab4job
, "Already loaded");
1509 fprintf(stderr
, "%s: %s\n", lab4job
, "Not loaded");
1512 fprintf(stderr
, "%s: %s\n", lab4job
, strerror(e
));
1520 fprintf(stderr
, "unknown respose from launchd!\n");
1523 launch_data_free(resp
);
1525 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1528 launch_data_free(msg
);
1532 start_stop_remove_cmd(int argc
, char *const argv
[])
1534 launch_data_t resp
, msg
;
1535 const char *lmsgcmd
= LAUNCH_KEY_STOPJOB
;
1538 if (0 == strcmp(argv
[0], "start"))
1539 lmsgcmd
= LAUNCH_KEY_STARTJOB
;
1541 if (0 == strcmp(argv
[0], "remove"))
1542 lmsgcmd
= LAUNCH_KEY_REMOVEJOB
;
1545 fprintf(stderr
, "usage: %s %s <job label>\n", getprogname(), argv
[0]);
1549 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1550 launch_data_dict_insert(msg
, launch_data_new_string(argv
[1]), lmsgcmd
);
1552 resp
= launch_msg(msg
);
1553 launch_data_free(msg
);
1556 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1558 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1559 if ((e
= launch_data_get_errno(resp
))) {
1560 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1564 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1568 launch_data_free(resp
);
1573 print_jobs(launch_data_t j
)
1575 static size_t depth
= 0;
1576 launch_data_t lo
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_LABEL
);
1577 launch_data_t pido
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PID
);
1578 launch_data_t stato
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_LASTEXITSTATUS
);
1579 launch_data_t sjobs
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_SUBJOBS
);
1580 const char *label
= launch_data_get_string(lo
);
1584 fprintf(stdout
, "%lld\t-\t", launch_data_get_integer(pido
));
1586 int wstatus
= (int)launch_data_get_integer(stato
);
1587 if (WIFEXITED(wstatus
)) {
1588 fprintf(stdout
, "-\t%d\t", WEXITSTATUS(wstatus
));
1589 } else if (WIFSIGNALED(wstatus
)) {
1590 fprintf(stdout
, "-\t-%d\t", WTERMSIG(wstatus
));
1592 fprintf(stdout
, "-\t???\t");
1595 fprintf(stdout
, "-\t-\t");
1597 for (i
= 0; i
< depth
; i
++)
1598 fprintf(stdout
, "\t");
1600 fprintf(stdout
, "%s\n", label
);
1605 c
= launch_data_array_get_count(sjobs
);
1608 for (i
= 0; i
< c
; i
++) {
1609 oai
= launch_data_array_get_index(sjobs
, i
);
1617 print_obj(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1619 static size_t indent
= 0;
1622 for (i
= 0; i
< indent
; i
++)
1623 fprintf(stdout
, "\t");
1626 fprintf(stdout
, "\"%s\" = ", key
);
1628 switch (launch_data_get_type(obj
)) {
1629 case LAUNCH_DATA_STRING
:
1630 fprintf(stdout
, "\"%s\";\n", launch_data_get_string(obj
));
1632 case LAUNCH_DATA_INTEGER
:
1633 fprintf(stdout
, "%lld;\n", launch_data_get_integer(obj
));
1635 case LAUNCH_DATA_REAL
:
1636 fprintf(stdout
, "%f;\n", launch_data_get_real(obj
));
1638 case LAUNCH_DATA_BOOL
:
1639 fprintf(stdout
, "%s;\n", launch_data_get_bool(obj
) ? "true" : "false");
1641 case LAUNCH_DATA_ARRAY
:
1642 c
= launch_data_array_get_count(obj
);
1643 fprintf(stdout
, "(\n");
1645 for (i
= 0; i
< c
; i
++)
1646 print_obj(launch_data_array_get_index(obj
, i
), NULL
, NULL
);
1648 for (i
= 0; i
< indent
; i
++)
1649 fprintf(stdout
, "\t");
1650 fprintf(stdout
, ");\n");
1652 case LAUNCH_DATA_DICTIONARY
:
1653 fprintf(stdout
, "{\n");
1655 launch_data_dict_iterate(obj
, print_obj
, NULL
);
1657 for (i
= 0; i
< indent
; i
++)
1658 fprintf(stdout
, "\t");
1659 fprintf(stdout
, "};\n");
1661 case LAUNCH_DATA_FD
:
1662 fprintf(stdout
, "file-descriptor-object;\n");
1664 case LAUNCH_DATA_MACHPORT
:
1665 fprintf(stdout
, "mach-port-object;\n");
1668 fprintf(stdout
, "???;\n");
1674 list_cmd(int argc
, char *const argv
[])
1676 launch_data_t resp
, msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1680 fprintf(stderr
, "usage: %s list [label]\n", getprogname());
1682 } else if (argc
== 2) {
1683 launch_data_dict_insert(msg
, launch_data_new_string(argv
[1]), LAUNCH_KEY_GETJOB
);
1685 launch_data_dict_insert(msg
, launch_data_new_string(""), LAUNCH_KEY_GETJOB
);
1688 resp
= launch_msg(msg
);
1689 launch_data_free(msg
);
1692 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1694 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_DICTIONARY
) {
1696 fprintf(stdout
, "PID\tStatus\tLabel\n");
1699 print_obj(resp
, NULL
, NULL
);
1702 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1706 launch_data_free(resp
);
1712 stdio_cmd(int argc
, char *const argv
[])
1714 launch_data_t resp
, msg
, tmp
;
1715 int e
, fd
= -1, r
= 0;
1718 fprintf(stderr
, "usage: %s %s <path>\n", getprogname(), argv
[0]);
1722 fd
= open(argv
[1], O_CREAT
|O_APPEND
|O_WRONLY
|O_NOCTTY
, DEFFILEMODE
);
1724 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1727 tmp
= launch_data_new_string(argv
[1]);
1729 tmp
= launch_data_new_fd(fd
);
1732 if (!strcmp(argv
[0], "stdout")) {
1733 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETSTDOUT
);
1735 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETSTDERR
);
1738 resp
= launch_msg(msg
);
1739 launch_data_free(msg
);
1742 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1744 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1745 if ((e
= launch_data_get_errno(resp
))) {
1746 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1750 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1757 launch_data_free(resp
);
1763 fyi_cmd(int argc
, char *const argv
[])
1765 launch_data_t resp
, msg
;
1766 const char *lmsgk
= LAUNCH_KEY_RELOADTTYS
;
1770 fprintf(stderr
, "usage: %s %s\n", getprogname(), argv
[0]);
1774 if (!strcmp(argv
[0], "shutdown")) {
1775 lmsgk
= LAUNCH_KEY_SHUTDOWN
;
1776 } else if (!strcmp(argv
[0], "singleuser")) {
1777 lmsgk
= LAUNCH_KEY_SINGLEUSER
;
1780 msg
= launch_data_new_string(lmsgk
);
1781 resp
= launch_msg(msg
);
1782 launch_data_free(msg
);
1785 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1787 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1788 if ((e
= launch_data_get_errno(resp
))) {
1789 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1793 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1797 launch_data_free(resp
);
1803 logupdate_cmd(int argc
, char *const argv
[])
1805 launch_data_t resp
, msg
;
1806 int e
, i
, j
, r
= 0, m
= 0;
1807 bool badargs
= false, maskmode
= false, onlymode
= false, levelmode
= false;
1808 const char *whichcmd
= LAUNCH_KEY_SETLOGMASK
;
1809 static const struct {
1813 { "debug", LOG_DEBUG
},
1814 { "info", LOG_INFO
},
1815 { "notice", LOG_NOTICE
},
1816 { "warning", LOG_WARNING
},
1817 { "error", LOG_ERR
},
1818 { "critical", LOG_CRIT
},
1819 { "alert", LOG_ALERT
},
1820 { "emergency", LOG_EMERG
},
1822 int logtblsz
= sizeof logtbl
/ sizeof logtbl
[0];
1825 if (!strcmp(argv
[1], "mask"))
1827 else if (!strcmp(argv
[1], "only"))
1829 else if (!strcmp(argv
[1], "level"))
1836 m
= LOG_UPTO(LOG_DEBUG
);
1838 if (argc
> 2 && (maskmode
|| onlymode
)) {
1839 for (i
= 2; i
< argc
; i
++) {
1840 for (j
= 0; j
< logtblsz
; j
++) {
1841 if (!strcmp(argv
[i
], logtbl
[j
].name
)) {
1843 m
&= ~(LOG_MASK(logtbl
[j
].level
));
1845 m
|= LOG_MASK(logtbl
[j
].level
);
1849 if (j
== logtblsz
) {
1854 } else if (argc
> 2 && levelmode
) {
1855 for (j
= 0; j
< logtblsz
; j
++) {
1856 if (!strcmp(argv
[2], logtbl
[j
].name
)) {
1857 m
= LOG_UPTO(logtbl
[j
].level
);
1863 } else if (argc
== 1) {
1864 whichcmd
= LAUNCH_KEY_GETLOGMASK
;
1870 fprintf(stderr
, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
1874 if (whichcmd
== LAUNCH_KEY_SETLOGMASK
) {
1875 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1876 launch_data_dict_insert(msg
, launch_data_new_integer(m
), whichcmd
);
1878 msg
= launch_data_new_string(whichcmd
);
1881 resp
= launch_msg(msg
);
1882 launch_data_free(msg
);
1885 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1887 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1888 if ((e
= launch_data_get_errno(resp
))) {
1889 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1892 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_INTEGER
) {
1893 if (whichcmd
== LAUNCH_KEY_GETLOGMASK
) {
1894 m
= launch_data_get_integer(resp
);
1895 for (j
= 0; j
< logtblsz
; j
++) {
1896 if (m
& LOG_MASK(logtbl
[j
].level
))
1897 fprintf(stdout
, "%s ", logtbl
[j
].name
);
1899 fprintf(stdout
, "\n");
1902 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1906 launch_data_free(resp
);
1911 static const struct {
1915 { "cpu", RLIMIT_CPU
},
1916 { "filesize", RLIMIT_FSIZE
},
1917 { "data", RLIMIT_DATA
},
1918 { "stack", RLIMIT_STACK
},
1919 { "core", RLIMIT_CORE
},
1920 { "rss", RLIMIT_RSS
},
1921 { "memlock", RLIMIT_MEMLOCK
},
1922 { "maxproc", RLIMIT_NPROC
},
1923 { "maxfiles", RLIMIT_NOFILE
}
1926 static const size_t limlookupcnt
= sizeof limlookup
/ sizeof limlookup
[0];
1929 name2num(const char *n
)
1933 for (i
= 0; i
< limlookupcnt
; i
++) {
1934 if (!strcmp(limlookup
[i
].name
, n
)) {
1935 return limlookup
[i
].lim
;
1946 for (i
= 0; i
< limlookupcnt
; i
++) {
1947 if (limlookup
[i
].lim
== n
)
1948 return limlookup
[i
].name
;
1954 lim2str(rlim_t val
, char *buf
)
1956 if (val
== RLIM_INFINITY
)
1957 strcpy(buf
, "unlimited");
1959 sprintf(buf
, "%lld", val
);
1964 str2lim(const char *buf
, rlim_t
*res
)
1967 *res
= strtoll(buf
, &endptr
, 10);
1968 if (!strcmp(buf
, "unlimited")) {
1969 *res
= RLIM_INFINITY
;
1971 } else if (*endptr
== '\0') {
1978 limit_cmd(int argc
__attribute__((unused
)), char *const argv
[])
1982 struct rlimit
*lmts
= NULL
;
1983 launch_data_t resp
, resp1
= NULL
, msg
, tmp
;
1987 rlim_t slim
= -1, hlim
= -1;
1988 bool badargs
= false;
1993 if (argc
>= 3 && str2lim(argv
[2], &slim
))
1998 if (argc
== 4 && str2lim(argv
[3], &hlim
))
2001 if (argc
>= 2 && -1 == (which
= name2num(argv
[1])))
2005 fprintf(stderr
, "usage: %s %s [", getprogname(), argv
[0]);
2006 for (i
= 0; i
< limlookupcnt
; i
++)
2007 fprintf(stderr
, "%s %s", limlookup
[i
].name
, (i
+ 1) == limlookupcnt
? "" : "| ");
2008 fprintf(stderr
, "[both | soft hard]]\n");
2012 msg
= launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS
);
2013 resp
= launch_msg(msg
);
2014 launch_data_free(msg
);
2017 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2019 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
2020 lmts
= launch_data_get_opaque(resp
);
2021 lsz
= launch_data_get_opaque_size(resp
);
2023 for (i
= 0; i
< (lsz
/ sizeof(struct rlimit
)); i
++) {
2024 if (argc
== 2 && (size_t)which
!= i
)
2026 fprintf(stdout
, "\t%-12s%-15s%-15s\n", num2name(i
),
2027 lim2str(lmts
[i
].rlim_cur
, slimstr
),
2028 lim2str(lmts
[i
].rlim_max
, hlimstr
));
2031 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
2032 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
2035 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2039 if (argc
<= 2 || r
!= 0) {
2040 launch_data_free(resp
);
2046 lmts
[which
].rlim_cur
= slim
;
2047 lmts
[which
].rlim_max
= hlim
;
2049 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2050 tmp
= launch_data_new_opaque(lmts
, lsz
);
2051 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETRESOURCELIMITS
);
2052 resp
= launch_msg(msg
);
2053 launch_data_free(msg
);
2056 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2058 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
2059 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
2061 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_OPAQUE
) {
2062 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2066 launch_data_free(resp
);
2067 launch_data_free(resp1
);
2073 umask_cmd(int argc
, char *const argv
[])
2075 launch_data_t resp
, msg
;
2076 bool badargs
= false;
2082 m
= strtol(argv
[1], &endptr
, 8);
2083 if (*endptr
!= '\0' || m
> 0777)
2087 if (argc
> 2 || badargs
) {
2088 fprintf(stderr
, "usage: %s %s <mask>\n", getprogname(), argv
[0]);
2094 msg
= launch_data_new_string(LAUNCH_KEY_GETUMASK
);
2096 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2097 launch_data_dict_insert(msg
, launch_data_new_integer(m
), LAUNCH_KEY_SETUMASK
);
2099 resp
= launch_msg(msg
);
2100 launch_data_free(msg
);
2103 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2105 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
2106 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
2108 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_INTEGER
) {
2109 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2111 } else if (argc
== 1) {
2112 fprintf(stdout
, "%o\n", (unsigned int)launch_data_get_integer(resp
));
2115 launch_data_free(resp
);
2121 submit_cmd(int argc
, char *const argv
[])
2123 launch_data_t msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2124 launch_data_t job
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2125 launch_data_t resp
, largv
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2128 launch_data_dict_insert(job
, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND
);
2130 while ((ch
= getopt(argc
, argv
, "l:p:o:e:")) != -1) {
2133 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_LABEL
);
2136 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_PROGRAM
);
2139 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_STANDARDOUTPATH
);
2142 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_STANDARDERRORPATH
);
2145 fprintf(stderr
, "usage: %s submit ...\n", getprogname());
2152 for (i
= 0; argv
[i
]; i
++) {
2153 launch_data_array_append(largv
, launch_data_new_string(argv
[i
]));
2156 launch_data_dict_insert(job
, largv
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
2158 launch_data_dict_insert(msg
, job
, LAUNCH_KEY_SUBMITJOB
);
2160 resp
= launch_msg(msg
);
2161 launch_data_free(msg
);
2164 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2166 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
2167 errno
= launch_data_get_errno(resp
);
2169 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(errno
));
2173 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], "unknown response");
2176 launch_data_free(resp
);
2182 getrusage_cmd(int argc
, char *const argv
[])
2184 launch_data_t resp
, msg
;
2185 bool badargs
= false;
2190 else if (strcmp(argv
[1], "self") && strcmp(argv
[1], "children"))
2194 fprintf(stderr
, "usage: %s %s self | children\n", getprogname(), argv
[0]);
2198 if (!strcmp(argv
[1], "self")) {
2199 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF
);
2201 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN
);
2204 resp
= launch_msg(msg
);
2205 launch_data_free(msg
);
2208 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2210 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
2211 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(launch_data_get_errno(resp
)));
2213 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
2214 struct rusage
*rusage
= launch_data_get_opaque(resp
);
2215 fprintf(stdout
, "\t%-10f\tuser time used\n",
2216 (double)rusage
->ru_utime
.tv_sec
+ (double)rusage
->ru_utime
.tv_usec
/ (double)1000000);
2217 fprintf(stdout
, "\t%-10f\tsystem time used\n",
2218 (double)rusage
->ru_stime
.tv_sec
+ (double)rusage
->ru_stime
.tv_usec
/ (double)1000000);
2219 fprintf(stdout
, "\t%-10ld\tmax resident set size\n", rusage
->ru_maxrss
);
2220 fprintf(stdout
, "\t%-10ld\tshared text memory size\n", rusage
->ru_ixrss
);
2221 fprintf(stdout
, "\t%-10ld\tunshared data size\n", rusage
->ru_idrss
);
2222 fprintf(stdout
, "\t%-10ld\tunshared stack size\n", rusage
->ru_isrss
);
2223 fprintf(stdout
, "\t%-10ld\tpage reclaims\n", rusage
->ru_minflt
);
2224 fprintf(stdout
, "\t%-10ld\tpage faults\n", rusage
->ru_majflt
);
2225 fprintf(stdout
, "\t%-10ld\tswaps\n", rusage
->ru_nswap
);
2226 fprintf(stdout
, "\t%-10ld\tblock input operations\n", rusage
->ru_inblock
);
2227 fprintf(stdout
, "\t%-10ld\tblock output operations\n", rusage
->ru_oublock
);
2228 fprintf(stdout
, "\t%-10ld\tmessages sent\n", rusage
->ru_msgsnd
);
2229 fprintf(stdout
, "\t%-10ld\tmessages received\n", rusage
->ru_msgrcv
);
2230 fprintf(stdout
, "\t%-10ld\tsignals received\n", rusage
->ru_nsignals
);
2231 fprintf(stdout
, "\t%-10ld\tvoluntary context switches\n", rusage
->ru_nvcsw
);
2232 fprintf(stdout
, "\t%-10ld\tinvoluntary context switches\n", rusage
->ru_nivcsw
);
2234 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2238 launch_data_free(resp
);
2244 launch_data_array_append(launch_data_t a
, launch_data_t o
)
2246 size_t offt
= launch_data_array_get_count(a
);
2248 return launch_data_array_set_index(a
, o
, offt
);
2252 str2bsport(const char *s
)
2254 bool getrootbs
= strcmp(s
, "/") == 0;
2255 mach_port_t last_bport
, bport
= bootstrap_port
;
2256 task_t task
= mach_task_self();
2257 kern_return_t result
;
2259 if (strcmp(s
, "..") == 0 || getrootbs
) {
2262 result
= bootstrap_parent(last_bport
, &bport
);
2264 if (result
== BOOTSTRAP_NOT_PRIVILEGED
) {
2265 fprintf(stderr
, "Permission denied\n");
2267 } else if (result
!= BOOTSTRAP_SUCCESS
) {
2268 fprintf(stderr
, "bootstrap_parent() %d\n", result
);
2271 } while (getrootbs
&& last_bport
!= bport
);
2275 result
= task_for_pid(mach_task_self(), pid
, &task
);
2277 if (result
!= KERN_SUCCESS
) {
2278 fprintf(stderr
, "task_for_pid() %s\n", mach_error_string(result
));
2282 result
= task_get_bootstrap_port(task
, &bport
);
2284 if (result
!= KERN_SUCCESS
) {
2285 fprintf(stderr
, "Couldn't get bootstrap port: %s\n", mach_error_string(result
));
2294 bsexec_cmd(int argc
, char *const argv
[])
2296 kern_return_t result
;
2300 fprintf(stderr
, "usage: %s bsexec <PID> prog...\n", getprogname());
2304 bport
= str2bsport(argv
[1]);
2306 result
= task_set_bootstrap_port(mach_task_self(), bport
);
2308 if (result
!= KERN_SUCCESS
) {
2309 fprintf(stderr
, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result
));
2316 execvp(argv
[2], argv
+ 2);
2317 fprintf(stderr
, "execvp(): %s\n", strerror(errno
));
2322 bslist_cmd(int argc
, char *const argv
[])
2324 kern_return_t result
;
2325 mach_port_t bport
= bootstrap_port
;
2326 name_array_t service_names
;
2327 mach_msg_type_number_t service_cnt
, service_active_cnt
;
2328 bootstrap_status_array_t service_actives
;
2332 bport
= str2bsport(argv
[1]);
2334 if (bport
== MACH_PORT_NULL
) {
2335 fprintf(stderr
, "Invalid bootstrap port\n");
2339 result
= bootstrap_info(bport
, &service_names
, &service_cnt
, &service_actives
, &service_active_cnt
);
2340 if (result
!= BOOTSTRAP_SUCCESS
) {
2341 fprintf(stderr
, "bootstrap_info(): %d\n", result
);
2345 #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
2347 for (i
= 0; i
< service_cnt
; i
++)
2348 fprintf(stdout
, "%-3s%s\n", bport_state((service_actives
[i
])), service_names
[i
]);
2354 is_legacy_mach_job(launch_data_t obj
)
2356 bool has_servicename
= launch_data_dict_lookup(obj
, MACHINIT_JOBKEY_SERVICENAME
);
2357 bool has_command
= launch_data_dict_lookup(obj
, MACHINIT_JOBKEY_COMMAND
);
2358 bool has_label
= launch_data_dict_lookup(obj
, LAUNCH_JOBKEY_LABEL
);
2360 return has_command
&& has_servicename
&& !has_label
;
2364 _log_launchctl_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
2366 int saved_errno
= errno
;
2368 const char *file
= strrchr(path
, '/');
2369 char *rcs_rev_tmp
= strchr(rcs_rev
, ' ');
2378 strlcpy(buf
, rcs_rev
, sizeof(buf
));
2380 strlcpy(buf
, rcs_rev_tmp
+ 1, sizeof(buf
));
2381 rcs_rev_tmp
= strchr(buf
, ' ');
2383 *rcs_rev_tmp
= '\0';
2386 fprintf(stderr
, "Bug: %s:%u (%s):%u: %s\n", file
, line
, buf
, saved_errno
, test
);
2390 loopback_setup_ipv4(void)
2392 struct ifaliasreq ifra
;
2396 memset(&ifr
, 0, sizeof(ifr
));
2397 strcpy(ifr
.ifr_name
, "lo0");
2399 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1)
2402 if (assumes(ioctl(s
, SIOCGIFFLAGS
, &ifr
) != -1)) {
2403 ifr
.ifr_flags
|= IFF_UP
;
2404 assumes(ioctl(s
, SIOCSIFFLAGS
, &ifr
) != -1);
2407 memset(&ifra
, 0, sizeof(ifra
));
2408 strcpy(ifra
.ifra_name
, "lo0");
2409 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
2410 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
2411 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
2412 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
2413 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
2414 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
2416 assumes(ioctl(s
, SIOCAIFADDR
, &ifra
) != -1);
2418 assumes(close(s
) == 0);
2422 loopback_setup_ipv6(void)
2424 struct in6_aliasreq ifra6
;
2428 memset(&ifr
, 0, sizeof(ifr
));
2429 strcpy(ifr
.ifr_name
, "lo0");
2431 if ((s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)) == -1)
2434 memset(&ifr
, 0, sizeof(ifr
));
2435 strcpy(ifr
.ifr_name
, "lo0");
2437 if (assumes(ioctl(s6
, SIOCGIFFLAGS
, &ifr
) != -1)) {
2438 ifr
.ifr_flags
|= IFF_UP
;
2439 assumes(ioctl(s6
, SIOCSIFFLAGS
, &ifr
) != -1);
2442 memset(&ifra6
, 0, sizeof(ifra6
));
2443 strcpy(ifra6
.ifra_name
, "lo0");
2445 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
2446 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
2447 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
2448 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
2449 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
2450 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
2451 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
2452 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
2454 assumes(ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) != -1);
2456 assumes(close(s6
) == 0);
2460 fwexec(const char *const *argv
, bool _wait
)
2465 switch ((p
= fork())) {
2470 execvp(argv
[0], (char *const *)argv
);
2471 _exit(EXIT_FAILURE
);
2476 if (p
== waitpid(p
, &wstatus
, 0)) {
2477 if (WIFEXITED(wstatus
) && WEXITSTATUS(wstatus
) == EXIT_SUCCESS
)
2487 do_potential_fsck(void)
2489 const char *safe_fsck_tool
[] = { "fsck", "-fy", NULL
};
2490 const char *fsck_tool
[] = { "fsck", "-p", NULL
};
2491 const char *remount_tool
[] = { "mount", "-uw", "/", NULL
};
2494 if (assumes(statfs("/", &sfs
) != -1)) {
2495 if (!(sfs
.f_flags
& MNT_RDONLY
)) {
2496 fprintf(stdout
, "Root file system is read-write, skipping fsck.\n");
2501 if (!is_safeboot()) {
2502 if (fwexec(fsck_tool
, true) != -1)
2506 if (fwexec(safe_fsck_tool
, true) != -1) {
2510 fprintf(stderr
, "fsck failed! Leaving the root file system read-only...\n");
2514 assumes(fwexec(remount_tool
, true) != -1);
2518 path_check(const char *path
)
2522 if (stat(path
, &sb
) == 0)
2530 int sbmib
[] = { CTL_KERN
, KERN_SAFEBOOT
};
2532 size_t sbsz
= sizeof(sb
);
2534 if (!assumes(sysctl(sbmib
, 2, &sb
, &sbsz
, NULL
, 0) == 0))
2543 int nbmib
[] = { CTL_KERN
, KERN_NETBOOT
};
2545 size_t nbsz
= sizeof(nb
);
2547 if (!assumes(sysctl(nbmib
, 2, &nb
, &nbsz
, NULL
, 0) == 0))
2554 apply_func_to_dir(const char *thedir
, void (*thefunc
)(const char *))
2560 if (!assumes((currend_dir_fd
= open(".", 0)) != -1))
2563 if (!assumes(chdir(thedir
) != -1))
2566 if (!assumes(od
= opendir(".")))
2569 while ((de
= readdir(od
))) {
2572 if (strcmp(de
->d_name
, ".") == 0)
2574 if (strcmp(de
->d_name
, "..") == 0)
2577 if (assumes(stat(de
->d_name
, &sb
) != -1)) {
2578 if (S_ISDIR(sb
.st_mode
))
2579 apply_func_to_dir(de
->d_name
, thefunc
);
2580 thefunc(de
->d_name
);
2584 assumes(closedir(od
) != -1);
2587 assumes(fchdir(currend_dir_fd
) != -1);
2588 assumes(close(currend_dir_fd
) != -1);
2592 empty_dir(const char *path
)
2594 assumes(chflags(path
, 0) != -1);
2595 assumes(remove(path
) != -1);
2599 touch_file(const char *path
, mode_t m
)
2601 int fd
= open(path
, O_CREAT
, m
);
2610 apply_sysctls_from_file(const char *thefile
)
2612 const char *sysctl_tool
[] = { "sysctl", "-w", NULL
, NULL
};
2617 if (!(sf
= fopen(thefile
, "r")))
2620 while ((val
= fgetln(sf
, &ln_len
))) {
2623 if (!assumes((tmpstr
= malloc(ln_len
+ 1)) != NULL
))
2625 memcpy(tmpstr
, val
, ln_len
);
2629 while (*val
&& isspace(*val
))
2631 if (*val
== '\0' || *val
== '#')
2632 goto skip_sysctl_tool
;
2633 sysctl_tool
[2] = val
;
2634 assumes(fwexec(sysctl_tool
, true) != -1);
2639 assumes(fclose(sf
) == 0);
2643 do_sysversion_sysctl(void)
2645 int mib
[] = { CTL_KERN
, KERN_OSVERSION
};
2646 CFDictionaryRef versdict
;
2647 CFStringRef buildvers
;
2649 size_t bufsz
= sizeof(buf
);
2651 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
2656 if (sysctl(mib
, 2, buf
, &bufsz
, NULL
, 0) == -1) {
2657 fprintf(stderr
, "sysctl(): %s\n", strerror(errno
));
2664 versdict
= _CFCopySystemVersionDictionary();
2665 buildvers
= CFDictionaryGetValue(versdict
, _kCFSystemVersionBuildVersionKey
);
2666 CFStringGetCString(buildvers
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
2668 if (sysctl(mib
, 2, NULL
, 0, buf
, strlen(buf
) + 1) == -1) {
2669 fprintf(stderr
, "sysctl(): %s\n", strerror(errno
));
2672 CFRelease(versdict
);