2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <mach/mach.h>
25 #include <servers/bootstrap.h>
26 #include <sys/types.h>
29 #include <sys/socket.h>
31 #include <sys/fcntl.h>
32 #include <sys/event.h>
33 #include <sys/resource.h>
34 #include <sys/param.h>
35 #include <netinet/in.h>
46 #include <readline/readline.h>
47 #include <readline/history.h>
51 #include "launch_priv.h"
53 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
55 #define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
56 #define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
57 #define MACHINIT_JOBKEY_COMMAND "Command"
58 #define MACHINIT_JOBKEY_ISKUNCSERVER "isKUNCServer"
61 static void myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
);
62 static bool launch_data_array_append(launch_data_t a
, launch_data_t o
);
63 static void distill_jobs(launch_data_t
);
64 static void distill_config_file(launch_data_t
);
65 static void sock_dict_cb(launch_data_t what
, const char *key
, void *context
);
66 static void sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
);
67 static launch_data_t
CF2launch_data(CFTypeRef
);
68 static launch_data_t
read_plist_file(const char *file
, bool editondisk
, bool load
);
69 static CFPropertyListRef
CreateMyPropertyListFromFile(const char *);
70 static void WriteMyPropertyListToFile(CFPropertyListRef
, const char *);
71 static void readpath(const char *, launch_data_t
, launch_data_t
, launch_data_t
, bool editondisk
, bool load
, bool forceload
);
72 static void readfile(const char *, launch_data_t
, launch_data_t
, launch_data_t
, bool editondisk
, bool load
, bool forceload
);
74 static int demux_cmd(int argc
, char *const argv
[]);
75 static launch_data_t
do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
);
76 static void submit_job_pass(launch_data_t jobs
);
77 static void submit_mach_jobs(launch_data_t jobs
);
78 static void let_go_of_mach_jobs(void);
79 static void do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
);
80 static void print_jobs(launch_data_t j
, const char *label
, void *context
);
81 static bool is_legacy_mach_job(launch_data_t obj
);
82 static bool delay_to_second_pass(launch_data_t o
);
83 static void delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
);
85 static int load_and_unload_cmd(int argc
, char *const argv
[]);
86 //static int reload_cmd(int argc, char *const argv[]);
87 static int start_and_stop_cmd(int argc
, char *const argv
[]);
88 static int list_cmd(int argc
, char *const argv
[]);
90 static int setenv_cmd(int argc
, char *const argv
[]);
91 static int unsetenv_cmd(int argc
, char *const argv
[]);
92 static int getenv_and_export_cmd(int argc
, char *const argv
[]);
94 static int limit_cmd(int argc
, char *const argv
[]);
95 static int stdio_cmd(int argc
, char *const argv
[]);
96 static int fyi_cmd(int argc
, char *const argv
[]);
97 static int logupdate_cmd(int argc
, char *const argv
[]);
98 static int umask_cmd(int argc
, char *const argv
[]);
99 static int getrusage_cmd(int argc
, char *const argv
[]);
101 static int help_cmd(int argc
, char *const argv
[]);
103 static const struct {
105 int (*func
)(int argc
, char *const argv
[]);
108 { "load", load_and_unload_cmd
, "Load configuration files and/or directories" },
109 { "unload", load_and_unload_cmd
, "Unload configuration files and/or directories" },
110 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
111 { "start", start_and_stop_cmd
, "Start specified jobs" },
112 { "stop", start_and_stop_cmd
, "Stop specified jobs" },
113 { "list", list_cmd
, "List jobs and information about jobs" },
114 { "setenv", setenv_cmd
, "Set an environmental variable in launchd" },
115 { "unsetenv", unsetenv_cmd
, "Unset an environmental variable in launchd" },
116 { "getenv", getenv_and_export_cmd
, "Get an environmental variable from launchd" },
117 { "export", getenv_and_export_cmd
, "Export shell settings from launchd" },
118 { "limit", limit_cmd
, "View and adjust launchd resource limits" },
119 { "stdout", stdio_cmd
, "Redirect launchd's standard out to the given path" },
120 { "stderr", stdio_cmd
, "Redirect launchd's standard error to the given path" },
121 { "shutdown", fyi_cmd
, "Prepare for system shutdown" },
122 { "reloadttys", fyi_cmd
, "Reload /etc/ttys" },
123 { "getrusage", getrusage_cmd
, "Get resource usage statistics from launchd" },
124 { "log", logupdate_cmd
, "Adjust the logging level or mask of launchd" },
125 { "umask", umask_cmd
, "Change launchd's umask" },
126 { "help", help_cmd
, "This help output" },
129 int main(int argc
, char *const argv
[])
131 bool istty
= isatty(STDIN_FILENO
);
135 exit(demux_cmd(argc
- 1, argv
+ 1));
137 if (NULL
== readline
) {
138 fprintf(stderr
, "missing library: readline\n");
142 while ((l
= readline(istty
? "launchd% " : NULL
))) {
143 char *inputstring
= l
, *argv2
[100], **ap
= argv2
;
146 while ((*ap
= strsep(&inputstring
, " \t"))) {
165 static int demux_cmd(int argc
, char *const argv
[])
172 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
173 if (!strcmp(cmds
[i
].name
, argv
[0]))
174 return cmds
[i
].func(argc
, argv
);
177 fprintf(stderr
, "%s: unknown subcommand \"%s\"\n", getprogname(), argv
[0]);
181 static int unsetenv_cmd(int argc
, char *const argv
[])
183 launch_data_t resp
, tmp
, msg
;
186 fprintf(stderr
, "%s usage: unsetenv <key>\n", getprogname());
190 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
192 tmp
= launch_data_new_string(argv
[1]);
193 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_UNSETUSERENVIRONMENT
);
195 resp
= launch_msg(msg
);
197 launch_data_free(msg
);
200 launch_data_free(resp
);
202 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT
, strerror(errno
));
208 static int setenv_cmd(int argc
, char *const argv
[])
210 launch_data_t resp
, tmp
, tmpv
, msg
;
213 fprintf(stderr
, "%s usage: setenv <key> <value>\n", getprogname());
217 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
218 tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
220 tmpv
= launch_data_new_string(argv
[2]);
221 launch_data_dict_insert(tmp
, tmpv
, argv
[1]);
222 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETUSERENVIRONMENT
);
224 resp
= launch_msg(msg
);
225 launch_data_free(msg
);
228 launch_data_free(resp
);
230 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT
, strerror(errno
));
236 static void print_launchd_env(launch_data_t obj
, const char *key
, void *context
)
238 bool *is_csh
= context
;
241 fprintf(stdout
, "setenv %s %s;\n", key
, launch_data_get_string(obj
));
243 fprintf(stdout
, "%s=%s; export %s;\n", key
, launch_data_get_string(obj
), key
);
246 static void print_key_value(launch_data_t obj
, const char *key
, void *context
)
248 const char *k
= context
;
251 fprintf(stdout
, "%s\n", launch_data_get_string(obj
));
254 static int getenv_and_export_cmd(int argc
, char *const argv
[] __attribute__((unused
)))
256 launch_data_t resp
, msg
;
260 if (!strcmp(argv
[0], "export")) {
261 char *s
= getenv("SHELL");
263 is_csh
= strstr(s
, "csh") ? true : false;
264 } else if (argc
!= 2) {
265 fprintf(stderr
, "%s usage: getenv <key>\n", getprogname());
271 msg
= launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT
);
273 resp
= launch_msg(msg
);
274 launch_data_free(msg
);
277 if (!strcmp(argv
[0], "export"))
278 launch_data_dict_iterate(resp
, print_launchd_env
, &is_csh
);
280 launch_data_dict_iterate(resp
, print_key_value
, k
);
281 launch_data_free(resp
);
283 fprintf(stderr
, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT
"\"): %s\n", strerror(errno
));
288 static void unloadjob(launch_data_t job
)
290 launch_data_t resp
, tmp
, tmps
, msg
;
293 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
294 tmp
= launch_data_alloc(LAUNCH_DATA_STRING
);
295 tmps
= launch_data_dict_lookup(job
, LAUNCH_JOBKEY_LABEL
);
298 fprintf(stderr
, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL
);
302 launch_data_set_string(tmp
, launch_data_get_string(tmps
));
303 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_REMOVEJOB
);
304 resp
= launch_msg(msg
);
305 launch_data_free(msg
);
307 fprintf(stderr
, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno
));
310 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(resp
)) {
311 if ((e
= launch_data_get_errno(resp
)))
312 fprintf(stderr
, "%s\n", strerror(e
));
314 launch_data_free(resp
);
318 read_plist_file(const char *file
, bool editondisk
, bool load
)
320 CFPropertyListRef plist
= CreateMyPropertyListFromFile(file
);
321 launch_data_t r
= NULL
;
324 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), file
);
330 CFDictionaryRemoveValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
));
332 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
), kCFBooleanTrue
);
333 WriteMyPropertyListToFile(plist
, file
);
336 r
= CF2launch_data(plist
);
344 delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
)
349 if (key
&& 0 == strcmp(key
, LAUNCH_JOBSOCKETKEY_BONJOUR
)) {
354 switch (launch_data_get_type(o
)) {
355 case LAUNCH_DATA_DICTIONARY
:
356 launch_data_dict_iterate(o
, delay_to_second_pass2
, context
);
358 case LAUNCH_DATA_ARRAY
:
359 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
360 delay_to_second_pass2(launch_data_array_get_index(o
, i
), NULL
, context
);
368 delay_to_second_pass(launch_data_t o
)
372 launch_data_t socks
= launch_data_dict_lookup(o
, LAUNCH_JOBKEY_SOCKETS
);
377 delay_to_second_pass2(socks
, NULL
, &res
);
383 readfile(const char *what
, launch_data_t pass0
, launch_data_t pass1
, launch_data_t pass2
, bool editondisk
, bool load
, bool forceload
)
385 launch_data_t tmpd
, thejob
;
386 bool job_disabled
= false;
388 if (NULL
== (thejob
= read_plist_file(what
, editondisk
, load
))) {
389 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), what
);
393 if (is_legacy_mach_job(thejob
)) {
394 launch_data_array_append(pass0
, thejob
);
398 if (NULL
== launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LABEL
)) {
399 fprintf(stderr
, "%s: missing the Label key: %s\n", getprogname(), what
);
400 launch_data_free(thejob
);
404 if ((tmpd
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_DISABLED
)))
405 job_disabled
= launch_data_get_bool(tmpd
);
408 job_disabled
= false;
410 if (job_disabled
&& load
) {
411 launch_data_free(thejob
);
415 if (delay_to_second_pass(thejob
))
416 launch_data_array_append(pass2
, thejob
);
418 launch_data_array_append(pass1
, thejob
);
422 readpath(const char *what
, launch_data_t pass0
, launch_data_t pass1
, launch_data_t pass2
, bool editondisk
, bool load
, bool forceload
)
424 char buf
[MAXPATHLEN
];
429 if (stat(what
, &sb
) == -1)
432 if (S_ISREG(sb
.st_mode
) && !(sb
.st_mode
& S_IWOTH
)) {
433 readfile(what
, pass0
, pass1
, pass2
, editondisk
, load
, forceload
);
435 if ((d
= opendir(what
)) == NULL
) {
436 fprintf(stderr
, "%s: opendir() failed to open the directory\n", getprogname());
440 while ((de
= readdir(d
))) {
441 if ((de
->d_name
[0] == '.'))
443 snprintf(buf
, sizeof(buf
), "%s/%s", what
, de
->d_name
);
445 readfile(buf
, pass0
, pass1
, pass2
, editondisk
, load
, forceload
);
451 struct distill_context
{
453 launch_data_t newsockdict
;
457 distill_jobs(launch_data_t jobs
)
459 size_t i
, c
= launch_data_array_get_count(jobs
);
461 for (i
= 0; i
< c
; i
++)
462 distill_config_file(launch_data_array_get_index(jobs
, i
));
466 distill_config_file(launch_data_t id_plist
)
468 struct distill_context dc
= { id_plist
, NULL
};
471 if ((tmp
= launch_data_dict_lookup(id_plist
, LAUNCH_JOBKEY_SOCKETS
))) {
472 dc
.newsockdict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
473 launch_data_dict_iterate(tmp
, sock_dict_cb
, &dc
);
474 launch_data_dict_insert(dc
.base
, dc
.newsockdict
, LAUNCH_JOBKEY_SOCKETS
);
478 static void sock_dict_cb(launch_data_t what
, const char *key
, void *context
)
480 struct distill_context
*dc
= context
;
481 launch_data_t fdarray
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
483 launch_data_dict_insert(dc
->newsockdict
, fdarray
, key
);
485 if (launch_data_get_type(what
) == LAUNCH_DATA_DICTIONARY
) {
486 sock_dict_edit_entry(what
, key
, fdarray
, dc
->base
);
487 } else if (launch_data_get_type(what
) == LAUNCH_DATA_ARRAY
) {
491 for (i
= 0; i
< launch_data_array_get_count(what
); i
++) {
492 tmp
= launch_data_array_get_index(what
, i
);
493 sock_dict_edit_entry(tmp
, key
, fdarray
, dc
->base
);
498 static void sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
)
500 launch_data_t a
, val
;
501 int sfd
, st
= SOCK_STREAM
;
504 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_TYPE
))) {
505 if (!strcasecmp(launch_data_get_string(val
), "stream")) {
507 } else if (!strcasecmp(launch_data_get_string(val
), "dgram")) {
509 } else if (!strcasecmp(launch_data_get_string(val
), "seqpacket")) {
514 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PASSIVE
)))
515 passive
= launch_data_get_bool(val
);
517 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY
))) {
518 char secdir
[] = LAUNCH_SECDIR
, buf
[1024];
519 launch_data_t uenv
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
522 uenv
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
523 launch_data_dict_insert(thejob
, uenv
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
528 sprintf(buf
, "%s/%s", secdir
, key
);
530 a
= launch_data_new_string(buf
);
531 launch_data_dict_insert(tmp
, a
, LAUNCH_JOBSOCKETKEY_PATHNAME
);
532 a
= launch_data_new_string(buf
);
533 launch_data_dict_insert(uenv
, a
, launch_data_get_string(val
));
536 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHNAME
))) {
537 struct sockaddr_un sun
;
542 memset(&sun
, 0, sizeof(sun
));
544 sun
.sun_family
= AF_UNIX
;
546 strncpy(sun
.sun_path
, launch_data_get_string(val
), sizeof(sun
.sun_path
));
548 if ((sfd
= _fd(socket(AF_UNIX
, st
, 0))) == -1)
551 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHMODE
))) {
552 sun_mode
= (mode_t
)launch_data_get_integer(val
);
557 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
561 oldmask
= umask(S_IRWXG
|S_IRWXO
);
562 if (bind(sfd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
569 chmod(sun
.sun_path
, sun_mode
);
571 if ((st
== SOCK_STREAM
|| st
== SOCK_SEQPACKET
)
572 && listen(sfd
, SOMAXCONN
) == -1) {
576 } else if (connect(sfd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
581 val
= launch_data_new_fd(sfd
);
582 launch_data_array_append(fdarray
, val
);
584 launch_data_t rnames
= NULL
;
585 const char *node
= NULL
, *serv
= NULL
, *mgroup
= NULL
;
587 struct addrinfo hints
, *res0
, *res
;
588 int gerr
, sock_opt
= 1;
589 bool rendezvous
= false;
591 memset(&hints
, 0, sizeof(hints
));
593 hints
.ai_socktype
= st
;
595 hints
.ai_flags
|= AI_PASSIVE
;
597 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_NODENAME
)))
598 node
= launch_data_get_string(val
);
599 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP
)))
600 mgroup
= launch_data_get_string(val
);
601 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SERVICENAME
))) {
602 if (LAUNCH_DATA_INTEGER
== launch_data_get_type(val
)) {
603 sprintf(servnbuf
, "%lld", launch_data_get_integer(val
));
606 serv
= launch_data_get_string(val
);
609 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_FAMILY
))) {
610 if (!strcasecmp("IPv4", launch_data_get_string(val
)))
611 hints
.ai_family
= AF_INET
;
612 else if (!strcasecmp("IPv6", launch_data_get_string(val
)))
613 hints
.ai_family
= AF_INET6
;
615 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PROTOCOL
))) {
616 if (!strcasecmp("TCP", launch_data_get_string(val
)))
617 hints
.ai_protocol
= IPPROTO_TCP
;
619 if ((rnames
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_BONJOUR
))) {
621 if (LAUNCH_DATA_BOOL
== launch_data_get_type(rnames
)) {
622 rendezvous
= launch_data_get_bool(rnames
);
627 if ((gerr
= getaddrinfo(node
, serv
, &hints
, &res0
)) != 0) {
628 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
632 for (res
= res0
; res
; res
= res
->ai_next
) {
633 launch_data_t rvs_fd
= NULL
;
634 if ((sfd
= _fd(socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
))) == -1) {
635 fprintf(stderr
, "socket(): %s\n", strerror(errno
));
638 if (hints
.ai_flags
& AI_PASSIVE
) {
639 if (AF_INET6
== res
->ai_family
&& -1 == setsockopt(sfd
, IPPROTO_IPV6
, IPV6_V6ONLY
,
640 (void *)&sock_opt
, sizeof(sock_opt
))) {
641 fprintf(stderr
, "setsockopt(IPV6_V6ONLY): %m");
645 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEPORT
, (void *)&sock_opt
, sizeof(sock_opt
)) == -1) {
646 fprintf(stderr
, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno
));
650 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&sock_opt
, sizeof(sock_opt
)) == -1) {
651 fprintf(stderr
, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno
));
655 if (bind(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
656 fprintf(stderr
, "bind(): %s\n", strerror(errno
));
661 do_mgroup_join(sfd
, res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
, mgroup
);
663 if ((res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_SEQPACKET
)
664 && listen(sfd
, SOMAXCONN
) == -1) {
665 fprintf(stderr
, "listen(): %s\n", strerror(errno
));
668 if (rendezvous
&& (res
->ai_family
== AF_INET
|| res
->ai_family
== AF_INET6
) &&
669 (res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_DGRAM
)) {
670 launch_data_t rvs_fds
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_BONJOURFDS
);
671 if (NULL
== rvs_fds
) {
672 rvs_fds
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
673 launch_data_dict_insert(thejob
, rvs_fds
, LAUNCH_JOBKEY_BONJOURFDS
);
675 if (NULL
== rnames
) {
676 rvs_fd
= do_rendezvous_magic(res
, serv
);
678 launch_data_array_append(rvs_fds
, rvs_fd
);
679 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(rnames
)) {
680 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rnames
));
682 launch_data_array_append(rvs_fds
, rvs_fd
);
683 } else if (LAUNCH_DATA_ARRAY
== launch_data_get_type(rnames
)) {
684 size_t rn_i
, rn_ac
= launch_data_array_get_count(rnames
);
686 for (rn_i
= 0; rn_i
< rn_ac
; rn_i
++) {
687 launch_data_t rn_tmp
= launch_data_array_get_index(rnames
, rn_i
);
689 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rn_tmp
));
691 launch_data_array_append(rvs_fds
, rvs_fd
);
696 if (connect(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
697 fprintf(stderr
, "connect(): %s\n", strerror(errno
));
701 val
= launch_data_new_fd(sfd
);
703 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
704 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
707 launch_data_array_append(fdarray
, val
);
712 static void do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
)
714 struct addrinfo hints
, *res0
, *res
;
716 struct ipv6_mreq m6req
;
719 memset(&hints
, 0, sizeof(hints
));
721 hints
.ai_flags
|= AI_PASSIVE
;
722 hints
.ai_family
= family
;
723 hints
.ai_socktype
= socktype
;
724 hints
.ai_protocol
= protocol
;
726 if ((gerr
= getaddrinfo(mgroup
, NULL
, &hints
, &res0
)) != 0) {
727 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
731 for (res
= res0
; res
; res
= res
->ai_next
) {
732 if (AF_INET
== family
) {
733 memset(&mreq
, 0, sizeof(mreq
));
734 mreq
.imr_multiaddr
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_addr
;
735 if (setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
)) == -1) {
736 fprintf(stderr
, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
740 } else if (AF_INET6
== family
) {
741 memset(&m6req
, 0, sizeof(m6req
));
742 m6req
.ipv6mr_multiaddr
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
;
743 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &m6req
, sizeof(m6req
)) == -1) {
744 fprintf(stderr
, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno
));
749 fprintf(stderr
, "unknown family during multicast group bind!\n");
758 static launch_data_t
do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
)
761 DNSServiceRef service
;
762 DNSServiceErrorType error
;
765 static int statres
= 1;
768 statres
= stat("/usr/sbin/mDNSResponder", &sb
);
773 sprintf(rvs_buf
, "_%s._%s.", serv
, res
->ai_socktype
== SOCK_STREAM
? "tcp" : "udp");
775 if (res
->ai_family
== AF_INET
)
776 port
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_port
;
778 port
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_port
;
780 error
= DNSServiceRegister(&service
, 0, 0, NULL
, rvs_buf
, NULL
, NULL
, port
, 0, NULL
, NULL
, NULL
);
782 if (error
== kDNSServiceErr_NoError
)
783 return launch_data_new_fd(DNSServiceRefSockFD(service
));
785 fprintf(stderr
, "DNSServiceRegister(\"%s\"): %d\n", serv
, error
);
789 static CFPropertyListRef
CreateMyPropertyListFromFile(const char *posixfile
)
791 CFPropertyListRef propertyList
;
792 CFStringRef errorString
;
793 CFDataRef resourceData
;
797 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)posixfile
, strlen(posixfile
), false);
799 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
800 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, fileURL
, &resourceData
, NULL
, NULL
, &errorCode
))
801 fprintf(stderr
, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
802 propertyList
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, resourceData
, kCFPropertyListMutableContainers
, &errorString
);
804 fprintf(stderr
, "%s: propertyList is NULL\n", getprogname());
809 static void WriteMyPropertyListToFile(CFPropertyListRef plist
, const char *posixfile
)
811 CFDataRef resourceData
;
815 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)posixfile
, strlen(posixfile
), false);
817 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
818 resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, plist
);
819 if (resourceData
== NULL
)
820 fprintf(stderr
, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile
);
821 if (!CFURLWriteDataAndPropertiesToResource(fileURL
, resourceData
, NULL
, &errorCode
))
822 fprintf(stderr
, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
825 void myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
)
827 launch_data_t ik
, iw
, where
= context
;
829 ik
= CF2launch_data(key
);
830 iw
= CF2launch_data(value
);
832 launch_data_dict_insert(where
, iw
, launch_data_get_string(ik
));
833 launch_data_free(ik
);
836 static launch_data_t
CF2launch_data(CFTypeRef cfr
)
839 CFTypeID cft
= CFGetTypeID(cfr
);
841 if (cft
== CFStringGetTypeID()) {
843 CFStringGetCString(cfr
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
844 r
= launch_data_alloc(LAUNCH_DATA_STRING
);
845 launch_data_set_string(r
, buf
);
846 } else if (cft
== CFBooleanGetTypeID()) {
847 r
= launch_data_alloc(LAUNCH_DATA_BOOL
);
848 launch_data_set_bool(r
, CFBooleanGetValue(cfr
));
849 } else if (cft
== CFArrayGetTypeID()) {
850 CFIndex i
, ac
= CFArrayGetCount(cfr
);
851 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
852 for (i
= 0; i
< ac
; i
++) {
853 CFTypeRef v
= CFArrayGetValueAtIndex(cfr
, i
);
855 launch_data_t iv
= CF2launch_data(v
);
856 launch_data_array_set_index(r
, iv
, i
);
859 } else if (cft
== CFDictionaryGetTypeID()) {
860 r
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
861 CFDictionaryApplyFunction(cfr
, myCFDictionaryApplyFunction
, r
);
862 } else if (cft
== CFDataGetTypeID()) {
863 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
864 launch_data_set_opaque(r
, CFDataGetBytePtr(cfr
), CFDataGetLength(cfr
));
865 } else if (cft
== CFNumberGetTypeID()) {
868 CFNumberType cfnt
= CFNumberGetType(cfr
);
870 case kCFNumberSInt8Type
:
871 case kCFNumberSInt16Type
:
872 case kCFNumberSInt32Type
:
873 case kCFNumberSInt64Type
:
874 case kCFNumberCharType
:
875 case kCFNumberShortType
:
876 case kCFNumberIntType
:
877 case kCFNumberLongType
:
878 case kCFNumberLongLongType
:
879 CFNumberGetValue(cfr
, kCFNumberLongLongType
, &n
);
880 r
= launch_data_alloc(LAUNCH_DATA_INTEGER
);
881 launch_data_set_integer(r
, n
);
883 case kCFNumberFloat32Type
:
884 case kCFNumberFloat64Type
:
885 case kCFNumberFloatType
:
886 case kCFNumberDoubleType
:
887 CFNumberGetValue(cfr
, kCFNumberDoubleType
, &d
);
888 r
= launch_data_alloc(LAUNCH_DATA_REAL
);
889 launch_data_set_real(r
, d
);
901 static int help_cmd(int argc
, char *const argv
[])
903 FILE *where
= stdout
;
907 if (argc
== 0 || argv
== NULL
)
910 fprintf(where
, "usage: %s <subcommand>\n", getprogname());
911 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
912 l
= strlen(cmds
[i
].name
);
916 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++)
917 fprintf(where
, "\t%-*s\t%s\n", cmdwidth
, cmds
[i
].name
, cmds
[i
].desc
);
922 static int _fd(int fd
)
925 fcntl(fd
, F_SETFD
, 1);
929 static int load_and_unload_cmd(int argc
, char *const argv
[])
931 launch_data_t pass0
, pass1
, pass2
;
937 if (!strcmp(argv
[0], "load"))
940 while ((ch
= getopt(argc
, argv
, "wF")) != -1) {
942 case 'w': wflag
= true; break;
943 case 'F': Fflag
= true; break;
945 fprintf(stderr
, "usage: %s load [-wF] paths...\n", getprogname());
953 fprintf(stderr
, "usage: %s load [-w] paths...\n", getprogname());
957 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
958 * And loading legacy mach init jobs is extra fun.
960 * In later versions of launchd, I hope to load everything in the first pass,
961 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
962 * I haven't thought through the various complexities of reloading jobs, and therefore
963 * launchd doesn't have reload support right now.
966 pass0
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
967 pass1
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
968 pass2
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
970 for (i
= 0; i
< argc
; i
++)
971 readpath(argv
[i
], pass0
, pass1
, pass2
, wflag
, lflag
, Fflag
);
973 if (launch_data_array_get_count(pass0
) == 0 &&
974 launch_data_array_get_count(pass1
) == 0 &&
975 launch_data_array_get_count(pass2
) == 0) {
976 fprintf(stderr
, "nothing found to %s\n", lflag
? "load" : "unload");
977 launch_data_free(pass0
);
978 launch_data_free(pass1
);
979 launch_data_free(pass2
);
985 submit_mach_jobs(pass0
);
986 submit_job_pass(pass1
);
987 let_go_of_mach_jobs();
989 submit_job_pass(pass2
);
991 for (i
= 0; i
< (int)launch_data_array_get_count(pass1
); i
++)
992 unloadjob(launch_data_array_get_index(pass1
, i
));
993 for (i
= 0; i
< (int)launch_data_array_get_count(pass2
); i
++)
994 unloadjob(launch_data_array_get_index(pass2
, i
));
1000 static mach_port_t
*msrvs
= NULL
;
1001 static size_t msrvs_cnt
= 0;
1004 submit_mach_jobs(launch_data_t jobs
)
1008 c
= launch_data_array_get_count(jobs
);
1010 msrvs
= calloc(1, sizeof(mach_port_t
) * c
);
1013 for (i
= 0; i
< c
; i
++) {
1014 launch_data_t tmp
, oai
= launch_data_array_get_index(jobs
, i
);
1015 const char *sn
= NULL
, *cmd
= NULL
;
1016 bool d
= true, k
= false;
1017 mach_port_t msr
, msv
, mhp
;
1021 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_ONDEMAND
)))
1022 d
= launch_data_get_bool(tmp
);
1023 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_ISKUNCSERVER
)))
1024 k
= launch_data_get_bool(tmp
);
1025 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_SERVICENAME
)))
1026 sn
= launch_data_get_string(tmp
);
1027 if ((tmp
= launch_data_dict_lookup(oai
, MACHINIT_JOBKEY_COMMAND
)))
1028 cmd
= launch_data_get_string(tmp
);
1030 if ((kr
= bootstrap_create_server(bootstrap_port
, (char *)cmd
, u
, d
, &msr
)) != KERN_SUCCESS
) {
1031 fprintf(stderr
, "%s: bootstrap_create_server(): %d\n", getprogname(), kr
);
1034 if ((kr
= bootstrap_create_service(msr
, (char*)sn
, &msv
)) != KERN_SUCCESS
) {
1035 fprintf(stderr
, "%s: bootstrap_create_service(): %d\n", getprogname(), kr
);
1036 mach_port_destroy(mach_task_self(), msr
);
1040 mhp
= mach_host_self();
1041 if ((kr
= host_set_UNDServer(mhp
, msv
)) != KERN_SUCCESS
)
1042 fprintf(stderr
, "%s: host_set_UNDServer(): %s\n", getprogname(), mach_error_string(kr
));
1043 mach_port_deallocate(mach_task_self(), mhp
);
1045 mach_port_deallocate(mach_task_self(), msv
);
1051 let_go_of_mach_jobs(void)
1055 for (i
= 0; i
< msrvs_cnt
; i
++)
1056 mach_port_destroy(mach_task_self(), msrvs
[i
]);
1060 submit_job_pass(launch_data_t jobs
)
1062 launch_data_t msg
, resp
;
1066 if (launch_data_array_get_count(jobs
) == 0)
1069 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1071 launch_data_dict_insert(msg
, jobs
, LAUNCH_KEY_SUBMITJOB
);
1073 resp
= launch_msg(msg
);
1076 switch (launch_data_get_type(resp
)) {
1077 case LAUNCH_DATA_ERRNO
:
1078 if ((e
= launch_data_get_errno(resp
)))
1079 fprintf(stderr
, "%s\n", strerror(e
));
1081 case LAUNCH_DATA_ARRAY
:
1082 for (i
= 0; i
< launch_data_array_get_count(jobs
); i
++) {
1083 launch_data_t obatind
= launch_data_array_get_index(resp
, i
);
1084 launch_data_t jatind
= launch_data_array_get_index(jobs
, i
);
1085 const char *lab4job
= launch_data_get_string(launch_data_dict_lookup(jatind
, LAUNCH_JOBKEY_LABEL
));
1086 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(obatind
)) {
1087 e
= launch_data_get_errno(obatind
);
1090 fprintf(stderr
, "%s: %s\n", lab4job
, "Already loaded");
1093 fprintf(stderr
, "%s: %s\n", lab4job
, "Not loaded");
1096 fprintf(stderr
, "%s: %s\n", lab4job
, strerror(e
));
1104 fprintf(stderr
, "unknown respose from launchd!\n");
1107 launch_data_free(resp
);
1109 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1112 launch_data_free(msg
);
1115 static int start_and_stop_cmd(int argc
, char *const argv
[])
1117 launch_data_t resp
, msg
;
1118 const char *lmsgcmd
= LAUNCH_KEY_STOPJOB
;
1121 if (!strcmp(argv
[0], "start"))
1122 lmsgcmd
= LAUNCH_KEY_STARTJOB
;
1125 fprintf(stderr
, "usage: %s %s <job label>\n", getprogname(), argv
[0]);
1129 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1130 launch_data_dict_insert(msg
, launch_data_new_string(argv
[1]), lmsgcmd
);
1132 resp
= launch_msg(msg
);
1133 launch_data_free(msg
);
1136 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1138 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1139 if ((e
= launch_data_get_errno(resp
))) {
1140 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1144 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1148 launch_data_free(resp
);
1152 static void print_jobs(launch_data_t j
__attribute__((unused
)), const char *label
, void *context
__attribute__((unused
)))
1154 fprintf(stdout
, "%s\n", label
);
1157 static int list_cmd(int argc
, char *const argv
[])
1159 launch_data_t resp
, msg
;
1163 while ((ch
= getopt(argc
, argv
, "v")) != -1) {
1169 fprintf(stderr
, "usage: %s list [-v]\n", getprogname());
1175 fprintf(stderr
, "usage: %s list: \"-v\" flag not implemented yet\n", getprogname());
1179 msg
= launch_data_new_string(LAUNCH_KEY_GETJOBS
);
1180 resp
= launch_msg(msg
);
1181 launch_data_free(msg
);
1184 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1186 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_DICTIONARY
) {
1187 launch_data_dict_iterate(resp
, print_jobs
, NULL
);
1189 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1193 launch_data_free(resp
);
1198 static int stdio_cmd(int argc
, char *const argv
[])
1200 launch_data_t resp
, msg
, tmp
;
1201 int e
, fd
= -1, r
= 0;
1204 fprintf(stderr
, "usage: %s %s <path>\n", getprogname(), argv
[0]);
1208 fd
= open(argv
[1], O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1210 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1213 tmp
= launch_data_new_string(argv
[1]);
1215 tmp
= launch_data_new_fd(fd
);
1218 if (!strcmp(argv
[0], "stdout")) {
1219 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETSTDOUT
);
1221 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETSTDERR
);
1224 resp
= launch_msg(msg
);
1225 launch_data_free(msg
);
1228 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1230 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1231 if ((e
= launch_data_get_errno(resp
))) {
1232 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1236 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1243 launch_data_free(resp
);
1248 static int fyi_cmd(int argc
, char *const argv
[])
1250 launch_data_t resp
, msg
;
1251 const char *lmsgk
= LAUNCH_KEY_RELOADTTYS
;
1255 fprintf(stderr
, "usage: %s %s\n", getprogname(), argv
[0]);
1259 if (!strcmp(argv
[0], "shutdown"))
1260 lmsgk
= LAUNCH_KEY_SHUTDOWN
;
1262 msg
= launch_data_new_string(lmsgk
);
1263 resp
= launch_msg(msg
);
1264 launch_data_free(msg
);
1267 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1269 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1270 if ((e
= launch_data_get_errno(resp
))) {
1271 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1275 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1279 launch_data_free(resp
);
1284 static int logupdate_cmd(int argc
, char *const argv
[])
1286 launch_data_t resp
, msg
;
1287 int e
, i
, j
, r
= 0, m
= 0;
1288 bool badargs
= false, maskmode
= false, onlymode
= false, levelmode
= false;
1289 const char *whichcmd
= LAUNCH_KEY_SETLOGMASK
;
1290 static const struct {
1294 { "debug", LOG_DEBUG
},
1295 { "info", LOG_INFO
},
1296 { "notice", LOG_NOTICE
},
1297 { "warning", LOG_WARNING
},
1298 { "error", LOG_ERR
},
1299 { "critical", LOG_CRIT
},
1300 { "alert", LOG_ALERT
},
1301 { "emergency", LOG_EMERG
},
1303 int logtblsz
= sizeof logtbl
/ sizeof logtbl
[0];
1306 if (!strcmp(argv
[1], "mask"))
1308 else if (!strcmp(argv
[1], "only"))
1310 else if (!strcmp(argv
[1], "level"))
1317 m
= LOG_UPTO(LOG_DEBUG
);
1319 if (argc
> 2 && (maskmode
|| onlymode
)) {
1320 for (i
= 2; i
< argc
; i
++) {
1321 for (j
= 0; j
< logtblsz
; j
++) {
1322 if (!strcmp(argv
[i
], logtbl
[j
].name
)) {
1324 m
&= ~(LOG_MASK(logtbl
[j
].level
));
1326 m
|= LOG_MASK(logtbl
[j
].level
);
1330 if (j
== logtblsz
) {
1335 } else if (argc
> 2 && levelmode
) {
1336 for (j
= 0; j
< logtblsz
; j
++) {
1337 if (!strcmp(argv
[2], logtbl
[j
].name
)) {
1338 m
= LOG_UPTO(logtbl
[j
].level
);
1344 } else if (argc
== 1) {
1345 whichcmd
= LAUNCH_KEY_GETLOGMASK
;
1351 fprintf(stderr
, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
1355 if (whichcmd
== LAUNCH_KEY_SETLOGMASK
) {
1356 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1357 launch_data_dict_insert(msg
, launch_data_new_integer(m
), whichcmd
);
1359 msg
= launch_data_new_string(whichcmd
);
1362 resp
= launch_msg(msg
);
1363 launch_data_free(msg
);
1366 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1368 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1369 if ((e
= launch_data_get_errno(resp
))) {
1370 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1373 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_INTEGER
) {
1374 if (whichcmd
== LAUNCH_KEY_GETLOGMASK
) {
1375 m
= launch_data_get_integer(resp
);
1376 for (j
= 0; j
< logtblsz
; j
++) {
1377 if (m
& LOG_MASK(logtbl
[j
].level
))
1378 fprintf(stdout
, "%s ", logtbl
[j
].name
);
1380 fprintf(stdout
, "\n");
1383 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1387 launch_data_free(resp
);
1392 static const struct {
1396 { "cpu", RLIMIT_CPU
},
1397 { "filesize", RLIMIT_FSIZE
},
1398 { "data", RLIMIT_DATA
},
1399 { "stack", RLIMIT_STACK
},
1400 { "core", RLIMIT_CORE
},
1401 { "rss", RLIMIT_RSS
},
1402 { "memlock", RLIMIT_MEMLOCK
},
1403 { "maxproc", RLIMIT_NPROC
},
1404 { "maxfiles", RLIMIT_NOFILE
}
1407 static const size_t limlookupcnt
= sizeof limlookup
/ sizeof limlookup
[0];
1409 static ssize_t
name2num(const char *n
)
1413 for (i
= 0; i
< limlookupcnt
; i
++) {
1414 if (!strcmp(limlookup
[i
].name
, n
)) {
1415 return limlookup
[i
].lim
;
1421 static const char *num2name(int n
)
1425 for (i
= 0; i
< limlookupcnt
; i
++) {
1426 if (limlookup
[i
].lim
== n
)
1427 return limlookup
[i
].name
;
1432 static const char *lim2str(rlim_t val
, char *buf
)
1434 if (val
== RLIM_INFINITY
)
1435 strcpy(buf
, "unlimited");
1437 sprintf(buf
, "%lld", val
);
1441 static bool str2lim(const char *buf
, rlim_t
*res
)
1444 *res
= strtoll(buf
, &endptr
, 10);
1445 if (!strcmp(buf
, "unlimited")) {
1446 *res
= RLIM_INFINITY
;
1448 } else if (*endptr
== '\0') {
1454 static int limit_cmd(int argc
__attribute__((unused
)), char *const argv
[])
1458 struct rlimit
*lmts
= NULL
;
1459 launch_data_t resp
, resp1
= NULL
, msg
, tmp
;
1463 rlim_t slim
= -1, hlim
= -1;
1464 bool badargs
= false;
1469 if (argc
>= 3 && str2lim(argv
[2], &slim
))
1474 if (argc
== 4 && str2lim(argv
[3], &hlim
))
1477 if (argc
>= 2 && -1 == (which
= name2num(argv
[1])))
1481 fprintf(stderr
, "usage: %s %s [", getprogname(), argv
[0]);
1482 for (i
= 0; i
< limlookupcnt
; i
++)
1483 fprintf(stderr
, "%s %s", limlookup
[i
].name
, (i
+ 1) == limlookupcnt
? "" : "| ");
1484 fprintf(stderr
, "[both | soft hard]]\n");
1488 msg
= launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS
);
1489 resp
= launch_msg(msg
);
1490 launch_data_free(msg
);
1493 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1495 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
1496 lmts
= launch_data_get_opaque(resp
);
1497 lsz
= launch_data_get_opaque_size(resp
);
1499 for (i
= 0; i
< (lsz
/ sizeof(struct rlimit
)); i
++) {
1500 if (argc
== 2 && (size_t)which
!= i
)
1502 fprintf(stdout
, "\t%-12s%-15s%-15s\n", num2name(i
),
1503 lim2str(lmts
[i
].rlim_cur
, slimstr
),
1504 lim2str(lmts
[i
].rlim_max
, hlimstr
));
1507 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
1508 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
1511 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1515 if (argc
<= 2 || r
!= 0) {
1516 launch_data_free(resp
);
1522 lmts
[which
].rlim_cur
= slim
;
1523 lmts
[which
].rlim_max
= hlim
;
1525 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1526 tmp
= launch_data_new_opaque(lmts
, lsz
);
1527 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETRESOURCELIMITS
);
1528 resp
= launch_msg(msg
);
1529 launch_data_free(msg
);
1532 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1534 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
1535 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
1537 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_OPAQUE
) {
1538 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1542 launch_data_free(resp
);
1543 launch_data_free(resp1
);
1548 static int umask_cmd(int argc
, char *const argv
[])
1550 launch_data_t resp
, msg
;
1551 bool badargs
= false;
1557 m
= strtol(argv
[1], &endptr
, 8);
1558 if (*endptr
!= '\0' || m
> 0777)
1562 if (argc
> 2 || badargs
) {
1563 fprintf(stderr
, "usage: %s %s <mask>\n", getprogname(), argv
[0]);
1569 msg
= launch_data_new_string(LAUNCH_KEY_GETUMASK
);
1571 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1572 launch_data_dict_insert(msg
, launch_data_new_integer(m
), LAUNCH_KEY_SETUMASK
);
1574 resp
= launch_msg(msg
);
1575 launch_data_free(msg
);
1578 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1580 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
1581 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
1583 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_INTEGER
) {
1584 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1586 } else if (argc
== 1) {
1587 fprintf(stdout
, "%o\n", (unsigned int)launch_data_get_integer(resp
));
1590 launch_data_free(resp
);
1595 static int getrusage_cmd(int argc
, char *const argv
[])
1597 launch_data_t resp
, msg
;
1598 bool badargs
= false;
1603 else if (strcmp(argv
[1], "self") && strcmp(argv
[1], "children"))
1607 fprintf(stderr
, "usage: %s %s self | children\n", getprogname(), argv
[0]);
1611 if (!strcmp(argv
[1], "self")) {
1612 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF
);
1614 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN
);
1617 resp
= launch_msg(msg
);
1618 launch_data_free(msg
);
1621 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1623 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1624 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(launch_data_get_errno(resp
)));
1626 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
1627 struct rusage
*rusage
= launch_data_get_opaque(resp
);
1628 fprintf(stdout
, "\t%-10f\tuser time used\n",
1629 (double)rusage
->ru_utime
.tv_sec
+ (double)rusage
->ru_utime
.tv_usec
/ (double)1000000);
1630 fprintf(stdout
, "\t%-10f\tsystem time used\n",
1631 (double)rusage
->ru_stime
.tv_sec
+ (double)rusage
->ru_stime
.tv_usec
/ (double)1000000);
1632 fprintf(stdout
, "\t%-10ld\tmax resident set size\n", rusage
->ru_maxrss
);
1633 fprintf(stdout
, "\t%-10ld\tshared text memory size\n", rusage
->ru_ixrss
);
1634 fprintf(stdout
, "\t%-10ld\tunshared data size\n", rusage
->ru_idrss
);
1635 fprintf(stdout
, "\t%-10ld\tunshared stack size\n", rusage
->ru_isrss
);
1636 fprintf(stdout
, "\t%-10ld\tpage reclaims\n", rusage
->ru_minflt
);
1637 fprintf(stdout
, "\t%-10ld\tpage faults\n", rusage
->ru_majflt
);
1638 fprintf(stdout
, "\t%-10ld\tswaps\n", rusage
->ru_nswap
);
1639 fprintf(stdout
, "\t%-10ld\tblock input operations\n", rusage
->ru_inblock
);
1640 fprintf(stdout
, "\t%-10ld\tblock output operations\n", rusage
->ru_oublock
);
1641 fprintf(stdout
, "\t%-10ld\tmessages sent\n", rusage
->ru_msgsnd
);
1642 fprintf(stdout
, "\t%-10ld\tmessages received\n", rusage
->ru_msgrcv
);
1643 fprintf(stdout
, "\t%-10ld\tsignals received\n", rusage
->ru_nsignals
);
1644 fprintf(stdout
, "\t%-10ld\tvoluntary context switches\n", rusage
->ru_nvcsw
);
1645 fprintf(stdout
, "\t%-10ld\tinvoluntary context switches\n", rusage
->ru_nivcsw
);
1647 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1651 launch_data_free(resp
);
1656 static bool launch_data_array_append(launch_data_t a
, launch_data_t o
)
1658 size_t offt
= launch_data_array_get_count(a
);
1660 return launch_data_array_set_index(a
, o
, offt
);
1664 is_legacy_mach_job(launch_data_t obj
)
1666 bool has_servicename
= launch_data_dict_lookup(obj
, MACHINIT_JOBKEY_SERVICENAME
);
1667 bool has_command
= launch_data_dict_lookup(obj
, MACHINIT_JOBKEY_COMMAND
);
1668 bool has_label
= launch_data_dict_lookup(obj
, LAUNCH_JOBKEY_LABEL
);
1670 return has_command
&& has_servicename
&& !has_label
;