You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1734 lines
38 KiB
1734 lines
38 KiB
/*
|
|
|
|
Copyright 1988, 1998 The Open Group
|
|
Copyright 2000-2005 Oswald Buddenhagen <ossi@kde.org>
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of a copyright holder shall
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization
|
|
from the copyright holder.
|
|
|
|
*/
|
|
|
|
/*
|
|
* xdm - display manager daemon
|
|
* Author: Keith Packard, MIT X Consortium
|
|
*
|
|
* display manager
|
|
*/
|
|
|
|
#include <tqglobal.h>
|
|
|
|
#include "dm.h"
|
|
#include "dm_auth.h"
|
|
#include "dm_error.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef HAVE_VTS
|
|
# include <sys/ioctl.h>
|
|
# include <sys/vt.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_PTHREAD_SETNAME_NP
|
|
#include <pthread.h>
|
|
#endif /* pthread_setname_np() */
|
|
|
|
// Limited by the number of VTs configured into the kernel or 256, whichever is less
|
|
#define MAX_VT_NUMBER 48
|
|
|
|
static void SigHandler( int n );
|
|
static int ScanConfigs( int force );
|
|
static void StartDisplays( void );
|
|
#define XS_KEEP 0
|
|
#define XS_RESTART 1
|
|
#define XS_RETRY 2
|
|
static void ExitDisplay( struct display *d, int endState, int serverCmd, int goodExit );
|
|
static void rStopDisplay( struct display *d, int endState );
|
|
static void MainLoop( void );
|
|
|
|
static int signalFds[2];
|
|
|
|
#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) && \
|
|
!defined(HAVE_PTHREAD_SETNAME_NP)
|
|
static char *Title;
|
|
static int TitleLen;
|
|
#endif
|
|
|
|
static int StorePid( void );
|
|
|
|
static int Stopping;
|
|
SdRec sdRec = { 0, 0, 0, TO_INF, TO_INF, 0, 0, 0 };
|
|
|
|
time_t now;
|
|
|
|
char *prog, *progpath;
|
|
|
|
int
|
|
main( int argc, char **argv )
|
|
{
|
|
int oldpid, oldumask, fd, noDaemonMode;
|
|
char *pt, *errorLogFile, **opts;
|
|
|
|
/* make sure at least world write access is disabled */
|
|
if (((oldumask = umask( 022 )) & 002) == 002)
|
|
(void)umask( oldumask );
|
|
|
|
/* give /dev/null as stdin */
|
|
if ((fd = open( "/dev/null", O_RDONLY )) > 0) {
|
|
dup2( fd, 0 );
|
|
close( fd );
|
|
}
|
|
if (fcntl( 1, F_GETFD ) < 0)
|
|
dup2( 0, 1 );
|
|
if (fcntl( 2, F_GETFD ) < 0)
|
|
dup2( 0, 2 );
|
|
|
|
if (argv[0][0] == '/') {
|
|
if (!StrDup( &progpath, argv[0] ))
|
|
Panic( "Out of memory" );
|
|
} else
|
|
#ifdef Q_OS_LINUX
|
|
{
|
|
/* note that this will resolve symlinks ... */
|
|
int len;
|
|
char fullpath[PATH_MAX];
|
|
if ((len = readlink( "/proc/self/exe", fullpath, sizeof(fullpath) )) < 0)
|
|
Panic( "Invoke with full path specification or mount /proc" );
|
|
if (!StrNDup( &progpath, fullpath, len ))
|
|
Panic( "Out of memory" );
|
|
}
|
|
#elif defined(Q_OS_SOLARIS)
|
|
{
|
|
/* note that this will resolve symlinks ... */
|
|
int len;
|
|
char fullpath[PATH_MAX];
|
|
if ((len = readlink( "/proc/self/path/a.out", fullpath, sizeof(fullpath) )) < 0)
|
|
Panic( "Invoke with full path specification or mount /proc" );
|
|
if (!StrNDup( &progpath, fullpath, len ))
|
|
Panic( "Out of memory" );
|
|
}
|
|
#else
|
|
# if 0
|
|
Panic( "Must be invoked with full path specification" );
|
|
# else
|
|
{
|
|
char directory[PATH_MAX+1];
|
|
if (!getcwd( directory, sizeof(directory) ))
|
|
Panic( "Can't find myself (getcwd failed)" );
|
|
if (strchr( argv[0], '/' ))
|
|
StrApp( &progpath, directory, "/", argv[0], (char *)0 );
|
|
else {
|
|
int len;
|
|
char *path, *name, *thenam, nambuf[PATH_MAX+1];
|
|
char *pathe;
|
|
|
|
if (!(path = getenv( "PATH" )))
|
|
Panic( "Can't find myself (no PATH)" );
|
|
len = strlen( argv[0] );
|
|
name = nambuf + PATH_MAX - len;
|
|
memcpy( name, argv[0], len + 1 );
|
|
*--name = '/';
|
|
do {
|
|
if (!(pathe = strchr( path, ':' )))
|
|
pathe = path + strlen( path );
|
|
len = pathe - path;
|
|
if (!len || (len == 1 && *path == '.')) {
|
|
len = strlen( directory );
|
|
path = directory;
|
|
}
|
|
thenam = name - len;
|
|
if (thenam >= nambuf) {
|
|
memcpy( thenam, path, len );
|
|
if (!access( thenam, X_OK ))
|
|
goto found;
|
|
}
|
|
path = pathe;
|
|
} while (*path++ != '\0');
|
|
Panic( "Can't find myself (not in PATH)" );
|
|
found:
|
|
if (!StrDup( &progpath, thenam ))
|
|
Panic( "Out of memory" );
|
|
}
|
|
}
|
|
# endif
|
|
#endif
|
|
prog = strrchr( progpath, '/' ) + 1;
|
|
|
|
#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) && \
|
|
!defined(HAVE_PTHREAD_SETNAME_NP)
|
|
Title = argv[0];
|
|
TitleLen = (argv[argc - 1] + strlen( argv[argc - 1] )) - Title;
|
|
#endif
|
|
|
|
/*
|
|
* Parse command line options
|
|
*/
|
|
noDaemonMode = getppid();
|
|
errorLogFile = 0;
|
|
if (!(opts = Malloc( 2 * sizeof(char *) )))
|
|
return 1;
|
|
opts[0] = (char *)"";
|
|
opts[1] = 0;
|
|
while (*++argv) {
|
|
if (**argv != '-')
|
|
break;
|
|
pt = *argv + 1;
|
|
if (*pt == '-')
|
|
pt++;
|
|
if (!strcmp( pt, "help" ) || !strcmp( pt, "h" )) {
|
|
printf( "Usage: %s [options] [tty]\n"
|
|
" -daemon\t - Daemonize even when started by init\n"
|
|
" -nodaemon\t - Don't daemonize even when started from command line\n"
|
|
" -config <file> - Use alternate master configuration file\n"
|
|
" -xrm <res>\t - Override frontend-specific resource\n"
|
|
" -error <file>\t - (Or -logfile <file>) Use alternate log file\n"
|
|
" -debug <num>\t - Debug option bitfield:\n"
|
|
"\t\t\t0x1 - core log\n"
|
|
"\t\t\t0x2 - config reader log\n"
|
|
"\t\t\t0x4 - greeter log\n"
|
|
"\t\t\t0x8 - IPC log\n"
|
|
"\t\t\t0x10 - session sub-daemon post-fork delay\n"
|
|
"\t\t\t0x20 - config reader post-start delay\n"
|
|
"\t\t\t0x40 - greeter post-start delay\n"
|
|
"\t\t\t0x80 - don't use syslog\n"
|
|
"\t\t\t0x100 - core Xauth log\n"
|
|
"\t\t\t0x400 - valgrind config reader and greeter\n"
|
|
"\t\t\t0x800 - strace config reader and greeter\n"
|
|
, prog );
|
|
exit( 0 );
|
|
} else if (!strcmp( pt, "daemon" ))
|
|
noDaemonMode = 0;
|
|
else if (!strcmp( pt, "nodaemon" ))
|
|
noDaemonMode = 1;
|
|
else if (argv[1] && !strcmp( pt, "config" ))
|
|
StrDup( opts, *++argv );
|
|
else if (argv[1] && !strcmp( pt, "xrm" ))
|
|
opts = addStrArr( opts, *++argv, -1 );
|
|
else if (argv[1] && !strcmp( pt, "debug" ))
|
|
sscanf( *++argv, "%i", &debugLevel );
|
|
else if (argv[1] && (!strcmp( pt, "error" ) || !strcmp( pt, "logfile" )))
|
|
errorLogFile = *++argv;
|
|
else {
|
|
fprintf( stderr, "\"%s\" is an unknown option or is missing a parameter\n", *argv );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Only allow root to run in non-debug mode to avoid problems
|
|
*/
|
|
if (!debugLevel && getuid()) {
|
|
fprintf( stderr, "Only root wants to run %s\n", prog );
|
|
exit( 1 );
|
|
}
|
|
|
|
InitErrorLog( errorLogFile );
|
|
|
|
if (noDaemonMode != 1)
|
|
BecomeDaemon();
|
|
|
|
/*
|
|
* Step 1 - load configuration parameters
|
|
*/
|
|
if (!InitResources( opts ) || ScanConfigs( FALSE ) < 0)
|
|
LogPanic( "Config reader failed. Aborting ...\n" );
|
|
|
|
/* SUPPRESS 560 */
|
|
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 );
|
|
}
|
|
|
|
#ifdef NEED_ENTROPY
|
|
AddOtherEntropy();
|
|
#endif
|
|
|
|
/*
|
|
* We used to clean up old authorization files here. As authDir is
|
|
* supposed to be /var/run/xauth or /tmp, we needn't to care for it.
|
|
*/
|
|
|
|
#ifdef XDMCP
|
|
init_session_id();
|
|
#else
|
|
Debug( "not compiled for XDMCP\n" );
|
|
#endif
|
|
if (pipe( signalFds ))
|
|
LogPanic( "Unable to create signal notification pipe.\n" );
|
|
RegisterInput( signalFds[0] );
|
|
RegisterCloseOnFork( signalFds[0] );
|
|
RegisterCloseOnFork( signalFds[1] );
|
|
(void)Signal( SIGTERM, SigHandler );
|
|
(void)Signal( SIGINT, SigHandler );
|
|
(void)Signal( SIGHUP, SigHandler );
|
|
(void)Signal( SIGCHLD, SigHandler );
|
|
(void)Signal( SIGUSR1, SigHandler );
|
|
|
|
/*
|
|
* Step 2 - run a sub-daemon for each entry
|
|
*/
|
|
#ifdef XDMCP
|
|
UpdateListenSockets();
|
|
#endif
|
|
openCtrl( 0 );
|
|
MainLoop();
|
|
closeCtrl( 0 );
|
|
if (sdRec.how) {
|
|
commitBootOption();
|
|
if (Fork() <= 0) {
|
|
char *cmd = sdRec.how == SHUT_HALT ? cmdHalt : cmdReboot;
|
|
execute( parseArgs( (char **)0, cmd ), (char **)0 );
|
|
LogError( "Failed to execute shutdown command %\"s\n", cmd );
|
|
exit( 1 );
|
|
} else {
|
|
sigset_t mask;
|
|
sigemptyset( &mask );
|
|
sigaddset( &mask, SIGCHLD );
|
|
sigaddset( &mask, SIGHUP );
|
|
sigsuspend( &mask );
|
|
}
|
|
}
|
|
Debug( "nothing left to do, exiting\n" );
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_VTS
|
|
int
|
|
TTYtoVT( const char *tty )
|
|
{
|
|
return memcmp( tty, "tty", 3 ) ? 0 : atoi( tty + 3 );
|
|
}
|
|
|
|
int
|
|
activateVT( int vt )
|
|
{
|
|
int ret = 0;
|
|
int con = open( "/dev/console", O_RDONLY );
|
|
if (con >= 0) {
|
|
if (!ioctl( con, VT_ACTIVATE, vt ))
|
|
ret = 1;
|
|
close( con );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void
|
|
WakeDisplay( struct display *d )
|
|
{
|
|
if (d->status == textMode) {
|
|
d->status = (d->displayType & d_lifetime) == dReserve ? reserve : notRunning;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
enum utState { UtDead, UtWait, UtActive };
|
|
|
|
struct utmps {
|
|
struct utmps *next;
|
|
#ifndef HAVE_VTS
|
|
struct display *d;
|
|
#endif
|
|
time_t time;
|
|
enum utState state;
|
|
int hadSess;
|
|
};
|
|
|
|
#define TIME_LOG 40
|
|
#define TIME_RELOG 10
|
|
|
|
static struct utmps *utmpList;
|
|
static time_t utmpTimeout = TO_INF;
|
|
|
|
static void
|
|
bombUtmp( void )
|
|
{
|
|
struct utmps *utp;
|
|
|
|
while ((utp = utmpList)) {
|
|
#ifdef HAVE_VTS
|
|
ForEachDisplay( WakeDisplay );
|
|
#else
|
|
utp->d->status = notRunning;
|
|
#endif
|
|
utmpList = utp->next;
|
|
free( utp );
|
|
}
|
|
}
|
|
|
|
static void
|
|
CheckUtmp( void )
|
|
{
|
|
static time_t modtim;
|
|
time_t nck;
|
|
time_t ends;
|
|
struct utmps *utp, **utpp;
|
|
struct stat st;
|
|
#ifdef BSD_UTMP
|
|
int fd;
|
|
struct utmp ut[1];
|
|
#else
|
|
STRUCTUTMP *ut;
|
|
#endif
|
|
|
|
if (!utmpList)
|
|
return;
|
|
if (stat( UTMP_FILE, &st )) {
|
|
LogError( UTMP_FILE " not found - cannot use console mode\n" );
|
|
bombUtmp();
|
|
return;
|
|
}
|
|
if (modtim != st.st_mtime) {
|
|
Debug( "rescanning " UTMP_FILE "\n" );
|
|
for (utp = utmpList; utp; utp = utp->next)
|
|
utp->state = UtDead;
|
|
#ifdef BSD_UTMP
|
|
if ((fd = open( UTMP_FILE, O_RDONLY )) < 0) {
|
|
LogError( "Cannot open " UTMP_FILE " - cannot use console mode\n" );
|
|
bombUtmp();
|
|
return;
|
|
}
|
|
while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0]))
|
|
#else
|
|
SETUTENT();
|
|
while ((ut = GETUTENT()))
|
|
#endif
|
|
{
|
|
for (utp = utmpList; utp; utp = utp->next) {
|
|
#ifdef HAVE_VTS
|
|
char **line;
|
|
for (line = consoleTTYs; *line; line++)
|
|
if (!strncmp( *line, ut->ut_line, sizeof(ut->ut_line) ))
|
|
goto hitlin;
|
|
continue;
|
|
hitlin:
|
|
#else
|
|
if (strncmp( utp->d->console, ut->ut_line, sizeof(ut->ut_line) ))
|
|
continue;
|
|
#endif
|
|
#ifdef BSD_UTMP
|
|
if (!*ut->ut_user) {
|
|
#else
|
|
if (ut->ut_type != USER_PROCESS) {
|
|
#endif
|
|
#ifdef HAVE_VTS
|
|
if (utp->state == UtActive)
|
|
break;
|
|
#endif
|
|
utp->state = UtWait;
|
|
} else {
|
|
utp->hadSess = 1;
|
|
utp->state = UtActive;
|
|
}
|
|
if (utp->time < ut->ut_time) /* theoretically superfluous */
|
|
utp->time = ut->ut_time;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef BSD_UTMP
|
|
close( fd );
|
|
#else
|
|
ENDUTENT();
|
|
#endif
|
|
modtim = st.st_mtime;
|
|
}
|
|
for (utpp = &utmpList; (utp = *utpp); ) {
|
|
if (utp->state != UtActive) {
|
|
if (utp->state == UtDead) /* shouldn't happen ... */
|
|
utp->time = 0;
|
|
ends = utp->time + (utp->hadSess ? TIME_RELOG : TIME_LOG);
|
|
if (ends <= now) {
|
|
#ifdef HAVE_VTS
|
|
ForEachDisplay( WakeDisplay );
|
|
Debug( "console login timed out\n" );
|
|
#else
|
|
utp->d->status = notRunning;
|
|
Debug( "console login for %s at %s timed out\n",
|
|
utp->d->name, utp->d->console );
|
|
#endif
|
|
*utpp = utp->next;
|
|
free( utp );
|
|
continue;
|
|
} else
|
|
nck = ends;
|
|
} else
|
|
nck = TIME_RELOG + now;
|
|
if (nck < utmpTimeout)
|
|
utmpTimeout = nck;
|
|
utpp = &(*utpp)->next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
#ifdef HAVE_VTS
|
|
SwitchToTty( void )
|
|
#else
|
|
SwitchToTty( struct display *d )
|
|
#endif
|
|
{
|
|
struct utmps *utp;
|
|
#ifdef HAVE_VTS
|
|
int vt;
|
|
#endif
|
|
|
|
if (!(utp = Malloc( sizeof(*utp) ))) {
|
|
#ifdef HAVE_VTS
|
|
ForEachDisplay( WakeDisplay );
|
|
#else
|
|
d->status = notRunning;
|
|
#endif
|
|
return;
|
|
}
|
|
#ifndef HAVE_VTS
|
|
d->status = textMode;
|
|
utp->d = d;
|
|
#endif
|
|
utp->time = now;
|
|
utp->hadSess = 0;
|
|
utp->next = utmpList;
|
|
utmpList = utp;
|
|
CheckUtmp();
|
|
|
|
#ifdef HAVE_VTS
|
|
if ((vt = TTYtoVT( *consoleTTYs )))
|
|
activateVT( vt );
|
|
#endif
|
|
|
|
/* XXX output something useful here */
|
|
}
|
|
|
|
#ifdef HAVE_VTS
|
|
static void
|
|
StopToTTY( struct display *d )
|
|
{
|
|
if ((d->displayType & d_location) == dLocal)
|
|
switch (d->status) {
|
|
default:
|
|
rStopDisplay(d, DS_TEXTMODE | 0x100);
|
|
case reserve:
|
|
case textMode:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
CheckTTYMode( void )
|
|
{
|
|
struct display *d;
|
|
|
|
for (d = displays; d; d = d->next) {
|
|
if (d->status == zombie) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
SwitchToTty();
|
|
}
|
|
|
|
#else
|
|
|
|
void
|
|
SwitchToX( struct display *d )
|
|
{
|
|
struct utmps *utp, **utpp;
|
|
|
|
for (utpp = &utmpList; (utp = *utpp); utpp = &(*utpp)->next)
|
|
if (utp->d == d) {
|
|
*utpp = utp->next;
|
|
free( utp );
|
|
d->status = notRunning;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef XDMCP
|
|
static void
|
|
StartRemoteLogin( struct display *d )
|
|
{
|
|
char **argv;
|
|
int pid;
|
|
|
|
Debug( "StartRemoteLogin for %s\n", d->name );
|
|
/* HACK: omitting LoadDisplayResources( d ) here! */
|
|
switch (pid = Fork()) {
|
|
case 0:
|
|
argv = PrepServerArgv( d, d->serverArgsRemote );
|
|
if (!(argv = addStrArr( argv, "-once", 5 )) ||
|
|
!(argv = addStrArr( argv, "-query", 6 )) ||
|
|
!(argv = addStrArr( argv, d->remoteHost, -1 )))
|
|
exit( 1 );
|
|
Debug( "exec %\"[s\n", argv );
|
|
(void)execv( argv[0], argv );
|
|
LogError( "X server %\"s cannot be executed\n", argv[0] );
|
|
|
|
/* Let's try again with some standard paths */
|
|
argv[0] = (char *)realloc(argv[0], strlen("/usr/X11R6/bin/X") + 1);
|
|
if (argv[0] != NULL) {
|
|
strcpy(argv[0], "/usr/X11R6/bin/X");
|
|
Debug( "exec %\"[s\n", argv );
|
|
(void)execv( argv[0], argv );
|
|
LogError( "X server %\"s cannot be executed\n", argv[0] );
|
|
|
|
strcpy(argv[0], "/usr/bin/X"); // Shorter than the previous file name
|
|
Debug( "exec %\"[s\n", argv );
|
|
(void)execv( argv[0], argv );
|
|
LogError( "X server %\"s cannot be executed\n", argv[0] );
|
|
}
|
|
|
|
exit( 1 );
|
|
case -1:
|
|
LogError( "Forking X server for remote login failed: %m" );
|
|
d->status = notRunning;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
Debug( "X server forked, pid %d\n", pid );
|
|
d->serverPid = pid;
|
|
|
|
d->status = remoteLogin;
|
|
}
|
|
#endif
|
|
|
|
|
|
static void
|
|
StopInactiveDisplay( struct display *d )
|
|
{
|
|
if (d->status != remoteLogin && d->userSess < 0)
|
|
StopDisplay( d );
|
|
}
|
|
|
|
static void
|
|
stoppen( int force )
|
|
{
|
|
#ifdef XDMCP
|
|
request_port = 0;
|
|
UpdateListenSockets();
|
|
#endif
|
|
if (force)
|
|
ForEachDisplay( StopDisplay );
|
|
else
|
|
ForEachDisplay( StopInactiveDisplay );
|
|
Stopping = 1;
|
|
}
|
|
|
|
|
|
void
|
|
setNLogin( struct display *d,
|
|
const char *nuser, const char *npass, char *nargs, int rl )
|
|
{
|
|
struct disphist *he = d->hstent;
|
|
he->rLogin =
|
|
(ReStr( &he->nuser, nuser ) &&
|
|
ReStr( &he->npass, npass ) &&
|
|
ReStr( &he->nargs, nargs )) ? rl : 0;
|
|
Debug( "set next login for %s, level %d\n", nuser, rl );
|
|
}
|
|
|
|
static void
|
|
processDPipe( struct display *d )
|
|
{
|
|
char *user, *pass, *args;
|
|
int cmd;
|
|
GTalk dpytalk;
|
|
#ifdef XDMCP
|
|
int ct, len;
|
|
ARRAY8 ca, ha;
|
|
#endif
|
|
|
|
dpytalk.pipe = &d->pipe;
|
|
if (Setjmp( dpytalk.errjmp )) {
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
GSet( &dpytalk );
|
|
if (!GRecvCmd( &cmd )) {
|
|
/* process already exited */
|
|
UnregisterInput( d->pipe.rfd );
|
|
return;
|
|
}
|
|
switch (cmd) {
|
|
case D_User:
|
|
d->userSess = GRecvInt();
|
|
d->userName = GRecvStr();
|
|
d->sessName = GRecvStr();
|
|
break;
|
|
case D_ReLogin:
|
|
user = GRecvStr();
|
|
pass = GRecvStr();
|
|
args = GRecvStr();
|
|
setNLogin( d, user, pass, args, 1 );
|
|
free( args );
|
|
free( pass );
|
|
free( user );
|
|
break;
|
|
#ifdef XDMCP
|
|
case D_ChooseHost:
|
|
ca.data = (unsigned char *)GRecvArr( &len );
|
|
ca.length = (CARD16)len;
|
|
ct = GRecvInt();
|
|
ha.data = (unsigned char *)GRecvArr( &len );
|
|
ha.length = (CARD16)len;
|
|
RegisterIndirectChoice( &ca, ct, &ha );
|
|
XdmcpDisposeARRAY8( &ha );
|
|
XdmcpDisposeARRAY8( &ca );
|
|
break;
|
|
case D_RemoteHost:
|
|
if (d->remoteHost)
|
|
free( d->remoteHost );
|
|
d->remoteHost = GRecvStr();
|
|
break;
|
|
#endif
|
|
case D_XConnOk:
|
|
startingServer = 0;
|
|
break;
|
|
default:
|
|
LogError( "Internal error: unknown D_* command %d\n", cmd );
|
|
StopDisplay( d );
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emitXSessG( struct display *di, struct display *d, void *ctx ATTR_UNUSED )
|
|
{
|
|
GSendStr( di->name );
|
|
GSendStr( "" );
|
|
#ifdef HAVE_VTS
|
|
GSendInt( di->serverVT );
|
|
#endif
|
|
#ifdef XDMCP
|
|
if (di->status == remoteLogin) {
|
|
GSendStr( "" );
|
|
GSendStr( di->remoteHost );
|
|
} else
|
|
#endif
|
|
{
|
|
GSendStr( di->userName );
|
|
GSendStr( di->sessName );
|
|
}
|
|
GSendInt( di == d ? isSelf : 0 );
|
|
}
|
|
|
|
static void
|
|
emitTTYSessG( STRUCTUTMP *ut, struct display *d ATTR_UNUSED, void *ctx ATTR_UNUSED )
|
|
{
|
|
GSendStrN( ut->ut_line, sizeof(ut->ut_line) );
|
|
GSendStrN( ut->ut_host, sizeof(ut->ut_host) );
|
|
#ifdef HAVE_VTS
|
|
GSendInt( TTYtoVT( ut->ut_line ) );
|
|
#endif
|
|
#ifdef BSD_UTMP
|
|
GSendStrN( *ut->ut_user ? ut->ut_user : 0, sizeof(ut->ut_user) );
|
|
#else
|
|
GSendStrN( ut->ut_type == USER_PROCESS ? ut->ut_user : 0, sizeof(ut->ut_user) );
|
|
#endif
|
|
GSendStr( 0 ); /* session type unknown */
|
|
GSendInt( isTTY );
|
|
}
|
|
|
|
static void
|
|
processGPipe( struct display *d )
|
|
{
|
|
char **opts, *option;
|
|
int cmd, ret, dflt, curr;
|
|
GTalk dpytalk;
|
|
|
|
dpytalk.pipe = &d->gpipe;
|
|
if (Setjmp( dpytalk.errjmp )) {
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
GSet( &dpytalk );
|
|
if (!GRecvCmd( &cmd )) {
|
|
/* process already exited */
|
|
UnregisterInput( d->gpipe.rfd );
|
|
return;
|
|
}
|
|
switch (cmd) {
|
|
case G_ListBootOpts:
|
|
ret = getBootOptions( &opts, &dflt, &curr );
|
|
GSendInt( ret );
|
|
if (ret == BO_OK) {
|
|
GSendArgv( opts );
|
|
freeStrArr( opts );
|
|
GSendInt( dflt );
|
|
GSendInt( curr );
|
|
}
|
|
break;
|
|
case G_Shutdown:
|
|
sdRec.how = GRecvInt();
|
|
sdRec.start = GRecvInt();
|
|
sdRec.timeout = GRecvInt();
|
|
sdRec.force = GRecvInt();
|
|
sdRec.uid = GRecvInt();
|
|
option = GRecvStr();
|
|
setBootOption( option, &sdRec );
|
|
if (option)
|
|
free( option );
|
|
break;
|
|
case G_QueryShutdown:
|
|
GSendInt( sdRec.how );
|
|
GSendInt( sdRec.start );
|
|
GSendInt( sdRec.timeout );
|
|
GSendInt( sdRec.force );
|
|
GSendInt( sdRec.uid );
|
|
GSendStr( sdRec.osname );
|
|
break;
|
|
case G_List:
|
|
ListSessions( GRecvInt(), d, 0, emitXSessG, emitTTYSessG );
|
|
GSendInt( 0 );
|
|
break;
|
|
#ifdef HAVE_VTS
|
|
case G_Activate:
|
|
activateVT( GRecvInt() );
|
|
break;
|
|
#endif
|
|
case G_Console:
|
|
#ifdef HAVE_VTS
|
|
if (*consoleTTYs) { /* sanity check against greeter */
|
|
ForEachDisplay(StopToTTY);
|
|
CheckTTYMode();
|
|
}
|
|
#else
|
|
if (*d->console) /* sanity check against greeter */
|
|
rStopDisplay(d, DS_TEXTMODE);
|
|
#endif
|
|
break;
|
|
default:
|
|
LogError( "Internal error: unknown G_* command %d\n", cmd );
|
|
StopDisplay( d );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
ScanConfigs( int force )
|
|
{
|
|
int ret;
|
|
|
|
if ((ret = LoadDMResources( force )) <= 0)
|
|
return ret;
|
|
ScanServers();
|
|
#ifdef XDMCP
|
|
ScanAccessDatabase( force );
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
MarkDisplay( struct display *d )
|
|
{
|
|
d->stillThere = 0;
|
|
}
|
|
|
|
static void
|
|
RescanConfigs( int force )
|
|
{
|
|
if (ScanConfigs( force ) > 0) {
|
|
#ifdef XDMCP
|
|
UpdateListenSockets();
|
|
#endif
|
|
updateCtrl();
|
|
}
|
|
}
|
|
|
|
void
|
|
cancelShutdown( void )
|
|
{
|
|
sdRec.how = 0;
|
|
if (sdRec.osname) {
|
|
free( sdRec.osname );
|
|
sdRec.osname = 0;
|
|
}
|
|
Stopping = 0;
|
|
RescanConfigs( TRUE );
|
|
}
|
|
|
|
static void
|
|
ReapChildren( void )
|
|
{
|
|
int pid;
|
|
struct display *d;
|
|
waitType status;
|
|
|
|
while ((pid = waitpid( -1, &status, WNOHANG)) > 0) {
|
|
Debug( "manager wait returns pid %d sig %d core %d code %d\n",
|
|
pid, waitSig( status ), waitCore( status ), waitCode( status ) );
|
|
/* SUPPRESS 560 */
|
|
if ((d = FindDisplayByPid(pid))) {
|
|
d->pid = -1;
|
|
UnregisterInput( d->pipe.rfd );
|
|
GClosen (&d->pipe);
|
|
UnregisterInput( d->gpipe.rfd );
|
|
GClosen (&d->gpipe);
|
|
closeCtrl( d );
|
|
switch (waitVal( status )) {
|
|
#ifdef XDMCP
|
|
case EX_REMOTE:
|
|
Debug( "display exited with EX_REMOTE\n" );
|
|
ExitDisplay( d, DS_REMOTE, 0, 0 );
|
|
break;
|
|
#endif
|
|
case EX_NORMAL:
|
|
/* (any type of) session ended */
|
|
Debug( "display exited with EX_NORMAL\n" );
|
|
if ((d->displayType & d_lifetime) == dReserve)
|
|
ExitDisplay( d, DS_RESERVE, 0, 0 );
|
|
else
|
|
ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE );
|
|
break;
|
|
#if 0
|
|
case EX_REMANAGE_DPY:
|
|
/* user session ended */
|
|
Debug( "display exited with EX_REMANAGE_DPY\n" );
|
|
ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE );
|
|
break;
|
|
#endif
|
|
case EX_OPENFAILED_DPY:
|
|
/* WaitForServer() failed */
|
|
LogError( "Display %s cannot be opened\n", d->name );
|
|
#ifdef XDMCP
|
|
/*
|
|
* no display connection was ever made, tell the
|
|
* terminal that the open attempt failed
|
|
*/
|
|
if ((d->displayType & d_origin) == dFromXDMCP)
|
|
SendFailed( d, "cannot open display" );
|
|
#endif
|
|
ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE );
|
|
break;
|
|
case waitCompose( SIGTERM,0,0 ):
|
|
/* killed before/during WaitForServer()
|
|
- local Xserver died
|
|
- display stopped (is zombie)
|
|
- "login now" and "suicide" pipe commands (is raiser)
|
|
*/
|
|
Debug( "display exited on SIGTERM\n" );
|
|
ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE );
|
|
break;
|
|
case EX_AL_RESERVER_DPY:
|
|
/* - killed after WaitForServer()
|
|
- Xserver dead after remote session exit
|
|
*/
|
|
Debug( "display exited with EX_AL_RESERVER_DPY\n" );
|
|
ExitDisplay( d, DS_RESTART, XS_RESTART, FALSE );
|
|
break;
|
|
case EX_RESERVER_DPY:
|
|
/* induced by greeter:
|
|
- could not secure display
|
|
- requested by user
|
|
*/
|
|
Debug( "display exited with EX_RESERVER_DPY\n" );
|
|
ExitDisplay( d, DS_RESTART, XS_RESTART, TRUE );
|
|
break;
|
|
case EX_UNMANAGE_DPY:
|
|
/* some fatal error */
|
|
Debug( "display exited with EX_UNMANAGE_DPY\n" );
|
|
ExitDisplay( d, DS_REMOVE, 0, 0 );
|
|
break;
|
|
default:
|
|
/* prolly crash */
|
|
LogError( "Unknown session exit code %d (sig %d) from manager process\n",
|
|
waitCode( status ), waitSig( status ) );
|
|
ExitDisplay( d, DS_REMOVE, 0, 0 );
|
|
break;
|
|
}
|
|
} else if ((d = FindDisplayByServerPid( pid ))) {
|
|
d->serverPid = -1;
|
|
switch (d->status) {
|
|
case zombie:
|
|
Debug( "zombie X server for display %s reaped\n", d->name );
|
|
#ifdef HAVE_VTS
|
|
if (d->serverVT && d->zstatus != DS_REMOTE) {
|
|
if (d->follower) {
|
|
d->follower->serverVT = d->serverVT;
|
|
d->follower = 0;
|
|
}
|
|
else {
|
|
int con = open("/dev/console", O_RDONLY);
|
|
if (con >= 0) {
|
|
struct vt_stat vtstat;
|
|
ioctl( con, VT_GETSTATE, &vtstat );
|
|
if (vtstat.v_active == d->serverVT) {
|
|
int vt = 1;
|
|
struct display *di;
|
|
for (di = displays; di; di = di->next) {
|
|
if (di != d && di->serverVT) {
|
|
vt = di->serverVT;
|
|
}
|
|
}
|
|
for (di = displays; di; di = di->next) {
|
|
if (di != d && di->serverVT &&
|
|
(di->userSess >= 0 ||
|
|
di->status == remoteLogin)) {
|
|
vt = di->serverVT;
|
|
}
|
|
}
|
|
ioctl(con, VT_ACTIVATE, vt);
|
|
}
|
|
ioctl(con, VT_DISALLOCATE, d->serverVT);
|
|
close(con);
|
|
}
|
|
}
|
|
d->serverVT = 0;
|
|
d->status = notRunning;
|
|
}
|
|
#endif
|
|
rStopDisplay(d, d->zstatus);
|
|
break;
|
|
case phoenix:
|
|
Debug( "phoenix X server arises, restarting display %s\n", d->name );
|
|
d->status = notRunning;
|
|
break;
|
|
case remoteLogin:
|
|
Debug( "remote login X server for display %s exited\n", d->name );
|
|
d->status = ((d->displayType & d_lifetime) == dReserve) ?
|
|
reserve : notRunning;
|
|
break;
|
|
case raiser:
|
|
LogError( "X server for display %s terminated unexpectedly\n",
|
|
d->name );
|
|
/* don't kill again */
|
|
break;
|
|
case running:
|
|
if (startingServer == d && d->serverStatus != ignore) {
|
|
if (d->serverStatus == starting && waitCode( status ) != 47)
|
|
LogError( "X server died during startup\n" );
|
|
StartServerFailed();
|
|
break;
|
|
}
|
|
LogError( "X server for display %s terminated unexpectedly\n",
|
|
d->name );
|
|
if (d->pid != -1) {
|
|
Debug( "terminating session pid %d\n", d->pid );
|
|
TerminateProcess( d->pid, SIGTERM );
|
|
}
|
|
break;
|
|
case notRunning:
|
|
case textMode:
|
|
case reserve:
|
|
/* this cannot happen */
|
|
Debug( "X server exited for passive (%d) session on display %s\n",
|
|
(int)d->status, d->name );
|
|
break;
|
|
}
|
|
} else
|
|
Debug( "unknown child termination\n" );
|
|
}
|
|
#ifdef NEED_ENTROPY
|
|
AddOtherEntropy();
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
wouldShutdown( void )
|
|
{
|
|
struct display *d;
|
|
|
|
if (sdRec.force != SHUT_CANCEL) {
|
|
if (sdRec.force == SHUT_FORCEMY) {
|
|
for (d = displays; d; d = d->next) {
|
|
if (d->status == remoteLogin ||
|
|
(d->userSess >= 0 && d->userSess != sdRec.uid)) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return !AnyActiveDisplays();
|
|
}
|
|
|
|
FD_TYPE WellKnownSocketsMask;
|
|
int WellKnownSocketsMax;
|
|
int WellKnownSocketsCount;
|
|
|
|
void
|
|
RegisterInput( int fd )
|
|
{
|
|
/* can be omited, as it is always called right after opening a socket
|
|
if (!FD_ISSET (fd, &WellKnownSocketsMask))
|
|
*/
|
|
{
|
|
FD_SET( fd, &WellKnownSocketsMask );
|
|
if (fd > WellKnownSocketsMax)
|
|
WellKnownSocketsMax = fd;
|
|
WellKnownSocketsCount++;
|
|
}
|
|
}
|
|
|
|
void
|
|
UnregisterInput( int fd )
|
|
{
|
|
/* the check _is_ necessary, as some handles are unregistered before
|
|
the regular close sequence.
|
|
*/
|
|
if (FD_ISSET( fd, &WellKnownSocketsMask )) {
|
|
FD_CLR( fd, &WellKnownSocketsMask );
|
|
WellKnownSocketsCount--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SigHandler( int n )
|
|
{
|
|
int olderrno = errno;
|
|
char buf = (char)n;
|
|
/* Debug( "caught signal %d\n", n ); this hangs in syslog() */
|
|
if (write(signalFds[1], &buf, 1) != 1) {
|
|
// ERROR
|
|
}
|
|
#ifdef __EMX__
|
|
(void)Signal( n, SigHandler );
|
|
#endif
|
|
errno = olderrno;
|
|
}
|
|
|
|
static void
|
|
MainLoop( void )
|
|
{
|
|
struct display *d;
|
|
struct timeval *tvp, tv;
|
|
time_t to;
|
|
int nready;
|
|
char buf;
|
|
FD_TYPE reads;
|
|
|
|
Debug( "MainLoop\n" );
|
|
time( &now );
|
|
while (
|
|
#ifdef XDMCP
|
|
AnyListenSockets() ||
|
|
#endif
|
|
(Stopping ? AnyRunningDisplays() : AnyDisplaysLeft()))
|
|
{
|
|
if (!Stopping)
|
|
StartDisplays();
|
|
to = TO_INF;
|
|
if (sdRec.how) {
|
|
if (sdRec.start != TO_INF && now < sdRec.start) {
|
|
/*if (sdRec.start < to)*/
|
|
to = sdRec.start;
|
|
} else {
|
|
sdRec.start = TO_INF;
|
|
if (now >= sdRec.timeout) {
|
|
sdRec.timeout = TO_INF;
|
|
if (wouldShutdown())
|
|
stoppen( TRUE );
|
|
else
|
|
cancelShutdown();
|
|
} else {
|
|
stoppen( FALSE );
|
|
/*if (sdRec.timeout < to)*/
|
|
to = sdRec.timeout;
|
|
}
|
|
}
|
|
}
|
|
if (serverTimeout < to)
|
|
to = serverTimeout;
|
|
if (utmpTimeout < to)
|
|
to = utmpTimeout;
|
|
if (to == TO_INF)
|
|
tvp = 0;
|
|
else {
|
|
to -= now;
|
|
if (to < 0)
|
|
to = 0;
|
|
tv.tv_sec = to;
|
|
tv.tv_usec = 0;
|
|
tvp = &tv;
|
|
}
|
|
reads = WellKnownSocketsMask;
|
|
nready = select( WellKnownSocketsMax + 1, &reads, 0, 0, tvp );
|
|
Debug( "select returns %d\n", nready );
|
|
time( &now );
|
|
#ifdef NEED_ENTROPY
|
|
AddTimerEntropy();
|
|
#endif
|
|
if (now >= serverTimeout) {
|
|
serverTimeout = TO_INF;
|
|
StartServerTimeout();
|
|
}
|
|
if (now >= utmpTimeout) {
|
|
utmpTimeout = TO_INF;
|
|
CheckUtmp();
|
|
}
|
|
if (nready > 0) {
|
|
/*
|
|
* we restart after the first handled fd, as
|
|
* a) it makes things simpler
|
|
* b) the probability that multiple fds trigger at once is
|
|
* ridiculously small. we handle it in the next iteration.
|
|
*/
|
|
/* XXX a cleaner solution would be a callback mechanism */
|
|
if (FD_ISSET( signalFds[0], &reads )) {
|
|
if (read( signalFds[0], &buf, 1 ) != 1)
|
|
LogPanic( "Signal notification pipe broken.\n" );
|
|
switch (buf) {
|
|
case SIGTERM:
|
|
case SIGINT:
|
|
Debug( "shutting down entire manager\n" );
|
|
stoppen( TRUE );
|
|
break;
|
|
case SIGHUP:
|
|
LogInfo( "Rescanning all config files\n" );
|
|
ForEachDisplay( MarkDisplay );
|
|
RescanConfigs( TRUE );
|
|
break;
|
|
case SIGCHLD:
|
|
ReapChildren();
|
|
if (!Stopping && autoRescan) {
|
|
RescanConfigs( FALSE );
|
|
}
|
|
break;
|
|
case SIGUSR1:
|
|
if (startingServer &&
|
|
startingServer->serverStatus == starting) {
|
|
StartServerSuccess();
|
|
}
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
#ifdef XDMCP
|
|
if (ProcessListenSockets( &reads )) {
|
|
continue;
|
|
}
|
|
#endif /* XDMCP */
|
|
if (handleCtrl( &reads, 0 )) {
|
|
continue;
|
|
}
|
|
/* Must be last (because of the breaks)! */
|
|
again:
|
|
for (d = displays; d; d = d->next) {
|
|
if (handleCtrl( &reads, d ))
|
|
goto again;
|
|
if (d->pipe.rfd >= 0 && FD_ISSET( d->pipe.rfd, &reads )) {
|
|
processDPipe( d );
|
|
break;
|
|
}
|
|
if (d->gpipe.rfd >= 0 && FD_ISSET( d->gpipe.rfd, &reads )) {
|
|
processGPipe( d );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
CheckDisplayStatus( struct display *d )
|
|
{
|
|
if ((d->displayType & d_origin) == dFromFile && !d->stillThere) {
|
|
StopDisplay( d );
|
|
}
|
|
else if ((d->displayType & d_lifetime) == dReserve &&
|
|
d->status == running && d->userSess < 0 && !d->idleTimeout) {
|
|
rStopDisplay(d, DS_RESERVE);
|
|
}
|
|
else if (d->status == notRunning) {
|
|
if (LoadDisplayResources( d ) < 0) {
|
|
LogError( "Unable to read configuration for display %s; "
|
|
"stopping it.\n", d->name );
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
KickDisplay( struct display *d )
|
|
{
|
|
if (d->status == notRunning)
|
|
StartDisplay( d );
|
|
if (d->serverStatus == awaiting && !startingServer)
|
|
StartServer( d );
|
|
}
|
|
|
|
#ifdef HAVE_VTS
|
|
static unsigned long active_vts;
|
|
|
|
static unsigned long
|
|
GetBusyVTs(void)
|
|
{
|
|
struct vt_stat vtstat;
|
|
int con;
|
|
|
|
if (active_vts == -1) {
|
|
unsigned short next_available_vt = 0;
|
|
vtstat.v_state = 0;
|
|
if ((con = open( "/dev/console", O_RDONLY )) >= 0) {
|
|
ioctl(con, VT_GETSTATE, &vtstat);
|
|
ioctl(con, VT_OPENQRY, &next_available_vt);
|
|
close(con);
|
|
}
|
|
active_vts = vtstat.v_state;
|
|
if (next_available_vt > 0xf) {
|
|
// Assume all VTs less than the next available VT are busy
|
|
// This is due to limitations in the Linux console driver
|
|
int i;
|
|
for (i = 0x10; i < next_available_vt; i++) {
|
|
active_vts |= (1 << i);
|
|
}
|
|
}
|
|
}
|
|
return active_vts;
|
|
}
|
|
|
|
static void
|
|
AllocateVT(struct display *d)
|
|
{
|
|
struct display *cd;
|
|
int i, tvt, volun;
|
|
|
|
if ((d->displayType & d_location) == dLocal &&
|
|
d->status == notRunning && !d->serverVT && d->reqSrvVT >= 0)
|
|
{
|
|
if (d->reqSrvVT && d->reqSrvVT < MAX_VT_NUMBER) {
|
|
d->serverVT = d->reqSrvVT;
|
|
}
|
|
else {
|
|
for (i = tvt = 0;;) {
|
|
if (serverVTs[i]) {
|
|
tvt = atoi(serverVTs[i++]);
|
|
volun = 0;
|
|
if (tvt < 0) {
|
|
tvt = -tvt;
|
|
volun = 1;
|
|
}
|
|
if (!tvt || tvt >= MAX_VT_NUMBER) {
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
if (++tvt >= MAX_VT_NUMBER) {
|
|
break;
|
|
}
|
|
volun = 1;
|
|
}
|
|
for (cd = displays; cd; cd = cd->next) {
|
|
if (cd->reqSrvVT == tvt && /* protect from lusers */
|
|
(cd->status != zombie || cd->zstatus != DS_REMOVE)) {
|
|
goto next;
|
|
}
|
|
if (cd->serverVT == tvt) {
|
|
if (cd->status != zombie || cd->zstatus == DS_REMOTE) {
|
|
goto next;
|
|
}
|
|
if (!cd->follower) {
|
|
d->serverVT = -1;
|
|
cd->follower = d;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (!volun || !((1 << (unsigned long)tvt) & GetBusyVTs())) {
|
|
d->serverVT = tvt;
|
|
return;
|
|
}
|
|
next: ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
StartDisplays( void )
|
|
{
|
|
ForEachDisplay( CheckDisplayStatus );
|
|
CloseGetter();
|
|
#ifdef HAVE_VTS
|
|
active_vts = -1;
|
|
ForEachDisplayRev( AllocateVT );
|
|
#endif
|
|
ForEachDisplay( KickDisplay );
|
|
}
|
|
|
|
void
|
|
StartDisplay( struct display *d )
|
|
{
|
|
if (Stopping) {
|
|
Debug( "stopping display %s because shutdown is scheduled\n", d->name );
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_VTS
|
|
if (d->serverVT < 0)
|
|
return;
|
|
#endif
|
|
|
|
d->status = running;
|
|
if ((d->displayType & d_location) == dLocal) {
|
|
Debug( "StartDisplay %s\n", d->name );
|
|
/* don't bother pinging local displays; we'll
|
|
* certainly notice when they exit
|
|
*/
|
|
d->pingInterval = 0;
|
|
if (d->authorize) {
|
|
SetLocalAuthorization( d );
|
|
/*
|
|
* reset the server after writing the authorization information
|
|
* to make it read the file (for compatibility with old
|
|
* servers which read auth file only on reset instead of
|
|
* at first connection)
|
|
*/
|
|
if (d->serverPid != -1 && d->resetForAuth && d->resetSignal)
|
|
kill( d->serverPid, d->resetSignal );
|
|
}
|
|
if (d->serverPid == -1) {
|
|
d->serverStatus = awaiting;
|
|
return;
|
|
}
|
|
} else {
|
|
Debug( "StartDisplay %s, try %d\n", d->name, d->startTries + 1 );
|
|
/* this will only happen when using XDMCP */
|
|
if (d->authorizations)
|
|
SaveServerAuthorizations( d, d->authorizations, d->authNum );
|
|
}
|
|
StartDisplayP2( d );
|
|
}
|
|
|
|
void
|
|
StartDisplayP2( struct display *d )
|
|
{
|
|
char *cname, *cgname;
|
|
int pid;
|
|
|
|
openCtrl( d );
|
|
Debug( "forking session\n" );
|
|
ASPrintf( &cname, "sub-daemon for display %s", d->name );
|
|
ASPrintf( &cgname, "greeter for display %s", d->name );
|
|
pid = GFork( &d->pipe, "master daemon", cname,
|
|
&d->gpipe, cgname );
|
|
switch (pid) {
|
|
case 0:
|
|
SetTitle( d->name );
|
|
if (debugLevel & DEBUG_WSESS)
|
|
sleep( 100 );
|
|
mstrtalk.pipe = &d->pipe;
|
|
(void)Signal( SIGPIPE, SIG_IGN );
|
|
SetAuthorization( d );
|
|
WaitForServer( d );
|
|
if ((d->displayType & d_location) == dLocal) {
|
|
GSet( &mstrtalk );
|
|
GSendInt( D_XConnOk );
|
|
}
|
|
ManageSession( d );
|
|
/* NOTREACHED */
|
|
case -1:
|
|
closeCtrl( d );
|
|
d->status = notRunning;
|
|
break;
|
|
default:
|
|
Debug( "forked session, pid %d\n", pid );
|
|
|
|
/* (void) fcntl (d->pipe.rfd, F_SETFL, O_NONBLOCK); */
|
|
/* (void) fcntl (d->gpipe.rfd, F_SETFL, O_NONBLOCK); */
|
|
RegisterInput( d->pipe.rfd );
|
|
RegisterInput( d->gpipe.rfd );
|
|
|
|
d->pid = pid;
|
|
d->hstent->lock = d->hstent->rLogin = d->hstent->goodExit =
|
|
d->hstent->sdRec.how = 0;
|
|
d->lastStart = now;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* transition from running to zombie, textmode, reserve or deleted
|
|
*/
|
|
|
|
static void
|
|
rStopDisplay( struct display *d, int endState )
|
|
{
|
|
Debug( "stopping display %s to state %d\n", d->name, endState );
|
|
AbortStartServer( d );
|
|
d->idleTimeout = 0;
|
|
if (d->serverPid != -1 || d->pid != -1) {
|
|
if (d->pid != -1) {
|
|
TerminateProcess( d->pid, SIGTERM );
|
|
}
|
|
if (d->serverPid != -1) {
|
|
TerminateProcess( d->serverPid, d->termSignal );
|
|
}
|
|
d->status = zombie;
|
|
d->zstatus = endState & 0xff;
|
|
Debug( " zombiefied\n" );
|
|
}
|
|
else if (endState == DS_TEXTMODE) {
|
|
#ifdef HAVE_VTS
|
|
d->status = textMode;
|
|
CheckTTYMode();
|
|
}
|
|
else if (endState == (DS_TEXTMODE | 0x100)) {
|
|
d->status = textMode;
|
|
#else
|
|
SwitchToTty( d );
|
|
#endif
|
|
}
|
|
else if (endState == DS_RESERVE) {
|
|
d->status = reserve;
|
|
}
|
|
#ifdef XDMCP
|
|
else if (endState == DS_REMOTE) {
|
|
StartRemoteLogin( d );
|
|
}
|
|
#endif
|
|
else {
|
|
#ifndef HAVE_VTS
|
|
SwitchToX( d );
|
|
#endif
|
|
RemoveDisplay( d );
|
|
}
|
|
}
|
|
|
|
void
|
|
StopDisplay( struct display *d )
|
|
{
|
|
rStopDisplay(d, DS_REMOVE);
|
|
}
|
|
|
|
static void
|
|
ExitDisplay(
|
|
struct display *d,
|
|
int endState,
|
|
int serverCmd,
|
|
int goodExit )
|
|
{
|
|
struct disphist *he;
|
|
|
|
if (d->status == raiser) {
|
|
serverCmd = XS_KEEP;
|
|
goodExit = TRUE;
|
|
}
|
|
|
|
Debug( "ExitDisplay %s, endState = %d, serverCmd = %d, GoodExit = %d\n",
|
|
d->name, endState, serverCmd, goodExit );
|
|
|
|
d->userSess = -1;
|
|
if (d->userName)
|
|
free( d->userName );
|
|
d->userName = 0;
|
|
if (d->sessName)
|
|
free( d->sessName );
|
|
d->sessName = 0;
|
|
he = d->hstent;
|
|
he->lastExit = now;
|
|
he->goodExit = goodExit;
|
|
if (he->sdRec.how) {
|
|
if (he->sdRec.force == SHUT_ASK &&
|
|
(AnyActiveDisplays() || d->allowShutdown == SHUT_ROOT))
|
|
{
|
|
endState = DS_RESTART;
|
|
} else {
|
|
if (!sdRec.how || sdRec.force != SHUT_FORCE ||
|
|
!((d->allowNuke == SHUT_NONE && sdRec.uid != he->sdRec.uid) ||
|
|
(d->allowNuke == SHUT_ROOT && he->sdRec.uid)))
|
|
{
|
|
if (sdRec.osname)
|
|
free( sdRec.osname );
|
|
sdRec = he->sdRec;
|
|
if (now < sdRec.timeout || wouldShutdown())
|
|
endState = DS_REMOVE;
|
|
} else if (he->sdRec.osname)
|
|
free( he->sdRec.osname );
|
|
he->sdRec.how = 0;
|
|
he->sdRec.osname = 0;
|
|
}
|
|
}
|
|
if (d->status == zombie) {
|
|
rStopDisplay(d, d->zstatus);
|
|
}
|
|
else {
|
|
if (Stopping) {
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
if (endState != DS_RESTART ||
|
|
(d->displayType & d_origin) != dFromFile) {
|
|
rStopDisplay(d, endState);
|
|
}
|
|
else {
|
|
if (serverCmd == XS_RETRY) {
|
|
if ((d->displayType & d_location) == dLocal) {
|
|
if (he->lastExit - d->lastStart < 120) {
|
|
LogError( "Unable to fire up local display %s;"
|
|
" disabling.\n", d->name );
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
} else {
|
|
if (++d->startTries > d->startAttempts) {
|
|
LogError( "Disabling foreign display %s"
|
|
" (too many attempts)\n", d->name );
|
|
StopDisplay( d );
|
|
return;
|
|
}
|
|
}
|
|
} else
|
|
d->startTries = 0;
|
|
if (d->serverPid != -1 &&
|
|
(serverCmd != XS_KEEP || d->terminateServer))
|
|
{
|
|
Debug( "killing X server for %s\n", d->name );
|
|
TerminateProcess( d->serverPid, d->termSignal );
|
|
d->status = phoenix;
|
|
} else
|
|
d->status = notRunning;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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, "%ld\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
|
|
|
|
void
|
|
SetTitle( const char *name )
|
|
{
|
|
#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) && \
|
|
!defined(HAVE_PTHREAD_SETNAME_NP)
|
|
char *p;
|
|
int left;
|
|
#endif
|
|
|
|
ASPrintf( &prog, "%s: %s", prog, name );
|
|
ReInitErrorLog();
|
|
#ifdef HAVE_SETPROCTITLE
|
|
setproctitle( "%s", name );
|
|
#elif defined(HAVE_PTHREAD_SETNAME_NP)
|
|
pthread_setname_np(pthread_self(), name);
|
|
#elif !defined(NOXDMTITLE)
|
|
p = Title;
|
|
left = TitleLen;
|
|
|
|
*p++ = '-';
|
|
--left;
|
|
while (*name && left > 0) {
|
|
*p++ = *name++;
|
|
--left;
|
|
}
|
|
while (left > 0) {
|
|
*p++ = '\0';
|
|
--left;
|
|
}
|
|
#endif
|
|
}
|