summaryrefslogtreecommitdiff
path: root/daemon/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/server.c')
-rw-r--r--daemon/server.c1583
1 files changed, 0 insertions, 1583 deletions
diff --git a/daemon/server.c b/daemon/server.c
deleted file mode 100644
index 50256cd5..00000000
--- a/daemon/server.c
+++ /dev/null
@@ -1,1583 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * GDM - The GNOME Display Manager
- * Copyright (C) 1999, 2000 Martin K. Petersen <mkp@mkp.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* This file contains functions for controlling local X servers */
-
-#include "config.h"
-
-#include <glib/gi18n.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <string.h>
-#include <signal.h>
-#include <errno.h>
-#include <time.h>
-#include <ctype.h>
-#include <X11/Xlib.h>
-
-#include "gdm.h"
-#include "server.h"
-#include "misc.h"
-#include "xdmcp.h"
-#include "display.h"
-#include "auth.h"
-#include "slave.h"
-#include "getvt.h"
-
-#include "gdm-common.h"
-#include "gdm-log.h"
-#include "gdm-daemon-config.h"
-
-#include "gdm-socket-protocol.h"
-
-/* Local prototypes */
-static void gdm_server_spawn (GdmDisplay *d, const char *vtarg);
-static void gdm_server_usr1_handler (gint);
-static void gdm_server_child_handler (gint);
-static char * get_font_path (const char *display);
-
-/* Global vars */
-static int server_signal_pipe[2];
-static GdmDisplay *d = NULL;
-static gboolean server_signal_notified = FALSE;
-static int gdm_in_signal = 0;
-
-static void do_server_wait (GdmDisplay *d);
-static gboolean setup_server_wait (GdmDisplay *d);
-
-void
-gdm_server_whack_lockfile (GdmDisplay *disp)
-{
- char buf[256];
-
- /* X seems to be sometimes broken with its lock files and
- doesn't seem to remove them always, and if you manage
- to get into the weird state where the old pid now
- corresponds to some new pid, X will just die with
- a stupid error. */
-
- /* Yes there could be a race here if another X server starts
- at this exact instant. Oh well such is life. Very unlikely
- to happen though as we should really be the only ones
- trying to start X servers, and we aren't starting an
- X server on this display yet. */
-
- /* if lock file exists and it is our process, whack it! */
- g_snprintf (buf, sizeof (buf), "/tmp/.X%d-lock", disp->dispnum);
- VE_IGNORE_EINTR (g_unlink (buf));
-
- /* whack the unix socket as well */
- g_snprintf (buf, sizeof (buf),
- "/tmp/.X11-unix/X%d", disp->dispnum);
- VE_IGNORE_EINTR (g_unlink (buf));
-}
-
-
-/* Wipe cookie files */
-void
-gdm_server_wipe_cookies (GdmDisplay *disp)
-{
- if ( ! ve_string_empty (disp->authfile)) {
- VE_IGNORE_EINTR (g_unlink (disp->authfile));
- }
- g_free (disp->authfile);
- disp->authfile = NULL;
- if ( ! ve_string_empty (disp->authfile_gdm)) {
- VE_IGNORE_EINTR (g_unlink (disp->authfile_gdm));
- }
- g_free (disp->authfile_gdm);
- disp->authfile_gdm = NULL;
-}
-
-static Jmp_buf reinitjmp;
-
-/* ignore handlers */
-static int
-ignore_xerror_handler (Display *disp, XErrorEvent *evt)
-{
- return 0;
-}
-
-static int
-jumpback_xioerror_handler (Display *disp)
-{
- Longjmp (reinitjmp, 1);
-}
-
-#ifdef HAVE_FBCONSOLE
-#define FBCONSOLE "/usr/openwin/bin/fbconsole"
-
-static void
-gdm_exec_fbconsole (GdmDisplay *disp)
-{
- pid_t pid;
- char *argv[6];
-
- argv[0] = FBCONSOLE;
- argv[1] = "-d";
- argv[2] = disp->name;
- argv[3] = NULL;
-
- g_debug ("Forking fbconsole");
-
- pid = fork ();
- if (pid == 0) {
- gdm_close_all_descriptors (0 /* from */, -1 /* except */, -1 /* except2 */)
-;
- VE_IGNORE_EINTR (execv (argv[0], argv));
- }
- if (pid == -1) {
- gdm_error (_("Can not start fallback console"));
- }
-}
-#endif
-
-/**
- * gdm_server_reinit:
- * @disp: Pointer to a GdmDisplay structure
- *
- * Reinit the display, basically sends a HUP signal
- * but only if the display exists
- */
-
-gboolean
-gdm_server_reinit (GdmDisplay *disp)
-{
- if (disp == NULL)
- return FALSE;
-
- if (disp->servpid <= 0) {
- /* Kill our connection if one existed, likely to result
- * in some bizzaro error right now */
- if (disp->dsp != NULL) {
- XCloseDisplay (disp->dsp);
- disp->dsp = NULL;
- }
- return FALSE;
- }
-
- gdm_debug ("gdm_server_reinit: Server for %s is about to be reinitialized!", disp->name);
-
- if ( ! setup_server_wait (disp))
- return FALSE;
-
- d->servstat = SERVER_PENDING;
-
- if (disp->dsp != NULL) {
- /* static because of the Setjmp */
- static int (*old_xerror_handler)(Display *, XErrorEvent *) = NULL;
- static int (*old_xioerror_handler)(Display *) = NULL;
-
- old_xerror_handler = NULL;
- old_xioerror_handler = NULL;
-
- /* Do note the interaction of this Setjmp and the signal
- handlers and the Setjmp in slave.c */
-
- /* Long live Setjmp, DIE DIE DIE XSetIOErrorHandler */
-
- if (Setjmp (reinitjmp) == 0) {
- /* come here and we'll whack the server and wait to get
- an xio error */
- old_xerror_handler = XSetErrorHandler (ignore_xerror_handler);
- old_xioerror_handler = XSetIOErrorHandler (jumpback_xioerror_handler);
-
- /* Now whack the server with a SIGHUP */
- gdm_sigchld_block_push ();
- if (disp->servpid > 1)
- kill (disp->servpid, SIGHUP);
- else
- d->servstat = SERVER_DEAD;
- gdm_sigchld_block_pop ();
-
- /* the server is dead, weird */
- if (disp->dsp != NULL) {
- XCloseDisplay (disp->dsp);
- disp->dsp = NULL;
- }
- }
- /* no more display */
- disp->dsp = NULL;
- XSetErrorHandler (old_xerror_handler);
- XSetIOErrorHandler (old_xioerror_handler);
- } else {
- /* Now whack the server with a SIGHUP */
- gdm_sigchld_block_push ();
- if (disp->servpid > 1)
- kill (disp->servpid, SIGHUP);
- else
- d->servstat = SERVER_DEAD;
- gdm_sigchld_block_pop ();
- }
-
- /* Wait for the SIGUSR1 */
- do_server_wait (d);
-
- if (d->servstat == SERVER_RUNNING) {
-#ifdef HAVE_FBCONSOLE
- gdm_exec_fbconsole (d);
-#endif
- return TRUE;
- } else {
- /* if something really REALLY screwed up, then whack the
- lockfiles for safety */
- gdm_server_whack_lockfile (d);
- return FALSE;
- }
-}
-
-/**
- * gdm_server_stop:
- * @disp: Pointer to a GdmDisplay structure
- *
- * Stops a local X server, but only if it exists
- */
-
-void
-gdm_server_stop (GdmDisplay *disp)
-{
- static gboolean waiting_for_server = FALSE;
- int old_servstat;
-
- if (disp == NULL)
- return;
-
- /* Kill our connection if one existed */
- if (disp->dsp != NULL) {
- /* on XDMCP servers first kill everything in sight */
- if (disp->type == TYPE_XDMCP)
- gdm_server_whack_clients (disp->dsp);
- XCloseDisplay (disp->dsp);
- disp->dsp = NULL;
- }
-
- /* Kill our parent connection if one existed */
- if (disp->parent_dsp != NULL) {
- /* on XDMCP servers first kill everything in sight */
- if (disp->type == TYPE_XDMCP_PROXY)
- gdm_server_whack_clients (disp->parent_dsp);
- XCloseDisplay (disp->parent_dsp);
- disp->parent_dsp = NULL;
- }
-
- if (disp->servpid <= 0)
- return;
-
- gdm_debug ("gdm_server_stop: Server for %s going down!", disp->name);
-
- old_servstat = disp->servstat;
- disp->servstat = SERVER_DEAD;
-
- if (disp->servpid > 0) {
- pid_t servpid;
-
- gdm_debug ("gdm_server_stop: Killing server pid %d",
- (int)disp->servpid);
-
- /* avoid SIGCHLD race */
- gdm_sigchld_block_push ();
- servpid = disp->servpid;
-
- if (waiting_for_server) {
- gdm_error ("gdm_server_stop: Some problem killing server, whacking with SIGKILL");
- if (disp->servpid > 1)
- kill (disp->servpid, SIGKILL);
-
- } else {
- if (disp->servpid > 1 &&
- kill (disp->servpid, SIGTERM) == 0) {
- waiting_for_server = TRUE;
- ve_waitpid_no_signal (disp->servpid, NULL, 0);
- waiting_for_server = FALSE;
- }
- }
- disp->servpid = 0;
-
- gdm_sigchld_block_pop ();
-
- if (old_servstat == SERVER_RUNNING)
- gdm_server_whack_lockfile (disp);
-
- gdm_debug ("gdm_server_stop: Server pid %d dead", (int)servpid);
-
- /* just in case we restart again wait at least
- one sec to avoid races */
- if (d->sleep_before_run < 1)
- d->sleep_before_run = 1;
- }
-
- gdm_server_wipe_cookies (disp);
-
- gdm_slave_whack_temp_auth_file ();
-}
-
-static gboolean
-busy_ask_user (GdmDisplay *disp)
-{
- /* if we have "open" we can talk to the user */
- if (g_access (LIBEXECDIR "/gdmopen", X_OK) == 0) {
- char *error = g_strdup_printf
- (C_(N_("There already appears to be an X server "
- "running on display %s. Should another "
- "display number by tried? Answering no will "
- "cause GDM to attempt starting the server "
- "on %s again.%s")),
- disp->name,
- disp->name,
-#ifdef __linux__
- C_(N_(" (You can change consoles by pressing Ctrl-Alt "
- "plus a function key, such as Ctrl-Alt-F7 to go "
- "to console 7. X servers usually run on consoles "
- "7 and higher.)"))
-#else /* ! __linux__ */
- /* no info for non linux users */
- ""
-#endif /* __linux__ */
- );
- gboolean ret = TRUE;
- /* default ret to TRUE */
- if ( ! gdm_text_yesno_dialog (error, &ret))
- ret = TRUE;
- g_free (error);
- return ret;
- } else {
- /* Well we'll just try another display number */
- return TRUE;
- }
-}
-
-/* Checks only output, no XFree86 v4 logfile */
-static gboolean
-display_parent_no_connect (GdmDisplay *disp)
-{
- char *logname = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_LOG_DIR), d->name, ".log");
- FILE *fp;
- char buf[256];
- char *getsret;
-
- VE_IGNORE_EINTR (fp = fopen (logname, "r"));
- g_free (logname);
-
- if (fp == NULL)
- return FALSE;
-
- for (;;) {
- VE_IGNORE_EINTR (getsret = fgets (buf, sizeof (buf), fp));
- if (getsret == NULL) {
- VE_IGNORE_EINTR (fclose (fp));
- return FALSE;
- }
- /* Note: this is probably XFree86 specific, and perhaps even
- * version 3 specific (I don't have xfree v4 to test this),
- * of course additions are welcome to make this more robust */
- if (strstr (buf, "Unable to open display \"") == buf) {
- gdm_error (_("Display '%s' cannot be opened by nested display"),
- ve_sure_string (disp->parent_disp));
- VE_IGNORE_EINTR (fclose (fp));
- return TRUE;
- }
- }
-}
-
-static gboolean
-display_busy (GdmDisplay *disp)
-{
- char *logname = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_LOG_DIR), d->name, ".log");
- FILE *fp;
- char buf[256];
- char *getsret;
-
- VE_IGNORE_EINTR (fp = fopen (logname, "r"));
- g_free (logname);
-
- if (fp == NULL)
- return FALSE;
-
- for (;;) {
- VE_IGNORE_EINTR (getsret = fgets (buf, sizeof (buf), fp));
- if (getsret == NULL) {
- VE_IGNORE_EINTR (fclose (fp));
- return FALSE;
- }
- /* Note: this is probably XFree86 specific */
- if (strstr (buf, "Server is already active for display")
- == buf) {
- gdm_error (_("Display %s is busy. There is another "
- "X server running already."),
- disp->name);
- VE_IGNORE_EINTR (fclose (fp));
- return TRUE;
- }
- }
-}
-
-/* if we find 'Log file: "foo"' switch fp to foo and
- return TRUE */
-/* Note: assumes buf is of size 256 and is writable */
-static gboolean
-open_another_logfile (char buf[256], FILE **fp)
-{
- if (strncmp (&buf[5], "Log file: \"", strlen ("Log file: \"")) == 0) {
- FILE *ffp;
- char *fname = &buf[5+strlen ("Log file: \"")];
- char *p = strchr (fname, '"');
- if (p == NULL)
- return FALSE;
- *p = '\0';
- VE_IGNORE_EINTR (ffp = fopen (fname, "r"));
- if (ffp == NULL)
- return FALSE;
- VE_IGNORE_EINTR (fclose (*fp));
- *fp = ffp;
- return TRUE;
- }
- return FALSE;
-}
-
-static int
-display_vt (GdmDisplay *disp)
-{
- char *logname = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_LOG_DIR), d->name, ".log");
- FILE *fp;
- char buf[256];
- gboolean switched = FALSE;
- char *getsret;
-
- VE_IGNORE_EINTR (fp = fopen (logname, "r"));
- g_free (logname);
-
- if (fp == NULL)
- return FALSE;
-
- for (;;) {
- int vt;
- char *p;
-
- VE_IGNORE_EINTR (getsret = fgets (buf, sizeof (buf), fp));
- if (getsret == NULL) {
- VE_IGNORE_EINTR (fclose (fp));
- return -1;
- }
-
- if ( ! switched &&
- /* this is XFree v4 specific */
- open_another_logfile (buf, &fp)) {
- switched = TRUE;
- continue;
- }
- /* Note: this is probably XFree86 specific (works with
- * both v3 and v4 though */
- p = strstr (buf, "using VT number ");
- if (p != NULL &&
- sscanf (p, "using VT number %d", &vt) == 1) {
- VE_IGNORE_EINTR (fclose (fp));
- return vt;
- }
- }
-}
-
-static struct sigaction old_svr_wait_chld;
-static sigset_t old_svr_wait_mask;
-
-static gboolean
-setup_server_wait (GdmDisplay *d)
-{
- struct sigaction usr1, chld;
- sigset_t mask;
-
- if (pipe (server_signal_pipe) != 0) {
- gdm_error (_("%s: Error opening a pipe: %s"),
- "setup_server_wait", strerror (errno));
- return FALSE;
- }
- server_signal_notified = FALSE;
-
- /* Catch USR1 from X server */
- usr1.sa_handler = gdm_server_usr1_handler;
- usr1.sa_flags = SA_RESTART;
- sigemptyset (&usr1.sa_mask);
-
- if (sigaction (SIGUSR1, &usr1, NULL) < 0) {
- gdm_error (_("%s: Error setting up %s signal handler: %s"),
- "gdm_server_start", "USR1", strerror (errno));
- VE_IGNORE_EINTR (close (server_signal_pipe[0]));
- VE_IGNORE_EINTR (close (server_signal_pipe[1]));
- return FALSE;
- }
-
- /* Catch CHLD from X server */
- chld.sa_handler = gdm_server_child_handler;
- chld.sa_flags = SA_RESTART|SA_NOCLDSTOP;
- sigemptyset (&chld.sa_mask);
-
- if (sigaction (SIGCHLD, &chld, &old_svr_wait_chld) < 0) {
- gdm_error (_("%s: Error setting up %s signal handler: %s"),
- "gdm_server_start", "CHLD", strerror (errno));
- gdm_signal_ignore (SIGUSR1);
- VE_IGNORE_EINTR (close (server_signal_pipe[0]));
- VE_IGNORE_EINTR (close (server_signal_pipe[1]));
- return FALSE;
- }
-
- /* Set signal mask */
- sigemptyset (&mask);
- sigaddset (&mask, SIGUSR1);
- sigaddset (&mask, SIGCHLD);
- sigprocmask (SIG_UNBLOCK, &mask, &old_svr_wait_mask);
-
- return TRUE;
-}
-
-static void
-do_server_wait (GdmDisplay *d)
-{
- /* Wait for X server to send ready signal */
- if (d->servstat == SERVER_PENDING) {
- if (d->server_uid != 0 && ! d->handled && ! d->chosen_hostname) {
- /* FIXME: If not handled, we just don't know, so
- * just wait a few seconds and hope things just work,
- * fortunately there is no such case yet and probably
- * never will, but just for code anality's sake */
- gdm_sleep_no_signal (gdm_daemon_config_get_value_int(GDM_KEY_XSERVER_TIMEOUT));
- } else if (d->server_uid != 0) {
- int i;
-
- /* FIXME: This is not likely to work in reinit,
- but we never reinit Nested servers nowdays,
- so that's fine */
-
- /* if we're running the server as a non-root, we can't
- * use USR1 of course, so try openning the display
- * as a test, but the */
-
- /* just in case it's set */
- g_unsetenv ("XAUTHORITY");
-
- gdm_auth_set_local_auth (d);
-
- for (i = 0;
- d->dsp == NULL &&
- d->servstat == SERVER_PENDING &&
- i < gdm_daemon_config_get_value_int(GDM_KEY_XSERVER_TIMEOUT);
- i++) {
- d->dsp = XOpenDisplay (d->name);
- if (d->dsp == NULL)
- gdm_sleep_no_signal (1);
- else
- d->servstat = SERVER_RUNNING;
- }
- if (d->dsp == NULL &&
- /* Note: we could have still gotten a SIGCHLD */
- d->servstat == SERVER_PENDING) {
- d->servstat = SERVER_TIMEOUT;
- }
- } else {
- time_t t = time (NULL);
-
- gdm_debug ("do_server_wait: Before mainloop waiting for server");
-
- do {
- fd_set rfds;
- struct timeval tv;
-
- /* Wait up to GDM_KEY_XSERVER_TIMEOUT seconds. */
- tv.tv_sec = MAX (1, gdm_daemon_config_get_value_int(GDM_KEY_XSERVER_TIMEOUT)
- - (time (NULL) - t));
- tv.tv_usec = 0;
-
- FD_ZERO (&rfds);
- FD_SET (server_signal_pipe[0], &rfds);
-
- if (select (server_signal_pipe[0]+1, &rfds, NULL, NULL, &tv) > 0) {
- char buf[4];
- /* read the Yay! */
- VE_IGNORE_EINTR (read (server_signal_pipe[0], buf, 4));
- }
- if ( ! server_signal_notified &&
- t + gdm_daemon_config_get_value_int(GDM_KEY_XSERVER_TIMEOUT) < time (NULL)) {
- gdm_debug ("do_server_wait: Server timeout");
- d->servstat = SERVER_TIMEOUT;
- server_signal_notified = TRUE;
- }
- if (d->servpid <= 1) {
- d->servstat = SERVER_ABORT;
- server_signal_notified = TRUE;
- }
- } while ( ! server_signal_notified);
-
- gdm_debug ("gdm_server_start: After mainloop waiting for server");
- }
- }
-
- /* restore default handlers */
- gdm_signal_ignore (SIGUSR1);
- sigaction (SIGCHLD, &old_svr_wait_chld, NULL);
- sigprocmask (SIG_SETMASK, &old_svr_wait_mask, NULL);
-
- VE_IGNORE_EINTR (close (server_signal_pipe[0]));
- VE_IGNORE_EINTR (close (server_signal_pipe[1]));
-
- if (d->servpid <= 1) {
- d->servstat = SERVER_ABORT;
- }
-
- if (d->servstat != SERVER_RUNNING) {
- /* bad things are happening */
- if (d->servpid > 0) {
- pid_t pid;
-
- d->dsp = NULL;
-
- gdm_sigchld_block_push ();
- pid = d->servpid;
- d->servpid = 0;
- if (pid > 1 &&
- kill (pid, SIGTERM) == 0)
- ve_waitpid_no_signal (pid, NULL, 0);
- gdm_sigchld_block_pop ();
- }
-
- /* We will rebake cookies anyway, so wipe these */
- gdm_server_wipe_cookies (d);
- }
-}
-
-/* We keep a connection (parent_dsp) open with the parent X server
- * before running a proxy on it to prevent the X server resetting
- * as we open and close other connections.
- * Note that XDMCP servers, by default, reset when the seed X
- * connection closes whereas usually the X server only quits when
- * all X connections have closed.
- */
-static gboolean
-connect_to_parent (GdmDisplay *d)
-{
- int maxtries;
- int openretries;
-
- gdm_debug ("gdm_server_start: Connecting to parent display \'%s\'",
- d->parent_disp);
-
- d->parent_dsp = NULL;
-
- maxtries = SERVER_IS_XDMCP (d) ? 10 : 2;
-
- openretries = 0;
- while (openretries < maxtries &&
- d->parent_dsp == NULL) {
- d->parent_dsp = XOpenDisplay (d->parent_disp);
-
- if G_UNLIKELY (d->parent_dsp == NULL) {
- gdm_debug ("gdm_server_start: Sleeping %d on a retry", 1+openretries*2);
- gdm_sleep_no_signal (1+openretries*2);
- openretries++;
- }
- }
-
- if (d->parent_dsp == NULL)
- gdm_error (_("%s: failed to connect to parent display \'%s\'"),
- "gdm_server_start", d->parent_disp);
-
- return d->parent_dsp != NULL;
-}
-
-/**
- * gdm_server_start:
- * @disp: Pointer to a GdmDisplay structure
- *
- * Starts a local X server. Handles retries and fatal errors properly.
- */
-
-gboolean
-gdm_server_start (GdmDisplay *disp,
- gboolean try_again_if_busy /* only affects non-flexi servers */,
- gboolean treat_as_flexi,
- int min_flexi_disp,
- int flexi_retries)
-{
- int flexi_disp = 20;
- char *vtarg = NULL;
- int vtfd = -1, vt = -1;
-
- if (disp == NULL)
- return FALSE;
-
- d = disp;
-
- /* if an X server exists, wipe it */
- gdm_server_stop (d);
-
- /* First clear the VT number */
- if (d->type == TYPE_STATIC ||
- d->type == TYPE_FLEXI) {
- d->vt = -1;
- gdm_slave_send_num (GDM_SOP_VT_NUM, -1);
- }
-
- if (SERVER_IS_FLEXI (d) ||
- treat_as_flexi) {
- flexi_disp = gdm_get_free_display
- (MAX (gdm_daemon_config_get_high_display_num () + 1, min_flexi_disp) /* start */,
- d->server_uid /* server uid */);
-
- g_free (d->name);
- d->name = g_strdup_printf (":%d", flexi_disp);
- d->dispnum = flexi_disp;
-
- gdm_slave_send_num (GDM_SOP_DISP_NUM, flexi_disp);
- }
-
- if (d->type == TYPE_XDMCP_PROXY &&
- ! connect_to_parent (d))
- return FALSE;
-
- gdm_debug ("gdm_server_start: %s", d->name);
-
- /* Create new cookie */
- if ( ! gdm_auth_secure_display (d))
- return FALSE;
- gdm_slave_send_string (GDM_SOP_COOKIE, d->cookie);
- gdm_slave_send_string (GDM_SOP_AUTHFILE, d->authfile);
- g_setenv ("DISPLAY", d->name, TRUE);
-
- if ( ! setup_server_wait (d))
- return FALSE;
-
- d->servstat = SERVER_DEAD;
-
- if (d->type == TYPE_STATIC ||
- d->type == TYPE_FLEXI) {
- vtarg = gdm_get_empty_vt_argument (&vtfd, &vt);
- }
-
- /* fork X server process */
- gdm_server_spawn (d, vtarg);
-
- /* we can now use d->handled since that's set up above */
- do_server_wait (d);
-
- /* If we were holding a vt open for the server, close it now as it has
- * already taken the bait. */
- if (vtfd > 0) {
- VE_IGNORE_EINTR (close (vtfd));
- }
-
- switch (d->servstat) {
-
- case SERVER_TIMEOUT:
- gdm_debug ("gdm_server_start: Temporary server failure (%s)", d->name);
- break;
-
- case SERVER_ABORT:
- gdm_debug ("gdm_server_start: Server %s died during startup!", d->name);
- break;
-
- case SERVER_RUNNING:
- gdm_debug ("gdm_server_start: Completed %s!", d->name);
-
- if (SERVER_IS_FLEXI (d))
- gdm_slave_send_num (GDM_SOP_FLEXI_OK, 0 /* bogus */);
- if (d->type == TYPE_STATIC ||
- d->type == TYPE_FLEXI) {
- if (vt >= 0)
- d->vt = vt;
-
- if (d->vt < 0)
- d->vt = display_vt (d);
- if (d->vt >= 0)
- gdm_slave_send_num (GDM_SOP_VT_NUM, d->vt);
- }
-
-#ifdef HAVE_FBCONSOLE
- gdm_exec_fbconsole (d);
-#endif
-
- return TRUE;
- default:
- break;
- }
-
- if (SERVER_IS_PROXY (disp) &&
- display_parent_no_connect (disp)) {
- gdm_slave_send_num (GDM_SOP_FLEXI_ERR,
- 5 /* proxy can't connect */);
- _exit (DISPLAY_REMANAGE);
- }
-
- /* if this was a busy fail, that is, there is already
- * a server on that display, we'll display an error and after
- * this we'll exit with DISPLAY_REMANAGE to try again if the
- * user wants to, or abort this display */
- if (display_busy (disp)) {
- if (SERVER_IS_FLEXI (disp) ||
- treat_as_flexi) {
- /* for flexi displays, try again a few times with different
- * display numbers */
- if (flexi_retries <= 0) {
- /* Send X too busy */
- gdm_error (_("%s: Cannot find a free "
- "display number"),
- "gdm_server_start");
- if (SERVER_IS_FLEXI (disp)) {
- gdm_slave_send_num (GDM_SOP_FLEXI_ERR,
- 4 /* X too busy */);
- }
- /* eki eki */
- _exit (DISPLAY_REMANAGE);
- }
- return gdm_server_start (d, FALSE /*try_again_if_busy */,
- treat_as_flexi,
- flexi_disp + 1,
- flexi_retries - 1);
- } else {
- if (try_again_if_busy) {
- gdm_debug ("%s: Display %s busy. Trying once again "
- "(after 2 sec delay)",
- "gdm_server_start", d->name);
- gdm_sleep_no_signal (2);
- return gdm_server_start (d,
- FALSE /* try_again_if_busy */,
- treat_as_flexi,
- flexi_disp,
- flexi_retries);
- }
- if (busy_ask_user (disp)) {
- gdm_error (_("%s: Display %s busy. Trying "
- "another display number."),
- "gdm_server_start",
- d->name);
- d->busy_display = TRUE;
- return gdm_server_start (d,
- FALSE /*try_again_if_busy */,
- TRUE /* treat as flexi */,
- gdm_daemon_config_get_high_display_num () + 1,
- flexi_retries - 1);
- }
- _exit (DISPLAY_REMANAGE);
- }
- }
-
- _exit (DISPLAY_XFAILED);
-
- return FALSE;
-}
-
-/* Do things that require checking the log,
- * we really do need to get called a bit later, after all init is done
- * as things aren't written to disk before that */
-void
-gdm_server_checklog (GdmDisplay *disp)
-{
- if (d->vt < 0 &&
- (d->type == TYPE_STATIC ||
- d->type == TYPE_FLEXI)) {
- d->vt = display_vt (d);
- if (d->vt >= 0)
- gdm_slave_send_num (GDM_SOP_VT_NUM, d->vt);
- }
-}
-
-/* somewhat safer rename (safer if the log dir is unsafe), may in fact
- lose the file though, it guarantees that a is gone, but not that
- b exists */
-static void
-safer_rename (const char *a, const char *b)
-{
- errno = 0;
- if (link (a, b) < 0) {
- if (errno == EEXIST) {
- VE_IGNORE_EINTR (g_unlink (a));
- return;
- }
- VE_IGNORE_EINTR (g_unlink (b));
- /* likely this system doesn't support hard links */
- g_rename (a, b);
- VE_IGNORE_EINTR (g_unlink (a));
- return;
- }
- VE_IGNORE_EINTR (g_unlink (a));
-}
-
-static void
-rotate_logs (const char *dname)
-{
- const gchar *logdir = gdm_daemon_config_get_value_string (GDM_KEY_LOG_DIR);
-
- /* I'm too lazy to write a loop */
- char *fname4 = gdm_make_filename (logdir, dname, ".log.4");
- char *fname3 = gdm_make_filename (logdir, dname, ".log.3");
- char *fname2 = gdm_make_filename (logdir, dname, ".log.2");
- char *fname1 = gdm_make_filename (logdir, dname, ".log.1");
- char *fname = gdm_make_filename (logdir, dname, ".log");
-
- /* Rotate the logs (keep 4 last) */
- VE_IGNORE_EINTR (g_unlink (fname4));
- safer_rename (fname3, fname4);
- safer_rename (fname2, fname3);
- safer_rename (fname1, fname2);
- safer_rename (fname, fname1);
-
- g_free (fname4);
- g_free (fname3);
- g_free (fname2);
- g_free (fname1);
- g_free (fname);
-}
-
-static void
-gdm_server_add_xserver_args (GdmDisplay *d, char **argv)
-{
- int count;
- char **args;
- int len;
- int i;
-
- len = gdm_vector_len (argv);
- g_shell_parse_argv (d->xserver_session_args, &count, &args, NULL);
- argv = g_renew (char *, argv, len + count + 1);
-
- for (i=0; i < count;i++) {
- argv[len++] = g_strdup(args[i]);
- }
-
- argv[len] = NULL;
- g_strfreev (args);
-}
-
-GdmXserver *
-gdm_server_resolve (GdmDisplay *disp)
-{
- char *bin;
- GdmXserver *svr = NULL;
-
- bin = ve_first_word (disp->command);
- if (bin != NULL && bin[0] != '/') {
- svr = gdm_daemon_config_find_xserver (bin);
- }
- g_free (bin);
- return svr;
-}
-
-
-static char **
-vector_merge (char * const *v1,
- int len1,
- char * const *v2,
- int len2)
-{
- int argc, i;
- char **argv;
-
- if (v1 == NULL && v2 == NULL)
- return NULL;
-
- argc = len1 + len2;
-
- argv = g_new (char *, argc + 1);
- for (i = 0; i < len1; i++)
- argv[i] = g_strdup (v1[i]);
- for (; i < argc; i++)
- argv[i] = g_strdup (v2[i - len1]);
- argv[i] = NULL;
-
- return argv;
-}
-
-gboolean
-gdm_server_resolve_command_line (GdmDisplay *disp,
- gboolean resolve_flags,
- const char *vtarg,
- int *argcp,
- char ***argvp)
-{
- char *bin;
- int argc;
- char **argv;
- int len;
- int i;
- gboolean gotvtarg = FALSE;
- gboolean query_in_arglist = FALSE;
-
- argv = NULL;
-
- bin = ve_first_word (disp->command);
- if (bin == NULL) {
- const char *str;
-
- gdm_error (_("Invalid server command '%s'"), disp->command);
- str = gdm_daemon_config_get_value_string (GDM_KEY_STANDARD_XSERVER);
- g_shell_parse_argv (str, &argc, &argv, NULL);
- } else if (bin[0] != '/') {
- GdmXserver *svr = gdm_daemon_config_find_xserver (bin);
- if (svr == NULL) {
- const char *str;
-
- gdm_error (_("Server name '%s' not found; "
- "using standard server"), bin);
- str = gdm_daemon_config_get_value_string (GDM_KEY_STANDARD_XSERVER);
- g_shell_parse_argv (str, &argc, &argv, NULL);
-
- } else {
- char **svr_command;
- const char *str;
- int svr_argc;
-
- str = ve_sure_string (svr->command);
- svr_command = NULL;
- g_shell_parse_argv (str, &svr_argc, &svr_command, NULL);
-
- g_shell_parse_argv (disp->command, &argc, &argv, NULL);
-
- if (argv[0] == NULL || argv[1] == NULL) {
- g_strfreev (argv);
- argv = svr_command;
- argc = svr_argc;
- } else {
- char **old_argv = argv;
- argv = vector_merge (svr_command,
- svr_argc,
- &old_argv[1],
- argc);
- g_strfreev (svr_command);
- g_strfreev (old_argv);
-
- argc += svr_argc;
- }
-
- if (resolve_flags) {
- /* Setup the handled function */
- disp->handled = svr->handled;
- /* never make use_chooser FALSE,
- it may have been set temporarily for
- us by the master */
- if (svr->chooser)
- disp->use_chooser = TRUE;
- disp->priority = svr->priority;
- }
- }
- } else {
- g_shell_parse_argv (disp->command, &argc, &argv, NULL);
- }
-
- for (len = 0; argv != NULL && argv[len] != NULL; len++) {
- char *arg = argv[len];
- /* HACK! Not to add vt argument to servers that already force
- * allocation. Mostly for backwards compat only */
- if (strncmp (arg, "vt", 2) == 0 &&
- isdigit (arg[2]) &&
- (arg[3] == '\0' ||
- (isdigit (arg[3]) && arg[4] == '\0')))
- gotvtarg = TRUE;
- if (strcmp (arg, "-query") == 0 ||
- strcmp (arg, "-indirect") == 0)
- query_in_arglist = TRUE;
- }
-
- argv = g_renew (char *, argv, len + 10);
- /* shift args down one */
- for (i = len - 1; i >= 1; i--) {
- argv[i+1] = argv[i];
- }
- /* server number is the FIRST argument, before any others */
- argv[1] = g_strdup (disp->name);
- len++;
-
- if (disp->authfile != NULL) {
- argv[len++] = g_strdup ("-auth");
- argv[len++] = g_strdup (disp->authfile);
- }
-
- if (resolve_flags && disp->chosen_hostname) {
- /* this display is NOT handled */
- disp->handled = FALSE;
- /* never ever ever use chooser here */
- disp->use_chooser = FALSE;
- disp->priority = 0;
- /* run just one session */
- argv[len++] = g_strdup ("-terminate");
- argv[len++] = g_strdup ("-query");
- argv[len++] = g_strdup (disp->chosen_hostname);
- query_in_arglist = TRUE;
- }
-
- if (resolve_flags && gdm_daemon_config_get_value_bool (GDM_KEY_DISALLOW_TCP) && ! query_in_arglist) {
- argv[len++] = g_strdup ("-nolisten");
- argv[len++] = g_strdup ("tcp");
- d->tcp_disallowed = TRUE;
- }
-
- if (vtarg != NULL &&
- ! gotvtarg) {
- argv[len++] = g_strdup (vtarg);
- }
-
- argv[len++] = NULL;
-
- *argvp = argv;
- *argcp = len;
-
- g_free (bin);
-
- return TRUE;
-}
-
-/**
- * gdm_server_spawn:
- * @disp: Pointer to a GdmDisplay structure
- *
- * forks an actual X server process
- *
- * Note that we can only use d->handled once we call this function
- * since otherwise the server might not yet be looked up yet.
- */
-
-static void
-gdm_server_spawn (GdmDisplay *d, const char *vtarg)
-{
- struct sigaction ign_signal;
- sigset_t mask;
- int argc;
- gchar **argv = NULL;
- char *logfile;
- int logfd;
- char *command;
- pid_t pid;
-
- if (d == NULL ||
- ve_string_empty (d->command)) {
- return;
- }
-
- d->servstat = SERVER_PENDING;
-
- gdm_sigchld_block_push ();
-
- /* eek, some previous copy, just wipe it */
- if (d->servpid > 0) {
- pid_t pid = d->servpid;
- d->servpid = 0;
- if (pid > 1 &&
- kill (pid, SIGTERM) == 0)
- ve_waitpid_no_signal (pid, NULL, 0);
- }
-
- /* Figure out the server command */
- argv = NULL;
- argc = 0;
- gdm_server_resolve_command_line (d,
- TRUE /* resolve flags */,
- vtarg,
- &argc,
- &argv);
-
- /* Do not support additional session arguments with Xnest. */
- if (d->type != TYPE_FLEXI_XNEST) {
- if (d->xserver_session_args)
- gdm_server_add_xserver_args (d, argv);
- }
-
- command = g_strjoinv (" ", argv);
-
- /* Fork into two processes. Parent remains the gdm process. Child
- * becomes the X server. */
-
- g_debug ("Forking X server process");
-
- gdm_sigterm_block_push ();
- pid = d->servpid = fork ();
- if (pid == 0)
- gdm_unset_signals ();
- gdm_sigterm_block_pop ();
- gdm_sigchld_block_pop ();
-
- switch (pid) {
-
- case 0:
- /* the pops whacked mask again */
- gdm_unset_signals ();
-
- gdm_log_shutdown ();
-
- /* close things */
- gdm_close_all_descriptors (0 /* from */, -1 /* except */, -1 /* except2 */);
-
- /* No error checking here - if it's messed the best response
- * is to ignore & try to continue */
- gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */
- gdm_open_dev_null (O_RDWR); /* open stdout - fd 1 */
- gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */
-
- gdm_log_init ();
-
- /* Rotate the X server logs */
- rotate_logs (d->name);
-
- /* Log all output from spawned programs to a file */
- logfile = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_LOG_DIR), d->name, ".log");
- VE_IGNORE_EINTR (g_unlink (logfile));
- VE_IGNORE_EINTR (logfd = open (logfile, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL, 0644));
-
- if (logfd != -1) {
- VE_IGNORE_EINTR (dup2 (logfd, 1));
- VE_IGNORE_EINTR (dup2 (logfd, 2));
- close (logfd);
- } else {
- gdm_error (_("%s: Could not open logfile for display %s!"),
- "gdm_server_spawn", d->name);
- }
-
- /* The X server expects USR1/TTIN/TTOU to be SIG_IGN */
- ign_signal.sa_handler = SIG_IGN;
- ign_signal.sa_flags = SA_RESTART;
- sigemptyset (&ign_signal.sa_mask);
-
- if (d->server_uid == 0) {
- /* only set this if we can actually listen */
- if (sigaction (SIGUSR1, &ign_signal, NULL) < 0) {
- gdm_error (_("%s: Error setting %s to %s"),
- "gdm_server_spawn", "USR1", "SIG_IGN");
- _exit (SERVER_ABORT);
- }
- }
- if (sigaction (SIGTTIN, &ign_signal, NULL) < 0) {
- gdm_error (_("%s: Error setting %s to %s"),
- "gdm_server_spawn", "TTIN", "SIG_IGN");
- _exit (SERVER_ABORT);
- }
- if (sigaction (SIGTTOU, &ign_signal, NULL) < 0) {
- gdm_error (_("%s: Error setting %s to %s"),
- "gdm_server_spawn", "TTOU", "SIG_IGN");
- _exit (SERVER_ABORT);
- }
-
- /* And HUP and TERM are at SIG_DFL from gdm_unset_signals,
- we also have an empty mask and all that fun stuff */
-
- /* unblock signals (especially HUP/TERM/USR1) so that we
- * can control the X server */
- sigemptyset (&mask);
- sigprocmask (SIG_SETMASK, &mask, NULL);
-
- if (SERVER_IS_PROXY (d)) {
- gboolean add_display = TRUE;
-
- g_unsetenv ("DISPLAY");
- if (d->parent_auth_file != NULL)
- g_setenv ("XAUTHORITY", d->parent_auth_file, TRUE);
- else
- g_unsetenv ("XAUTHORITY");
-
- if (d->type == TYPE_FLEXI_XNEST) {
- char *font_path = NULL;
- /* Add -fp with the current font path, but only if not
- * already among the arguments */
- if (strstr (command, "-fp") == NULL)
- font_path = get_font_path (d->parent_disp);
- if (font_path != NULL) {
- argv = g_renew (char *, argv, argc + 2);
- argv[argc++] = "-fp";
- argv[argc++] = font_path;
- command = g_strconcat (command, " -fp ",
- font_path, NULL);
- }
- add_display = FALSE;
- }
-
- /*
- * Set the DISPLAY environment variable when calling
- * nested server since some Xnest commands like Xephyr
- * do not support the -display argument.
- */
- if (add_display == TRUE) {
- argv = g_renew (char *, argv, argc + 3);
- argv[argc++] = "-display";
- argv[argc++] = d->parent_disp;
- argv[argc++] = NULL;
- command = g_strconcat (command, " -display ",
- d->parent_disp, NULL);
- } else {
- argv = g_renew (char *, argv, argc + 1);
- argv[argc++] = NULL;
- g_setenv ("DISPLAY", d->parent_disp, TRUE);
- }
- }
-
- if (argv[0] == NULL) {
- gdm_error (_("%s: Empty server command for display %s"),
- "gdm_server_spawn",
- d->name);
- _exit (SERVER_ABORT);
- }
-
- gdm_debug ("gdm_server_spawn: '%s'", command);
-
- if (d->priority != 0) {
- if (setpriority (PRIO_PROCESS, 0, d->priority)) {
- gdm_error (_("%s: Server priority couldn't be set to %d: %s"),
- "gdm_server_spawn", d->priority,
- strerror (errno));
- }
- }
-
- setpgid (0, 0);
-
- if (d->server_uid != 0) {
- struct passwd *pwent;
- pwent = getpwuid (d->server_uid);
- if (pwent == NULL) {
- gdm_error (_("%s: Server was to be spawned by uid %d but "
- "that user doesn't exist"),
- "gdm_server_spawn",
- (int)d->server_uid);
- _exit (SERVER_ABORT);
- }
- if (pwent->pw_dir != NULL &&
- g_file_test (pwent->pw_dir, G_FILE_TEST_EXISTS))
- g_setenv ("HOME", pwent->pw_dir, TRUE);
- else
- g_setenv ("HOME", "/", TRUE); /* Hack */
- g_setenv ("SHELL", pwent->pw_shell, TRUE);
- g_unsetenv ("MAIL");
-
- if (setgid (pwent->pw_gid) < 0) {
- gdm_error (_("%s: Couldn't set groupid to %d"),
- "gdm_server_spawn", (int)pwent->pw_gid);
- _exit (SERVER_ABORT);
- }
-
- if (initgroups (pwent->pw_name, pwent->pw_gid) < 0) {
- gdm_error (_("%s: initgroups () failed for %s"),
- "gdm_server_spawn", pwent->pw_name);
- _exit (SERVER_ABORT);
- }
-
- if (setuid (d->server_uid) < 0) {
- gdm_error (_("%s: Couldn't set userid to %d"),
- "gdm_server_spawn", (int)d->server_uid);
- _exit (SERVER_ABORT);
- }
- } else {
- gid_t groups[1] = { 0 };
- if (setgid (0) < 0) {
- gdm_error (_("%s: Couldn't set groupid to 0"),
- "gdm_server_spawn");
- /* Don't error out, it's not fatal, if it fails we'll
- * just still be */
- }
- /* this will get rid of any suplementary groups etc... */
- setgroups (1, groups);
- }
-
-#if sun
- {
- /* Remove old communication pipe, if present */
- char old_pipe[MAXPATHLEN];
-
- sprintf (old_pipe, "%s/%d", SDTLOGIN_DIR, d->name);
- g_unlink (old_pipe);
- }
-#endif
-
- VE_IGNORE_EINTR (execv (argv[0], argv));
-
- gdm_fdprintf (2, "GDM: Xserver not found: %s\n"
- "Error: Command could not be executed!\n"
- "Please install the X server or correct "
- "GDM configuration and restart GDM.",
- command);
-
- gdm_error (_("%s: Xserver not found: %s"),
- "gdm_server_spawn", command);
-
- _exit (SERVER_ABORT);
-
- case -1:
- g_strfreev (argv);
- g_free (command);
- gdm_error (_("%s: Can't fork Xserver process!"),
- "gdm_server_spawn");
- d->servpid = 0;
- d->servstat = SERVER_DEAD;
-
- break;
-
- default:
- g_strfreev (argv);
- g_free (command);
- gdm_debug ("%s: Forked server on pid %d",
- "gdm_server_spawn", (int)pid);
- break;
- }
-}
-
-/**
- * gdm_server_usr1_handler:
- * @sig: Signal value
- *
- * Received when the server is ready to accept connections
- */
-
-static void
-gdm_server_usr1_handler (gint sig)
-{
- gdm_in_signal++;
-
- d->servstat = SERVER_RUNNING; /* Server ready to accept connections */
- d->starttime = time (NULL);
-
- server_signal_notified = TRUE;
- /* this will quit the select */
- VE_IGNORE_EINTR (write (server_signal_pipe[1], "Yay!", 4));
-
- gdm_in_signal--;
-}
-
-
-/**
- * gdm_server_child_handler:
- * @sig: Signal value
- *
- * Received when server died during startup
- */
-
-static void
-gdm_server_child_handler (int signal)
-{
- gdm_in_signal++;
-
- /* go to the main child handler */
- gdm_slave_child_handler (signal);
-
- /* this will quit the select */
- VE_IGNORE_EINTR (write (server_signal_pipe[1], "Yay!", 4));
-
- gdm_in_signal--;
-}
-
-
-void
-gdm_server_whack_clients (Display *dsp)
-{
- int i, screen_count;
- int (* old_xerror_handler) (Display *, XErrorEvent *);
-
- if (dsp == NULL)
- return;
-
- old_xerror_handler = XSetErrorHandler (ignore_xerror_handler);
-
- XGrabServer (dsp);
-
- screen_count = ScreenCount (dsp);
-
- for (i = 0; i < screen_count; i++) {
- Window root_ret, parent_ret;
- Window *childs = NULL;
- unsigned int childs_count = 0;
- Window root = RootWindow (dsp, i);
-
- while (XQueryTree (dsp, root, &root_ret, &parent_ret,
- &childs, &childs_count) &&
- childs_count > 0) {
- int ii;
-
- for (ii = 0; ii < childs_count; ii++) {
- XKillClient (dsp, childs[ii]);
- }
-
- XFree (childs);
- }
- }
-
- XUngrabServer (dsp);
-
- XSync (dsp, False);
- XSetErrorHandler (old_xerror_handler);
-}
-
-static char *
-get_font_path (const char *display)
-{
- Display *disp;
- char **font_path;
- int n_fonts;
- int i;
- GString *gs;
-
- disp = XOpenDisplay (display);
- if (disp == NULL)
- return NULL;
-
- font_path = XGetFontPath (disp, &n_fonts);
- if (font_path == NULL) {
- XCloseDisplay (disp);
- return NULL;
- }
-
- gs = g_string_new (NULL);
- for (i = 0; i < n_fonts; i++) {
- if (i != 0)
- g_string_append_c (gs, ',');
-
- if (gdm_daemon_config_get_value_bool (GDM_KEY_XNEST_UNSCALED_FONT_PATH) == TRUE)
- g_string_append (gs, font_path[i]);
- else {
- gchar *unscaled_ptr = NULL;
-
- /*
- * When using Xsun Xnest, it doesn't support the
- * ":unscaled" suffix in fontpath entries, so strip it.
- */
- unscaled_ptr = g_strrstr (font_path[i], ":unscaled");
- if (unscaled_ptr != NULL) {
- gchar *temp_string;
-
- temp_string = g_strndup (font_path[i],
- strlen (font_path[i]) -
- strlen (":unscaled"));
-
-gdm_debug ("font_path[i] is %s, temp_string is %s", font_path[i], temp_string);
- g_string_append (gs, temp_string);
- g_free (temp_string);
- } else {
-gdm_debug ("font_path[i] is %s", font_path[i]);
- g_string_append (gs, font_path[i]);
- }
- }
- }
-
- XFreeFontPath (font_path);
-
- XCloseDisplay (disp);
-
- return g_string_free (gs, FALSE);
-}
-
-/* EOF */