xref: /OK3568_Linux_fs/external/xserver/hw/xquartz/mach-startup/stub.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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