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 <sys/types.h>
27 #include <sys/socket.h>
29 #include <sys/fcntl.h>
30 #include <sys/event.h>
31 #include <sys/resource.h>
32 #include <sys/param.h>
33 #include <netinet/in.h>
44 #include <readline/readline.h>
45 #include <readline/history.h>
49 #include "launch_priv.h"
51 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
53 static bool launch_data_array_append(launch_data_t a
, launch_data_t o
);
54 static void distill_config_file(launch_data_t
);
55 static void sock_dict_cb(launch_data_t what
, const char *key
, void *context
);
56 static void sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
);
57 static launch_data_t
CF2launch_data(CFTypeRef
);
58 static launch_data_t
read_plist_file(const char *file
, bool editondisk
, bool load
);
59 static CFPropertyListRef
CreateMyPropertyListFromFile(const char *);
60 static void WriteMyPropertyListToFile(CFPropertyListRef
, const char *);
61 static void readpath(const char *, launch_data_t
, launch_data_t
, bool editondisk
, bool load
, bool forceload
);
63 static int demux_cmd(int argc
, char *const argv
[]);
64 static launch_data_t
do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
);
65 static void submit_job_pass(launch_data_t jobs
);
66 static void do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
);
68 static int load_and_unload_cmd(int argc
, char *const argv
[]);
69 //static int reload_cmd(int argc, char *const argv[]);
70 static int start_and_stop_cmd(int argc
, char *const argv
[]);
71 static int list_cmd(int argc
, char *const argv
[]);
73 static int setenv_cmd(int argc
, char *const argv
[]);
74 static int unsetenv_cmd(int argc
, char *const argv
[]);
75 static int getenv_and_export_cmd(int argc
, char *const argv
[]);
77 static int limit_cmd(int argc
, char *const argv
[]);
78 static int stdio_cmd(int argc
, char *const argv
[]);
79 static int fyi_cmd(int argc
, char *const argv
[]);
80 static int logupdate_cmd(int argc
, char *const argv
[]);
81 static int umask_cmd(int argc
, char *const argv
[]);
82 static int getrusage_cmd(int argc
, char *const argv
[]);
84 static int help_cmd(int argc
, char *const argv
[]);
88 int (*func
)(int argc
, char *const argv
[]);
91 { "load", load_and_unload_cmd
, "Load configuration files and/or directories" },
92 { "unload", load_and_unload_cmd
, "Unload configuration files and/or directories" },
93 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
94 { "start", start_and_stop_cmd
, "Start specified jobs" },
95 { "stop", start_and_stop_cmd
, "Stop specified jobs" },
96 { "list", list_cmd
, "List jobs and information about jobs" },
97 { "setenv", setenv_cmd
, "Set an environmental variable in launchd" },
98 { "unsetenv", unsetenv_cmd
, "Unset an environmental variable in launchd" },
99 { "getenv", getenv_and_export_cmd
, "Get an environmental variable from launchd" },
100 { "export", getenv_and_export_cmd
, "Export shell settings from launchd" },
101 { "limit", limit_cmd
, "View and adjust launchd resource limits" },
102 { "stdout", stdio_cmd
, "Redirect launchd's standard out to the given path" },
103 { "stderr", stdio_cmd
, "Redirect launchd's standard error to the given path" },
104 { "shutdown", fyi_cmd
, "Prepare for system shutdown" },
105 { "reloadttys", fyi_cmd
, "Reload /etc/ttys" },
106 { "getrusage", getrusage_cmd
, "Get resource usage statistics from launchd" },
107 { "log", logupdate_cmd
, "Adjust the logging level or mask of launchd" },
108 { "umask", umask_cmd
, "Change launchd's umask" },
109 { "help", help_cmd
, "This help output" },
112 int main(int argc
, char *const argv
[])
114 bool istty
= isatty(STDIN_FILENO
);
118 exit(demux_cmd(argc
- 1, argv
+ 1));
120 if (NULL
== readline
) {
121 fprintf(stderr
, "missing library: readline\n");
125 while ((l
= readline(istty
? "launchd% " : NULL
))) {
126 char *inputstring
= l
, *argv2
[100], **ap
= argv2
;
129 while ((*ap
= strsep(&inputstring
, " \t"))) {
148 static int demux_cmd(int argc
, char *const argv
[])
155 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
156 if (!strcmp(cmds
[i
].name
, argv
[0]))
157 return cmds
[i
].func(argc
, argv
);
160 fprintf(stderr
, "%s: unknown subcommand \"%s\"\n", getprogname(), argv
[0]);
164 static int unsetenv_cmd(int argc
, char *const argv
[])
166 launch_data_t resp
, tmp
, msg
;
169 fprintf(stderr
, "%s usage: unsetenv <key>\n", getprogname());
173 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
175 tmp
= launch_data_new_string(argv
[1]);
176 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_UNSETUSERENVIRONMENT
);
178 resp
= launch_msg(msg
);
180 launch_data_free(msg
);
183 launch_data_free(resp
);
185 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT
, strerror(errno
));
191 static int setenv_cmd(int argc
, char *const argv
[])
193 launch_data_t resp
, tmp
, tmpv
, msg
;
196 fprintf(stderr
, "%s usage: setenv <key> <value>\n", getprogname());
200 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
201 tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
203 tmpv
= launch_data_new_string(argv
[2]);
204 launch_data_dict_insert(tmp
, tmpv
, argv
[1]);
205 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETUSERENVIRONMENT
);
207 resp
= launch_msg(msg
);
208 launch_data_free(msg
);
211 launch_data_free(resp
);
213 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT
, strerror(errno
));
219 static int getenv_and_export_cmd(int argc
, char *const argv
[] __attribute__((unused
)))
221 launch_data_t resp
, msg
;
224 void print_launchd_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
))) {
226 fprintf(stdout
, "setenv %s %s;\n", key
, launch_data_get_string(obj
));
228 fprintf(stdout
, "%s=%s; export %s;\n", key
, launch_data_get_string(obj
), key
);
230 void print_key_value(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
))) {
232 fprintf(stdout
, "%s\n", launch_data_get_string(obj
));
235 if (!strcmp(argv
[0], "export")) {
236 char *s
= getenv("SHELL");
238 is_csh
= strstr(s
, "csh") ? true : false;
239 } else if (argc
!= 2) {
240 fprintf(stderr
, "%s usage: getenv <key>\n", getprogname());
246 msg
= launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT
);
248 resp
= launch_msg(msg
);
249 launch_data_free(msg
);
252 launch_data_dict_iterate(resp
, (!strcmp(argv
[0], "export")) ? print_launchd_env
: print_key_value
, NULL
);
253 launch_data_free(resp
);
255 fprintf(stderr
, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT
"\"): %s\n", strerror(errno
));
260 static void unloadjob(launch_data_t job
)
262 launch_data_t resp
, tmp
, tmps
, msg
;
265 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
266 tmp
= launch_data_alloc(LAUNCH_DATA_STRING
);
267 tmps
= launch_data_dict_lookup(job
, LAUNCH_JOBKEY_LABEL
);
270 fprintf(stderr
, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL
);
274 launch_data_set_string(tmp
, launch_data_get_string(tmps
));
275 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_REMOVEJOB
);
276 resp
= launch_msg(msg
);
277 launch_data_free(msg
);
279 fprintf(stderr
, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno
));
282 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(resp
)) {
283 if ((e
= launch_data_get_errno(resp
)))
284 fprintf(stderr
, "%s\n", strerror(e
));
286 launch_data_free(resp
);
289 static launch_data_t
read_plist_file(const char *file
, bool editondisk
, bool load
)
291 CFPropertyListRef plist
= CreateMyPropertyListFromFile(file
);
292 launch_data_t r
= NULL
;
295 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), file
);
301 CFDictionaryRemoveValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
));
303 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
), kCFBooleanTrue
);
304 WriteMyPropertyListToFile(plist
, file
);
307 r
= CF2launch_data(plist
);
314 static void delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
)
319 if (key
&& 0 == strcmp(key
, LAUNCH_JOBSOCKETKEY_BONJOUR
)) {
324 switch (launch_data_get_type(o
)) {
325 case LAUNCH_DATA_DICTIONARY
:
326 launch_data_dict_iterate(o
, delay_to_second_pass2
, context
);
328 case LAUNCH_DATA_ARRAY
:
329 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
330 delay_to_second_pass2(launch_data_array_get_index(o
, i
), NULL
, context
);
337 static bool delay_to_second_pass(launch_data_t o
)
341 launch_data_t socks
= launch_data_dict_lookup(o
, LAUNCH_JOBKEY_SOCKETS
);
346 delay_to_second_pass2(socks
, NULL
, &res
);
351 static void readfile(const char *what
, launch_data_t pass1
, launch_data_t pass2
, bool editondisk
, bool load
, bool forceload
)
353 launch_data_t tmpd
, thejob
;
354 bool job_disabled
= false;
356 if (NULL
== (thejob
= read_plist_file(what
, editondisk
, load
))) {
357 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), what
);
361 if (NULL
== launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LABEL
)) {
362 fprintf(stderr
, "%s: missing the Label key: %s\n", getprogname(), what
);
363 launch_data_free(thejob
);
367 if ((tmpd
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_DISABLED
)))
368 job_disabled
= launch_data_get_bool(tmpd
);
371 job_disabled
= false;
373 if (job_disabled
&& load
) {
374 launch_data_free(thejob
);
378 if (delay_to_second_pass(thejob
))
379 launch_data_array_append(pass2
, thejob
);
381 launch_data_array_append(pass1
, thejob
);
384 static void readpath(const char *what
, launch_data_t pass1
, launch_data_t pass2
, bool editondisk
, bool load
, bool forceload
)
386 char buf
[MAXPATHLEN
];
391 if (stat(what
, &sb
) == -1)
394 if (S_ISREG(sb
.st_mode
) && !(sb
.st_mode
& S_IWOTH
)) {
395 readfile(what
, pass1
, pass2
, editondisk
, load
, forceload
);
397 if ((d
= opendir(what
)) == NULL
) {
398 fprintf(stderr
, "%s: opendir() failed to open the directory\n", getprogname());
402 while ((de
= readdir(d
))) {
403 if ((de
->d_name
[0] == '.'))
405 snprintf(buf
, sizeof(buf
), "%s/%s", what
, de
->d_name
);
407 readfile(buf
, pass1
, pass2
, editondisk
, load
, forceload
);
413 struct distill_context
{
415 launch_data_t newsockdict
;
418 static void distill_config_file(launch_data_t id_plist
)
420 struct distill_context dc
= { id_plist
, NULL
};
423 if ((tmp
= launch_data_dict_lookup(id_plist
, LAUNCH_JOBKEY_SOCKETS
))) {
424 dc
.newsockdict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
425 launch_data_dict_iterate(tmp
, sock_dict_cb
, &dc
);
426 launch_data_dict_insert(dc
.base
, dc
.newsockdict
, LAUNCH_JOBKEY_SOCKETS
);
430 static void sock_dict_cb(launch_data_t what
, const char *key
, void *context
)
432 struct distill_context
*dc
= context
;
433 launch_data_t fdarray
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
435 launch_data_dict_insert(dc
->newsockdict
, fdarray
, key
);
437 if (launch_data_get_type(what
) == LAUNCH_DATA_DICTIONARY
) {
438 sock_dict_edit_entry(what
, key
, fdarray
, dc
->base
);
439 } else if (launch_data_get_type(what
) == LAUNCH_DATA_ARRAY
) {
443 for (i
= 0; i
< launch_data_array_get_count(what
); i
++) {
444 tmp
= launch_data_array_get_index(what
, i
);
445 sock_dict_edit_entry(tmp
, key
, fdarray
, dc
->base
);
450 static void sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
)
452 launch_data_t a
, val
;
453 int sfd
, st
= SOCK_STREAM
;
456 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_TYPE
))) {
457 if (!strcasecmp(launch_data_get_string(val
), "stream")) {
459 } else if (!strcasecmp(launch_data_get_string(val
), "dgram")) {
461 } else if (!strcasecmp(launch_data_get_string(val
), "seqpacket")) {
466 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PASSIVE
)))
467 passive
= launch_data_get_bool(val
);
469 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY
))) {
470 char secdir
[] = LAUNCH_SECDIR
, buf
[1024];
471 launch_data_t uenv
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
474 uenv
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
475 launch_data_dict_insert(thejob
, uenv
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
480 sprintf(buf
, "%s/%s", secdir
, key
);
482 a
= launch_data_new_string(buf
);
483 launch_data_dict_insert(tmp
, a
, LAUNCH_JOBSOCKETKEY_PATHNAME
);
484 a
= launch_data_new_string(buf
);
485 launch_data_dict_insert(uenv
, a
, launch_data_get_string(val
));
488 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHNAME
))) {
489 struct sockaddr_un sun
;
491 memset(&sun
, 0, sizeof(sun
));
493 sun
.sun_family
= AF_UNIX
;
495 strncpy(sun
.sun_path
, launch_data_get_string(val
), sizeof(sun
.sun_path
));
497 if ((sfd
= _fd(socket(AF_UNIX
, st
, 0))) == -1)
501 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
505 if (bind(sfd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
509 if ((st
== SOCK_STREAM
|| st
== SOCK_SEQPACKET
)
510 && listen(sfd
, SOMAXCONN
) == -1) {
514 } else if (connect(sfd
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1) {
519 val
= launch_data_new_fd(sfd
);
520 launch_data_array_append(fdarray
, val
);
522 launch_data_t rnames
= NULL
;
523 const char *node
= NULL
, *serv
= NULL
, *mgroup
= NULL
;
525 struct addrinfo hints
, *res0
, *res
;
526 int gerr
, sock_opt
= 1;
527 bool rendezvous
= false;
529 memset(&hints
, 0, sizeof(hints
));
531 hints
.ai_socktype
= st
;
533 hints
.ai_flags
|= AI_PASSIVE
;
535 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_NODENAME
)))
536 node
= launch_data_get_string(val
);
537 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP
)))
538 mgroup
= launch_data_get_string(val
);
539 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SERVICENAME
))) {
540 if (LAUNCH_DATA_INTEGER
== launch_data_get_type(val
)) {
541 sprintf(servnbuf
, "%lld", launch_data_get_integer(val
));
544 serv
= launch_data_get_string(val
);
547 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_FAMILY
))) {
548 if (!strcasecmp("IPv4", launch_data_get_string(val
)))
549 hints
.ai_family
= AF_INET
;
550 else if (!strcasecmp("IPv6", launch_data_get_string(val
)))
551 hints
.ai_family
= AF_INET6
;
553 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PROTOCOL
))) {
554 if (!strcasecmp("TCP", launch_data_get_string(val
)))
555 hints
.ai_protocol
= IPPROTO_TCP
;
557 if ((rnames
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_BONJOUR
))) {
559 if (LAUNCH_DATA_BOOL
== launch_data_get_type(rnames
)) {
560 rendezvous
= launch_data_get_bool(rnames
);
565 if ((gerr
= getaddrinfo(node
, serv
, &hints
, &res0
)) != 0) {
566 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
570 for (res
= res0
; res
; res
= res
->ai_next
) {
571 launch_data_t rvs_fd
= NULL
;
572 if ((sfd
= _fd(socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
))) == -1) {
573 fprintf(stderr
, "socket(): %s\n", strerror(errno
));
576 if (hints
.ai_flags
& AI_PASSIVE
) {
577 if (AF_INET6
== res
->ai_family
&& -1 == setsockopt(sfd
, IPPROTO_IPV6
, IPV6_V6ONLY
,
578 (void *)&sock_opt
, sizeof(sock_opt
))) {
579 fprintf(stderr
, "setsockopt(IPV6_V6ONLY): %m");
583 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEPORT
, (void *)&sock_opt
, sizeof(sock_opt
)) == -1) {
584 fprintf(stderr
, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno
));
588 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&sock_opt
, sizeof(sock_opt
)) == -1) {
589 fprintf(stderr
, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno
));
593 if (bind(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
594 fprintf(stderr
, "bind(): %s\n", strerror(errno
));
599 do_mgroup_join(sfd
, res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
, mgroup
);
601 if ((res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_SEQPACKET
)
602 && listen(sfd
, SOMAXCONN
) == -1) {
603 fprintf(stderr
, "listen(): %s\n", strerror(errno
));
606 if (rendezvous
&& (res
->ai_family
== AF_INET
|| res
->ai_family
== AF_INET6
) &&
607 (res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_DGRAM
)) {
608 launch_data_t rvs_fds
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_BONJOURFDS
);
609 if (NULL
== rvs_fds
) {
610 rvs_fds
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
611 launch_data_dict_insert(thejob
, rvs_fds
, LAUNCH_JOBKEY_BONJOURFDS
);
613 if (NULL
== rnames
) {
614 rvs_fd
= do_rendezvous_magic(res
, serv
);
616 launch_data_array_append(rvs_fds
, rvs_fd
);
617 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(rnames
)) {
618 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rnames
));
620 launch_data_array_append(rvs_fds
, rvs_fd
);
621 } else if (LAUNCH_DATA_ARRAY
== launch_data_get_type(rnames
)) {
622 size_t rn_i
, rn_ac
= launch_data_array_get_count(rnames
);
624 for (rn_i
= 0; rn_i
< rn_ac
; rn_i
++) {
625 launch_data_t rn_tmp
= launch_data_array_get_index(rnames
, rn_i
);
627 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rn_tmp
));
629 launch_data_array_append(rvs_fds
, rvs_fd
);
634 if (connect(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
635 fprintf(stderr
, "connect(): %s\n", strerror(errno
));
639 val
= launch_data_new_fd(sfd
);
641 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
642 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
645 launch_data_array_append(fdarray
, val
);
650 static void do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
)
652 struct addrinfo hints
, *res0
, *res
;
654 struct ipv6_mreq m6req
;
657 memset(&hints
, 0, sizeof(hints
));
659 hints
.ai_flags
|= AI_PASSIVE
;
660 hints
.ai_family
= family
;
661 hints
.ai_socktype
= socktype
;
662 hints
.ai_protocol
= protocol
;
664 if ((gerr
= getaddrinfo(mgroup
, NULL
, &hints
, &res0
)) != 0) {
665 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
669 for (res
= res0
; res
; res
= res
->ai_next
) {
670 if (AF_INET
== family
) {
671 memset(&mreq
, 0, sizeof(mreq
));
672 mreq
.imr_multiaddr
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_addr
;
673 if (setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
)) == -1) {
674 fprintf(stderr
, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
678 } else if (AF_INET6
== family
) {
679 memset(&m6req
, 0, sizeof(m6req
));
680 m6req
.ipv6mr_multiaddr
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
;
681 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &m6req
, sizeof(m6req
)) == -1) {
682 fprintf(stderr
, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno
));
687 fprintf(stderr
, "unknown family during multicast group bind!\n");
696 static launch_data_t
do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
)
699 DNSServiceRef service
;
700 DNSServiceErrorType error
;
703 static int statres
= 1;
706 statres
= stat("/usr/sbin/mDNSResponder", &sb
);
711 sprintf(rvs_buf
, "_%s._%s.", serv
, res
->ai_socktype
== SOCK_STREAM
? "tcp" : "udp");
713 if (res
->ai_family
== AF_INET
)
714 port
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_port
;
716 port
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_port
;
718 error
= DNSServiceRegister(&service
, 0, 0, NULL
, rvs_buf
, NULL
, NULL
, port
, 0, NULL
, NULL
, NULL
);
720 if (error
== kDNSServiceErr_NoError
)
721 return launch_data_new_fd(DNSServiceRefSockFD(service
));
723 fprintf(stderr
, "DNSServiceRegister(\"%s\"): %d\n", serv
, error
);
727 static CFPropertyListRef
CreateMyPropertyListFromFile(const char *posixfile
)
729 CFPropertyListRef propertyList
;
730 CFStringRef errorString
;
731 CFDataRef resourceData
;
735 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, posixfile
, strlen(posixfile
), false);
737 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
738 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, fileURL
, &resourceData
, NULL
, NULL
, &errorCode
))
739 fprintf(stderr
, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
740 propertyList
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, resourceData
, kCFPropertyListMutableContainers
, &errorString
);
742 fprintf(stderr
, "%s: propertyList is NULL\n", getprogname());
747 static void WriteMyPropertyListToFile(CFPropertyListRef plist
, const char *posixfile
)
749 CFDataRef resourceData
;
753 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, posixfile
, strlen(posixfile
), false);
755 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
756 resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, plist
);
757 if (resourceData
== NULL
)
758 fprintf(stderr
, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile
);
759 if (!CFURLWriteDataAndPropertiesToResource(fileURL
, resourceData
, NULL
, &errorCode
))
760 fprintf(stderr
, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
763 void myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
)
765 launch_data_t ik
, iw
, where
= context
;
767 ik
= CF2launch_data(key
);
768 iw
= CF2launch_data(value
);
770 launch_data_dict_insert(where
, iw
, launch_data_get_string(ik
));
771 launch_data_free(ik
);
774 static launch_data_t
CF2launch_data(CFTypeRef cfr
)
777 CFTypeID cft
= CFGetTypeID(cfr
);
779 if (cft
== CFStringGetTypeID()) {
781 CFStringGetCString(cfr
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
782 r
= launch_data_alloc(LAUNCH_DATA_STRING
);
783 launch_data_set_string(r
, buf
);
784 } else if (cft
== CFBooleanGetTypeID()) {
785 r
= launch_data_alloc(LAUNCH_DATA_BOOL
);
786 launch_data_set_bool(r
, CFBooleanGetValue(cfr
));
787 } else if (cft
== CFArrayGetTypeID()) {
788 CFIndex i
, ac
= CFArrayGetCount(cfr
);
789 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
790 for (i
= 0; i
< ac
; i
++) {
791 CFTypeRef v
= CFArrayGetValueAtIndex(cfr
, i
);
793 launch_data_t iv
= CF2launch_data(v
);
794 launch_data_array_set_index(r
, iv
, i
);
797 } else if (cft
== CFDictionaryGetTypeID()) {
798 r
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
799 CFDictionaryApplyFunction(cfr
, myCFDictionaryApplyFunction
, r
);
800 } else if (cft
== CFDataGetTypeID()) {
801 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
802 launch_data_set_opaque(r
, CFDataGetBytePtr(cfr
), CFDataGetLength(cfr
));
803 } else if (cft
== CFNumberGetTypeID()) {
806 CFNumberType cfnt
= CFNumberGetType(cfr
);
808 case kCFNumberSInt8Type
:
809 case kCFNumberSInt16Type
:
810 case kCFNumberSInt32Type
:
811 case kCFNumberSInt64Type
:
812 case kCFNumberCharType
:
813 case kCFNumberShortType
:
814 case kCFNumberIntType
:
815 case kCFNumberLongType
:
816 case kCFNumberLongLongType
:
817 CFNumberGetValue(cfr
, kCFNumberLongLongType
, &n
);
818 r
= launch_data_alloc(LAUNCH_DATA_INTEGER
);
819 launch_data_set_integer(r
, n
);
821 case kCFNumberFloat32Type
:
822 case kCFNumberFloat64Type
:
823 case kCFNumberFloatType
:
824 case kCFNumberDoubleType
:
825 CFNumberGetValue(cfr
, kCFNumberDoubleType
, &d
);
826 r
= launch_data_alloc(LAUNCH_DATA_REAL
);
827 launch_data_set_real(r
, d
);
839 static int help_cmd(int argc
, char *const argv
[])
841 FILE *where
= stdout
;
845 if (argc
== 0 || argv
== NULL
)
848 fprintf(where
, "usage: %s <subcommand>\n", getprogname());
849 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
850 l
= strlen(cmds
[i
].name
);
854 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++)
855 fprintf(where
, "\t%-*s\t%s\n", cmdwidth
, cmds
[i
].name
, cmds
[i
].desc
);
860 static int _fd(int fd
)
863 fcntl(fd
, F_SETFD
, 1);
867 static int load_and_unload_cmd(int argc
, char *const argv
[])
869 launch_data_t pass1
, pass2
;
875 if (!strcmp(argv
[0], "load"))
878 while ((ch
= getopt(argc
, argv
, "wF")) != -1) {
880 case 'w': wflag
= true; break;
881 case 'F': Fflag
= true; break;
883 fprintf(stderr
, "usage: %s load [-wF] paths...\n", getprogname());
891 fprintf(stderr
, "usage: %s load [-w] paths...\n", getprogname());
895 /* I wish I didn't need to do two passes, but I need to load mDNSResponder and use it too.
897 * In later versions of launchd, I hope to load everything in the first pass,
898 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
899 * I haven't thought through the various complexities of reloading jobs, and therefore
900 * launchd doesn't have reload support right now.
903 pass1
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
904 pass2
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
906 for (i
= 0; i
< argc
; i
++)
907 readpath(argv
[i
], pass1
, pass2
, wflag
, lflag
, Fflag
);
909 if (0 == launch_data_array_get_count(pass1
) && 0 == launch_data_array_get_count(pass2
)) {
910 fprintf(stderr
, "nothing found to %s\n", lflag
? "load" : "unload");
911 launch_data_free(pass1
);
912 launch_data_free(pass2
);
917 if (0 < launch_data_array_get_count(pass1
)) {
918 submit_job_pass(pass1
);
920 if (0 < launch_data_array_get_count(pass2
)) {
921 submit_job_pass(pass2
);
924 for (i
= 0; i
< (int)launch_data_array_get_count(pass1
); i
++)
925 unloadjob(launch_data_array_get_index(pass1
, i
));
926 for (i
= 0; i
< (int)launch_data_array_get_count(pass2
); i
++)
927 unloadjob(launch_data_array_get_index(pass2
, i
));
933 static void submit_job_pass(launch_data_t jobs
)
935 launch_data_t msg
, resp
;
939 for (i
= 0; i
< launch_data_array_get_count(jobs
); i
++)
940 distill_config_file(launch_data_array_get_index(jobs
, i
));
942 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
944 launch_data_dict_insert(msg
, jobs
, LAUNCH_KEY_SUBMITJOB
);
946 resp
= launch_msg(msg
);
949 switch (launch_data_get_type(resp
)) {
950 case LAUNCH_DATA_ERRNO
:
951 if ((e
= launch_data_get_errno(resp
)))
952 fprintf(stderr
, "%s\n", strerror(e
));
954 case LAUNCH_DATA_ARRAY
:
955 for (i
= 0; i
< launch_data_array_get_count(jobs
); i
++) {
956 launch_data_t obatind
= launch_data_array_get_index(resp
, i
);
957 launch_data_t jatind
= launch_data_array_get_index(jobs
, i
);
958 const char *lab4job
= launch_data_get_string(launch_data_dict_lookup(jatind
, LAUNCH_JOBKEY_LABEL
));
959 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(obatind
)) {
960 e
= launch_data_get_errno(obatind
);
963 fprintf(stderr
, "%s: %s\n", lab4job
, "Already loaded");
966 fprintf(stderr
, "%s: %s\n", lab4job
, "Not loaded");
969 fprintf(stderr
, "%s: %s\n", lab4job
, strerror(e
));
977 fprintf(stderr
, "unknown respose from launchd!\n");
980 launch_data_free(resp
);
982 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
985 launch_data_free(msg
);
988 static int start_and_stop_cmd(int argc
, char *const argv
[])
990 launch_data_t resp
, msg
;
991 const char *lmsgcmd
= LAUNCH_KEY_STOPJOB
;
994 if (!strcmp(argv
[0], "start"))
995 lmsgcmd
= LAUNCH_KEY_STARTJOB
;
998 fprintf(stderr
, "usage: %s %s <job label>\n", getprogname(), argv
[0]);
1002 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1003 launch_data_dict_insert(msg
, launch_data_new_string(argv
[1]), lmsgcmd
);
1005 resp
= launch_msg(msg
);
1006 launch_data_free(msg
);
1009 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1011 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1012 if ((e
= launch_data_get_errno(resp
))) {
1013 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1017 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1021 launch_data_free(resp
);
1025 static void print_jobs(launch_data_t j
__attribute__((unused
)), const char *label
, void *context
__attribute__((unused
)))
1027 fprintf(stdout
, "%s\n", label
);
1030 static int list_cmd(int argc
, char *const argv
[])
1032 launch_data_t resp
, msg
;
1036 while ((ch
= getopt(argc
, argv
, "v")) != -1) {
1042 fprintf(stderr
, "usage: %s list [-v]\n", getprogname());
1048 fprintf(stderr
, "usage: %s list: \"-v\" flag not implemented yet\n", getprogname());
1052 msg
= launch_data_new_string(LAUNCH_KEY_GETJOBS
);
1053 resp
= launch_msg(msg
);
1054 launch_data_free(msg
);
1057 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1059 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_DICTIONARY
) {
1060 launch_data_dict_iterate(resp
, print_jobs
, NULL
);
1062 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1066 launch_data_free(resp
);
1071 static int stdio_cmd(int argc
, char *const argv
[])
1073 launch_data_t resp
, msg
, tmp
;
1074 int e
, fd
= -1, r
= 0;
1077 fprintf(stderr
, "usage: %s %s <path>\n", getprogname(), argv
[0]);
1081 fd
= open(argv
[1], O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1083 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1086 tmp
= launch_data_new_string(argv
[1]);
1088 tmp
= launch_data_new_fd(fd
);
1091 if (!strcmp(argv
[0], "stdout")) {
1092 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETSTDOUT
);
1094 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETSTDERR
);
1097 resp
= launch_msg(msg
);
1098 launch_data_free(msg
);
1101 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1103 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1104 if ((e
= launch_data_get_errno(resp
))) {
1105 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1109 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1116 launch_data_free(resp
);
1121 static int fyi_cmd(int argc
, char *const argv
[])
1123 launch_data_t resp
, msg
;
1124 const char *lmsgk
= LAUNCH_KEY_RELOADTTYS
;
1128 fprintf(stderr
, "usage: %s %s\n", getprogname(), argv
[0]);
1132 if (!strcmp(argv
[0], "shutdown"))
1133 lmsgk
= LAUNCH_KEY_SHUTDOWN
;
1135 msg
= launch_data_new_string(lmsgk
);
1136 resp
= launch_msg(msg
);
1137 launch_data_free(msg
);
1140 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1142 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1143 if ((e
= launch_data_get_errno(resp
))) {
1144 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1148 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1152 launch_data_free(resp
);
1157 static int logupdate_cmd(int argc
, char *const argv
[])
1159 launch_data_t resp
, msg
;
1160 int e
, i
, j
, r
= 0, m
= 0;
1161 bool badargs
= false, maskmode
= false, onlymode
= false, levelmode
= false;
1162 const char *whichcmd
= LAUNCH_KEY_SETLOGMASK
;
1163 static const struct {
1167 { "debug", LOG_DEBUG
},
1168 { "info", LOG_INFO
},
1169 { "notice", LOG_NOTICE
},
1170 { "warning", LOG_WARNING
},
1171 { "error", LOG_ERR
},
1172 { "critical", LOG_CRIT
},
1173 { "alert", LOG_ALERT
},
1174 { "emergency", LOG_EMERG
},
1176 int logtblsz
= sizeof logtbl
/ sizeof logtbl
[0];
1179 if (!strcmp(argv
[1], "mask"))
1181 else if (!strcmp(argv
[1], "only"))
1183 else if (!strcmp(argv
[1], "level"))
1190 m
= LOG_UPTO(LOG_DEBUG
);
1192 if (argc
> 2 && (maskmode
|| onlymode
)) {
1193 for (i
= 2; i
< argc
; i
++) {
1194 for (j
= 0; j
< logtblsz
; j
++) {
1195 if (!strcmp(argv
[i
], logtbl
[j
].name
)) {
1197 m
&= ~(LOG_MASK(logtbl
[j
].level
));
1199 m
|= LOG_MASK(logtbl
[j
].level
);
1203 if (j
== logtblsz
) {
1208 } else if (argc
> 2 && levelmode
) {
1209 for (j
= 0; j
< logtblsz
; j
++) {
1210 if (!strcmp(argv
[2], logtbl
[j
].name
)) {
1211 m
= LOG_UPTO(logtbl
[j
].level
);
1217 } else if (argc
== 1) {
1218 whichcmd
= LAUNCH_KEY_GETLOGMASK
;
1224 fprintf(stderr
, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
1228 if (whichcmd
== LAUNCH_KEY_SETLOGMASK
) {
1229 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1230 launch_data_dict_insert(msg
, launch_data_new_integer(m
), whichcmd
);
1232 msg
= launch_data_new_string(whichcmd
);
1235 resp
= launch_msg(msg
);
1236 launch_data_free(msg
);
1239 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1241 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1242 if ((e
= launch_data_get_errno(resp
))) {
1243 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
1246 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_INTEGER
) {
1247 if (whichcmd
== LAUNCH_KEY_GETLOGMASK
) {
1248 m
= launch_data_get_integer(resp
);
1249 for (j
= 0; j
< logtblsz
; j
++) {
1250 if (m
& LOG_MASK(logtbl
[j
].level
))
1251 fprintf(stdout
, "%s ", logtbl
[j
].name
);
1253 fprintf(stdout
, "\n");
1256 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1260 launch_data_free(resp
);
1265 static int limit_cmd(int argc
__attribute__((unused
)), char *const argv
[])
1269 struct rlimit
*lmts
= NULL
;
1270 launch_data_t resp
, resp1
= NULL
, msg
, tmp
;
1272 size_t i
, lsz
= -1, which
= 0;
1273 rlim_t slim
= -1, hlim
= -1;
1274 bool badargs
= false;
1275 static const struct {
1279 { "cpu", RLIMIT_CPU
},
1280 { "filesize", RLIMIT_FSIZE
},
1281 { "data", RLIMIT_DATA
},
1282 { "stack", RLIMIT_STACK
},
1283 { "core", RLIMIT_CORE
},
1284 { "rss", RLIMIT_RSS
},
1285 { "memlock", RLIMIT_MEMLOCK
},
1286 { "maxproc", RLIMIT_NPROC
},
1287 { "maxfiles", RLIMIT_NOFILE
}
1289 size_t limlookupcnt
= sizeof limlookup
/ sizeof limlookup
[0];
1290 bool name2num(const char *n
) {
1291 for (i
= 0; i
< limlookupcnt
; i
++) {
1292 if (!strcmp(limlookup
[i
].name
, n
)) {
1293 which
= limlookup
[i
].lim
;
1299 const char *num2name(int n
) {
1300 for (i
= 0; i
< limlookupcnt
; i
++) {
1301 if (limlookup
[i
].lim
== n
)
1302 return limlookup
[i
].name
;
1306 const char *lim2str(rlim_t val
, char *buf
) {
1307 if (val
== RLIM_INFINITY
)
1308 strcpy(buf
, "unlimited");
1310 sprintf(buf
, "%lld", val
);
1313 bool str2lim(const char *buf
, rlim_t
*res
) {
1315 *res
= strtoll(buf
, &endptr
, 10);
1316 if (!strcmp(buf
, "unlimited")) {
1317 *res
= RLIM_INFINITY
;
1319 } else if (*endptr
== '\0') {
1328 if (argc
>= 3 && str2lim(argv
[2], &slim
))
1333 if (argc
== 4 && str2lim(argv
[3], &hlim
))
1336 if (argc
>= 2 && name2num(argv
[1]))
1340 fprintf(stderr
, "usage: %s %s [", getprogname(), argv
[0]);
1341 for (i
= 0; i
< limlookupcnt
; i
++)
1342 fprintf(stderr
, "%s %s", limlookup
[i
].name
, (i
+ 1) == limlookupcnt
? "" : "| ");
1343 fprintf(stderr
, "[both | soft hard]]\n");
1347 msg
= launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS
);
1348 resp
= launch_msg(msg
);
1349 launch_data_free(msg
);
1352 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1354 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
1355 lmts
= launch_data_get_opaque(resp
);
1356 lsz
= launch_data_get_opaque_size(resp
);
1358 for (i
= 0; i
< (lsz
/ sizeof(struct rlimit
)); i
++) {
1359 if (argc
== 2 && which
!= i
)
1361 fprintf(stdout
, "\t%-12s%-15s%-15s\n", num2name(i
),
1362 lim2str(lmts
[i
].rlim_cur
, slimstr
),
1363 lim2str(lmts
[i
].rlim_max
, hlimstr
));
1366 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
1367 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
1370 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1374 if (argc
<= 2 || r
!= 0) {
1375 launch_data_free(resp
);
1381 lmts
[which
].rlim_cur
= slim
;
1382 lmts
[which
].rlim_max
= hlim
;
1384 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1385 tmp
= launch_data_new_opaque(lmts
, lsz
);
1386 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETRESOURCELIMITS
);
1387 resp
= launch_msg(msg
);
1388 launch_data_free(msg
);
1391 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1393 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
1394 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
1396 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_OPAQUE
) {
1397 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1401 launch_data_free(resp
);
1402 launch_data_free(resp1
);
1407 static int umask_cmd(int argc
, char *const argv
[])
1409 launch_data_t resp
, msg
;
1410 bool badargs
= false;
1416 m
= strtol(argv
[1], &endptr
, 8);
1417 if (*endptr
!= '\0' || m
> 0777)
1421 if (argc
> 2 || badargs
) {
1422 fprintf(stderr
, "usage: %s %s <mask>\n", getprogname(), argv
[0]);
1428 msg
= launch_data_new_string(LAUNCH_KEY_GETUMASK
);
1430 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1431 launch_data_dict_insert(msg
, launch_data_new_integer(m
), LAUNCH_KEY_SETUMASK
);
1433 resp
= launch_msg(msg
);
1434 launch_data_free(msg
);
1437 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1439 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
1440 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
1442 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_INTEGER
) {
1443 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1445 } else if (argc
== 1) {
1446 fprintf(stdout
, "%o\n", (unsigned int)launch_data_get_integer(resp
));
1449 launch_data_free(resp
);
1454 static int getrusage_cmd(int argc
, char *const argv
[])
1456 launch_data_t resp
, msg
;
1457 bool badargs
= false;
1462 else if (strcmp(argv
[1], "self") && strcmp(argv
[1], "children"))
1466 fprintf(stderr
, "usage: %s %s self | children\n", getprogname(), argv
[0]);
1470 if (!strcmp(argv
[1], "self")) {
1471 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF
);
1473 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN
);
1476 resp
= launch_msg(msg
);
1477 launch_data_free(msg
);
1480 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
1482 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
1483 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(launch_data_get_errno(resp
)));
1485 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
1486 struct rusage
*rusage
= launch_data_get_opaque(resp
);
1487 fprintf(stdout
, "\t%-10f\tuser time used\n",
1488 (double)rusage
->ru_utime
.tv_sec
+ (double)rusage
->ru_utime
.tv_usec
/ (double)1000000);
1489 fprintf(stdout
, "\t%-10f\tsystem time used\n",
1490 (double)rusage
->ru_stime
.tv_sec
+ (double)rusage
->ru_stime
.tv_usec
/ (double)1000000);
1491 fprintf(stdout
, "\t%-10ld\tmax resident set size\n", rusage
->ru_maxrss
);
1492 fprintf(stdout
, "\t%-10ld\tshared text memory size\n", rusage
->ru_ixrss
);
1493 fprintf(stdout
, "\t%-10ld\tunshared data size\n", rusage
->ru_idrss
);
1494 fprintf(stdout
, "\t%-10ld\tunshared stack size\n", rusage
->ru_isrss
);
1495 fprintf(stdout
, "\t%-10ld\tpage reclaims\n", rusage
->ru_minflt
);
1496 fprintf(stdout
, "\t%-10ld\tpage faults\n", rusage
->ru_majflt
);
1497 fprintf(stdout
, "\t%-10ld\tswaps\n", rusage
->ru_nswap
);
1498 fprintf(stdout
, "\t%-10ld\tblock input operations\n", rusage
->ru_inblock
);
1499 fprintf(stdout
, "\t%-10ld\tblock output operations\n", rusage
->ru_oublock
);
1500 fprintf(stdout
, "\t%-10ld\tmessages sent\n", rusage
->ru_msgsnd
);
1501 fprintf(stdout
, "\t%-10ld\tmessages received\n", rusage
->ru_msgrcv
);
1502 fprintf(stdout
, "\t%-10ld\tsignals received\n", rusage
->ru_nsignals
);
1503 fprintf(stdout
, "\t%-10ld\tvoluntary context switches\n", rusage
->ru_nvcsw
);
1504 fprintf(stdout
, "\t%-10ld\tinvoluntary context switches\n", rusage
->ru_nivcsw
);
1506 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
1510 launch_data_free(resp
);
1515 static bool launch_data_array_append(launch_data_t a
, launch_data_t o
)
1517 size_t offt
= launch_data_array_get_count(a
);
1519 return launch_data_array_set_index(a
, o
, offt
);