1 /* Copyright (c) 2008-2012 Apple Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person
4 * obtaining a copy of this software and associated documentation files
5 * (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge,
7 * publish, distribute, sublicense, and/or sell copies of the Software,
8 * and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be
12 * included in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
18 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Except as contained in this notice, the name(s) of the above
24 * copyright holders shall not be used in advertising or otherwise to
25 * promote the sale, use or other dealings in this Software without
26 * prior written authorization.
27 */
28
29 #include <CoreServices/CoreServices.h>
30
31 #ifdef HAVE_DIX_CONFIG_H
32 #include <dix-config.h>
33 #endif
34
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <asl.h>
39
40 #include <sys/socket.h>
41 #include <sys/un.h>
42
43 #define kX11AppBundleId BUNDLE_ID_PREFIX ".X11"
44 #define kX11AppBundlePath "/Contents/MacOS/X11"
45
46 #include <mach/mach.h>
47 #include <mach/mach_error.h>
48 #include <servers/bootstrap.h>
49 #include "mach_startup.h"
50
51 #include <signal.h>
52
53 #include "launchd_fd.h"
54
55 static char x11_path[PATH_MAX + 1];
56 static pid_t x11app_pid = 0;
57 aslclient aslc;
58
59 static void
set_x11_path(void)60 set_x11_path(void)
61 {
62 CFURLRef appURL = NULL;
63 OSStatus osstatus =
64 LSFindApplicationForInfo(kLSUnknownCreator, CFSTR(
65 kX11AppBundleId), nil, nil, &appURL);
66
67 switch (osstatus) {
68 case noErr:
69 if (appURL == NULL) {
70 asl_log(
71 aslc, NULL, ASL_LEVEL_ERR,
72 "Xquartz: Invalid response from LSFindApplicationForInfo(%s)",
73 kX11AppBundleId);
74 exit(1);
75 }
76
77 if (!CFURLGetFileSystemRepresentation(appURL, true,
78 (unsigned char *)x11_path,
79 sizeof(x11_path))) {
80 asl_log(aslc, NULL, ASL_LEVEL_ERR,
81 "Xquartz: Error resolving URL for %s",
82 kX11AppBundleId);
83 exit(3);
84 }
85
86 strlcat(x11_path, kX11AppBundlePath, sizeof(x11_path));
87 asl_log(aslc, NULL, ASL_LEVEL_INFO, "Xquartz: X11.app = %s", x11_path);
88 break;
89
90 case kLSApplicationNotFoundErr:
91 asl_log(aslc, NULL, ASL_LEVEL_ERR,
92 "Xquartz: Unable to find application for %s",
93 kX11AppBundleId);
94 exit(10);
95
96 default:
97 asl_log(aslc, NULL, ASL_LEVEL_ERR,
98 "Xquartz: Unable to find application for %s, error code = %d",
99 kX11AppBundleId,
100 (int)osstatus);
101 exit(11);
102 }
103 }
104
105 static int
connect_to_socket(const char * filename)106 connect_to_socket(const char *filename)
107 {
108 struct sockaddr_un servaddr_un;
109 struct sockaddr *servaddr;
110 socklen_t servaddr_len;
111 int ret_fd;
112
113 /* Setup servaddr_un */
114 memset(&servaddr_un, 0, sizeof(struct sockaddr_un));
115 servaddr_un.sun_family = AF_UNIX;
116 strlcpy(servaddr_un.sun_path, filename, sizeof(servaddr_un.sun_path));
117
118 servaddr = (struct sockaddr *)&servaddr_un;
119 servaddr_len = sizeof(struct sockaddr_un) -
120 sizeof(servaddr_un.sun_path) + strlen(filename);
121
122 ret_fd = socket(PF_UNIX, SOCK_STREAM, 0);
123 if (ret_fd == -1) {
124 asl_log(aslc, NULL, ASL_LEVEL_ERR,
125 "Xquartz: Failed to create socket: %s - %s", filename,
126 strerror(
127 errno));
128 return -1;
129 }
130
131 if (connect(ret_fd, servaddr, servaddr_len) < 0) {
132 asl_log(aslc, NULL, ASL_LEVEL_ERR,
133 "Xquartz: Failed to connect to socket: %s - %d - %s",
134 filename, errno,
135 strerror(
136 errno));
137 close(ret_fd);
138 return -1;
139 }
140
141 return ret_fd;
142 }
143
144 static void
send_fd_handoff(int connected_fd,int launchd_fd)145 send_fd_handoff(int connected_fd, int launchd_fd)
146 {
147 char databuf[] = "display";
148 struct iovec iov[1];
149
150 union {
151 struct cmsghdr hdr;
152 char bytes[CMSG_SPACE(sizeof(int))];
153 } buf;
154
155 struct msghdr msg;
156 struct cmsghdr *cmsg;
157
158 iov[0].iov_base = databuf;
159 iov[0].iov_len = sizeof(databuf);
160
161 msg.msg_iov = iov;
162 msg.msg_iovlen = 1;
163 msg.msg_control = buf.bytes;
164 msg.msg_controllen = sizeof(buf);
165 msg.msg_name = 0;
166 msg.msg_namelen = 0;
167 msg.msg_flags = 0;
168
169 cmsg = CMSG_FIRSTHDR(&msg);
170 cmsg->cmsg_level = SOL_SOCKET;
171 cmsg->cmsg_type = SCM_RIGHTS;
172 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
173
174 msg.msg_controllen = cmsg->cmsg_len;
175
176 *((int *)CMSG_DATA(cmsg)) = launchd_fd;
177
178 if (sendmsg(connected_fd, &msg, 0) < 0) {
179 asl_log(
180 aslc, NULL, ASL_LEVEL_ERR,
181 "Xquartz: Error sending $DISPLAY file descriptor over fd %d: %d -- %s",
182 connected_fd, errno, strerror(errno));
183 return;
184 }
185
186 asl_log(aslc, NULL, ASL_LEVEL_DEBUG,
187 "Xquartz: Message sent. Closing handoff fd.");
188 close(connected_fd);
189 }
190
191 __attribute__((__noreturn__))
192 static void
signal_handler(int sig)193 signal_handler(int sig)
194 {
195 if (x11app_pid)
196 kill(x11app_pid, sig);
197 _exit(0);
198 }
199
200 int
main(int argc,char ** argv,char ** envp)201 main(int argc, char **argv, char **envp)
202 {
203 int envpc;
204 kern_return_t kr;
205 mach_port_t mp;
206 string_array_t newenvp;
207 string_array_t newargv;
208 size_t i;
209 int launchd_fd;
210 string_t handoff_socket_filename;
211 sig_t handler;
212 char *asl_sender;
213 char *asl_facility;
214 char *server_bootstrap_name = kX11AppBundleId;
215
216 if (getenv("X11_PREFS_DOMAIN"))
217 server_bootstrap_name = getenv("X11_PREFS_DOMAIN");
218
219 asprintf(&asl_sender, "%s.stub", server_bootstrap_name);
220 assert(asl_sender);
221
222 asl_facility = strdup(server_bootstrap_name);
223 assert(asl_facility);
224 if (strcmp(asl_facility + strlen(asl_facility) - 4, ".X11") == 0)
225 asl_facility[strlen(asl_facility) - 4] = '\0';
226
227 assert(aslc = asl_open(asl_sender, asl_facility, ASL_OPT_NO_DELAY));
228 free(asl_sender);
229 free(asl_facility);
230
231 /* We don't have a mechanism in place to handle this interrupt driven
232 * server-start notification, so just send the signal now, so xinit doesn't
233 * time out waiting for it and will just poll for the server.
234 */
235 handler = signal(SIGUSR1, SIG_IGN);
236 if (handler == SIG_IGN)
237 kill(getppid(), SIGUSR1);
238 signal(SIGUSR1, handler);
239
240 /* Pass on SIGs to X11.app */
241 signal(SIGINT, signal_handler);
242 signal(SIGTERM, signal_handler);
243
244 /* Get the $DISPLAY FD */
245 launchd_fd = launchd_display_fd();
246
247 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp);
248 if (kr != KERN_SUCCESS) {
249 pid_t child;
250
251 asl_log(aslc, NULL, ASL_LEVEL_WARNING,
252 "Xquartz: Unable to locate waiting server: %s",
253 server_bootstrap_name);
254 set_x11_path();
255
256 /* This forking is ugly and will be cleaned up later */
257 child = fork();
258 if (child == -1) {
259 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: Could not fork: %s",
260 strerror(
261 errno));
262 return EXIT_FAILURE;
263 }
264
265 if (child == 0) {
266 char *_argv[3];
267 _argv[0] = x11_path;
268 _argv[1] = "--listenonly";
269 _argv[2] = NULL;
270 asl_log(aslc, NULL, ASL_LEVEL_NOTICE,
271 "Xquartz: Starting X server: %s --listenonly",
272 x11_path);
273 return execvp(x11_path, _argv);
274 }
275
276 /* Try connecting for 10 seconds */
277 for (i = 0; i < 80; i++) {
278 usleep(250000);
279 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp);
280 if (kr == KERN_SUCCESS)
281 break;
282 }
283
284 if (kr != KERN_SUCCESS) {
285 asl_log(aslc, NULL, ASL_LEVEL_ERR,
286 "Xquartz: bootstrap_look_up(): %s", bootstrap_strerror(
287 kr));
288 return EXIT_FAILURE;
289 }
290 }
291
292 /* Get X11.app's pid */
293 request_pid(mp, &x11app_pid);
294
295 /* Handoff the $DISPLAY FD */
296 if (launchd_fd != -1) {
297 size_t try, try_max;
298 int handoff_fd = -1;
299
300 for (try = 0, try_max = 5; try < try_max; try++) {
301 if (request_fd_handoff_socket(mp,
302 handoff_socket_filename) !=
303 KERN_SUCCESS) {
304 asl_log(
305 aslc, NULL, ASL_LEVEL_INFO,
306 "Xquartz: Failed to request a socket from the server to send the $DISPLAY fd over (try %d of %d)",
307 (int)try + 1, (int)try_max);
308 continue;
309 }
310
311 handoff_fd = connect_to_socket(handoff_socket_filename);
312 if (handoff_fd == -1) {
313 asl_log(aslc, NULL, ASL_LEVEL_ERR,
314 "Xquartz: Failed to connect to socket (try %d of %d)",
315 (int)try + 1,
316 (int)try_max);
317 continue;
318 }
319
320 asl_log(
321 aslc, NULL, ASL_LEVEL_INFO,
322 "Xquartz: Handoff connection established (try %d of %d) on fd %d, \"%s\". Sending message.",
323 (int)try + 1, (int)try_max, handoff_fd,
324 handoff_socket_filename);
325 send_fd_handoff(handoff_fd, launchd_fd);
326 close(handoff_fd);
327 break;
328 }
329 }
330
331 /* Count envp */
332 for (envpc = 0; envp[envpc]; envpc++) ;
333
334 /* We have fixed-size string lengths due to limitations in IPC,
335 * so we need to copy our argv and envp.
336 */
337 newargv = (string_array_t)calloc((1 + argc), sizeof(string_t));
338 newenvp = (string_array_t)calloc((1 + envpc), sizeof(string_t));
339
340 if (!newargv || !newenvp) {
341 /* Silence the clang static analyzer */
342 free(newargv);
343 free(newenvp);
344
345 asl_log(aslc, NULL, ASL_LEVEL_ERR,
346 "Xquartz: Memory allocation failure");
347 return EXIT_FAILURE;
348 }
349
350 for (i = 0; i < argc; i++) {
351 strlcpy(newargv[i], argv[i], STRING_T_SIZE);
352 }
353 for (i = 0; i < envpc; i++) {
354 strlcpy(newenvp[i], envp[i], STRING_T_SIZE);
355 }
356
357 kr = start_x11_server(mp, newargv, argc, newenvp, envpc);
358
359 free(newargv);
360 free(newenvp);
361
362 if (kr != KERN_SUCCESS) {
363 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: start_x11_server: %s",
364 mach_error_string(
365 kr));
366 return EXIT_FAILURE;
367 }
368 return EXIT_SUCCESS;
369 }
370