simple-xinit.c revision 1b5d61b8
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
631b5d61b8Smrg/* Starts the X server, returning its pid. */
641b5d61b8Smrgstatic int
651b5d61b8Smrgstart_server(char *const *server_args)
661b5d61b8Smrg{
671b5d61b8Smrg    int server_pid = fork();
681b5d61b8Smrg
691b5d61b8Smrg    if (server_pid == -1) {
701b5d61b8Smrg        fprintf(stderr, "Fork failed: %s\n", strerror(errno));
711b5d61b8Smrg        exit(1);
721b5d61b8Smrg    } else if (server_pid != 0) {
731b5d61b8Smrg        /* Continue along the main process that will exec the client. */
741b5d61b8Smrg        return server_pid;
751b5d61b8Smrg    }
761b5d61b8Smrg
771b5d61b8Smrg    /* Execute the server.  This only returns if an error occurred. */
781b5d61b8Smrg    execvp(server_args[0], server_args);
791b5d61b8Smrg    fprintf(stderr, "Error starting the server: %s\n", strerror(errno));
801b5d61b8Smrg    exit(1);
811b5d61b8Smrg}
821b5d61b8Smrg
831b5d61b8Smrg/* Reads the display number out of the started server's display socket. */
841b5d61b8Smrgstatic int
851b5d61b8Smrgget_display(int displayfd)
861b5d61b8Smrg{
871b5d61b8Smrg    char display_string[10];
881b5d61b8Smrg    ssize_t ret;
891b5d61b8Smrg
901b5d61b8Smrg    ret = read(displayfd, display_string, sizeof(display_string) - 1);
911b5d61b8Smrg    if (ret <= 0) {
921b5d61b8Smrg        fprintf(stderr, "Failed reading displayfd: %s\n", strerror(errno));
931b5d61b8Smrg        exit(1);
941b5d61b8Smrg    }
951b5d61b8Smrg
961b5d61b8Smrg    /* We've read in the display number as a string terminated by
971b5d61b8Smrg     * '\n', but not '\0'.  Cap it and parse the number.
981b5d61b8Smrg     */
991b5d61b8Smrg    display_string[ret] = '\0';
1001b5d61b8Smrg    return atoi(display_string);
1011b5d61b8Smrg}
1021b5d61b8Smrg
1031b5d61b8Smrgstatic int
1041b5d61b8Smrgstart_client(char *const *client_args, int display)
1051b5d61b8Smrg{
1061b5d61b8Smrg    char *display_string;
1071b5d61b8Smrg    int ret;
1081b5d61b8Smrg    int client_pid;
1091b5d61b8Smrg
1101b5d61b8Smrg    ret = asprintf(&display_string, ":%d", display);
1111b5d61b8Smrg    if (ret < 0) {
1121b5d61b8Smrg        fprintf(stderr, "asprintf fail\n");
1131b5d61b8Smrg        exit(1);
1141b5d61b8Smrg    }
1151b5d61b8Smrg
1161b5d61b8Smrg    ret = setenv("DISPLAY", display_string, true);
1171b5d61b8Smrg    if (ret) {
1181b5d61b8Smrg        fprintf(stderr, "Failed to set DISPLAY\n");
1191b5d61b8Smrg        exit(1);
1201b5d61b8Smrg    }
1211b5d61b8Smrg
1221b5d61b8Smrg    client_pid = fork();
1231b5d61b8Smrg    if (client_pid == -1) {
1241b5d61b8Smrg        fprintf(stderr, "Fork failed: %s\n", strerror(errno));
1251b5d61b8Smrg        exit(1);
1261b5d61b8Smrg    } else if (client_pid) {
1271b5d61b8Smrg        int wstatus;
1281b5d61b8Smrg
1291b5d61b8Smrg        ret = waitpid(client_pid, &wstatus, 0);
1301b5d61b8Smrg        if (ret < 0) {
1311b5d61b8Smrg            fprintf(stderr, "Error waiting for client to start: %s\n",
1321b5d61b8Smrg                    strerror(errno));
1331b5d61b8Smrg            return 1;
1341b5d61b8Smrg        }
1351b5d61b8Smrg
1361b5d61b8Smrg        if (!WIFEXITED(wstatus))
1371b5d61b8Smrg            return 1;
1381b5d61b8Smrg
1391b5d61b8Smrg        return WEXITSTATUS(wstatus);
1401b5d61b8Smrg    } else {
1411b5d61b8Smrg        execvp(client_args[0], client_args);
1421b5d61b8Smrg        /* exec only returns if an error occurred. */
1431b5d61b8Smrg        fprintf(stderr, "Error starting the client: %s\n", strerror(errno));
1441b5d61b8Smrg        exit(1);
1451b5d61b8Smrg    }
1461b5d61b8Smrg}
1471b5d61b8Smrg
1481b5d61b8Smrg/* Splits the incoming argc/argv into a pair of NULL-terminated arrays
1491b5d61b8Smrg * of args.
1501b5d61b8Smrg */
1511b5d61b8Smrgstatic void
1521b5d61b8Smrgparse_args(int argc, char **argv,
1531b5d61b8Smrg           char * const **out_client_args,
1541b5d61b8Smrg           char * const **out_server_args,
1551b5d61b8Smrg           int displayfd)
1561b5d61b8Smrg{
1571b5d61b8Smrg    /* We're stripping the -- and the program name, inserting two
1581b5d61b8Smrg     * NULLs, and also the -displayfd and fd number.
1591b5d61b8Smrg     */
1601b5d61b8Smrg    char **args_storage = calloc(argc + 2, sizeof(char *));
1611b5d61b8Smrg    char *const *client_args;
1621b5d61b8Smrg    char *const *server_args = NULL;
1631b5d61b8Smrg    char **next_arg = args_storage;
1641b5d61b8Smrg    bool parsing_client = true;
1651b5d61b8Smrg    int i, ret;
1661b5d61b8Smrg    char *displayfd_string;
1671b5d61b8Smrg
1681b5d61b8Smrg    if (!args_storage)
1691b5d61b8Smrg        exit(1);
1701b5d61b8Smrg
1711b5d61b8Smrg    client_args = args_storage;
1721b5d61b8Smrg    for (i = 1; i < argc; i++) {
1731b5d61b8Smrg        if (strcmp(argv[i], "--") == 0) {
1741b5d61b8Smrg            if (!parsing_client)
1751b5d61b8Smrg                usage(argc, argv);
1761b5d61b8Smrg
1771b5d61b8Smrg            /* Cap the client list */
1781b5d61b8Smrg            *next_arg = NULL;
1791b5d61b8Smrg            next_arg++;
1801b5d61b8Smrg
1811b5d61b8Smrg            /* Move to adding into server_args. */
1821b5d61b8Smrg            server_args = next_arg;
1831b5d61b8Smrg            parsing_client = false;
1841b5d61b8Smrg            continue;
1851b5d61b8Smrg        }
1861b5d61b8Smrg
1871b5d61b8Smrg        *next_arg = argv[i];
1881b5d61b8Smrg        next_arg++;
1891b5d61b8Smrg    }
1901b5d61b8Smrg
1911b5d61b8Smrg    if (client_args[0] == NULL || !server_args || server_args[0] == NULL)
1921b5d61b8Smrg        usage(argc, argv);
1931b5d61b8Smrg
1941b5d61b8Smrg    /* Give the server -displayfd X */
1951b5d61b8Smrg    *next_arg = (char *)"-displayfd";
1961b5d61b8Smrg    next_arg++;
1971b5d61b8Smrg
1981b5d61b8Smrg    ret = asprintf(&displayfd_string, "%d", displayfd);
1991b5d61b8Smrg    if (ret < 0) {
2001b5d61b8Smrg        fprintf(stderr, "asprintf fail\n");
2011b5d61b8Smrg        exit(1);
2021b5d61b8Smrg    }
2031b5d61b8Smrg    *next_arg = displayfd_string;
2041b5d61b8Smrg    next_arg++;
2051b5d61b8Smrg
2061b5d61b8Smrg    *out_client_args = client_args;
2071b5d61b8Smrg    *out_server_args = server_args;
2081b5d61b8Smrg}
2091b5d61b8Smrg
2101b5d61b8Smrgint
2111b5d61b8Smrgmain(int argc, char **argv)
2121b5d61b8Smrg{
2131b5d61b8Smrg    char * const *client_args;
2141b5d61b8Smrg    char * const *server_args;
2151b5d61b8Smrg    int displayfd_pipe[2];
2161b5d61b8Smrg    int display, server_pid;
2171b5d61b8Smrg    int ret;
2181b5d61b8Smrg
2191b5d61b8Smrg    ret = pipe(displayfd_pipe);
2201b5d61b8Smrg    if (ret) {
2211b5d61b8Smrg        fprintf(stderr, "Pipe creation failure: %s", strerror(errno));
2221b5d61b8Smrg        exit(1);
2231b5d61b8Smrg    }
2241b5d61b8Smrg
2251b5d61b8Smrg    parse_args(argc, argv, &client_args, &server_args, displayfd_pipe[1]);
2261b5d61b8Smrg    server_pid = start_server(server_args);
2271b5d61b8Smrg    display = get_display(displayfd_pipe[0]);
2281b5d61b8Smrg    ret = start_client(client_args, display);
2291b5d61b8Smrg    kill_server(server_pid);
2301b5d61b8Smrg
2311b5d61b8Smrg    exit(ret);
2321b5d61b8Smrg}
233