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 37static void 38kill_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 56static void 57usage(int argc, char **argv) 58{ 59 fprintf(stderr, "%s <client command> -- <server command>\n", argv[0]); 60 exit(1); 61} 62 63static int server_displayfd; 64static const char *server_dead = "server_dead"; 65 66static void 67handle_sigchld(int sig) 68{ 69 write(server_displayfd, server_dead, strlen(server_dead)); 70} 71 72/* Starts the X server, returning its pid. */ 73static int 74start_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. */ 104static int 105get_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 129static int 130start_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 */ 177static void 178parse_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 242int 243main(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