Ejemplo n.º 1
0
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 __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" );
	}
#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, *pathe, *name, *thenam, nambuf[PATH_MAX+1];

			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)
	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 alternative master configuration file\n"
"  -xrm <res>\t  - Override frontend-specific resource\n"
"  -error <file>\t  - Use alternative 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;
}
Ejemplo n.º 2
0
static void
checkUtmp(void)
{
    static time_t modtim;
    time_t nck;
    time_t ends;
    struct utmps *utp;
#ifndef HAVE_VTS
    struct utmps **utpp;
#endif
    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");
        wakeDisplays();
        return;
    }
    if (modtim != st.st_mtime) {
        debug("rescanning " UTMP_FILE "\n");
#ifdef HAVE_VTS
        utp = utmpList;
#else
        for (utp = utmpList; utp; utp = utp->next)
#endif
            utp->state = UtDead;
#ifdef BSD_UTMP
        if ((fd = open(UTMP_FILE, O_RDONLY)) < 0) {
            logError("Cannot open " UTMP_FILE " - cannot use console mode\n");
            wakeDisplays();
            return;
        }
        while (reader(fd, ut, sizeof(ut[0])) == sizeof(ut[0]))
#else
        SETUTENT();
        while ((ut = GETUTENT()))
#endif
        {
            /* first, match the tty to a utemps */
#ifdef HAVE_VTS
            char **line;
            for (line = consoleTTYs; *line; line++)
                if (!strncmp(*line, ut->ut_line, sizeof(ut->ut_line)))
#else
            for (utp = utmpList; utp; utp = utp->next)
                if (!strncmp(utp->d->console, ut->ut_line, sizeof(ut->ut_line)))
#endif
                    goto hitlin;
            continue;
          hitlin:
            /* then, update the utemps accordingly */
#ifdef BSD_UTMP
            if (!*ut->ut_user) {
#else
            if (ut->ut_type != USER_PROCESS) {
#endif
#ifdef HAVE_VTS
                /* don't allow "downgrading" the singular utemps */
                if (utp->state == UtActive)
                    continue;
#endif
                utp->state = UtWait;
            } else {
                utp->hadSess = True;
                utp->state = UtActive;
            }
            nck = ut->ut_time - nowWallDelta;
            if (nck > now)
                nck = 0; /* Clock jumped. Time out immediately. */
#ifdef HAVE_VTS
            /* tty with latest activity wins */
            if (utp->time < nck)
#endif
                utp->time = nck;
        }
#ifdef BSD_UTMP
        close(fd);
#else
        ENDUTENT();
#endif
        modtim = st.st_mtime;
    }
#ifdef HAVE_VTS
    utp = utmpList;
#else
    for (utpp = &utmpList; (utp = *utpp);) {
#endif
        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
                wakeDisplays();
                debug("console login timed out\n");
                return;
#else
                utp->d->status = notRunning;
                debug("console login for %s at %s timed out\n",
                       utp->d->name, utp->d->console);
                *utpp = utp->next;
                free(utp);
                continue;
#endif
            } else
                nck = ends;
        } else
            nck = TIME_RELOG + now;
        if (nck < utmpTimeout)
            utmpTimeout = nck;
#ifndef HAVE_VTS
        utpp = &(*utpp)->next;
    }
#endif
}

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
        wakeDisplays();
#else
        d->status = notRunning;
#endif
        return;
    }
#ifndef HAVE_VTS
    d->status = textMode;
    utp->d = d;
    utp->next = utmpList;
#endif
    utp->time = now;
    utp->hadSess = False;
    utmpList = utp;
    checkUtmp();

#ifdef HAVE_VTS
    if ((vt = TTYtoVT(*consoleTTYs)))
        activateVT(vt);
#endif
}

#ifdef HAVE_VTS
static void
stopToTTY(struct display *d)
{
    if ((d->displayType & d_location) == dLocal)
        switch (d->status) {
        default:
            rStopDisplay(d, DS_TEXTMODE | DS_SCHEDULE);
        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;

    debug("startRemoteLogin for %s\n", d->name);
    /* HACK: omitting loadDisplayResources(d) here! */
    switch (Fork(&d->serverPid)) {
    case 0:
        argv = prepareServerArgv(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]);
        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", d->serverPid);

    d->status = remoteLogin;
}
Ejemplo n.º 3
0
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] );
		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;
}
Ejemplo n.º 4
0
int
startClient( volatile int *pid )
{
	const char *home, *sessargs, *desksess;
	char **env, *xma;
	char **argv, *fname, *str;
#ifdef USE_PAM
	char ** volatile pam_env;
# ifndef HAVE_PAM_GETENVLIST
	char **saved_env;
# endif
	int pretc;
#else
# ifdef _AIX
	char *msg;
	char **theenv;
	extern char **newenv; /* from libs.a, this is set up by setpenv */
# endif
#endif
#ifdef HAVE_SETUSERCONTEXT
	extern char **environ;
#endif
	char *failsafeArgv[2];
	char *buf, *buf2;
	int i;

	if (strCmp( dmrcuser, curuser )) {
		if (curdmrc) { free( curdmrc ); curdmrc = 0; }
		if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; }
	}

#if defined(USE_PAM) || defined(_AIX)
	if (!(p = getpwnam( curuser ))) {
		logError( "getpwnam(%s) failed.\n", curuser );
	  pError:
		displayStr( V_MSG_ERR, 0 );
		return 0;
	}
#endif

#ifndef USE_PAM
# ifdef _AIX
	msg = NULL;
	loginsuccess( curuser, hostname, tty, &msg );
	if (msg) {
		debug( "loginsuccess() - %s\n", msg );
		free( (void *)msg );
	}
# else /* _AIX */
#  if defined(KERBEROS) && defined(AFS)
	if (krbtkfile[0] != '\0') {
		if (k_hasafs()) {
			int fail = 0;
			if (k_setpag() == -1) {
				logError( "setpag() for %s failed\n", curuser );
				fail = 1;
			}
			if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS) {
				logError( "AFS Warning: %s\n", krb_get_err_text( ret ) );
				fail = 1;
			}
			if (fail)
				displayMsg( V_MSG_ERR,
				            "Warning: Problems during Kerberos4/AFS setup." );
		}
	}
#  endif /* KERBEROS && AFS */
# endif /* _AIX */
#endif	/* !PAM */

	curuid = p->pw_uid;
	curgid = p->pw_gid;

	env = baseEnv( curuser );
	xma = 0;
	strApp( &xma, "method=", curtype, (char *)0 );
	if (td_setup)
		strApp( &xma, ",auto", (char *)0 );
	if (xma) {
		env = setEnv( env, "XDM_MANAGED", xma );
		free( xma );
	}
	if (td->autoLock && cursource == PWSRC_AUTOLOGIN)
		env = setEnv( env, "DESKTOP_LOCKED", "true" );
	env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath );
	env = setEnv( env, "SHELL", p->pw_shell );
	env = setEnv( env, "HOME", p->pw_dir );
#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
	if (krbtkfile[0] != '\0')
		env = setEnv( env, "KRBTKFILE", krbtkfile );
#endif
	userEnviron = inheritEnv( env, envvars );
	env = systemEnv( curuser );
	systemEnviron = setEnv( env, "HOME", p->pw_dir );
	debug( "user environment:\n%[|''>'\n's"
	       "system environment:\n%[|''>'\n's"
	       "end of environments\n",
	       userEnviron,
	       systemEnviron );

	/*
	 * for user-based authorization schemes,
	 * add the user to the server's allowed "hosts" list.
	 */
	for (i = 0; i < td->authNum; i++) {
#ifdef SECURE_RPC
		if (td->authorizations[i]->name_length == 9 &&
		    !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 ))
		{
			XHostAddress addr;
			char netname[MAXNETNAMELEN+1];
			char domainname[MAXNETNAMELEN+1];

			getdomainname( domainname, sizeof(domainname) );
			user2netname( netname, curuid, domainname );
			addr.family = FamilyNetname;
			addr.length = strlen( netname );
			addr.address = netname;
			XAddHost( dpy, &addr );
		}
#endif
#ifdef K5AUTH
		if (td->authorizations[i]->name_length == 14 &&
		    !memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 ))
		{
			/* Update server's auth file with user-specific info.
			 * Don't need to AddHost because X server will do that
			 * automatically when it reads the cache we are about
			 * to point it at.
			 */
			XauDisposeAuth( td->authorizations[i] );
			td->authorizations[i] =
				krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name );
			saveServerAuthorizations( td, td->authorizations, td->authNum );
		}
#endif
	}

	if (*dmrcDir)
		mergeSessionArgs( TRUE );

	debug( "now starting the session\n" );

#ifdef USE_PAM

# ifdef HAVE_SETUSERCONTEXT
	if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) {
		logError( "setusercontext(groups) for %s failed: %m\n",
		          curuser );
		goto pError;
	}
# else
	if (!setGid( curuser, curgid ))
		goto pError;
# endif

# ifndef HAVE_PAM_GETENVLIST
	if (!(pam_env = initStrArr( 0 ))) {
		resetGids();
		goto pError;
	}
	saved_env = environ;
	environ = pam_env;
# endif
	removeCreds = 1; /* set it first - i don't trust PAM's rollback */
	pretc = pam_setcred( pamh, 0 );
	reInitErrorLog();
# ifndef HAVE_PAM_GETENVLIST
	pam_env = environ;
	environ = saved_env;
# endif
# ifdef HAVE_INITGROUPS
	/* This seems to be a strange place for it, but do it:
	   - after the initial groups are set
	   - after pam_setcred might have set something, even in the error case
	   - before pam_setcred(DELETE_CRED) might need it
	 */
	if (!saveGids())
		goto pError;
# endif
	if (pretc != PAM_SUCCESS) {
		logError( "pam_setcred() for %s failed: %s\n",
		          curuser, pam_strerror( pamh, pretc ) );
		resetGids();
		return 0;
	}

	removeSession = 1; /* set it first - same as above */
	pretc = pam_open_session( pamh, 0 );
	reInitErrorLog();
	if (pretc != PAM_SUCCESS) {
		logError( "pam_open_session() for %s failed: %s\n",
		          curuser, pam_strerror( pamh, pretc ) );
		resetGids();
		return 0;
	}

	/* we don't want sessreg and the startup/reset scripts run with user
	   credentials. unfortunately, we can reset only the gids. */
	resetGids();

# define D_LOGIN_SETGROUP LOGIN_SETGROUP
#else /* USE_PAM */
# define D_LOGIN_SETGROUP 0
#endif /* USE_PAM */

	removeAuth = 1;
	chownCtrl( &td->ctrl, curuid );
	endpwent();
#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX)
	endspent();
#endif
	ctltalk.pipe = &ctlpipe;
	ASPrintf( &buf, "sub-daemon for display %s", td->name );
	ASPrintf( &buf2, "client for display %s", td->name );
	switch (gFork( &ctlpipe, buf, buf2, 0, 0, mstrtalk.pipe, pid )) {
	case 0:

		gCloseOnExec( ctltalk.pipe );
		if (Setjmp( ctltalk.errjmp ))
			exit( 1 );

		gCloseOnExec( mstrtalk.pipe );
		if (Setjmp( mstrtalk.errjmp ))
			goto cError;

#ifndef NOXDMTITLE
		setproctitle( "%s'", td->name );
#endif
		strApp( &prog, " '", (char *)0 );
		reInitErrorLog();

		setsid();

		sessreg( td, getpid(), curuser, curuid );

		/* We do this here, as we want to have the session as parent. */
		switch (source( systemEnviron, td->startup, td_setup )) {
		case 0:
			break;
		case wcCompose( 0, 0, 127 ):
			goto cError;
		default: /* Explicit failure => message already displayed. */
			logError( "Startup script returned non-zero exit code\n" );
			exit( 1 );
		}

	/* Memory leaks are ok here as we exec() soon. */

#if defined(USE_PAM) || !defined(_AIX)

# ifdef USE_PAM
		/* pass in environment variables set by libpam and modules it called */
#  ifdef HAVE_PAM_GETENVLIST
		pam_env = pam_getenvlist( pamh );
		reInitErrorLog();
#  endif
		if (pam_env)
			for (; *pam_env; pam_env++)
				userEnviron = putEnv( *pam_env, userEnviron );
# endif

# ifdef HAVE_SETLOGIN
		if (setlogin( curuser ) < 0) {
			logError( "setlogin for %s failed: %m\n", curuser );
			goto cError;
		}
#  define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
# else
#  define D_LOGIN_SETLOGIN 0
# endif

# if defined(USE_PAM) && defined(HAVE_INITGROUPS)
		if (!restoreGids())
			goto cError;
# endif

# ifndef HAVE_SETUSERCONTEXT

#  ifdef USE_PAM
		if (!setUid( curuser, curuid ))
			goto cError;
#  else
		if (!setUser( curuser, curuid, curgid ))
			goto cError;
#  endif

# else /* !HAVE_SETUSERCONTEXT */

		/*
		 * Destroy environment.
		 * We need to do this before setusercontext() because that may
		 * set or reset some environment variables.
		 */
		if (!(environ = initStrArr( 0 )))
			goto cError;

		/*
		 * Set the user's credentials: uid, gid, groups,
		 * environment variables, resource limits, and umask.
		 */
		if (setusercontext( lc, p, p->pw_uid,
		        LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0)
		{
			logError( "setusercontext for %s failed: %m\n", curuser );
			goto cError;
		}

		for (i = 0; environ[i]; i++)
			userEnviron = putEnv( environ[i], userEnviron );

# endif /* !HAVE_SETUSERCONTEXT */

#else /* PAM || !_AIX */
		/*
		 * Set the user's credentials: uid, gid, groups,
		 * audit classes, user limits, and umask.
		 */
		if (setpcred( curuser, NULL ) == -1) {
			logError( "setpcred for %s failed: %m\n", curuser );
			goto cError;
		}

		/*
		 * Set the users process environment. Store protected variables and
		 * obtain updated user environment list. This call will initialize
		 * global 'newenv'.
		 */
		if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC,
		             userEnviron, NULL ) != 0)
		{
			logError( "Cannot set %s's process environment\n", curuser );
			goto cError;
		}
		userEnviron = newenv;

#endif /* _AIX */

		/*
		 * for user-based authorization schemes,
		 * use the password to get the user's credentials.
		 */
#ifdef SECURE_RPC
		/* do like "keylogin" program */
		if (!curpass[0])
			logInfo( "No password for NIS provided.\n" );
		else {
			char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
			int nameret, keyret;
			int len;
			int key_set_ok = 0;
			struct key_netstarg netst;

			nameret = getnetname( netname );
			debug( "user netname: %s\n", netname );
			len = strlen( curpass );
			if (len > 8)
				bzero( curpass + 8, len - 8 );
			keyret = getsecretkey( netname, secretkey, curpass );
			debug( "getsecretkey returns %d, key length %d\n",
			       keyret, strlen( secretkey ) );
			netst.st_netname = netname;
			memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES );
			memset( netst.st_pub_key, 0, HEXKEYBYTES );
			if (key_setnet( &netst ) < 0)
				debug( "Could not set secret key.\n" );
			/* is there a key, and do we have the right password? */
			if (keyret == 1) {
				if (*secretkey) {
					keyret = key_setsecret( secretkey );
					debug( "key_setsecret returns %d\n", keyret );
					if (keyret == -1)
						logError( "Failed to set NIS secret key\n" );
					else
						key_set_ok = 1;
				} else {
					/* found a key, but couldn't interpret it */
					logError( "Password incorrect for NIS principal %s\n",
					          nameret ? netname : curuser );
				}
			}
			if (!key_set_ok)
				nukeAuth( 9, "SUN-DES-1" );
			bzero( secretkey, strlen( secretkey ) );
		}
#endif
#ifdef K5AUTH
		/* do like "kinit" program */
		if (!curpass[0])
			logInfo( "No password for Kerberos5 provided.\n" );
		else
			if ((str = krb5Init( curuser, curpass, td->name )))
				userEnviron = setEnv( userEnviron, "KRB5CCNAME", str );
			else
				nukeAuth( 14, "MIT-KERBEROS-5" );
#endif /* K5AUTH */
		if (td->autoReLogin) {
			gSet( &mstrtalk );
			gSendInt( D_ReLogin );
			gSendStr( curuser );
			gSendStr( curpass );
			gSendStr( newdmrc );
		}
		if (curpass)
			bzero( curpass, strlen( curpass ) );
		setUserAuthorization( td );
		home = getEnv( userEnviron, "HOME" );
		if (home && chdir( home ) < 0) {
			logError( "Cannot chdir to %s's home %s: %m\n", curuser, home );
			sendStr( V_MSG_ERR, "Cannot enter home directory. Using /.\n" );
			chdir( "/" );
			userEnviron = setEnv( userEnviron, "HOME", "/" );
			home = 0;
		}
		if (home || td->clientLogFile[0] == '/') {
			if (!createClientLog( td->clientLogFile )) {
				logWarn( "Session log file according to %s cannot be created: %m\n",
				         td->clientLogFile );
				goto tmperr;
			}
		} else {
		  tmperr:
			if (!createClientLog( td->clientLogFallback ))
				logError( "Fallback session log file according to %s cannot be created: %m\n",
				          td->clientLogFallback );
			/* Could inform the user, but I guess this is only confusing. */
		}
		if (!*dmrcDir)
			mergeSessionArgs( home != 0 );
		if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 )))
			desksess = "failsafe"; /* only due to OOM */
		gSet( &mstrtalk );
		gSendInt( D_User );
		gSendInt( curuid );
		gSendStr( curuser );
		gSendStr( desksess );
		close( mstrtalk.pipe->fd.w );
		userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess );
		for (i = 0; td->sessionsDirs[i]; i++) {
			fname = 0;
			if (strApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) {
				if ((str = iniLoad( fname ))) {
					if (!strCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) ||
					    !(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 )))
						sessargs = "";
					free( str );
					free( fname );
					goto gotit;
				}
				free( fname );
			}
		}
		if (!strcmp( desksess, "failsafe" ) ||
		    !strcmp( desksess, "default" ) ||
		    !strcmp( desksess, "custom" ))
			sessargs = desksess;
		else
			sessargs = "";
	  gotit:
		if (!(argv = parseArgs( (char **)0, td->session )) ||
		    !(argv = addStrArr( argv, sessargs, -1 )))
			exit( 1 );
		if (argv[0] && *argv[0]) {
			debug( "executing session %\"[s\n", argv );
			execute( argv, userEnviron );
			logError( "Session %\"s execution failed: %m\n", argv[0] );
		} else
			logError( "Session has no command/arguments\n" );
		failsafeArgv[0] = td->failsafeClient;
		failsafeArgv[1] = 0;
		execute( failsafeArgv, userEnviron );
		logError( "Failsafe client %\"s execution failed: %m\n",
		          failsafeArgv[0] );
	  cError:
		sendStr( V_MSG_ERR, 0 );
		exit( 1 );
	case -1:
		free( buf );
		return 0;
	}
	debug( "StartSession, fork succeeded %d\n", *pid );
	free( buf );

	gSet( &ctltalk );
	if (!Setjmp( ctltalk.errjmp ))
		while (gRecvCmd( &i )) {
			buf = gRecvStr();
			displayStr( i, buf );
			free( buf );
			gSet( &ctltalk );
			gSendInt( 0 );
		}
	gClosen( ctltalk.pipe );
	finishGreet();

	return 1;
}