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