bundle-main.c revision 706f2543
1/* main.c -- X application launcher
2
3 Copyright (c) 2007 Jeremy Huddleston
4 Copyright (c) 2007 Apple Inc
5
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation files
8 (the "Software"), to deal in the Software without restriction,
9 including without limitation the rights to use, copy, modify, merge,
10 publish, distribute, sublicense, and/or sell copies of the Software,
11 and to permit persons to whom the Software is furnished to do so,
12 subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
21 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 DEALINGS IN THE SOFTWARE.
25
26 Except as contained in this notice, the name(s) of the above
27 copyright holders shall not be used in advertising or otherwise to
28 promote the sale, use or other dealings in this Software without
29 prior written authorization. */
30
31#include <CoreFoundation/CoreFoundation.h>
32#include <AvailabilityMacros.h>
33
34#ifdef HAVE_DIX_CONFIG_H
35#include <dix-config.h>
36#endif
37
38#include <X11/Xlib.h>
39#include <assert.h>
40#include <unistd.h>
41#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <pthread.h>
45#include <stdbool.h>
46#include <signal.h>
47
48#include <sys/socket.h>
49#include <sys/un.h>
50
51#include <fcntl.h>
52
53#include <mach/mach.h>
54#include <mach/mach_error.h>
55#include <servers/bootstrap.h>
56#include "mach_startup.h"
57#include "mach_startupServer.h"
58
59#include "launchd_fd.h"
60/* From darwinEvents.c ... but don't want to pull in all the server cruft */
61void DarwinListenOnOpenFD(int fd);
62
63extern int noPanoramiXExtension;
64
65#define DEFAULT_CLIENT X11BINDIR "/xterm"
66#define DEFAULT_STARTX X11BINDIR "/startx"
67#define DEFAULT_SHELL  "/bin/sh"
68
69#ifndef BUILD_DATE
70#define BUILD_DATE ""
71#endif
72#ifndef XSERVER_VERSION
73#define XSERVER_VERSION "?"
74#endif
75
76static char __crashreporter_info_buff__[4096] = {0};
77static const char *__crashreporter_info__ __attribute__((__used__)) = &__crashreporter_info_buff__[0];
78#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
79// This is actually a toolchain requirement, but I'm not sure the correct check,
80// but it should be fine to just only include it for Leopard and later.  This line
81// just tells the linker to never strip this symbol (such as for space optimization)
82asm (".desc ___crashreporter_info__, 0x10");
83#endif
84
85static const char *__crashreporter_info__base = "X.Org X Server " XSERVER_VERSION " Build Date: " BUILD_DATE;
86
87static char *launchd_id_prefix = NULL;
88static char *server_bootstrap_name = NULL;
89
90#define DEBUG 1
91
92/* This is in quartzStartup.c */
93int server_main(int argc, char **argv, char **envp);
94
95static int execute(const char *command);
96static char *command_from_prefs(const char *key, const char *default_value);
97
98static char *pref_app_to_run;
99static char *pref_login_shell;
100static char *pref_startx_script;
101
102/*** Pthread Magics ***/
103static pthread_t create_thread(void *(*func)(void *), void *arg) {
104    pthread_attr_t attr;
105    pthread_t tid;
106
107    pthread_attr_init (&attr);
108    pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
109    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
110    pthread_create (&tid, &attr, func, arg);
111    pthread_attr_destroy (&attr);
112
113    return tid;
114}
115
116/*** Mach-O IPC Stuffs ***/
117
118union MaxMsgSize {
119	union __RequestUnion__mach_startup_subsystem req;
120	union __ReplyUnion__mach_startup_subsystem rep;
121};
122
123static mach_port_t checkin_or_register(char *bname) {
124    kern_return_t kr;
125    mach_port_t mp;
126
127    /* If we're started by launchd or the old mach_init */
128    kr = bootstrap_check_in(bootstrap_port, bname, &mp);
129    if (kr == KERN_SUCCESS)
130        return mp;
131
132    /* We probably were not started by launchd or the old mach_init */
133    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
134    if (kr != KERN_SUCCESS) {
135        fprintf(stderr, "mach_port_allocate(): %s\n", mach_error_string(kr));
136        exit(EXIT_FAILURE);
137    }
138
139    kr = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND);
140    if (kr != KERN_SUCCESS) {
141        fprintf(stderr, "mach_port_insert_right(): %s\n", mach_error_string(kr));
142        exit(EXIT_FAILURE);
143    }
144
145    kr = bootstrap_register(bootstrap_port, bname, mp);
146    if (kr != KERN_SUCCESS) {
147        fprintf(stderr, "bootstrap_register(): %s\n", mach_error_string(kr));
148        exit(EXIT_FAILURE);
149    }
150
151    return mp;
152}
153
154/*** $DISPLAY handoff ***/
155static int accept_fd_handoff(int connected_fd) {
156    int launchd_fd;
157
158    char databuf[] = "display";
159    struct iovec iov[1];
160
161    union {
162        struct cmsghdr hdr;
163        char bytes[CMSG_SPACE(sizeof(int))];
164    } buf;
165
166    struct msghdr msg;
167    struct cmsghdr *cmsg;
168
169    iov[0].iov_base = databuf;
170    iov[0].iov_len  = sizeof(databuf);
171
172    msg.msg_iov = iov;
173    msg.msg_iovlen = 1;
174    msg.msg_control = buf.bytes;
175    msg.msg_controllen = sizeof(buf);
176    msg.msg_name = 0;
177    msg.msg_namelen = 0;
178    msg.msg_flags = 0;
179
180    cmsg = CMSG_FIRSTHDR (&msg);
181    cmsg->cmsg_level = SOL_SOCKET;
182    cmsg->cmsg_type = SCM_RIGHTS;
183    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
184
185    msg.msg_controllen = cmsg->cmsg_len;
186
187    *((int*)CMSG_DATA(cmsg)) = -1;
188
189    if(recvmsg(connected_fd, &msg, 0) < 0) {
190        fprintf(stderr, "X11.app: Error receiving $DISPLAY file descriptor.  recvmsg() error: %s\n", strerror(errno));
191        return -1;
192    }
193
194    launchd_fd = *((int*)CMSG_DATA(cmsg));
195
196    return launchd_fd;
197}
198
199typedef struct {
200    int fd;
201    string_t filename;
202} socket_handoff_t;
203
204/* This thread accepts an incoming connection and hands off the file
205 * descriptor for the new connection to accept_fd_handoff()
206 */
207static void *socket_handoff_thread(void *arg) {
208    socket_handoff_t *handoff_data = (socket_handoff_t *)arg;
209    int launchd_fd = -1;
210    int connected_fd;
211
212    /* Now actually get the passed file descriptor from this connection
213     * If we encounter an error, keep listening.
214     */
215    while(launchd_fd == -1) {
216        connected_fd = accept(handoff_data->fd, NULL, NULL);
217        if(connected_fd == -1) {
218            fprintf(stderr, "X11.app: Failed to accept incoming connection on socket (fd=%d): %s\n", handoff_data->fd, strerror(errno));
219            sleep(2);
220            continue;
221        }
222
223        launchd_fd = accept_fd_handoff(connected_fd);
224        if(launchd_fd == -1)
225            fprintf(stderr, "X11.app: Error receiving $DISPLAY file descriptor, no descriptor received?  Waiting for another connection.\n");
226
227        close(connected_fd);
228    }
229
230    close(handoff_data->fd);
231    unlink(handoff_data->filename);
232    free(handoff_data);
233
234    fprintf(stderr, "X11.app Handing off fd to server thread via DarwinListenOnOpenFD(%d)\n", launchd_fd);
235    DarwinListenOnOpenFD(launchd_fd);
236
237    return NULL;
238}
239
240static int create_socket(char *filename_out) {
241    struct sockaddr_un servaddr_un;
242    struct sockaddr *servaddr;
243    socklen_t servaddr_len;
244    int ret_fd;
245    size_t try, try_max;
246
247    for(try=0, try_max=5; try < try_max; try++) {
248        tmpnam(filename_out);
249
250        /* Setup servaddr_un */
251        memset (&servaddr_un, 0, sizeof (struct sockaddr_un));
252        servaddr_un.sun_family = AF_UNIX;
253        strlcpy(servaddr_un.sun_path, filename_out, sizeof(servaddr_un.sun_path));
254
255        servaddr = (struct sockaddr *) &servaddr_un;
256        servaddr_len = sizeof(struct sockaddr_un) - sizeof(servaddr_un.sun_path) + strlen(filename_out);
257
258        ret_fd = socket(PF_UNIX, SOCK_STREAM, 0);
259        if(ret_fd == -1) {
260            fprintf(stderr, "X11.app: Failed to create socket (try %d / %d): %s - %s\n", (int)try+1, (int)try_max, filename_out, strerror(errno));
261            continue;
262        }
263
264        if(bind(ret_fd, servaddr, servaddr_len) != 0) {
265            fprintf(stderr, "X11.app: Failed to bind socket: %d - %s\n", errno, strerror(errno));
266            close(ret_fd);
267            return 0;
268        }
269
270        if(listen(ret_fd, 10) != 0) {
271            fprintf(stderr, "X11.app: Failed to listen to socket: %s - %d - %s\n", filename_out, errno, strerror(errno));
272            close(ret_fd);
273            return 0;
274        }
275
276#ifdef DEBUG
277        fprintf(stderr, "X11.app: Listening on socket for fd handoff:  (%d) %s\n", ret_fd, filename_out);
278#endif
279
280        return ret_fd;
281    }
282
283    return 0;
284}
285
286static int launchd_socket_handed_off = 0;
287
288kern_return_t do_request_fd_handoff_socket(mach_port_t port, string_t filename) {
289    socket_handoff_t *handoff_data;
290
291    launchd_socket_handed_off = 1;
292
293    handoff_data = (socket_handoff_t *)calloc(1,sizeof(socket_handoff_t));
294    if(!handoff_data) {
295        fprintf(stderr, "X11.app: Error allocating memory for handoff_data\n");
296        return KERN_FAILURE;
297    }
298
299    handoff_data->fd = create_socket(handoff_data->filename);
300    if(!handoff_data->fd) {
301        free(handoff_data);
302        return KERN_FAILURE;
303    }
304
305    strlcpy(filename, handoff_data->filename, STRING_T_SIZE);
306
307    create_thread(socket_handoff_thread, handoff_data);
308
309#ifdef DEBUG
310    fprintf(stderr, "X11.app: Thread created for handoff.  Returning success to tell caller to connect and push the fd.\n");
311#endif
312
313    return KERN_SUCCESS;
314}
315
316kern_return_t do_request_pid(mach_port_t port, int *my_pid) {
317    *my_pid = getpid();
318    return KERN_SUCCESS;
319}
320
321/*** Server Startup ***/
322kern_return_t do_start_x11_server(mach_port_t port, string_array_t argv,
323                                  mach_msg_type_number_t argvCnt,
324                                  string_array_t envp,
325                                  mach_msg_type_number_t envpCnt) {
326    /* And now back to char ** */
327    char **_argv = alloca((argvCnt + 1) * sizeof(char *));
328    char **_envp = alloca((envpCnt + 1) * sizeof(char *));
329    size_t i;
330
331    /* If we didn't get handed a launchd DISPLAY socket, we should
332     * unset DISPLAY or we can run into problems with pbproxy
333     */
334    if(!launchd_socket_handed_off) {
335        fprintf(stderr, "X11.app: No launchd socket handed off, unsetting DISPLAY\n");
336        unsetenv("DISPLAY");
337    }
338
339    if(!_argv || !_envp) {
340        return KERN_FAILURE;
341    }
342
343    fprintf(stderr, "X11.app: do_start_x11_server(): argc=%d\n", argvCnt);
344    for(i=0; i < argvCnt; i++) {
345        _argv[i] = argv[i];
346        fprintf(stderr, "\targv[%u] = %s\n", (unsigned)i, argv[i]);
347    }
348    _argv[argvCnt] = NULL;
349
350    for(i=0; i < envpCnt; i++) {
351        _envp[i] = envp[i];
352    }
353    _envp[envpCnt] = NULL;
354
355    if(server_main(argvCnt, _argv, _envp) == 0)
356        return KERN_SUCCESS;
357    else
358        return KERN_FAILURE;
359}
360
361static int startup_trigger(int argc, char **argv, char **envp) {
362    Display *display;
363    const char *s;
364
365    /* Take care of the case where we're called like a normal DDX */
366    if(argc > 1 && argv[1][0] == ':') {
367        size_t i;
368        kern_return_t kr;
369        mach_port_t mp;
370        string_array_t newenvp;
371        string_array_t newargv;
372
373        /* We need to count envp */
374        int envpc;
375        for(envpc=0; envp[envpc]; envpc++);
376
377        /* We have fixed-size string lengths due to limitations in IPC,
378         * so we need to copy our argv and envp.
379         */
380        newargv = (string_array_t)alloca(argc * sizeof(string_t));
381        newenvp = (string_array_t)alloca(envpc * sizeof(string_t));
382
383        if(!newargv || !newenvp) {
384            fprintf(stderr, "Memory allocation failure\n");
385            exit(EXIT_FAILURE);
386        }
387
388        for(i=0; i < argc; i++) {
389            strlcpy(newargv[i], argv[i], STRING_T_SIZE);
390        }
391        for(i=0; i < envpc; i++) {
392            strlcpy(newenvp[i], envp[i], STRING_T_SIZE);
393        }
394
395        kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp);
396        if (kr != KERN_SUCCESS) {
397#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
398            fprintf(stderr, "bootstrap_look_up(%s): %s\n", server_bootstrap_name, bootstrap_strerror(kr));
399#else
400            fprintf(stderr, "bootstrap_look_up(%s): %ul\n", server_bootstrap_name, (unsigned long)kr);
401#endif
402            exit(EXIT_FAILURE);
403        }
404
405        kr = start_x11_server(mp, newargv, argc, newenvp, envpc);
406        if (kr != KERN_SUCCESS) {
407            fprintf(stderr, "start_x11_server: %s\n", mach_error_string(kr));
408            exit(EXIT_FAILURE);
409        }
410        exit(EXIT_SUCCESS);
411    }
412
413    /* If we have a process serial number and it's our only arg, act as if
414     * the user double clicked the app bundle: launch app_to_run if possible
415     */
416    if(argc == 1 || (argc == 2 && !strncmp(argv[1], "-psn_", 5))) {
417        /* Now, try to open a display, if so, run the launcher */
418        display = XOpenDisplay(NULL);
419        if(display) {
420            /* Could open the display, start the launcher */
421            XCloseDisplay(display);
422
423            return execute(pref_app_to_run);
424        }
425    }
426
427    /* Start the server */
428    if((s = getenv("DISPLAY"))) {
429        fprintf(stderr, "X11.app: Could not connect to server (DISPLAY=\"%s\", unsetting).  Starting X server.\n", s);
430        unsetenv("DISPLAY");
431    } else {
432        fprintf(stderr, "X11.app: Could not connect to server (DISPLAY is not set).  Starting X server.\n");
433    }
434    return execute(pref_startx_script);
435}
436
437/** Setup the environment we want our child processes to inherit */
438static void ensure_path(const char *dir) {
439    char buf[1024], *temp;
440
441    /* Make sure /usr/X11/bin is in the $PATH */
442    temp = getenv("PATH");
443    if(temp == NULL || temp[0] == 0) {
444        snprintf(buf, sizeof(buf), "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:%s", dir);
445        setenv("PATH", buf, TRUE);
446    } else if(strnstr(temp, X11BINDIR, sizeof(temp)) == NULL) {
447        snprintf(buf, sizeof(buf), "%s:%s", temp, dir);
448        setenv("PATH", buf, TRUE);
449    }
450}
451
452static void setup_env(void) {
453    char *temp;
454    const char *pds = NULL;
455    const char *disp = getenv("DISPLAY");
456    size_t len;
457
458    /* Pass on our prefs domain to startx and its inheritors (mainly for
459     * quartz-wm and the Xquartz stub's MachIPC)
460     */
461    CFBundleRef bundle = CFBundleGetMainBundle();
462    if(bundle) {
463        CFStringRef pd = CFBundleGetIdentifier(bundle);
464        if(pd) {
465            pds = CFStringGetCStringPtr(pd, 0);
466        }
467    }
468
469    /* fallback to hardcoded value if we can't discover it */
470    if(!pds) {
471        pds = LAUNCHD_ID_PREFIX".X11";
472    }
473
474    server_bootstrap_name = strdup(pds);
475    if(!server_bootstrap_name) {
476        fprintf(stderr, "X11.app: Memory allocation error.\n");
477        exit(1);
478    }
479    setenv("X11_PREFS_DOMAIN", server_bootstrap_name, 1);
480
481    len = strlen(server_bootstrap_name);
482    launchd_id_prefix = malloc(sizeof(char) * (len - 3));
483    if(!launchd_id_prefix) {
484        fprintf(stderr, "X11.app: Memory allocation error.\n");
485        exit(1);
486    }
487    strlcpy(launchd_id_prefix, server_bootstrap_name, len - 3);
488
489    /* We need to unset DISPLAY if it is not our socket */
490    if(disp) {
491        /* s = basename(disp) */
492        const char *d, *s;
493	    for(s = NULL, d = disp; *d; d++) {
494            if(*d == '/')
495                s = d + 1;
496        }
497
498        if(s && *s) {
499            if(strcmp(launchd_id_prefix, "org.x") == 0 && strcmp(s, ":0") == 0) {
500                fprintf(stderr, "X11.app: Detected old style launchd DISPLAY, please update xinit.\n");
501            } else {
502                temp = (char *)malloc(sizeof(char) * len);
503                if(!temp) {
504                    fprintf(stderr, "X11.app: Memory allocation error creating space for socket name test.\n");
505                    exit(1);
506                }
507                strlcpy(temp, launchd_id_prefix, len);
508                strlcat(temp, ":0", len);
509
510                if(strcmp(temp, s) != 0) {
511                    /* If we don't have a match, unset it. */
512                    fprintf(stderr, "X11.app: DISPLAY (\"%s\") does not match our id (\"%s\"), unsetting.\n", disp, launchd_id_prefix);
513                    unsetenv("DISPLAY");
514                }
515                free(temp);
516            }
517        } else {
518            /* The DISPLAY environment variable is not formatted like a launchd socket, so reset. */
519            fprintf(stderr, "X11.app: DISPLAY does not look like a launchd set variable, unsetting.\n");
520            unsetenv("DISPLAY");
521        }
522    }
523
524    /* Make sure PATH is right */
525    ensure_path(X11BINDIR);
526
527    /* cd $HOME */
528    temp = getenv("HOME");
529    if(temp != NULL && temp[0] != '\0')
530        chdir(temp);
531}
532
533/*** Main ***/
534int main(int argc, char **argv, char **envp) {
535    Bool listenOnly = FALSE;
536    int i;
537    mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE;
538    mach_port_t mp;
539    kern_return_t kr;
540
541    /* Setup our environment for our children */
542    setup_env();
543
544    /* The server must not run the PanoramiX operations. */
545    noPanoramiXExtension = TRUE;
546
547    /* Setup the initial crasherporter info */
548    strlcpy(__crashreporter_info_buff__, __crashreporter_info__base, sizeof(__crashreporter_info_buff__));
549
550    fprintf(stderr, "X11.app: main(): argc=%d\n", argc);
551    for(i=0; i < argc; i++) {
552        fprintf(stderr, "\targv[%u] = %s\n", (unsigned)i, argv[i]);
553        if(!strcmp(argv[i], "--listenonly")) {
554            listenOnly = TRUE;
555        }
556    }
557
558    mp = checkin_or_register(server_bootstrap_name);
559    if(mp == MACH_PORT_NULL) {
560        fprintf(stderr, "NULL mach service: %s", server_bootstrap_name);
561        return EXIT_FAILURE;
562    }
563
564    /* Check if we need to do something other than listen, and make another
565     * thread handle it.
566     */
567    if(!listenOnly) {
568        pid_t child1, child2;
569        int status;
570
571        pref_app_to_run = command_from_prefs("app_to_run", DEFAULT_CLIENT);
572        assert(pref_app_to_run);
573
574        pref_login_shell = command_from_prefs("login_shell", DEFAULT_SHELL);
575        assert(pref_login_shell);
576
577        pref_startx_script = command_from_prefs("startx_script", DEFAULT_STARTX);
578        assert(pref_startx_script);
579
580        /* Do the fork-twice trick to avoid having to reap zombies */
581        child1 = fork();
582        switch (child1) {
583            case -1:                                /* error */
584                break;
585
586            case 0:                                 /* child1 */
587                child2 = fork();
588
589                switch (child2) {
590                    int max_files, i;
591
592                    case -1:                            /* error */
593                        break;
594
595                    case 0:                             /* child2 */
596                        /* close all open files except for standard streams */
597                        max_files = sysconf(_SC_OPEN_MAX);
598                        for(i = 3; i < max_files; i++)
599                            close(i);
600
601                        /* ensure stdin is on /dev/null */
602                        close(0);
603                        open("/dev/null", O_RDONLY);
604
605                        return startup_trigger(argc, argv, envp);
606
607                    default:                            /* parent (child1) */
608                        _exit(0);
609                }
610                break;
611
612            default:                                /* parent */
613              waitpid(child1, &status, 0);
614        }
615
616        free(pref_app_to_run);
617        free(pref_login_shell);
618        free(pref_startx_script);
619    }
620
621    /* Main event loop */
622    fprintf(stderr, "Waiting for startup parameters via Mach IPC.\n");
623    kr = mach_msg_server(mach_startup_server, mxmsgsz, mp, 0);
624    if (kr != KERN_SUCCESS) {
625        fprintf(stderr, "%s.X11(mp): %s\n", LAUNCHD_ID_PREFIX, mach_error_string(kr));
626        return EXIT_FAILURE;
627    }
628
629    return EXIT_SUCCESS;
630}
631
632static int execute(const char *command) {
633    const char *newargv[4];
634    const char **p;
635
636    newargv[0] = pref_login_shell;
637    newargv[1] = "-c";
638    newargv[2] = command;
639    newargv[3] = NULL;
640
641    fprintf(stderr, "X11.app: Launching %s:\n", command);
642    for(p=newargv; *p; p++) {
643        fprintf(stderr, "\targv[%ld] = %s\n", (long int)(p - newargv), *p);
644    }
645
646    execvp (newargv[0], (char * const *) newargv);
647    perror ("X11.app: Couldn't exec.");
648    return 1;
649}
650
651static char *command_from_prefs(const char *key, const char *default_value) {
652    char *command = NULL;
653
654    CFStringRef cfKey;
655    CFPropertyListRef PlistRef;
656
657    if(!key)
658        return NULL;
659
660    cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingASCII);
661
662    if(!cfKey)
663        return NULL;
664
665    PlistRef = CFPreferencesCopyAppValue(cfKey, kCFPreferencesCurrentApplication);
666
667    if ((PlistRef == NULL) || (CFGetTypeID(PlistRef) != CFStringGetTypeID())) {
668        CFStringRef cfDefaultValue = CFStringCreateWithCString(NULL, default_value, kCFStringEncodingASCII);
669        int len = strlen(default_value) + 1;
670
671        if(!cfDefaultValue)
672            goto command_from_prefs_out;
673
674        CFPreferencesSetAppValue(cfKey, cfDefaultValue, kCFPreferencesCurrentApplication);
675        CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
676        CFRelease(cfDefaultValue);
677
678        command = (char *)malloc(len * sizeof(char));
679        if(!command)
680            goto command_from_prefs_out;
681        strcpy(command, default_value);
682    } else {
683        int len = CFStringGetLength((CFStringRef)PlistRef) + 1;
684        command = (char *)malloc(len * sizeof(char));
685        if(!command)
686            goto command_from_prefs_out;
687        CFStringGetCString((CFStringRef)PlistRef, command, len,  kCFStringEncodingASCII);
688    }
689
690command_from_prefs_out:
691    if (PlistRef)
692        CFRelease(PlistRef);
693    if(cfKey)
694        CFRelease(cfKey);
695    return command;
696}
697