# include "dm.h"
# include "dm_auth.h"
# include "dm_error.h"
# include <stdio.h>
#ifdef X_POSIX_C_SOURCE
#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
#include <signal.h>
#undef _POSIX_C_SOURCE
#else
#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
#include <signal.h>
#else
#define _POSIX_SOURCE
#include <signal.h>
#undef _POSIX_SOURCE
#endif
#endif
#ifdef __NetBSD__
#include <sys/param.h>
#endif
#ifndef sigmask
#define sigmask(m) (1 << ((m - 1)))
#endif
# include <sys/stat.h>
# include <errno.h>
# include <X11/Xfuncproto.h>
# include <stdarg.h>
#ifndef F_TLOCK
#ifndef X_NOT_POSIX
# include <unistd.h>
#endif
#endif
#if defined(SVR4) && !defined(SCO) && !defined(sun)
extern FILE *fdopen();
#endif
static SIGVAL StopAll (int n), RescanNotify (int n);
static void RescanServers (void);
static void RestartDisplay (struct display *d, int forceReserver);
static void ScanServers (void);
static void SetAccessFileTime (void);
static void SetConfigFileTime (void);
static void StartDisplays (void);
static void TerminateProcess (int pid, int signal);
volatile int Rescan;
static long ServersModTime, ConfigModTime, AccessFileModTime;
int nofork_session = 0;
#ifndef NOXDMTITLE
static char *Title;
static int TitleLen;
#endif
#ifndef UNRELIABLE_SIGNALS
static SIGVAL ChildNotify (int n);
#endif
static int StorePid (void);
static int parent_pid = -1;
int
main (int argc, char **argv)
{
int oldpid, oldumask;
char cmdbuf[1024];
if (((oldumask = umask(022)) & 002) == 002)
(void) umask (oldumask);
#ifndef NOXDMTITLE
Title = argv[0];
TitleLen = (argv[argc - 1] + strlen(argv[argc - 1])) - Title;
#endif
InitResources (argc, argv);
SetConfigFileTime ();
LoadDMResources ();
if (debugLevel == 0 && getuid() != 0)
{
fprintf (stderr, "Only root wants to run %s\n", argv[0]);
exit (1);
}
if (debugLevel == 0 && daemonMode)
BecomeDaemon ();
if (debugLevel >= 10)
nofork_session = 1;
if ((oldpid = StorePid ()))
{
if (oldpid == -1)
LogError ("Can't create/lock pid file %s\n", pidFile);
else
LogError ("Can't lock pid file %s, another xdm is running (pid %d)\n",
pidFile, oldpid);
exit (1);
}
if (debugLevel == 0)
InitErrorLog ();
if (nofork_session == 0) {
snprintf(cmdbuf, sizeof(cmdbuf), "/bin/rm -f %s/authdir/authfiles/A*", authDir);
system(cmdbuf);
}
#if!defined(ARC4_RANDOM) && !defined(DEV_RANDOM)
AddOtherEntropy ();
#endif
#ifdef XDMCP
init_session_id ();
CreateWellKnownSockets ();
#else
Debug ("xdm: not compiled for XDMCP\n");
#endif
parent_pid = getpid ();
(void) Signal (SIGTERM, StopAll);
(void) Signal (SIGINT, StopAll);
SetAccessFileTime ();
#ifdef XDMCP
ScanAccessDatabase ();
UpdateListenSockets ();
#endif
ScanServers ();
StartDisplays ();
#if !defined(ARC4_RANDOM) && !defined(DEV_RANDOM)
AddOtherEntropy();
#endif
(void) Signal (SIGHUP, RescanNotify);
#ifndef UNRELIABLE_SIGNALS
(void) Signal (SIGCHLD, ChildNotify);
#endif
while (
#ifdef XDMCP
AnyWellKnownSockets() ||
#endif
AnyDisplaysLeft ())
{
if (Rescan)
{
RescanServers ();
Rescan = 0;
}
#if defined(UNRELIABLE_SIGNALS) || !defined(XDMCP)
WaitForChild ();
#else
WaitForSomething ();
#endif
}
Debug ("Nothing left to do, exiting\n");
exit(0);
}
static SIGVAL
RescanNotify (int n)
{
int olderrno = errno;
Debug ("Caught SIGHUP\n");
Rescan = 1;
#ifdef SIGNALS_RESET_WHEN_CAUGHT
(void) Signal (SIGHUP, RescanNotify);
#endif
errno = olderrno;
}
static void
ScanServers (void)
{
char lineBuf[10240];
int len;
FILE *serversFile;
struct stat statb;
static DisplayType acceptableTypes[] =
{ { Local, Permanent, FromFile },
{ Foreign, Permanent, FromFile },
};
#define NumTypes (sizeof (acceptableTypes) / sizeof (acceptableTypes[0]))
if (servers[0] == '/')
{
serversFile = fopen (servers, "r");
if (serversFile == NULL)
{
LogError ("cannot access servers file %s\n", servers);
return;
}
if (ServersModTime == 0)
{
fstat (fileno (serversFile), &statb);
ServersModTime = statb.st_mtime;
}
while (fgets (lineBuf, sizeof (lineBuf)-1, serversFile))
{
len = strlen (lineBuf);
if (lineBuf[len-1] == '\n')
lineBuf[len-1] = '\0';
ParseDisplay (lineBuf, acceptableTypes, NumTypes);
}
fclose (serversFile);
}
else
{
ParseDisplay (servers, acceptableTypes, NumTypes);
}
}
static void
MarkDisplay (struct display *d)
{
d->state = MissingEntry;
}
static void
RescanServers (void)
{
Debug ("rescanning servers\n");
LogInfo ("Rescanning both config and servers files\n");
ForEachDisplay (MarkDisplay);
SetConfigFileTime ();
ReinitResources ();
LoadDMResources ();
ScanServers ();
SetAccessFileTime ();
#ifdef XDMCP
ScanAccessDatabase ();
UpdateListenSockets ();
#endif
StartDisplays ();
}
static void
SetConfigFileTime (void)
{
struct stat statb;
if (stat (config, &statb) != -1)
ConfigModTime = statb.st_mtime;
}
static void
SetAccessFileTime (void)
{
struct stat statb;
if (stat (accessFile, &statb) != -1)
AccessFileModTime = statb.st_mtime;
}
static void
RescanIfMod (void)
{
struct stat statb;
if (config && stat (config, &statb) != -1)
{
if (statb.st_mtime != ConfigModTime)
{
Debug ("Config file %s has changed, rereading\n", config);
LogInfo ("Rereading configuration file %s\n", config);
ConfigModTime = statb.st_mtime;
ReinitResources ();
LoadDMResources ();
}
}
if (servers[0] == '/' && stat(servers, &statb) != -1)
{
if (statb.st_mtime != ServersModTime)
{
Debug ("Servers file %s has changed, rescanning\n", servers);
LogInfo ("Rereading servers file %s\n", servers);
ServersModTime = statb.st_mtime;
ForEachDisplay (MarkDisplay);
ScanServers ();
}
}
#ifdef XDMCP
if (accessFile && accessFile[0] && stat (accessFile, &statb) != -1)
{
if (statb.st_mtime != AccessFileModTime)
{
Debug ("Access file %s has changed, rereading\n", accessFile);
LogInfo ("Rereading access file %s\n", accessFile);
AccessFileModTime = statb.st_mtime;
ScanAccessDatabase ();
UpdateListenSockets();
}
}
#endif
}
static SIGVAL
StopAll (int n)
{
int olderrno = errno;
if (parent_pid != getpid())
{
Debug ("Child xdm caught SIGTERM before it remove that signal.\n");
(void) Signal (n, SIG_DFL);
TerminateProcess (getpid(), SIGTERM);
errno = olderrno;
return;
}
Debug ("Shutting down entire manager\n");
#ifdef XDMCP
DestroyWellKnownSockets ();
#endif
ForEachDisplay (StopDisplay);
#ifdef SIGNALS_RESET_WHEN_CAUGHT
(void) Signal (SIGTERM, StopAll);
(void) Signal (SIGINT, StopAll);
#endif
errno = olderrno;
}
int ChildReady;
#ifndef UNRELIABLE_SIGNALS
static SIGVAL
ChildNotify (int n)
{
int olderrno = errno;
ChildReady = 1;
#ifdef ISC
(void) Signal (SIGCHLD, ChildNotify);
#endif
errno = olderrno;
}
#endif
void
WaitForChild (void)
{
int pid;
struct display *d;
waitType status;
#if !defined(X_NOT_POSIX) && !defined(__UNIXOS2__)
sigset_t mask, omask;
#else
int omask;
#endif
#ifdef UNRELIABLE_SIGNALS
if ((pid = wait (&status)) != -1)
#else
#ifndef X_NOT_POSIX
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGHUP);
sigprocmask(SIG_BLOCK, &mask, &omask);
Debug ("signals blocked\n");
#else
omask = sigblock (sigmask (SIGCHLD) | sigmask (SIGHUP));
Debug ("signals blocked, mask was 0x%x\n", omask);
#endif
if (!ChildReady && !Rescan)
#ifndef X_NOT_POSIX
sigsuspend(&omask);
#else
sigpause (omask);
#endif
ChildReady = 0;
#ifndef X_NOT_POSIX
sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
#else
sigsetmask (omask);
#endif
#ifndef X_NOT_POSIX
while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
#else
while ((pid = wait3 (&status, WNOHANG, (struct rusage *) 0)) > 0)
#endif
#endif
{
Debug ("Manager wait returns pid: %d sig %d core %d code %d\n",
pid, waitSig(status), waitCore(status), waitCode(status));
if (autoRescan)
RescanIfMod ();
if ((d = FindDisplayByPid (pid))) {
d->pid = -1;
switch (waitVal (status)) {
case UNMANAGE_DISPLAY:
Debug ("Display exited with UNMANAGE_DISPLAY\n");
StopDisplay (d);
break;
case OBEYSESS_DISPLAY:
d->startTries = 0;
Debug ("Display exited with OBEYSESS_DISPLAY\n");
if (d->displayType.lifetime != Permanent ||
d->status == zombie)
StopDisplay (d);
else
RestartDisplay (d, FALSE);
break;
case OPENFAILED_DISPLAY:
Debug ("Display exited with OPENFAILED_DISPLAY, try %d of %d\n",
d->startTries, d->startAttempts);
LogError ("Display %s cannot be opened\n", d->name);
#ifdef XDMCP
if (d->displayType.origin == FromXDMCP)
SendFailed (d, "Cannot open display");
#endif
if (d->displayType.origin == FromXDMCP ||
d->status == zombie ||
++d->startTries >= d->startAttempts)
{
LogError ("Display %s is being disabled\n", d->name);
StopDisplay (d);
}
else
{
RestartDisplay (d, TRUE);
}
break;
case RESERVER_DISPLAY:
d->startTries = 0;
Debug ("Display exited with RESERVER_DISPLAY\n");
if (d->displayType.origin == FromXDMCP || d->status == zombie)
StopDisplay(d);
else
RestartDisplay (d, TRUE);
{
Time_t Time;
time(&Time);
Debug("time %i %i\n",Time,d->lastCrash);
if (d->lastCrash &&
((Time - d->lastCrash) < XDM_BROKEN_INTERVAL)) {
Debug("Server crash frequency too high:"
" removing display %s\n",d->name);
LogError("Server crash rate too high:"
" removing display %s\n",d->name);
#if !defined(ARC4_RANDOM) && !defined(DEV_RANDOM)
AddTimerEntropy();
#endif
RemoveDisplay (d);
} else
d->lastCrash = Time;
}
break;
case waitCompose (SIGTERM,0,0):
Debug ("Display exited on SIGTERM, try %d of %d\n",
d->startTries, d->startAttempts);
if (d->displayType.origin == FromXDMCP ||
d->status == zombie ||
++d->startTries >= d->startAttempts) {
LogError ("Display %s is being disabled\n", d->name);
StopDisplay(d);
} else
RestartDisplay (d, TRUE);
break;
case REMANAGE_DISPLAY:
d->startTries = 0;
Debug ("Display exited with REMANAGE_DISPLAY\n");
if (d->displayType.origin == FromXDMCP || d->status == zombie)
StopDisplay(d);
else
RestartDisplay (d, FALSE);
break;
default:
Debug ("Display exited with unknown status %d\n", waitVal(status));
LogError ("Unknown session exit code %d from process %d\n",
waitVal (status), pid);
StopDisplay (d);
break;
}
}
else if ((d = FindDisplayByServerPid (pid)))
{
d->serverPid = -1;
switch (d->status)
{
case zombie:
Debug ("Zombie server reaped, removing display %s\n", d->name);
RemoveDisplay (d);
break;
case phoenix:
Debug ("Phoenix server arises, restarting display %s\n", d->name);
d->status = notRunning;
break;
case running:
Debug ("Server for display %s terminated unexpectedly, status %d %d\n", d->name, waitVal (status), status);
LogError ("Server for display %s terminated unexpectedly: %d\n", d->name, waitVal (status));
if (d->pid != -1)
{
Debug ("Terminating session pid %d\n", d->pid);
TerminateProcess (d->pid, SIGTERM);
}
break;
case notRunning:
Debug ("Server exited for notRunning session on display %s\n", d->name);
break;
}
}
else
{
Debug ("Unknown child termination, status %d\n", waitVal (status));
}
}
StartDisplays ();
}
static void
CheckDisplayStatus (struct display *d)
{
if (d->displayType.origin == FromFile)
{
switch (d->state) {
case MissingEntry:
StopDisplay (d);
break;
case NewEntry:
d->state = OldEntry;
case OldEntry:
if (d->status == notRunning)
StartDisplay (d);
break;
}
}
}
static void
StartDisplays (void)
{
ForEachDisplay (CheckDisplayStatus);
}
void
StartDisplay (struct display *d)
{
int pid;
Debug ("StartDisplay %s\n", d->name);
LoadServerResources (d);
if (d->displayType.location == Local)
{
d->pingInterval = 0;
if (d->authorize)
{
Debug ("SetLocalAuthorization %s, auth %s\n",
d->name, d->authNames[0]);
SetLocalAuthorization (d);
if (d->serverPid != -1 && d->resetForAuth && d->resetSignal)
kill (d->serverPid, d->resetSignal);
}
if (d->serverPid == -1 && !StartServer (d))
{
LogError ("Server for display %s can't be started, session disabled\n", d->name);
RemoveDisplay (d);
return;
}
}
else
{
if (d->authorizations)
SaveServerAuthorizations (d, d->authorizations, d->authNum);
}
if (!nofork_session)
pid = fork ();
else
pid = 0;
switch (pid)
{
case 0:
if (!nofork_session) {
CleanUpChild ();
(void) Signal (SIGPIPE, SIG_IGN);
}
LoadSessionResources (d);
SetAuthorization (d);
if (!WaitForServer (d))
exit (OPENFAILED_DISPLAY);
#ifdef XDMCP
if (d->useChooser)
RunChooser (d);
else
#endif
ManageSession (d);
exit (REMANAGE_DISPLAY);
case -1:
break;
default:
Debug ("pid: %d\n", pid);
d->pid = pid;
d->status = running;
break;
}
}
static void
TerminateProcess (int pid, int signal)
{
kill (pid, signal);
#ifdef SIGCONT
kill (pid, SIGCONT);
#endif
}
void
StopDisplay (struct display *d)
{
if (d->serverPid != -1)
d->status = zombie;
if (d->pid != -1)
TerminateProcess (d->pid, SIGTERM);
if (d->serverPid != -1)
TerminateProcess (d->serverPid, d->termSignal);
else
RemoveDisplay (d);
}
static void
RestartDisplay (struct display *d, int forceReserver)
{
if (d->serverPid != -1 && (forceReserver || d->terminateServer))
{
TerminateProcess (d->serverPid, d->termSignal);
d->status = phoenix;
}
else
{
d->status = notRunning;
}
}
static FD_TYPE CloseMask;
static int max;
void
RegisterCloseOnFork (int fd)
{
FD_SET (fd, &CloseMask);
if (fd > max)
max = fd;
}
void
ClearCloseOnFork (int fd)
{
FD_CLR (fd, &CloseMask);
if (fd == max) {
while (--fd >= 0)
if (FD_ISSET (fd, &CloseMask))
break;
max = fd;
}
}
void
CloseOnFork (void)
{
int fd;
for (fd = 0; fd <= max; fd++)
if (FD_ISSET (fd, &CloseMask))
{
close (fd);
}
FD_ZERO (&CloseMask);
max = 0;
}
static int pidFd;
static FILE *pidFilePtr;
static int
StorePid (void)
{
int oldpid;
if (pidFile[0] != '\0') {
pidFd = open (pidFile, O_RDWR);
if (pidFd == -1 && errno == ENOENT)
pidFd = open (pidFile, O_RDWR|O_CREAT, 0666);
if (pidFd == -1 || !(pidFilePtr = fdopen (pidFd, "r+")))
{
LogError ("process-id file %s cannot be opened\n",
pidFile);
return -1;
}
if (fscanf (pidFilePtr, "%d\n", &oldpid) != 1)
oldpid = -1;
fseek (pidFilePtr, 0l, 0);
if (lockPidFile)
{
#ifdef F_SETLK
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
struct flock lock_data;
lock_data.l_type = F_WRLCK;
lock_data.l_whence = SEEK_SET;
lock_data.l_start = lock_data.l_len = 0;
if (fcntl(pidFd, F_SETLK, &lock_data) == -1)
{
if (errno == EAGAIN)
return oldpid;
else
return -1;
}
#else
#ifdef LOCK_EX
if (flock (pidFd, LOCK_EX|LOCK_NB) == -1)
{
if (errno == EWOULDBLOCK)
return oldpid;
else
return -1;
}
#else
if (lockf (pidFd, F_TLOCK, 0) == -1)
{
if (errno == EACCES)
return oldpid;
else
return -1;
}
#endif
#endif
}
fprintf (pidFilePtr, "%5ld\n", (long)getpid ());
(void) fflush (pidFilePtr);
RegisterCloseOnFork (pidFd);
}
return 0;
}
#if 0
void
UnlockPidFile (void)
{
if (lockPidFile)
#ifdef F_SETLK
{
struct flock lock_data;
lock_data.l_type = F_UNLCK;
lock_data.l_whence = SEEK_SET;
lock_data.l_start = lock_data.l_len = 0;
(void) fcntl(pidFd, F_SETLK, &lock_data);
}
#else
#ifdef F_ULOCK
lockf (pidFd, F_ULOCK, 0);
#else
flock (pidFd, LOCK_UN);
#endif
#endif
close (pidFd);
fclose (pidFilePtr);
}
#endif
#ifndef HAS_SETPROCTITLE
void SetTitle (char *name, ...)
{
#ifndef NOXDMTITLE
char *p = Title;
int left = TitleLen;
char *s;
va_list args;
va_start(args,name);
*p++ = '-';
--left;
s = name;
while (s)
{
while (*s && left > 0)
{
*p++ = *s++;
left--;
}
s = va_arg (args, char *);
}
while (left > 0)
{
*p++ = ' ';
--left;
}
va_end(args);
#endif
}
#endif