home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xdm / dm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-31  |  15.8 KB  |  766 lines

  1. /*
  2.  * xdm - display manager daemon
  3.  *
  4.  * $XConsortium: dm.c,v 1.64 91/07/31 16:55:01 keith Exp $
  5.  *
  6.  * Copyright 1988 Massachusetts Institute of Technology
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies and that both that
  11.  * copyright notice and this permission notice appear in supporting
  12.  * documentation, and that the name of M.I.T. not be used in advertising or
  13.  * publicity pertaining to distribution of the software without specific,
  14.  * written prior permission.  M.I.T. makes no representations about the
  15.  * suitability of this software for any purpose.  It is provided "as is"
  16.  * without express or implied warranty.
  17.  *
  18.  * Author:  Keith Packard, MIT X Consortium
  19.  */
  20.  
  21. /*
  22.  * display manager
  23.  */
  24.  
  25. # include    "dm.h"
  26.  
  27. # include    <stdio.h>
  28. #if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
  29. # include    <signal.h>
  30. #else
  31. #define _POSIX_SOURCE
  32. # include    <signal.h>
  33. #undef _POSIX_SOURCE
  34. #endif
  35.  
  36. #ifndef sigmask
  37. #define sigmask(m)  (1 << ((m - 1)))
  38. #endif
  39.  
  40. # include    <sys/stat.h>
  41. # include    <errno.h>
  42. # include    <X11/Xfuncproto.h>
  43. #if NeedVarargsPrototypes
  44. # include <stdarg.h>
  45. # define Va_start(a,b) va_start(a,b)
  46. #else
  47. # include <varargs.h>
  48. # define Va_start(a,b) va_start(a)
  49. #endif
  50.  
  51. #ifndef F_TLOCK
  52. #ifndef X_NOT_POSIX
  53. # include    <unistd.h>
  54. #endif
  55. #endif
  56.  
  57. extern int    errno;
  58.  
  59. #ifdef SVR4
  60. extern FILE    *fdopen();
  61. #endif
  62.  
  63. static void    RescanServers ();
  64. int        Rescan;
  65. static long    ServersModTime, ConfigModTime, AccessFileModTime;
  66. static SIGVAL    StopAll (), RescanNotify ();
  67. void        StopDisplay ();
  68. static void    RestartDisplay ();
  69.  
  70. #ifndef NOXDMTITLE
  71. static char *Title;
  72. static int TitleLen;
  73. #endif
  74.  
  75. #ifndef UNRELIABLE_SIGNALS
  76. static SIGVAL ChildNotify ();
  77. #endif
  78.  
  79. main (argc, argv)
  80. int    argc;
  81. char    **argv;
  82. {
  83.     int    oldpid, oldumask;
  84.  
  85.     /* make sure at least world write access is disabled */
  86.     if (((oldumask = umask(022)) & 002) == 002)
  87.     (void) umask (oldumask);
  88. #ifndef NOXDMTITLE
  89.     Title = argv[0];
  90.     TitleLen = (argv[argc - 1] + strlen(argv[argc - 1])) - Title;
  91. #endif
  92.  
  93.     /*
  94.      * Step 1 - load configuration parameters
  95.      */
  96.     InitResources (argc, argv);
  97.     SetConfigFileTime ();
  98.     LoadDMResources ();
  99.     /*
  100.      * Only allow root to run in non-debug mode to avoid problems
  101.      */
  102.     if (debugLevel == 0 && getuid() != 0)
  103.     {
  104.     fprintf (stderr, "Only root wants to run %s\n", argv[0]);
  105.     exit (1);
  106.     }
  107.     if (debugLevel == 0 && daemonMode)
  108.     BecomeOrphan ();
  109.     /* SUPPRESS 560 */
  110.     if (oldpid = StorePid ())
  111.     {
  112.     if (oldpid == -1)
  113.         LogError ("Can't create/lock pid file %s\n", pidFile);
  114.     else
  115.         LogError ("Can't lock pid file %s, another xdm is running (pid %d)\n",
  116.          pidFile, oldpid);
  117.     exit (1);
  118.     }
  119.     if (debugLevel == 0 && daemonMode)
  120.     BecomeDaemon ();
  121.     InitErrorLog ();
  122. #ifdef XDMCP
  123.     CreateWellKnownSockets ();
  124. #else
  125.     Debug ("xdm: not compiled for XDMCP\n");
  126. #endif
  127.     (void) Signal (SIGTERM, StopAll);
  128.     (void) Signal (SIGINT, StopAll);
  129.     /*
  130.      * Step 2 - Read /etc/Xservers and set up
  131.      *        the socket.
  132.      *
  133.      *        Keep a sub-daemon running
  134.      *        for each entry
  135.      */
  136.     SetAccessFileTime ();
  137. #ifdef XDMCP
  138.     ScanAccessDatabase ();
  139. #endif
  140.     ScanServers ();
  141.     StartDisplays ();
  142.     (void) Signal (SIGHUP, RescanNotify);
  143. #ifndef UNRELIABLE_SIGNALS
  144.     (void) Signal (SIGCHLD, ChildNotify);
  145. #endif
  146.     while (
  147. #ifdef XDMCP
  148.        AnyWellKnownSockets() ||
  149. #endif
  150.        AnyDisplaysLeft ())
  151.     {
  152.     if (Rescan)
  153.     {
  154.         RescanServers ();
  155.         Rescan = 0;
  156.     }
  157. #if defined(UNRELIABLE_SIGNALS) || !defined(XDMCP)
  158.     WaitForChild ();
  159. #else
  160.     WaitForSomething ();
  161. #endif
  162.     }
  163.     Debug ("Nothing left to do, exiting\n");
  164.     exit(0);
  165.     /*NOTREACHED*/
  166. }
  167.  
  168. /* ARGSUSED */
  169. static SIGVAL
  170. RescanNotify (n)
  171.     int n;
  172. {
  173.     Debug ("Caught SIGHUP\n");
  174.     Rescan = 1;
  175. #ifdef SIGNALS_RESET_WHEN_CAUGHT
  176.     (void) Signal (SIGHUP, RescanNotify);
  177. #endif
  178. }
  179.  
  180. ScanServers ()
  181. {
  182.     char    lineBuf[10240];
  183.     int        len;
  184.     FILE    *serversFile;
  185.     struct stat    statb;
  186.     static DisplayType    acceptableTypes[] =
  187.         { { Local, Permanent, FromFile },
  188.           { Foreign, Permanent, FromFile },
  189.         };
  190.  
  191. #define NumTypes    (sizeof (acceptableTypes) / sizeof (acceptableTypes[0]))
  192.  
  193.     if (servers[0] == '/')
  194.     {
  195.     serversFile = fopen (servers, "r");
  196.     if (serversFile == NULL)
  197.      {
  198.         LogError ("cannot access servers file %s\n", servers);
  199.         return;
  200.     }
  201.     if (ServersModTime == 0)
  202.     {
  203.         fstat (fileno (serversFile), &statb);
  204.         ServersModTime = statb.st_mtime;
  205.     }
  206.     while (fgets (lineBuf, sizeof (lineBuf)-1, serversFile))
  207.     {
  208.         len = strlen (lineBuf);
  209.         if (lineBuf[len-1] == '\n')
  210.         lineBuf[len-1] = '\0';
  211.         ParseDisplay (lineBuf, acceptableTypes, NumTypes);
  212.     }
  213.     fclose (serversFile);
  214.     }
  215.     else
  216.     {
  217.     ParseDisplay (servers, acceptableTypes, NumTypes);
  218.     }
  219. }
  220.  
  221. static void
  222. MarkDisplay (d)
  223. struct display    *d;
  224. {
  225.     d->state = MissingEntry;
  226. }
  227.  
  228. static void
  229. RescanServers ()
  230. {
  231.     Debug ("rescanning servers\n");
  232.     LogInfo ("Rescanning both config and servers files\n");
  233.     ForEachDisplay (MarkDisplay);
  234.     SetConfigFileTime ();
  235.     ReinitResources ();
  236.     LoadDMResources ();
  237.     ScanServers ();
  238.     SetAccessFileTime ();
  239. #ifdef XDMCP
  240.     ScanAccessDatabase ();
  241. #endif
  242.     StartDisplays ();
  243. }
  244.  
  245. SetConfigFileTime ()
  246. {
  247.     struct stat    statb;
  248.  
  249.     if (stat (config, &statb) != -1)
  250.     ConfigModTime = statb.st_mtime;
  251. }
  252.  
  253. SetAccessFileTime ()
  254. {
  255.     struct stat    statb;
  256.  
  257.     if (stat (accessFile, &statb) != -1)
  258.     AccessFileModTime = statb.st_mtime;
  259. }
  260.  
  261. static
  262. RescanIfMod ()
  263. {
  264.     struct stat    statb;
  265.  
  266.     if (config && stat (config, &statb) != -1)
  267.     {
  268.     if (statb.st_mtime != ConfigModTime)
  269.     {
  270.         Debug ("Config file %s has changed, rereading\n", config);
  271.         LogInfo ("Rereading configuration file %s\n", config);
  272.         ConfigModTime = statb.st_mtime;
  273.         ReinitResources ();
  274.         LoadDMResources ();
  275.     }
  276.     }
  277.     if (servers[0] == '/' && stat(servers, &statb) != -1)
  278.     {
  279.     if (statb.st_mtime != ServersModTime)
  280.     {
  281.         Debug ("Servers file %s has changed, rescanning\n", servers);
  282.         LogInfo ("Rereading servers file %s\n", servers);
  283.         ServersModTime = statb.st_mtime;
  284.         ForEachDisplay (MarkDisplay);
  285.         ScanServers ();
  286.     }
  287.     }
  288. #ifdef XDMCP
  289.     if (accessFile && accessFile[0] && stat (accessFile, &statb) != -1)
  290.     {
  291.     if (statb.st_mtime != AccessFileModTime)
  292.     {
  293.         Debug ("Access file %s has changed, rereading\n", accessFile);
  294.         LogInfo ("Rereading access file %s\n", accessFile);
  295.         AccessFileModTime = statb.st_mtime;
  296.         ScanAccessDatabase ();
  297.     }
  298.     }
  299. #endif
  300. }
  301.  
  302. /*
  303.  * catch a SIGTERM, kill all displays and exit
  304.  */
  305.  
  306. /* ARGSUSED */
  307. static SIGVAL
  308. StopAll (n)
  309.     int n;
  310. {
  311.     Debug ("Shutting down entire manager\n");
  312. #ifdef XDMCP
  313.     DestroyWellKnownSockets ();
  314. #endif
  315.     ForEachDisplay (StopDisplay);
  316. #ifdef SIGNALS_RESET_WHEN_CAUGHT
  317.     /* to avoid another one from killing us unceremoniously */
  318.     (void) Signal (SIGTERM, StopAll);
  319.     (void) Signal (SIGINT, StopAll);
  320. #endif
  321. }
  322.  
  323. /*
  324.  * notice that a child has died and may need another
  325.  * sub-daemon started
  326.  */
  327.  
  328. int    ChildReady;
  329.  
  330. #ifndef UNRELIABLE_SIGNALS
  331. /* ARGSUSED */
  332. static SIGVAL
  333. ChildNotify (n)
  334.     int n;
  335. {
  336.     ChildReady = 1;
  337. }
  338. #endif
  339.  
  340. WaitForChild ()
  341. {
  342.     int        pid;
  343.     struct display    *d;
  344.     waitType    status;
  345. #ifndef X_NOT_POSIX
  346.     sigset_t mask, omask;
  347. #else
  348.     int        omask;
  349. #endif
  350.  
  351. #ifdef UNRELIABLE_SIGNALS
  352.     /* XXX classic System V signal race condition here with RescanNotify */
  353.     if ((pid = wait (&status)) != -1)
  354. #else
  355. #ifndef X_NOT_POSIX
  356.     sigemptyset(&mask);
  357.     sigaddset(&mask, SIGCHLD);
  358.     sigaddset(&mask, SIGHUP);
  359.     sigprocmask(SIG_BLOCK, &mask, &omask);
  360. #else
  361.     omask = sigblock (sigmask (SIGCHLD) | sigmask (SIGHUP));
  362. #endif
  363.     Debug ("signals blocked, mask was 0x%x\n", omask);
  364.     if (!ChildReady && !Rescan)
  365. #ifndef X_NOT_POSIX
  366.     sigsuspend(&omask);
  367. #else
  368.     sigpause (omask);
  369. #endif
  370.     ChildReady = 0;
  371. #ifndef X_NOT_POSIX
  372.     sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
  373. #else
  374.     sigsetmask (omask);
  375. #endif
  376. #ifndef X_NOT_POSIX
  377.     while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
  378. #else
  379.     while ((pid = wait3 (&status, WNOHANG, (struct rusage *) 0)) > 0)
  380. #endif
  381. #endif
  382.     {
  383.     Debug ("Manager wait returns pid: %d sig %d core %d code %d\n",
  384.            pid, waitSig(status), waitCore(status), waitCode(status));
  385.     if (autoRescan)
  386.         RescanIfMod ();
  387.     /* SUPPRESS 560 */
  388.     if (d = FindDisplayByPid (pid)) {
  389.         d->pid = -1;
  390.         switch (waitVal (status)) {
  391.         case UNMANAGE_DISPLAY:
  392.         Debug ("Display exited with UNMANAGE_DISPLAY\n");
  393.         StopDisplay (d);
  394.         break;
  395.         case OBEYSESS_DISPLAY:
  396.         d->startTries = 0;
  397.         Debug ("Display exited with OBEYSESS_DISPLAY\n");
  398.         if (d->displayType.lifetime != Permanent ||
  399.             d->status == zombie)
  400.             StopDisplay (d);
  401.         else
  402.             RestartDisplay (d, FALSE);
  403.         break;
  404.         default:
  405.         Debug ("Display exited with unknown status %d\n", waitVal(status));
  406.         LogError ("Unknown session exit code %d from process %d\n",
  407.               waitVal (status), pid);
  408.         StopDisplay (d);
  409.         break;
  410.         case OPENFAILED_DISPLAY:
  411.         Debug ("Display exited with OPENFAILED_DISPLAY, try %d of %d\n",
  412.                d->startTries, d->startAttempts);
  413.         LogError ("Display %s cannot be opened\n", d->name);
  414.         /*
  415.           * no display connection was ever made, tell the
  416.          * terminal that the open attempt failed
  417.           */
  418. #ifdef XDMCP
  419.         if (d->displayType.origin == FromXDMCP)
  420.             SendFailed (d, "Cannot open display");
  421. #endif
  422.         if (d->displayType.origin == FromXDMCP ||
  423.             d->status == zombie ||
  424.             ++d->startTries >= d->startAttempts)
  425.         {
  426.             LogError ("Display %s is being disabled\n", d->name);
  427.             StopDisplay (d);
  428.         }
  429.         else
  430.         {
  431.             RestartDisplay (d, TRUE);
  432.         }
  433.         break;
  434.         case RESERVER_DISPLAY:
  435.         d->startTries = 0;
  436.         Debug ("Display exited with RESERVER_DISPLAY\n");
  437.         if (d->displayType.origin == FromXDMCP || d->status == zombie)
  438.             StopDisplay(d);
  439.         else
  440.             RestartDisplay (d, TRUE);
  441.         break;
  442.         case waitCompose (SIGTERM,0,0):
  443.         d->startTries = 0;
  444.         Debug ("Display exited on SIGTERM\n");
  445.         if (d->displayType.origin == FromXDMCP || d->status == zombie)
  446.             StopDisplay(d);
  447.         else
  448.             RestartDisplay (d, TRUE);
  449.         break;
  450.         case REMANAGE_DISPLAY:
  451.         d->startTries = 0;
  452.         Debug ("Display exited with REMANAGE_DISPLAY\n");
  453.         /*
  454.           * XDMCP will restart the session if the display
  455.          * requests it
  456.          */
  457.         if (d->displayType.origin == FromXDMCP || d->status == zombie)
  458.             StopDisplay(d);
  459.         else
  460.             RestartDisplay (d, FALSE);
  461.         break;
  462.         }
  463.     }
  464.     /* SUPPRESS 560 */
  465.     else if (d = FindDisplayByServerPid (pid))
  466.     {
  467.         d->serverPid = -1;
  468.         switch (d->status)
  469.         {
  470.         case zombie:
  471.         Debug ("Zombie server reaped, removing display %s\n", d->name);
  472.         RemoveDisplay (d);
  473.         break;
  474.         case phoenix:
  475.         Debug ("Phoenix server arises, restarting display %s\n", d->name);
  476.         d->status = notRunning;
  477.         break;
  478.         case running:
  479.         Debug ("Server for display %s terminated unexpectedly, status %d\n", d->name, waitVal (status));
  480.         LogError ("Server for display %s terminated unexpectedly: %d\n", d->name, waitVal (status));
  481.         if (d->pid != -1)
  482.         {
  483.             Debug ("Terminating session pid %d\n", d->pid);
  484.             TerminateProcess (d->pid, SIGTERM);
  485.         }        
  486.         break;
  487.         case notRunning:
  488.         Debug ("Server exited for notRunning session on display %s\n", d->name);
  489.         break;
  490.         }
  491.     }
  492.     else
  493.     {
  494.         Debug ("Unknown child termination, status %d\n", waitVal (status));
  495.     }
  496.     }
  497.     StartDisplays ();
  498. }
  499.  
  500. static void
  501. CheckDisplayStatus (d)
  502. struct display    *d;
  503. {
  504.     if (d->displayType.origin == FromFile)
  505.     {
  506.     switch (d->state) {
  507.     case MissingEntry:
  508.         StopDisplay (d);
  509.         break;
  510.     case NewEntry:
  511.         d->state = OldEntry;
  512.     case OldEntry:
  513.         if (d->status == notRunning)
  514.         StartDisplay (d);
  515.         break;
  516.     }
  517.     }
  518. }
  519.  
  520. StartDisplays ()
  521. {
  522.     ForEachDisplay (CheckDisplayStatus);
  523. }
  524.  
  525. StartDisplay (d)
  526. struct display    *d;
  527. {
  528.     int    pid;
  529.  
  530.     Debug ("StartDisplay %s\n", d->name);
  531.     LoadServerResources (d);
  532.     if (d->displayType.location == Local)
  533.     {
  534.     /* don't bother pinging local displays; we'll
  535.      * certainly notice when they exit
  536.      */
  537.     d->pingInterval = 0;
  538.         if (d->authorize)
  539.         {
  540.         Debug ("SetLocalAuthorization %s, auth %s\n",
  541.             d->name, d->authNames[0]);
  542.         SetLocalAuthorization (d);
  543.         /*
  544.          * reset the server after writing the authorization information
  545.          * to make it read the file (for compatibility with old
  546.          * servers which read auth file only on reset instead of
  547.          * at first connection)
  548.          */
  549.         if (d->serverPid != -1 && d->resetForAuth && d->resetSignal)
  550.         kill (d->serverPid, d->resetSignal);
  551.         }
  552.     if (d->serverPid == -1 && !StartServer (d))
  553.     {
  554.         LogError ("Server for display %s can't be started, session disabled\n", d->name);
  555.         RemoveDisplay (d);
  556.         return;
  557.     }
  558.     }
  559.     else
  560.     {
  561.     /* this will only happen when using XDMCP */
  562.     if (d->authorizations)
  563.         SaveServerAuthorizations (d, d->authorizations, d->authNum);
  564.     }
  565.     switch (pid = fork ())
  566.     {
  567.     case 0:
  568.     CleanUpChild ();
  569.     (void) Signal (SIGPIPE, SIG_IGN);
  570.     LoadSessionResources (d);
  571.     SetAuthorization (d);
  572.     if (!WaitForServer (d))
  573.         exit (OPENFAILED_DISPLAY);
  574. #ifdef XDMCP
  575.     if (d->useChooser)
  576.         RunChooser (d);
  577.     else
  578. #endif
  579.         ManageSession (d);
  580.     exit (REMANAGE_DISPLAY);
  581.     case -1:
  582.     break;
  583.     default:
  584.     Debug ("pid: %d\n", pid);
  585.     d->pid = pid;
  586.     d->status = running;
  587.     break;
  588.     }
  589. }
  590.  
  591. TerminateProcess (pid, signal)
  592. {
  593.     kill (pid, signal);
  594. #ifdef SIGCONT
  595.     kill (pid, SIGCONT);
  596. #endif
  597. }
  598.  
  599. /*
  600.  * transition from running to zombie or deleted
  601.  */
  602.  
  603. void
  604. StopDisplay (d)
  605.     struct display    *d;
  606. {
  607.     if (d->serverPid != -1)
  608.     d->status = zombie; /* be careful about race conditions */
  609.     if (d->pid != -1)
  610.     TerminateProcess (d->pid, SIGTERM);
  611.     if (d->serverPid != -1)
  612.     TerminateProcess (d->serverPid, d->termSignal);
  613.     else
  614.     RemoveDisplay (d);
  615. }
  616.  
  617. /*
  618.  * transition from running to phoenix or notRunning
  619.  */
  620.  
  621. static void
  622. RestartDisplay (d, forceReserver)
  623.     struct display  *d;
  624.     int            forceReserver;
  625. {
  626.     if (d->serverPid != -1 && (forceReserver || d->terminateServer))
  627.     {
  628.     TerminateProcess (d->serverPid, d->termSignal);
  629.     d->status = phoenix;
  630.     }
  631.     else
  632.     {
  633.     d->status = notRunning;
  634.     }
  635. }
  636.  
  637. static FD_TYPE    CloseMask;
  638. static int    max;
  639.  
  640. RegisterCloseOnFork (fd)
  641. int    fd;
  642. {
  643.     FD_SET (fd, &CloseMask);
  644.     if (fd > max)
  645.     max = fd;
  646. }
  647.  
  648. ClearCloseOnFork (fd)
  649. int    fd;
  650. {
  651.     FD_CLR (fd, &CloseMask);
  652.     if (fd == max) {
  653.     while (--fd >= 0)
  654.         if (FD_ISSET (fd, &CloseMask))
  655.         break;
  656.     max = fd;
  657.     }
  658. }
  659.  
  660. CloseOnFork ()
  661. {
  662.     int    fd;
  663.  
  664.     for (fd = 0; fd <= max; fd++)
  665.     if (FD_ISSET (fd, &CloseMask))
  666.         close (fd);
  667.     FD_ZERO (&CloseMask);
  668.     max = 0;
  669. }
  670.  
  671. static int  pidFd;
  672. static FILE *pidFilePtr;
  673.  
  674. StorePid ()
  675. {
  676.     int        oldpid;
  677.  
  678.     if (pidFile[0] != '\0') {
  679.     pidFd = open (pidFile, 2);
  680.     if (pidFd == -1 && errno == ENOENT)
  681.         pidFd = creat (pidFile, 0666);
  682.     if (pidFd == -1 || !(pidFilePtr = fdopen (pidFd, "r+")))
  683.     {
  684.         LogError ("process-id file %s cannot be opened\n",
  685.               pidFile);
  686.         return -1;
  687.     }
  688.     if (fscanf (pidFilePtr, "%d\n", &oldpid) != 1)
  689.         oldpid = -1;
  690.     fseek (pidFilePtr, 0l, 0);
  691.     if (lockPidFile)
  692.     {
  693. #ifdef LOCK_EX
  694.         if (flock (pidFd, LOCK_EX|LOCK_NB) == -1)
  695.         {
  696.         if (errno == EWOULDBLOCK)
  697.             return oldpid;
  698.         else
  699.             return -1;
  700.         }
  701. #else
  702.         if (lockf (pidFd, F_TLOCK, 0) == -1)
  703.         {
  704.         if (errno == EACCES)
  705.             return oldpid;
  706.         else
  707.             return -1;
  708.         }
  709. #endif
  710.     }
  711.     fprintf (pidFilePtr, "%5d\n", getpid ());
  712.     (void) fflush (pidFilePtr);
  713.     RegisterCloseOnFork (pidFd);
  714.     }
  715.     return 0;
  716. }
  717.  
  718. UnlockPidFile ()
  719. {
  720.     if (lockPidFile)
  721. #ifdef F_ULOCK
  722.     lockf (pidFd, F_ULOCK, 0);
  723. #else
  724.     flock (pidFd, LOCK_UN);
  725. #endif
  726.     close (pidFd);
  727.     fclose (pidFilePtr);
  728. }
  729.  
  730. #if NeedVarargsPrototypes
  731. SetTitle (char *name, ...)
  732. #else
  733. /*VARARGS*/
  734. SetTitle (name, va_alist)
  735. char *name;
  736. va_dcl
  737. #endif
  738. {
  739. #ifndef NOXDMTITLE
  740.     char    *p = Title;
  741.     int    left = TitleLen;
  742.     char    *s;
  743.     va_list    args;
  744.  
  745.     Va_start(args,name);
  746.     *p++ = '-';
  747.     --left;
  748.     s = name;
  749.     while (s)
  750.     {
  751.     while (*s && left > 0)
  752.     {
  753.         *p++ = *s++;
  754.         left--;
  755.     }
  756.     s = va_arg (args, char *);
  757.     }
  758.     while (left > 0)
  759.     {
  760.     *p++ = ' ';
  761.     --left;
  762.     }
  763.     va_end(args);
  764. #endif    
  765. }
  766.