X-Git-Url: https://git.saurik.com/apple/launchd.git/blobdiff_plain/aa59983ad5a41ca0272b5584ad9dabbde5b0c49b..ddbbfbc16a73ae4ed2ebe2c11ac6fb84b944c0dd:/launchd/src/launchctl.c diff --git a/launchd/src/launchctl.c b/launchd/src/launchctl.c index a0a405c..fd1381e 100644 --- a/launchd/src/launchctl.c +++ b/launchd/src/launchctl.c @@ -1,38 +1,67 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ + +static const char *const __rcs_file_version__ = "$Revision: 23930 $"; + +#include "config.h" +#include "launch.h" +#include "launch_priv.h" +#include "bootstrap.h" +#include "vproc.h" +#include "vproc_priv.h" +#include "vproc_internal.h" +#include "bootstrap_priv.h" +#include "launch_internal.h" + #include +#include +#include +#include +#if HAVE_SECURITY +#include +#include +#endif +#include +#include #include -#include #include +#include #include +#include #include #include +#ifndef SO_EXECPATH +/* This is just so it's easy for me to compile launchctl without buildit. */ + #define SO_EXECPATH 0x1085 +#endif #include #include #include #include #include +#include +#include +#include #include +#include +#include #include #include #include @@ -43,22 +72,54 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include -#include "launch.h" -#include "launch_priv.h" +#if HAVE_LIBAUDITD +#include +#ifndef AUDITD_PLIST_FILE +#define AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist" +#endif +#endif -#define LAUNCH_SECDIR "/tmp/launch-XXXXXX" +extern char **environ; + + +#define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX" #define MACHINIT_JOBKEY_ONDEMAND "OnDemand" #define MACHINIT_JOBKEY_SERVICENAME "ServiceName" #define MACHINIT_JOBKEY_COMMAND "Command" -#define MACHINIT_JOBKEY_ISKUNCSERVER "isKUNCServer" +#define MACHINIT_JOBKEY_SERVERPORT "ServerPort" +#define MACHINIT_JOBKEY_SERVICEPORT "ServicePort" + +#define assumes(e) \ + (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) + +#define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID()) +struct load_unload_state { + launch_data_t pass0; + launch_data_t pass1; + launch_data_t pass2; + char *session_type; + bool editondisk:1, load:1, forceload:1; +}; static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context); +static void job_override(CFTypeRef key, CFTypeRef val, CFMutableDictionaryRef job); +static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj); +static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr); +static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict); static bool launch_data_array_append(launch_data_t a, launch_data_t o); static void distill_jobs(launch_data_t); static void distill_config_file(launch_data_t); @@ -67,29 +128,78 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data static launch_data_t CF2launch_data(CFTypeRef); static launch_data_t read_plist_file(const char *file, bool editondisk, bool load); static CFPropertyListRef CreateMyPropertyListFromFile(const char *); +static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL); static void WriteMyPropertyListToFile(CFPropertyListRef, const char *); -static void readpath(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload); -static void readfile(const char *, launch_data_t, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload); +static bool path_goodness_check(const char *path, bool forceload); +static void readpath(const char *, struct load_unload_state *); +static void readfile(const char *, struct load_unload_state *); static int _fd(int); static int demux_cmd(int argc, char *const argv[]); static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv); static void submit_job_pass(launch_data_t jobs); static void submit_mach_jobs(launch_data_t jobs); -static void let_go_of_mach_jobs(void); +static void let_go_of_mach_jobs(launch_data_t jobs); static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); -static void print_jobs(launch_data_t j, const char *label, void *context); +static mach_port_t str2bsport(const char *s); +static void print_jobs(launch_data_t j, const char *key, void *context); +static void print_obj(launch_data_t obj, const char *key, void *context); static bool is_legacy_mach_job(launch_data_t obj); static bool delay_to_second_pass(launch_data_t o); static void delay_to_second_pass2(launch_data_t o, const char *key, void *context); - +static bool str2lim(const char *buf, rlim_t *res); +static const char *lim2str(rlim_t val, char *buf); +static const char *num2name(int n); +static ssize_t name2num(const char *n); +static void unloadjob(launch_data_t job); +static void print_key_value(launch_data_t obj, const char *key, void *context); +static void print_launchd_env(launch_data_t obj, const char *key, void *context); +static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); +static void loopback_setup_ipv4(void); +static void loopback_setup_ipv6(void); +static pid_t fwexec(const char *const *argv, int *wstatus); +static void do_potential_fsck(void); +static bool path_check(const char *path); +static bool is_safeboot(void); +static bool is_netboot(void); +static void apply_sysctls_from_file(const char *thefile); +static void empty_dir(const char *thedir, struct stat *psb); +static int touch_file(const char *path, mode_t m); +static void do_sysversion_sysctl(void); +static void do_application_firewall_magic(int sfd, launch_data_t thejob); +static void preheat_page_cache_hack(void); +static void do_bootroot_magic(void); +static void do_single_user_mode(bool); +static bool do_single_user_mode2(void); +static void do_crash_debug_mode(void); +static bool do_crash_debug_mode2(void); +static void read_launchd_conf(void); +static void read_environment_dot_plist(void); +static bool job_disabled_logic(launch_data_t obj); +static void fix_bogus_file_metadata(void); +static void do_file_init(void) __attribute__((constructor)); +static void setup_system_context(void); +static void handle_system_bootstrapper_crashes_separately(void); +static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); + +typedef enum { + BOOTCACHE_START = 1, + BOOTCACHE_TAG, + BOOTCACHE_STOP, +} BootCache_action_t; + +static void do_BootCache_magic(BootCache_action_t what); + +static int bootstrap_cmd(int argc, char *const argv[]); static int load_and_unload_cmd(int argc, char *const argv[]); //static int reload_cmd(int argc, char *const argv[]); -static int start_and_stop_cmd(int argc, char *const argv[]); +static int start_stop_remove_cmd(int argc, char *const argv[]); +static int submit_cmd(int argc, char *const argv[]); static int list_cmd(int argc, char *const argv[]); static int setenv_cmd(int argc, char *const argv[]); static int unsetenv_cmd(int argc, char *const argv[]); static int getenv_and_export_cmd(int argc, char *const argv[]); +static int wait4debugger_cmd(int argc, char *const argv[]); static int limit_cmd(int argc, char *const argv[]); static int stdio_cmd(int argc, char *const argv[]); @@ -97,7 +207,15 @@ static int fyi_cmd(int argc, char *const argv[]); static int logupdate_cmd(int argc, char *const argv[]); static int umask_cmd(int argc, char *const argv[]); static int getrusage_cmd(int argc, char *const argv[]); - +static int bsexec_cmd(int argc, char *const argv[]); +static int _bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only); +static int bslist_cmd(int argc, char *const argv[]); +static int _bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs); +static int bstree_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); +static int managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); +static int manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); +static int managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); +static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn)); static int help_cmd(int argc, char *const argv[]); static const struct { @@ -105,80 +223,332 @@ static const struct { int (*func)(int argc, char *const argv[]); const char *desc; } cmds[] = { - { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, - { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, -// { "reload", reload_cmd, "Reload configuration files and/or directories" }, - { "start", start_and_stop_cmd, "Start specified jobs" }, - { "stop", start_and_stop_cmd, "Stop specified jobs" }, - { "list", list_cmd, "List jobs and information about jobs" }, - { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, - { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, - { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" }, - { "export", getenv_and_export_cmd, "Export shell settings from launchd" }, - { "limit", limit_cmd, "View and adjust launchd resource limits" }, - { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, - { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, - { "shutdown", fyi_cmd, "Prepare for system shutdown" }, - { "reloadttys", fyi_cmd, "Reload /etc/ttys" }, - { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, - { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, - { "umask", umask_cmd, "Change launchd's umask" }, - { "help", help_cmd, "This help output" }, + { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, + { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, +// { "reload", reload_cmd, "Reload configuration files and/or directories" }, + { "start", start_stop_remove_cmd, "Start specified job" }, + { "stop", start_stop_remove_cmd, "Stop specified job" }, + { "submit", submit_cmd, "Submit a job from the command line" }, + { "remove", start_stop_remove_cmd, "Remove specified job" }, + { "bootstrap", bootstrap_cmd, "Bootstrap launchd" }, + { "list", list_cmd, "List jobs and information about jobs" }, + { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, + { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, + { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" }, + { "export", getenv_and_export_cmd, "Export shell settings from launchd" }, + { "debug", wait4debugger_cmd, "Set the WaitForDebugger flag for the target job to true." }, + { "limit", limit_cmd, "View and adjust launchd resource limits" }, + { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, + { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, + { "shutdown", fyi_cmd, "Prepare for system shutdown" }, + { "singleuser", fyi_cmd, "Switch to single-user mode" }, + { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, + { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, + { "umask", umask_cmd, "Change launchd's umask" }, + { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" }, + { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" }, + { "bstree", bstree_cmd, "Show the entire Mach bootstrap tree. Requires root privileges." }, + { "managerpid", managerpid_cmd, "Print the PID of the launchd managing this Mach bootstrap." }, + { "manageruid", manageruid_cmd, "Print the UID of the launchd managing this Mach bootstrap." }, + { "managername", managername_cmd, "Print the name of this Mach bootstrap." }, + { "exit", exit_cmd, "Exit the interactive invocation of launchctl" }, + { "quit", exit_cmd, "Quit the interactive invocation of launchctl" }, + { "help", help_cmd, "This help output" }, }; -int main(int argc, char *const argv[]) +static bool istty; +static bool verbose; +static bool is_managed; +static bool do_apple_internal_magic; +static bool system_context; +static bool rootuser_context; +static bool bootstrapping_system; +static bool bootstrapping_peruser; +static bool g_verbose_boot = false; + +static bool g_job_overrides_db_has_changed = false; +static CFMutableDictionaryRef g_job_overrides_db = NULL; +static char g_job_overrides_db_path[PATH_MAX]; + +#if 0 +static bool g_job_cache_db_has_changed = false; +static launch_data_t g_job_cache_db = NULL; +static char g_job_cache_db_path[PATH_MAX]; +#endif + +int +main(int argc, char *const argv[]) { - bool istty = isatty(STDIN_FILENO); + int64_t is_managed_val = 0; char *l; - if (argc > 1) - exit(demux_cmd(argc - 1, argv + 1)); + if (vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed_val) == NULL && is_managed_val) { + is_managed = true; + } + + istty = isatty(STDIN_FILENO); + argc--, argv++; + + if (argc > 0 && argv[0][0] == '-') { + char *flago; + + for (flago = argv[0] + 1; *flago; flago++) { + switch (*flago) { + case 'v': + verbose = true; + break; + case 'u': + if( argc > 1 ) { + if( strncmp(argv[1], "root", sizeof("root")) == 0 ) { + rootuser_context = true; + } else { + fprintf(stderr, "Unknown user: %s\n", argv[1]); + exit(EXIT_FAILURE); + } + argc--, argv++; + } else { + fprintf(stderr, "-u option requires an argument. Currently, only \"root\" is supported.\n"); + } + break; + case '1': + system_context = true; + break; + default: + fprintf(stderr, "Unknown argument: '-%c'\n", *flago); + break; + } + } + argc--, argv++; + } + + /* Running in the context of the root user's per-user launchd is only supported ... well + * in the root user's per-user context. I know it's confusing. I'm genuinely sorry. + */ + if( rootuser_context ) { + int64_t manager_uid = -1, manager_pid = -1; + if( vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid) == NULL ) { + if( vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid) == NULL ) { + if( manager_uid || manager_pid == 1 ) { + fprintf(stderr, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.\n"); + exit(EXIT_FAILURE); + } + } + } + } else if( !(system_context || rootuser_context) ) { + /* Running in the system context is implied when we're running as root and not running as a bootstrapper. */ + system_context = ( !is_managed && getuid() == 0 ); + } + if( system_context ) { + if( getuid() == 0 ) { + setup_system_context(); + } else { + fprintf(stderr, "You must be root to run in the system context.\n"); + exit(EXIT_FAILURE); + } + } else if( rootuser_context ) { + if( getuid() != 0 ) { + fprintf(stderr, "You must be root to run in the root user context.\n"); + exit(EXIT_FAILURE); + } + } + if (NULL == readline) { fprintf(stderr, "missing library: readline\n"); exit(EXIT_FAILURE); } - while ((l = readline(istty ? "launchd% " : NULL))) { - char *inputstring = l, *argv2[100], **ap = argv2; - int i = 0; + if (argc == 0) { + while ((l = readline(istty ? "launchd% " : NULL))) { + char *inputstring = l, *argv2[100], **ap = argv2; + int i = 0; + + while ((*ap = strsep(&inputstring, " \t"))) { + if (**ap != '\0') { + ap++; + i++; + } + } - while ((*ap = strsep(&inputstring, " \t"))) { - if (**ap != '\0') { - ap++; - i++; + if (i > 0) { + demux_cmd(i, argv2); } - } - if (i > 0) - demux_cmd(i, argv2); + free(l); + } - free(l); + if (istty) { + fputc('\n', stdout); + } } - if (istty) - fputc('\n', stdout); + if (argc > 0) { + exit(demux_cmd(argc, argv)); + } exit(EXIT_SUCCESS); } -static int demux_cmd(int argc, char *const argv[]) +int +demux_cmd(int argc, char *const argv[]) { size_t i; optind = 1; optreset = 1; - + for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { - if (!strcmp(cmds[i].name, argv[0])) + if (!strcmp(cmds[i].name, argv[0])) { return cmds[i].func(argc, argv); + } } fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]); return 1; } -static int unsetenv_cmd(int argc, char *const argv[]) +void +read_launchd_conf(void) +{ + char s[1000], *c, *av[100]; + const char *file; + size_t len; + int i; + FILE *f; + + if (getppid() == 1) { + file = "/etc/launchd.conf"; + } else { + file = "/etc/launchd-user.conf"; + } + + if (!(f = fopen(file, "r"))) { + return; + } + + while ((c = fgets(s, (int) sizeof s, f))) { + len = strlen(c); + if (len && c[len - 1] == '\n') { + c[len - 1] = '\0'; + } + + i = 0; + + while ((av[i] = strsep(&c, " \t"))) { + if (*(av[i]) != '\0') { + i++; + } + } + + if (i > 0) { + demux_cmd(i, av); + } + } + + fclose(f); +} + +CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL) +{ + CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL); + + CFErrorRef streamErr = NULL; + if( !CFReadStreamOpen(plistReadStream) ) { + streamErr = CFReadStreamCopyError(plistReadStream); + CFStringRef errString = CFErrorCopyDescription(streamErr); + + CFShow(errString); + + CFRelease(errString); + CFRelease(streamErr); + } + + CFPropertyListRef plist = NULL; + if( plistReadStream ) { + CFStringRef errString = NULL; + CFPropertyListFormat plistFormat = 0; + plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString); + if( !plist ) { + CFShow(errString); + CFRelease(errString); + } + } + + CFReadStreamClose(plistReadStream); + CFRelease(plistReadStream); + + return plist; +} + +#define CFReleaseIfNotNULL(cf) if( cf ) CFRelease(cf); +void +read_environment_dot_plist(void) +{ + CFStringRef plistPath = NULL; + CFURLRef plistURL = NULL; + CFDictionaryRef envPlist = NULL; + launch_data_t req = NULL, launch_env_dict = NULL, resp = NULL; + + char plist_path_str[PATH_MAX]; + plist_path_str[PATH_MAX - 1] = 0; + snprintf(plist_path_str, sizeof(plist_path_str), "%s/.MacOSX/environment.plist", getenv("HOME")); + + struct stat sb; + if( stat(plist_path_str, &sb) == -1 ) { + goto out; + } + + plistPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), plist_path_str); + if( !assumes(plistPath != NULL) ) { + goto out; + } + + plistURL = CFURLCreateWithFileSystemPath(NULL, plistPath, kCFURLPOSIXPathStyle, false); + if( !assumes(plistURL != NULL) ) { + goto out; + } + + envPlist = (CFDictionaryRef)CFPropertyListCreateFromFile(plistURL); + if( !assumes(envPlist != NULL) ) { + goto out; + } + + launch_env_dict = CF2launch_data(envPlist); + if( !assumes(launch_env_dict != NULL) ) { + goto out; + } + + req = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + if( !assumes(req != NULL) ) { + goto out; + } + + launch_data_dict_insert(req, launch_env_dict, LAUNCH_KEY_SETUSERENVIRONMENT); + resp = launch_msg(req); + if( !assumes(resp != NULL) ) { + goto out; + } + + if( !assumes(launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) ) { + goto out; + } + + assumes(launch_data_get_errno(resp) == 0); +out: + CFReleaseIfNotNULL(plistPath); + CFReleaseIfNotNULL(plistURL); + CFReleaseIfNotNULL(envPlist); + if( req ) { + launch_data_free(req); + } + + if( resp ) { + launch_data_free(resp); + } +} + +int +unsetenv_cmd(int argc, char *const argv[]) { launch_data_t resp, tmp, msg; @@ -205,7 +575,8 @@ static int unsetenv_cmd(int argc, char *const argv[]) return 0; } -static int setenv_cmd(int argc, char *const argv[]) +int +setenv_cmd(int argc, char *const argv[]) { launch_data_t resp, tmp, tmpv, msg; @@ -233,34 +604,41 @@ static int setenv_cmd(int argc, char *const argv[]) return 0; } -static void print_launchd_env(launch_data_t obj, const char *key, void *context) +void +print_launchd_env(launch_data_t obj, const char *key, void *context) { bool *is_csh = context; - if (*is_csh) - fprintf(stdout, "setenv %s %s;\n", key, launch_data_get_string(obj)); - else - fprintf(stdout, "%s=%s; export %s;\n", key, launch_data_get_string(obj), key); + /* XXX escape the double quotes */ + if (*is_csh) { + fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj)); + } else { + fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key); + } } -static void print_key_value(launch_data_t obj, const char *key, void *context) +void +print_key_value(launch_data_t obj, const char *key, void *context) { const char *k = context; - if (!strcmp(key, k)) + if (!strcmp(key, k)) { fprintf(stdout, "%s\n", launch_data_get_string(obj)); + } } -static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused))) +int +getenv_and_export_cmd(int argc, char *const argv[]) { - launch_data_t resp, msg; + launch_data_t resp; bool is_csh = false; char *k; if (!strcmp(argv[0], "export")) { char *s = getenv("SHELL"); - if (s) + if (s) { is_csh = strstr(s, "csh") ? true : false; + } } else if (argc != 2) { fprintf(stderr, "%s usage: getenv \n", getprogname()); return 1; @@ -268,30 +646,57 @@ static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unu k = argv[1]; - msg = launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT); - - resp = launch_msg(msg); - launch_data_free(msg); - - if (resp) { - if (!strcmp(argv[0], "export")) + if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &resp) == NULL) { + if (!strcmp(argv[0], "export")) { launch_data_dict_iterate(resp, print_launchd_env, &is_csh); - else + } else { launch_data_dict_iterate(resp, print_key_value, k); + } launch_data_free(resp); + return 0; } else { - fprintf(stderr, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT "\"): %s\n", strerror(errno)); + return 1; } + return 0; } -static void unloadjob(launch_data_t job) +int +wait4debugger_cmd(int argc, char * const argv[]) { - launch_data_t resp, tmp, tmps, msg; - int e; + if( argc != 3 ) { + fprintf(stderr, "%s usage: debug