simple-xinit.c revision ed6184df
11b5d61b8Smrg/*
21b5d61b8Smrg * Copyright © 2016 Broadcom
31b5d61b8Smrg *
41b5d61b8Smrg * Permission is hereby granted, free of charge, to any person obtaining a
51b5d61b8Smrg * copy of this software and associated documentation files (the "Software"),
61b5d61b8Smrg * to deal in the Software without restriction, including without limitation
71b5d61b8Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
81b5d61b8Smrg * and/or sell copies of the Software, and to permit persons to whom the
91b5d61b8Smrg * Software is furnished to do so, subject to the following conditions:
101b5d61b8Smrg *
111b5d61b8Smrg * The above copyright notice and this permission notice (including the next
121b5d61b8Smrg * paragraph) shall be included in all copies or substantial portions of the
131b5d61b8Smrg * Software.
141b5d61b8Smrg *
151b5d61b8Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
161b5d61b8Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
171b5d61b8Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
181b5d61b8Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
191b5d61b8Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
201b5d61b8Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
211b5d61b8Smrg * IN THE SOFTWARE.
221b5d61b8Smrg */
231b5d61b8Smrg
241b5d61b8Smrg#ifdef HAVE_DIX_CONFIG_H
251b5d61b8Smrg#include <dix-config.h>
261b5d61b8Smrg#endif
271b5d61b8Smrg
281b5d61b8Smrg#include <errno.h>
291b5d61b8Smrg#include <signal.h>
301b5d61b8Smrg#include <stdbool.h>
311b5d61b8Smrg#include <stdio.h>
321b5d61b8Smrg#include <stdlib.h>
331b5d61b8Smrg#include <string.h>
341b5d61b8Smrg#include <sys/wait.h>
351b5d61b8Smrg#include <unistd.h>
361b5d61b8Smrg
371b5d61b8Smrgstatic void
381b5d61b8Smrgkill_server(int server_pid)
391b5d61b8Smrg{
401b5d61b8Smrg    int ret = kill(server_pid, SIGTERM);
411b5d61b8Smrg    int wstatus;
421b5d61b8Smrg
431b5d61b8Smrg    if (ret) {
441b5d61b8Smrg        fprintf(stderr, "Failed to send kill to the server: %s\n",
451b5d61b8Smrg                strerror(errno));
461b5d61b8Smrg        exit(1);
471b5d61b8Smrg    }
481b5d61b8Smrg
491b5d61b8Smrg    ret = waitpid(server_pid, &wstatus, 0);
501b5d61b8Smrg    if (ret < 0) {
511b5d61b8Smrg        fprintf(stderr, "Failed to wait for X to die: %s\n", strerror(errno));
521b5d61b8Smrg        exit(1);
531b5d61b8Smrg    }
541b5d61b8Smrg}
551b5d61b8Smrg
561b5d61b8Smrgstatic void
571b5d61b8Smrgusage(int argc, char **argv)
581b5d61b8Smrg{
591b5d61b8Smrg    fprintf(stderr, "%s <client command> -- <server command>\n", argv[0]);
601b5d61b8Smrg    exit(1);
611b5d61b8Smrg}
621b5d61b8Smrg
63ed6184dfSmrgstatic int server_displayfd;
64ed6184dfSmrgstatic const char *server_dead = "server_dead";
65ed6184dfSmrg
66ed6184dfSmrgstatic void
67ed6184dfSmrghandle_sigchld(int sig)
68ed6184dfSmrg{
69ed6184dfSmrg    write(server_displayfd, server_dead, strlen(server_dead));
70ed6184dfSmrg}
71ed6184dfSmrg
721b5d61b8Smrg/* Starts the X server, returning its pid. */
731b5d61b8Smrgstatic int
741b5d61b8Smrgstart_server(char *const *server_args)
751b5d61b8Smrg{
761b5d61b8Smrg    int server_pid = fork();
771b5d61b8Smrg
781b5d61b8Smrg    if (server_pid == -1) {
791b5d61b8Smrg        fprintf(stderr, "Fork failed: %s\n", strerror(errno));
801b5d61b8Smrg        exit(1);
811b5d61b8Smrg    } else if (server_pid != 0) {
821b5d61b8Smrg        /* Continue along the main process that will exec the client. */
83ed6184dfSmrg
84ed6184dfSmrg        struct sigaction sa;
85ed6184dfSmrg        sa.sa_handler = handle_sigchld;
86ed6184dfSmrg        sigemptyset(&sa.sa_mask);
87ed6184dfSmrg        sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
88ed6184dfSmrg        if (sigaction(SIGCHLD, &sa, 0) == -1) {
89ed6184dfSmrg            fprintf(stderr, "Failed to set up signal handler: %s\n",
90ed6184dfSmrg                    strerror(errno));
91ed6184dfSmrg            exit(1);
92ed6184dfSmrg        }
93ed6184dfSmrg
941b5d61b8Smrg        return server_pid;
951b5d61b8Smrg    }
961b5d61b8Smrg
971b5d61b8Smrg    /* Execute the server.  This only returns if an error occurred. */
981b5d61b8Smrg    execvp(server_args[0], server_args);
991b5d61b8Smrg    fprintf(stderr, "Error starting the server: %s\n", strerror(errno));
1001b5d61b8Smrg    exit(1);
1011b5d61b8Smrg}
1021b5d61b8Smrg
1031b5d61b8Smrg/* Reads the display number out of the started server's display socket. */
1041b5d61b8Smrgstatic int
1051b5d61b8Smrgget_display(int displayfd)
1061b5d61b8Smrg{
107ed6184dfSmrg    char display_string[20];
1081b5d61b8Smrg    ssize_t ret;
1091b5d61b8Smrg
1101b5d61b8Smrg    ret = read(displayfd, display_string, sizeof(display_string) - 1);
1111b5d61b8Smrg    if (ret <= 0) {
1121b5d61b8Smrg        fprintf(stderr, "Failed reading displayfd: %s\n", strerror(errno));
1131b5d61b8Smrg        exit(1);
1141b5d61b8Smrg    }
1151b5d61b8Smrg
1161b5d61b8Smrg    /* We've read in the display number as a string terminated by
1171b5d61b8Smrg     * '\n', but not '\0'.  Cap it and parse the number.
1181b5d61b8Smrg     */
1191b5d61b8Smrg    display_string[ret] = '\0';
120ed6184dfSmrg
121ed6184dfSmrg    if (strncmp(display_string, server_dead, strlen(server_dead)) == 0) {
122ed6184dfSmrg        fprintf(stderr, "Server failed to start before setting up displayfd\n");
123ed6184dfSmrg        exit(1);
124ed6184dfSmrg    }
125ed6184dfSmrg
1261b5d61b8Smrg    return atoi(display_string);
1271b5d61b8Smrg}
1281b5d61b8Smrg
1291b5d61b8Smrgstatic int
1301b5d61b8Smrgstart_client(char *const *client_args, int display)
1311b5d61b8Smrg{
1321b5d61b8Smrg    char *display_string;
1331b5d61b8Smrg    int ret;
1341b5d61b8Smrg    int client_pid;
1351b5d61b8Smrg
1361b5d61b8Smrg    ret = asprintf(&display_string, ":%d", display);
1371b5d61b8Smrg    if (ret < 0) {
1381b5d61b8Smrg        fprintf(stderr, "asprintf fail\n");
1391b5d61b8Smrg        exit(1);
1401b5d61b8Smrg    }
1411b5d61b8Smrg
1421b5d61b8Smrg    ret = setenv("DISPLAY", display_string, true);
1431b5d61b8Smrg    if (ret) {
1441b5d61b8Smrg        fprintf(stderr, "Failed to set DISPLAY\n");
1451b5d61b8Smrg        exit(1);
1461b5d61b8Smrg    }
1471b5d61b8Smrg
1481b5d61b8Smrg    client_pid = fork();
1491b5d61b8Smrg    if (client_pid == -1) {
1501b5d61b8Smrg        fprintf(stderr, "Fork failed: %s\n", strerror(errno));
1511b5d61b8Smrg        exit(1);
1521b5d61b8Smrg    } else if (client_pid) {
1531b5d61b8Smrg        int wstatus;
1541b5d61b8Smrg
1551b5d61b8Smrg        ret = waitpid(client_pid, &wstatus, 0);
1561b5d61b8Smrg        if (ret < 0) {
1571b5d61b8Smrg            fprintf(stderr, "Error waiting for client to start: %s\n",
1581b5d61b8Smrg                    strerror(errno));
1591b5d61b8Smrg            return 1;
1601b5d61b8Smrg        }
1611b5d61b8Smrg
1621b5d61b8Smrg        if (!WIFEXITED(wstatus))
1631b5d61b8Smrg            return 1;
1641b5d61b8Smrg
1651b5d61b8Smrg        return WEXITSTATUS(wstatus);
1661b5d61b8Smrg    } else {
1671b5d61b8Smrg        execvp(client_args[0], client_args);
1681b5d61b8Smrg        /* exec only returns if an error occurred. */
1691b5d61b8Smrg        fprintf(stderr, "Error starting the client: %s\n", strerror(errno));
1701b5d61b8Smrg        exit(1);
1711b5d61b8Smrg    }
1721b5d61b8Smrg}
1731b5d61b8Smrg
1741b5d61b8Smrg/* Splits the incoming argc/argv into a pair of NULL-terminated arrays
1751b5d61b8Smrg * of args.
1761b5d61b8Smrg */
1771b5d61b8Smrgstatic void
1781b5d61b8Smrgparse_args(int argc, char **argv,
1791b5d61b8Smrg           char * const **out_client_args,
1801b5d61b8Smrg           char * const **out_server_args,
1811b5d61b8Smrg           int displayfd)
1821b5d61b8Smrg{
1831b5d61b8Smrg    /* We're stripping the -- and the program name, inserting two
1841b5d61b8Smrg     * NULLs, and also the -displayfd and fd number.
1851b5d61b8Smrg     */
1861b5d61b8Smrg    char **args_storage = calloc(argc + 2, sizeof(char *));
1871b5d61b8Smrg    char *const *client_args;
1881b5d61b8Smrg    char *const *server_args = NULL;
1891b5d61b8Smrg    char **next_arg = args_storage;
1901b5d61b8Smrg    bool parsing_client = true;
1911b5d61b8Smrg    int i, ret;
1921b5d61b8Smrg    char *displayfd_string;
1931b5d61b8Smrg
1941b5d61b8Smrg    if (!args_storage)
1951b5d61b8Smrg        exit(1);
1961b5d61b8Smrg
1971b5d61b8Smrg    client_args = args_storage;
1981b5d61b8Smrg    for (i = 1; i < argc; i++) {
1991b5d61b8Smrg        if (strcmp(argv[i], "--") == 0) {
2001b5d61b8Smrg            if (!parsing_client)
2011b5d61b8Smrg                usage(argc, argv);
2021b5d61b8Smrg
2031b5d61b8Smrg            /* Cap the client list */
2041b5d61b8Smrg            *next_arg = NULL;
2051b5d61b8Smrg            next_arg++;
2061b5d61b8Smrg
2071b5d61b8Smrg            /* Move to adding into server_args. */
2081b5d61b8Smrg            server_args = next_arg;
2091b5d61b8Smrg            parsing_client = false;
2101b5d61b8Smrg            continue;
2111b5d61b8Smrg        }
2121b5d61b8Smrg
213ed6184dfSmrg        /* A sort of escaped "--" argument so we can nest server
214ed6184dfSmrg         * invocations for testing.
215ed6184dfSmrg         */
216ed6184dfSmrg        if (strcmp(argv[i], "----") == 0)
217ed6184dfSmrg            *next_arg = (char *)"--";
218ed6184dfSmrg        else
219ed6184dfSmrg            *next_arg = argv[i];
2201b5d61b8Smrg        next_arg++;
2211b5d61b8Smrg    }
2221b5d61b8Smrg
2231b5d61b8Smrg    if (client_args[0] == NULL || !server_args || server_args[0] == NULL)
2241b5d61b8Smrg        usage(argc, argv);
2251b5d61b8Smrg
2261b5d61b8Smrg    /* Give the server -displayfd X */
2271b5d61b8Smrg    *next_arg = (char *)"-displayfd";
2281b5d61b8Smrg    next_arg++;
2291b5d61b8Smrg
2301b5d61b8Smrg    ret = asprintf(&displayfd_string, "%d", displayfd);
2311b5d61b8Smrg    if (ret < 0) {
2321b5d61b8Smrg        fprintf(stderr, "asprintf fail\n");
2331b5d61b8Smrg        exit(1);
2341b5d61b8Smrg    }
2351b5d61b8Smrg    *next_arg = displayfd_string;
2361b5d61b8Smrg    next_arg++;
2371b5d61b8Smrg
2381b5d61b8Smrg    *out_client_args = client_args;
2391b5d61b8Smrg    *out_server_args = server_args;
2401b5d61b8Smrg}
2411b5d61b8Smrg
2421b5d61b8Smrgint
2431b5d61b8Smrgmain(int argc, char **argv)
2441b5d61b8Smrg{
2451b5d61b8Smrg    char * const *client_args;
2461b5d61b8Smrg    char * const *server_args;
2471b5d61b8Smrg    int displayfd_pipe[2];
2481b5d61b8Smrg    int display, server_pid;
2491b5d61b8Smrg    int ret;
2501b5d61b8Smrg
2511b5d61b8Smrg    ret = pipe(displayfd_pipe);
2521b5d61b8Smrg    if (ret) {
2531b5d61b8Smrg        fprintf(stderr, "Pipe creation failure: %s", strerror(errno));
2541b5d61b8Smrg        exit(1);
2551b5d61b8Smrg    }
2561b5d61b8Smrg
257ed6184dfSmrg    server_displayfd = displayfd_pipe[1];
258ed6184dfSmrg    parse_args(argc, argv, &client_args, &server_args, server_displayfd);
2591b5d61b8Smrg    server_pid = start_server(server_args);
2601b5d61b8Smrg    display = get_display(displayfd_pipe[0]);
2611b5d61b8Smrg    ret = start_client(client_args, display);
2621b5d61b8Smrg    kill_server(server_pid);
2631b5d61b8Smrg
2641b5d61b8Smrg    exit(ret);
2651b5d61b8Smrg}
266