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