1 /*
2 * Copyright © 2016 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #ifdef HAVE_DIX_CONFIG_H
25 #include <dix-config.h>
26 #endif
27
28 #include <errno.h>
29 #include <signal.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36
37 static void
kill_server(int server_pid)38 kill_server(int server_pid)
39 {
40 int ret = kill(server_pid, SIGTERM);
41 int wstatus;
42
43 if (ret) {
44 fprintf(stderr, "Failed to send kill to the server: %s\n",
45 strerror(errno));
46 exit(1);
47 }
48
49 ret = waitpid(server_pid, &wstatus, 0);
50 if (ret < 0) {
51 fprintf(stderr, "Failed to wait for X to die: %s\n", strerror(errno));
52 exit(1);
53 }
54 }
55
56 static void
usage(int argc,char ** argv)57 usage(int argc, char **argv)
58 {
59 fprintf(stderr, "%s <client command> -- <server command>\n", argv[0]);
60 exit(1);
61 }
62
63 static int server_displayfd;
64 static const char *server_dead = "server_dead";
65
66 static void
handle_sigchld(int sig)67 handle_sigchld(int sig)
68 {
69 write(server_displayfd, server_dead, strlen(server_dead));
70 }
71
72 /* Starts the X server, returning its pid. */
73 static int
start_server(char * const * server_args)74 start_server(char *const *server_args)
75 {
76 int server_pid = fork();
77
78 if (server_pid == -1) {
79 fprintf(stderr, "Fork failed: %s\n", strerror(errno));
80 exit(1);
81 } else if (server_pid != 0) {
82 /* Continue along the main process that will exec the client. */
83
84 struct sigaction sa;
85 sa.sa_handler = handle_sigchld;
86 sigemptyset(&sa.sa_mask);
87 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
88 if (sigaction(SIGCHLD, &sa, 0) == -1) {
89 fprintf(stderr, "Failed to set up signal handler: %s\n",
90 strerror(errno));
91 exit(1);
92 }
93
94 return server_pid;
95 }
96
97 /* Execute the server. This only returns if an error occurred. */
98 execvp(server_args[0], server_args);
99 fprintf(stderr, "Error starting the server: %s\n", strerror(errno));
100 exit(1);
101 }
102
103 /* Reads the display number out of the started server's display socket. */
104 static int
get_display(int displayfd)105 get_display(int displayfd)
106 {
107 char display_string[20];
108 ssize_t ret;
109
110 ret = read(displayfd, display_string, sizeof(display_string) - 1);
111 if (ret <= 0) {
112 fprintf(stderr, "Failed reading displayfd: %s\n", strerror(errno));
113 exit(1);
114 }
115
116 /* We've read in the display number as a string terminated by
117 * '\n', but not '\0'. Cap it and parse the number.
118 */
119 display_string[ret] = '\0';
120
121 if (strncmp(display_string, server_dead, strlen(server_dead)) == 0) {
122 fprintf(stderr, "Server failed to start before setting up displayfd\n");
123 exit(1);
124 }
125
126 return atoi(display_string);
127 }
128
129 static int
start_client(char * const * client_args,int display)130 start_client(char *const *client_args, int display)
131 {
132 char *display_string;
133 int ret;
134 int client_pid;
135
136 ret = asprintf(&display_string, ":%d", display);
137 if (ret < 0) {
138 fprintf(stderr, "asprintf fail\n");
139 exit(1);
140 }
141
142 ret = setenv("DISPLAY", display_string, true);
143 if (ret) {
144 fprintf(stderr, "Failed to set DISPLAY\n");
145 exit(1);
146 }
147
148 client_pid = fork();
149 if (client_pid == -1) {
150 fprintf(stderr, "Fork failed: %s\n", strerror(errno));
151 exit(1);
152 } else if (client_pid) {
153 int wstatus;
154
155 ret = waitpid(client_pid, &wstatus, 0);
156 if (ret < 0) {
157 fprintf(stderr, "Error waiting for client to start: %s\n",
158 strerror(errno));
159 return 1;
160 }
161
162 if (!WIFEXITED(wstatus))
163 return 1;
164
165 return WEXITSTATUS(wstatus);
166 } else {
167 execvp(client_args[0], client_args);
168 /* exec only returns if an error occurred. */
169 fprintf(stderr, "Error starting the client: %s\n", strerror(errno));
170 exit(1);
171 }
172 }
173
174 /* Splits the incoming argc/argv into a pair of NULL-terminated arrays
175 * of args.
176 */
177 static void
parse_args(int argc,char ** argv,char * const ** out_client_args,char * const ** out_server_args,int displayfd)178 parse_args(int argc, char **argv,
179 char * const **out_client_args,
180 char * const **out_server_args,
181 int displayfd)
182 {
183 /* We're stripping the -- and the program name, inserting two
184 * NULLs, and also the -displayfd and fd number.
185 */
186 char **args_storage = calloc(argc + 2, sizeof(char *));
187 char *const *client_args;
188 char *const *server_args = NULL;
189 char **next_arg = args_storage;
190 bool parsing_client = true;
191 int i, ret;
192 char *displayfd_string;
193
194 if (!args_storage)
195 exit(1);
196
197 client_args = args_storage;
198 for (i = 1; i < argc; i++) {
199 if (strcmp(argv[i], "--") == 0) {
200 if (!parsing_client)
201 usage(argc, argv);
202
203 /* Cap the client list */
204 *next_arg = NULL;
205 next_arg++;
206
207 /* Move to adding into server_args. */
208 server_args = next_arg;
209 parsing_client = false;
210 continue;
211 }
212
213 /* A sort of escaped "--" argument so we can nest server
214 * invocations for testing.
215 */
216 if (strcmp(argv[i], "----") == 0)
217 *next_arg = (char *)"--";
218 else
219 *next_arg = argv[i];
220 next_arg++;
221 }
222
223 if (client_args[0] == NULL || !server_args || server_args[0] == NULL)
224 usage(argc, argv);
225
226 /* Give the server -displayfd X */
227 *next_arg = (char *)"-displayfd";
228 next_arg++;
229
230 ret = asprintf(&displayfd_string, "%d", displayfd);
231 if (ret < 0) {
232 fprintf(stderr, "asprintf fail\n");
233 exit(1);
234 }
235 *next_arg = displayfd_string;
236 next_arg++;
237
238 *out_client_args = client_args;
239 *out_server_args = server_args;
240 }
241
242 int
main(int argc,char ** argv)243 main(int argc, char **argv)
244 {
245 char * const *client_args;
246 char * const *server_args;
247 int displayfd_pipe[2];
248 int display, server_pid;
249 int ret;
250
251 ret = pipe(displayfd_pipe);
252 if (ret) {
253 fprintf(stderr, "Pipe creation failure: %s", strerror(errno));
254 exit(1);
255 }
256
257 server_displayfd = displayfd_pipe[1];
258 parse_args(argc, argv, &client_args, &server_args, server_displayfd);
259 server_pid = start_server(server_args);
260 display = get_display(displayfd_pipe[0]);
261 ret = start_client(client_args, display);
262 kill_server(server_pid);
263
264 exit(ret);
265 }
266