Home | History | Annotate | Line # | Download | only in xdm
      1 /*
      2 
      3 Copyright 1988, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included
     12 in all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     17 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 OTHER DEALINGS IN THE SOFTWARE.
     21 
     22 Except as contained in this notice, the name of The Open Group shall
     23 not be used in advertising or otherwise to promote the sale, use or
     24 other dealings in this Software without prior written authorization
     25 from The Open Group.
     26 
     27 */
     28 
     29 /*
     30  * xdm - display manager daemon
     31  * Author:  Keith Packard, MIT X Consortium
     32  *
     33  * display manager
     34  */
     35 
     36 #include	"dm.h"
     37 #include	"dm_auth.h"
     38 #include	"dm_error.h"
     39 
     40 #include	<stdio.h>
     41 #ifdef X_POSIX_C_SOURCE
     42 # define _POSIX_C_SOURCE X_POSIX_C_SOURCE
     43 # include <signal.h>
     44 # undef _POSIX_C_SOURCE
     45 #else
     46 # if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
     47 #  include <signal.h>
     48 # else
     49 #  define _POSIX_SOURCE
     50 #  include <signal.h>
     51 #  undef _POSIX_SOURCE
     52 # endif
     53 #endif
     54 #ifdef __NetBSD__
     55 # include <sys/param.h>
     56 #endif
     57 
     58 #ifndef sigmask
     59 # define sigmask(m)  (1 << ((m - 1)))
     60 #endif
     61 
     62 #include	<sys/stat.h>
     63 #include	<errno.h>
     64 #include	<X11/Xfuncproto.h>
     65 #include	<X11/Xatom.h>
     66 #include	<stdarg.h>
     67 #include	<stdint.h>
     68 
     69 #ifndef F_TLOCK
     70 # ifndef X_NOT_POSIX
     71 #  include	<unistd.h>
     72 # endif
     73 #endif
     74 
     75 #if defined(HAVE_OPENLOG) && defined(HAVE_SYSLOG_H)
     76 # define USE_SYSLOG
     77 # include <syslog.h>
     78 # ifndef LOG_AUTHPRIV
     79 #  define LOG_AUTHPRIV LOG_AUTH
     80 # endif
     81 # ifndef LOG_PID
     82 #  define LOG_PID 0
     83 # endif
     84 #endif
     85 
     86 static void	StopAll (int n), RescanNotify (int n);
     87 static void	RescanServers (void);
     88 static void	RestartDisplay (struct display *d, int forceReserver);
     89 static void	ScanServers (void);
     90 static void	SetAccessFileTime (void);
     91 static void	SetConfigFileTime (void);
     92 static void	StartDisplays (void);
     93 static void	TerminateProcess (pid_t pid, int signal);
     94 
     95 volatile int	Rescan;
     96 static long	ServersModTime, ConfigModTime, AccessFileModTime;
     97 
     98 int nofork_session = 0;
     99 
    100 #ifndef NOXDMTITLE
    101 static char *Title;
    102 static int TitleLen;
    103 #endif
    104 
    105 static void ChildNotify (int n);
    106 
    107 static long StorePid (void);
    108 static void RemovePid (void);
    109 
    110 static pid_t parent_pid = -1; 	/* PID of parent xdm process */
    111 
    112 int
    113 main (int argc, char **argv)
    114 {
    115     int	oldpid;
    116     mode_t oldumask;
    117     char cmdbuf[1024];
    118 
    119     /* make sure at least world write access is disabled */
    120     if (((oldumask = umask(022)) & 002) == 002)
    121 	(void) umask (oldumask);
    122 #ifndef NOXDMTITLE
    123     if (argc > 0) {
    124         Title = argv[0];
    125         TitleLen = (argv[argc - 1] + strlen(argv[argc - 1])) - Title;
    126     } else {
    127         Title = NULL;
    128         TitleLen = 0;
    129     }
    130 #endif
    131 
    132     /*
    133      * Step 1 - load configuration parameters
    134      */
    135     InitResources (argc, argv);
    136     SetConfigFileTime ();
    137     LoadDMResources ();
    138     /*
    139      * Only allow root to run in non-debug mode to avoid problems
    140      */
    141     if (debugLevel == 0 && getuid() != 0)
    142     {
    143 	fprintf (stderr, "Only root wants to run %s\n", argv[0]);
    144 	exit (1);
    145     }
    146     if (debugLevel == 0 && daemonMode)
    147 	BecomeDaemon ();
    148     if (debugLevel == 0)
    149 	InitErrorLog ();
    150     if (debugLevel >= 10)
    151 	nofork_session = 1;
    152     /* SUPPRESS 560 */
    153     if ((oldpid = StorePid ()))
    154     {
    155 	if (oldpid == -1)
    156 	    LogError ("Can't create/lock pid file %s\n", pidFile);
    157 	else
    158 	    LogError ("Can't lock pid file %s, another xdm is running (pid %d)\n",
    159 		 pidFile, oldpid);
    160 	exit (1);
    161     }
    162 
    163     LogInfo ("Starting %s\n", PACKAGE_STRING);
    164     if (debugLevel > 0)
    165     {
    166 	Debug("%s was built with these options:\n", PACKAGE_STRING);
    167 #ifdef USE_PAM
    168 	Debug(" - USE_PAM = yes\n");
    169 #else
    170 	Debug(" - USE_PAM = no\n");
    171 #endif
    172 #ifdef USE_SELINUX
    173 	Debug(" - USE_SELINUX = yes\n");
    174 #else
    175 	Debug(" - USE_SELINUX = no\n");
    176 #endif
    177 #ifdef USE_SYSTEMD_DAEMON
    178 	Debug(" - USE_SYSTEMD_DAEMON = yes\n");
    179 #else
    180 	Debug(" - USE_SYSTEMD_DAEMON = no\n");
    181 #endif
    182 #ifdef USE_XFT
    183 	Debug(" - USE_XFT = yes\n");
    184 #else
    185 	Debug(" - USE_XFT = no\n");
    186 #endif
    187 #ifdef USE_XINERAMA
    188 	Debug(" - USE_XINERAMA = yes\n");
    189 #else
    190 	Debug(" - USE_XINERAMA = no\n");
    191 #endif
    192 #ifdef XPM
    193 	Debug(" - XPM = yes\n");
    194 #else
    195 	Debug(" - XPM = no\n");
    196 #endif
    197 #ifdef HAVE_ARC4RANDOM
    198 	Debug(" - Random number source: arc4random()\n\n");
    199 #elif defined(DEV_RANDOM)
    200 	Debug(" - Random number source: %s\n", DEV_RANDOM);
    201 #else
    202 	Debug(" - Random number source: built-in PRNG\n\n");
    203 #endif
    204     }
    205 
    206     if (atexit (RemovePid))
    207 	LogError ("could not register RemovePid() with atexit()\n");
    208 
    209     if (nofork_session == 0) {
    210 	/* Clean up any old Authorization files */
    211 	/* AUD: all good? */
    212 	snprintf(cmdbuf, sizeof(cmdbuf), "/bin/rm -f %s/authdir/authfiles/A*", authDir);
    213 	system(cmdbuf);
    214     }
    215 #if!defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM)
    216     AddOtherEntropy ();
    217 #endif
    218 #ifdef XDMCP
    219     init_session_id ();
    220     CreateWellKnownSockets ();
    221 #else
    222     Debug ("xdm: not compiled for XDMCP\n");
    223 #endif
    224     parent_pid = getpid ();
    225     (void) Signal (SIGTERM, StopAll);
    226     (void) Signal (SIGINT, StopAll);
    227     /*
    228      * Step 2 - Read /etc/Xservers and set up
    229      *	    the socket.
    230      *
    231      *	    Keep a sub-daemon running
    232      *	    for each entry
    233      */
    234     SetAccessFileTime ();
    235 #ifdef XDMCP
    236     ScanAccessDatabase ();
    237     UpdateListenSockets ();
    238 #endif
    239     ScanServers ();
    240     StartDisplays ();
    241 #if !defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM)
    242     AddOtherEntropy();
    243 #endif
    244     (void) Signal (SIGHUP, RescanNotify);
    245     (void) Signal (SIGCHLD, ChildNotify);
    246     Debug ("startup successful; entering main loop\n");
    247     while (
    248 #ifdef XDMCP
    249 	   AnyWellKnownSockets() ||
    250 #endif
    251 	   AnyDisplaysLeft ())
    252     {
    253 	if (Rescan)
    254 	{
    255 	    RescanServers ();
    256 	    Rescan = 0;
    257 	}
    258 #ifndef XDMCP
    259 	WaitForChild ();
    260 #else
    261 	WaitForSomething ();
    262 #endif
    263     }
    264     Debug ("Nothing left to do, exiting\n");
    265     LogInfo ("Exiting\n");
    266     exit(0);
    267     /*NOTREACHED*/
    268 }
    269 
    270 /* ARGSUSED */
    271 static void
    272 RescanNotify (int n)
    273 {
    274     int olderrno = errno;
    275 
    276     Debug ("Caught SIGHUP\n");
    277     Rescan = 1;
    278     errno = olderrno;
    279 }
    280 
    281 static void
    282 ScanServers (void)
    283 {
    284     char	lineBuf[10240];
    285     FILE	*serversFile;
    286     struct stat	statb;
    287     static DisplayType	acceptableTypes[] =
    288 	    { { Local, Permanent, FromFile },
    289 	      { Foreign, Permanent, FromFile },
    290 	    };
    291 
    292 #define NumTypes    (sizeof (acceptableTypes) / sizeof (acceptableTypes[0]))
    293 
    294     if (servers[0] == '/')
    295     {
    296 	serversFile = fopen (servers, "r");
    297 	if (serversFile == NULL)
    298 	{
    299 	    LogError ("cannot access servers file %s\n", servers);
    300 	    return;
    301 	}
    302 	if (ServersModTime == 0)
    303 	{
    304 	    fstat (fileno (serversFile), &statb);
    305 	    ServersModTime = statb.st_mtime;
    306 	}
    307 	while (fgets (lineBuf, sizeof (lineBuf)-1, serversFile))
    308 	{
    309 	    size_t len = strlen (lineBuf);
    310 	    if (len > 0) {
    311 		if (lineBuf[len-1] == '\n')
    312 		    lineBuf[len-1] = '\0';
    313 		ParseDisplay (lineBuf, acceptableTypes, NumTypes);
    314 	    }
    315 	}
    316 	fclose (serversFile);
    317     }
    318     else
    319     {
    320 	ParseDisplay (servers, acceptableTypes, NumTypes);
    321     }
    322 }
    323 
    324 static void
    325 MarkDisplay (struct display *d)
    326 {
    327     d->state = MissingEntry;
    328 }
    329 
    330 static void
    331 RescanServers (void)
    332 {
    333     Debug ("rescanning servers\n");
    334     LogInfo ("Rescanning both config and servers files\n");
    335     ForEachDisplay (MarkDisplay);
    336     SetConfigFileTime ();
    337     ReinitResources ();
    338     LoadDMResources ();
    339     ScanServers ();
    340     SetAccessFileTime ();
    341 #ifdef XDMCP
    342     ScanAccessDatabase ();
    343     UpdateListenSockets ();
    344 #endif
    345     StartDisplays ();
    346 }
    347 
    348 static void
    349 SetConfigFileTime (void)
    350 {
    351     struct stat	statb;
    352 
    353     if (stat (config, &statb) != -1)
    354 	ConfigModTime = statb.st_mtime;
    355 }
    356 
    357 static void
    358 SetAccessFileTime (void)
    359 {
    360     struct stat	statb;
    361 
    362     if (stat (accessFile, &statb) != -1)
    363 	AccessFileModTime = statb.st_mtime;
    364 }
    365 
    366 static void
    367 RescanIfMod (void)
    368 {
    369     struct stat	statb;
    370 
    371     if (config && stat (config, &statb) != -1)
    372     {
    373 	if (statb.st_mtime != ConfigModTime)
    374 	{
    375 	    Debug ("Config file %s has changed, rereading\n", config);
    376 	    LogInfo ("Rereading configuration file %s\n", config);
    377 	    ConfigModTime = statb.st_mtime;
    378 	    ReinitResources ();
    379 	    LoadDMResources ();
    380 	}
    381     }
    382     if (servers[0] == '/' && stat(servers, &statb) != -1)
    383     {
    384 	if (statb.st_mtime != ServersModTime)
    385 	{
    386 	    Debug ("Servers file %s has changed, rescanning\n", servers);
    387 	    LogInfo ("Rereading servers file %s\n", servers);
    388 	    ServersModTime = statb.st_mtime;
    389 	    ForEachDisplay (MarkDisplay);
    390 	    ScanServers ();
    391 	}
    392     }
    393 #ifdef XDMCP
    394     if (accessFile && accessFile[0] && stat (accessFile, &statb) != -1)
    395     {
    396 	if (statb.st_mtime != AccessFileModTime)
    397 	{
    398 	    Debug ("Access file %s has changed, rereading\n", accessFile);
    399 	    LogInfo ("Rereading access file %s\n", accessFile);
    400 	    AccessFileModTime = statb.st_mtime;
    401 	    ScanAccessDatabase ();
    402 	    UpdateListenSockets();
    403 	}
    404     }
    405 #endif
    406 }
    407 
    408 /*
    409  * catch a SIGTERM, kill all displays and exit
    410  */
    411 
    412 /* ARGSUSED */
    413 static void
    414 StopAll (int n)
    415 {
    416     int olderrno = errno;
    417 
    418     if (parent_pid != getpid())
    419     {
    420 	/*
    421 	 * We are a child xdm process that was killed by the
    422 	 * parent xdm before we were able to return from fork()
    423 	 * and remove this signal handler.
    424 	 *
    425 	 * See defect XWSog08655 for more information.
    426 	 */
    427 	Debug ("Child xdm caught SIGTERM before it removed that signal.\n");
    428 	(void) Signal (n, SIG_DFL);
    429 	TerminateProcess (getpid(), SIGTERM);
    430 	errno = olderrno;
    431 	return;
    432     }
    433     Debug ("Shutting down entire manager\n");
    434     LogInfo ("Shutting down\n");
    435 #ifdef XDMCP
    436     DestroyWellKnownSockets ();
    437 #endif
    438     ForEachDisplay (StopDisplay);
    439     errno = olderrno;
    440 }
    441 
    442 /*
    443  * notice that a child has died and may need another
    444  * sub-daemon started
    445  */
    446 
    447 int	ChildReady;
    448 
    449 /* ARGSUSED */
    450 static void
    451 ChildNotify (int n)
    452 {
    453     int olderrno = errno;
    454 
    455     ChildReady = 1;
    456     errno = olderrno;
    457 }
    458 
    459 void
    460 WaitForChild (void)
    461 {
    462     pid_t		pid;
    463     struct display	*d;
    464     waitType	status;
    465 #if !defined(X_NOT_POSIX)
    466     sigset_t mask, omask;
    467 #else
    468     int		omask;
    469 #endif
    470 
    471 # ifndef X_NOT_POSIX
    472     sigemptyset(&mask);
    473     sigaddset(&mask, SIGCHLD);
    474     sigaddset(&mask, SIGHUP);
    475     sigprocmask(SIG_BLOCK, &mask, &omask);
    476     Debug ("signals blocked\n");
    477 # else
    478     omask = sigblock (sigmask (SIGCHLD) | sigmask (SIGHUP));
    479     Debug ("signals blocked, mask was 0x%x\n", omask);
    480 # endif
    481     if (!ChildReady && !Rescan)
    482 # ifndef X_NOT_POSIX
    483 	sigsuspend(&omask);
    484 # else
    485 	sigpause (omask);
    486 # endif
    487     ChildReady = 0;
    488 # ifndef X_NOT_POSIX
    489     sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
    490 # else
    491     sigsetmask (omask);
    492 # endif
    493     while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
    494     {
    495 	Debug ("Manager wait returns pid: %d sig %d core %d code %d\n",
    496 	       pid, waitSig(status), waitCore(status), waitCode(status));
    497 	if (autoRescan)
    498 	    RescanIfMod ();
    499 	/* SUPPRESS 560 */
    500 	if ((d = FindDisplayByPid (pid))) {
    501 	    d->pid = -1;
    502 	    switch (waitVal (status)) {
    503 	    case UNMANAGE_DISPLAY:
    504 		Debug ("Display exited with UNMANAGE_DISPLAY\n");
    505 		StopDisplay (d);
    506 		break;
    507 	    case OBEYSESS_DISPLAY:
    508 		d->startTries = 0;
    509 		d->reservTries = 0;
    510 		Debug ("Display exited with OBEYSESS_DISPLAY\n");
    511 		if (d->displayType.lifetime != Permanent ||
    512 		    d->status == zombie)
    513 		    StopDisplay (d);
    514 		else
    515 		    RestartDisplay (d, FALSE);
    516 		break;
    517 	    case OPENFAILED_DISPLAY:
    518 		Debug ("Display exited with OPENFAILED_DISPLAY, try %d of %d\n",
    519 		       d->startTries, d->startAttempts);
    520 		LogError ("Display %s cannot be opened\n", d->name);
    521 		/*
    522 		 * no display connection was ever made, tell the
    523 		 * terminal that the open attempt failed
    524 		 */
    525 #ifdef XDMCP
    526 		if (d->displayType.origin == FromXDMCP)
    527 		    SendFailed (d, "Cannot open display");
    528 #endif
    529 		if (d->displayType.origin == FromXDMCP ||
    530 		    d->status == zombie ||
    531 		    ++d->startTries >= d->startAttempts)
    532 		{
    533 		    LogError ("Display %s is being disabled\n", d->name);
    534 		    StopDisplay (d);
    535 		}
    536 		else
    537 		{
    538 		    RestartDisplay (d, TRUE);
    539 		}
    540 		break;
    541 	    case RESERVER_DISPLAY:
    542 		d->startTries = 0;
    543 		Debug ("Display exited with RESERVER_DISPLAY\n");
    544 		if (d->displayType.origin == FromXDMCP || d->status == zombie)
    545 		    StopDisplay(d);
    546 		else {
    547 		  Time_t now;
    548 		  int crash;
    549 
    550 		  time(&now);
    551 		  crash = d->lastReserv &&
    552 		    ((now - d->lastReserv) < XDM_BROKEN_INTERVAL);
    553 		  Debug("time %lli %lli try %i of %i%s\n",
    554 			(long long)now, (long long)d->lastReserv,
    555 			d->reservTries, d->reservAttempts,
    556 			crash ? " crash" : "");
    557 
    558 		  if (!crash)
    559 		    d->reservTries = 0;
    560 
    561 		  if (crash && ++d->reservTries >= d->reservAttempts) {
    562 		    const char *msg =
    563 			"Server crash frequency too high: stopping display";
    564 		    Debug("%s %s\n", msg, d->name);
    565 		    LogError("%s %s\n", msg, d->name);
    566 #if !defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM)
    567 		    AddTimerEntropy();
    568 #endif
    569 		    /* For a local X server either:
    570 		     * 1. The server exit was returned by waitpid().  So
    571 		     *    serverPid==-1 => StopDisplay() calls RemoveDisplay()
    572 		     *
    573 		     * 2. The server is in zombie state or still running.  So
    574 		     *    serverPid>1 => StopDisplay()
    575 		     *                   a. sets status=zombie,
    576 		     *                   b. kills the server.
    577 		     *    The next waitpid() returns this zombie server pid
    578 		     *    and the 'case zombie:' below then calls
    579 		     *    RemoveDisplay().
    580 		     */
    581 		    StopDisplay(d);
    582 		  } else {
    583 		    RestartDisplay(d, TRUE);
    584 		  }
    585 		  d->lastReserv = now;
    586 		}
    587 		break;
    588 	    case waitCompose (SIGTERM,0,0):
    589 		Debug ("Display exited on SIGTERM, try %d of %d\n",
    590 			d->startTries, d->startAttempts);
    591 		if (d->displayType.origin == FromXDMCP ||
    592 		    d->status == zombie ||
    593 		    ++d->startTries >= d->startAttempts)
    594 		{
    595 		    /*
    596 		     * During normal xdm shutdown, killed local X servers
    597 		     * can be zombies; this is not an error.
    598 		     */
    599 		    if (d->status == zombie &&
    600 			(d->startTries < d->startAttempts))
    601 			LogInfo ("display %s is being disabled\n", d->name);
    602 		    else
    603 			LogError ("display %s is being disabled\n", d->name);
    604 		    StopDisplay(d);
    605 		} else
    606 		    RestartDisplay (d, TRUE);
    607 		break;
    608 	    case REMANAGE_DISPLAY:
    609 		d->startTries = 0;
    610 		Debug ("Display exited with REMANAGE_DISPLAY\n");
    611 		/*
    612 		 * XDMCP will restart the session if the display
    613 		 * requests it
    614 		 */
    615 		if (d->displayType.origin == FromXDMCP || d->status == zombie)
    616 		    StopDisplay(d);
    617 		else
    618 		    RestartDisplay (d, FALSE);
    619 		break;
    620 	    default:
    621 		Debug ("Display exited with unknown status %d\n", waitVal(status));
    622 		LogError ("Unknown session exit code %d from process %d\n",
    623 			  waitVal (status), pid);
    624 		StopDisplay (d);
    625 		break;
    626 	    }
    627 	}
    628 	/* SUPPRESS 560 */
    629 	else if ((d = FindDisplayByServerPid (pid)))
    630 	{
    631 	    d->serverPid = -1;
    632 	    switch (d->status)
    633 	    {
    634 	    case zombie:
    635 		Debug ("Zombie server reaped, removing display %s\n", d->name);
    636 		RemoveDisplay (d);
    637 		break;
    638 	    case phoenix:
    639 		Debug ("Phoenix server arises, restarting display %s\n", d->name);
    640 		d->status = notRunning;
    641 		break;
    642 	    case running:
    643 		Debug ("Server for display %s terminated unexpectedly, status %d %d\n", d->name, waitVal (status), status);
    644 		LogError ("Server for display %s terminated unexpectedly: %d\n", d->name, waitVal (status));
    645 		if (d->pid != -1)
    646 		{
    647 		    Debug ("Terminating session pid %d\n", d->pid);
    648 		    TerminateProcess (d->pid, SIGTERM);
    649 		}
    650 		break;
    651 	    case notRunning:
    652 		Debug ("Server exited for notRunning session on display %s\n", d->name);
    653 		break;
    654 	    }
    655 	}
    656 	else
    657 	{
    658 	    Debug ("Unknown child termination, status %d\n", waitVal (status));
    659 	}
    660     }
    661     StartDisplays ();
    662 }
    663 
    664 static void
    665 CheckDisplayStatus (struct display *d)
    666 {
    667     if (d->displayType.origin == FromFile)
    668     {
    669 	switch (d->state) {
    670 	case MissingEntry:
    671 	    StopDisplay (d);
    672 	    break;
    673 	case NewEntry:
    674 	    d->state = OldEntry;
    675 	case OldEntry:
    676 	    if (d->status == notRunning)
    677 		StartDisplay (d);
    678 	    break;
    679 	}
    680     }
    681 }
    682 
    683 static void
    684 StartDisplays (void)
    685 {
    686     ForEachDisplay (CheckDisplayStatus);
    687 }
    688 
    689 static void
    690 SetWindowPath(struct display *d)
    691 {
    692 	/* setting WINDOWPATH for clients */
    693 	Atom prop;
    694 	Atom actualtype;
    695 	int actualformat;
    696 	unsigned long nitems;
    697 	unsigned long bytes_after;
    698 	unsigned char *buf;
    699 	const char *windowpath;
    700 	char *newwindowpath;
    701 	unsigned long num;
    702 
    703 	prop = XInternAtom(d->dpy, "XFree86_VT", False);
    704 	if (prop == None) {
    705 		fprintf(stderr, "no XFree86_VT atom\n");
    706 		return;
    707 	}
    708 	if (XGetWindowProperty(d->dpy, DefaultRootWindow(d->dpy), prop, 0, 1,
    709 		False, AnyPropertyType, &actualtype, &actualformat,
    710 		&nitems, &bytes_after, &buf)) {
    711 		fprintf(stderr, "no XFree86_VT property\n");
    712 		return;
    713 	}
    714 	if (nitems != 1) {
    715 		fprintf(stderr, "%lu items in XFree86_VT property!\n", nitems);
    716 		XFree(buf);
    717 		return;
    718 	}
    719 	switch (actualtype) {
    720 	case XA_CARDINAL:
    721 	case XA_INTEGER:
    722 	case XA_WINDOW:
    723 		switch (actualformat) {
    724 		case  8:
    725 			num = (*(uint8_t  *)(void *)buf);
    726 			break;
    727 		case 16:
    728 			num = (*(uint16_t *)(void *)buf);
    729 			break;
    730 		case 32:
    731 			num = (*(uint32_t *)(void *)buf);
    732 			break;
    733 		default:
    734 			fprintf(stderr, "format %d in XFree86_VT property!\n", actualformat);
    735 			XFree(buf);
    736 			return;
    737 		}
    738 		break;
    739 	default:
    740 		fprintf(stderr, "type %lx in XFree86_VT property!\n", actualtype);
    741 		XFree(buf);
    742 		return;
    743 	}
    744 	XFree(buf);
    745 	windowpath = getenv("WINDOWPATH");
    746 	if (!windowpath) {
    747 		asprintf(&newwindowpath, "%lu", num);
    748 	} else {
    749 		asprintf(&newwindowpath, "%s:%lu", windowpath, num);
    750 	}
    751 	free(d->windowPath);
    752 	d->windowPath = newwindowpath;
    753 }
    754 
    755 void
    756 StartDisplay (struct display *d)
    757 {
    758     pid_t	pid;
    759 
    760     Debug ("StartDisplay %s\n", d->name);
    761     LogInfo ("Starting X server on %s\n", d->name);
    762     LoadServerResources (d);
    763     if (d->displayType.location == Local)
    764     {
    765 	/* don't bother pinging local displays; we'll
    766 	 * certainly notice when they exit
    767 	 */
    768 	d->pingInterval = 0;
    769 	if (d->authorize)
    770 	{
    771 	    Debug ("SetLocalAuthorization %s, auth %s\n",
    772 		    d->name, d->authNames[0]);
    773 	    SetLocalAuthorization (d);
    774 	    /*
    775 	     * reset the server after writing the authorization information
    776 	     * to make it read the file (for compatibility with old
    777 	     * servers which read auth file only on reset instead of
    778 	     * at first connection)
    779 	     */
    780 	    if (d->serverPid != -1 && d->resetForAuth && d->resetSignal)
    781 		kill (d->serverPid, d->resetSignal);
    782 	}
    783 	if (d->serverPid == -1 && !StartServer (d))
    784 	{
    785 	    LogError ("Server for display %s can't be started, session disabled\n", d->name);
    786 	    RemoveDisplay (d);
    787 	    return;
    788 	}
    789     }
    790     else
    791     {
    792 	/* this will only happen when using XDMCP */
    793 	if (d->authorizations)
    794 	    SaveServerAuthorizations (d, d->authorizations, d->authNum);
    795     }
    796     if (!nofork_session)
    797 	pid = fork ();
    798     else
    799 	pid = 0;
    800     switch (pid)
    801     {
    802     case 0:
    803 	if (!nofork_session) {
    804 	    CleanUpChild ();
    805 	    (void) Signal (SIGPIPE, SIG_IGN);
    806 	}
    807 #ifdef USE_SYSLOG
    808 	openlog("xdm", LOG_PID, LOG_AUTHPRIV);
    809 #endif
    810 	LoadSessionResources (d);
    811 	SetAuthorization (d);
    812 	if (!WaitForServer (d))
    813 	    exit (OPENFAILED_DISPLAY);
    814 	SetWindowPath(d);
    815 #ifdef XDMCP
    816 	if (d->useChooser)
    817 	    RunChooser (d);
    818 	else
    819 #endif
    820 	    ManageSession (d);
    821 	exit (REMANAGE_DISPLAY);
    822     case -1:
    823 	break;
    824     default:
    825 	Debug ("pid: %d\n", pid);
    826 	d->pid = pid;
    827 	d->status = running;
    828 	break;
    829     }
    830 }
    831 
    832 static void
    833 TerminateProcess (pid_t pid, int signal)
    834 {
    835     kill (pid, signal);
    836 #ifdef SIGCONT
    837     kill (pid, SIGCONT);
    838 #endif
    839 }
    840 
    841 /*
    842  * transition from running to zombie or deleted
    843  */
    844 
    845 void
    846 StopDisplay (struct display *d)
    847 {
    848     if (d->serverPid != -1)
    849 	d->status = zombie; /* be careful about race conditions */
    850     if (d->pid != -1)
    851 	TerminateProcess (d->pid, SIGTERM);
    852     if (d->serverPid != -1)
    853 	TerminateProcess (d->serverPid, d->termSignal);
    854     else
    855 	RemoveDisplay (d);
    856 }
    857 
    858 /*
    859  * transition from running to phoenix or notRunning
    860  */
    861 
    862 static void
    863 RestartDisplay (struct display *d, int forceReserver)
    864 {
    865     if (d->serverPid != -1 && (forceReserver || d->terminateServer))
    866     {
    867 	TerminateProcess (d->serverPid, d->termSignal);
    868 	d->status = phoenix;
    869     }
    870     else
    871     {
    872 	d->status = notRunning;
    873     }
    874 }
    875 
    876 static FD_TYPE	CloseMask;
    877 static int	max;
    878 
    879 void
    880 RegisterCloseOnFork (int fd)
    881 {
    882     FD_SET (fd, &CloseMask);
    883     if (fd > max)
    884 	max = fd;
    885 }
    886 
    887 void
    888 ClearCloseOnFork (int fd)
    889 {
    890     FD_CLR (fd, &CloseMask);
    891     if (fd == max) {
    892 	while (--fd >= 0)
    893 	    if (FD_ISSET (fd, &CloseMask))
    894 		break;
    895 	max = fd;
    896     }
    897 }
    898 
    899 void
    900 CloseOnFork (void)
    901 {
    902     int	fd;
    903 
    904     for (fd = 0; fd <= max; fd++)
    905 	if (FD_ISSET (fd, &CloseMask))
    906 	{
    907 	    close (fd);
    908         }
    909     FD_ZERO (&CloseMask);
    910     max = 0;
    911 }
    912 
    913 static int  pidFd;
    914 static FILE *pidFilePtr;
    915 
    916 /*
    917  * Create and populate file storing xdm's process ID.
    918  */
    919 static long
    920 StorePid (void)
    921 {
    922     long	oldpid;
    923     char	pidstr[11]; /* enough space for a 32-bit pid plus \0 */
    924     size_t	pidstrlen;
    925 
    926     if (pidFile[0] != '\0')
    927     {
    928 	Debug ("storing process ID in %s\n", pidFile);
    929 	pidFd = open (pidFile, O_WRONLY|O_CREAT|O_EXCL, 0666);
    930 	if (pidFd == -1 && errno == ENOENT)
    931 	{
    932 	    /* Make sure directory exists if needed
    933 	       Allows setting pidDir to /var/run/xdm
    934 	     */
    935 	    char *pidDir = strdup(pidFile);
    936 
    937 	    if (pidDir != NULL)
    938 	    {
    939 		char *p = strrchr(pidDir, '/');
    940 		int r;
    941 
    942 		if ((p != NULL) && (p != pidDir)) {
    943 		    *p = '\0';
    944 		}
    945 		r = mkdir(pidDir, 0755);
    946 		if ( (r < 0) && (errno != EEXIST) ) {
    947 		     LogError ("process-id directory %s cannot be created\n",
    948 			       pidDir);
    949 		}
    950 		free(pidDir);
    951 	    }
    952 
    953 	    pidFd = open (pidFile, O_WRONLY|O_CREAT|O_EXCL, 0666);
    954 	}
    955 	if (pidFd == -1)
    956 	{
    957 	    if (errno == EEXIST)
    958 	    {
    959 		/* pidFile already exists; see if we can open it */
    960 		pidFilePtr = fopen (pidFile, "r");
    961 		if (pidFilePtr == NULL)
    962 		{
    963 		    LogError ("cannot open process ID file %s for reading: "
    964 			      "%s\n", pidFile, _SysErrorMsg (errno));
    965 		    return -1;
    966 		}
    967 		if (fscanf (pidFilePtr, "%ld\n", &oldpid) != 1)
    968 		{
    969 		    LogError ("existing process ID file %s empty or contains "
    970 			      "garbage\n", pidFile);
    971 		    oldpid = -1;
    972 		}
    973 		fclose (pidFilePtr);
    974 		return oldpid;
    975 	    }
    976 		else
    977 	    {
    978 		LogError ("cannot fdopen process ID file %s for writing: "
    979 			  "%s\n", pidFile, _SysErrorMsg (errno));
    980 		return -1;
    981 	    }
    982 	}
    983 	if ((pidFilePtr = fdopen (pidFd, "w")) == NULL)
    984 	{
    985 	    LogError ("cannot open process ID file %s for writing: %s\n",
    986 		      pidFile, _SysErrorMsg (errno));
    987 	    return -1;
    988 	}
    989 	(void) snprintf (pidstr, 11, "%ld", (long) getpid ());
    990 	pidstrlen = strlen (pidstr);
    991 	if (fprintf (pidFilePtr, "%s\n", pidstr) != ( pidstrlen + 1))
    992 	{
    993 	    LogError ("cannot write to process ID file %s: %s\n", pidFile,
    994 		      _SysErrorMsg (errno));
    995 	    return -1;
    996 	}
    997 	(void) fflush (pidFilePtr);
    998 	(void) fclose (pidFilePtr);
    999     }
   1000     return 0;
   1001 }
   1002 
   1003 /*
   1004  * Remove process ID file.  This function is registered with atexit().
   1005  */
   1006 static void
   1007 RemovePid (void)
   1008 {
   1009     if (parent_pid != getpid())
   1010 	return;
   1011 
   1012     Debug ("unlinking process ID file %s\n", pidFile);
   1013     if (unlink (pidFile))
   1014 	if (errno != ENOENT)
   1015 	    LogError ("cannot remove process ID file %s: %s\n", pidFile,
   1016 		      _SysErrorMsg (errno));
   1017 }
   1018 
   1019 #ifndef HAVE_SETPROCTITLE
   1020 void SetTitle (char *name, ...)
   1021 {
   1022 # ifndef NOXDMTITLE
   1023     char	*p = Title;
   1024     int	left = TitleLen;
   1025     char	*s;
   1026     va_list	args;
   1027 
   1028     if (p != NULL && left > 0) {
   1029         va_start(args,name);
   1030         *p++ = '-';
   1031         --left;
   1032         s = name;
   1033         while (s)
   1034         {
   1035             while (*s && left > 0)
   1036             {
   1037                 *p++ = *s++;
   1038                 left--;
   1039             }
   1040             s = va_arg (args, char *);
   1041         }
   1042         while (left > 0)
   1043         {
   1044             *p++ = ' ';
   1045             --left;
   1046         }
   1047         va_end(args);
   1048     }
   1049 # endif
   1050 }
   1051 #endif
   1052