summaryrefslogtreecommitdiffstats
path: root/tdm/backend
diff options
context:
space:
mode:
Diffstat (limited to 'tdm/backend')
-rw-r--r--tdm/backend/CMakeLists.txt48
-rw-r--r--tdm/backend/Imakefile203
-rw-r--r--tdm/backend/Makefile.am47
-rw-r--r--tdm/backend/access.c468
-rw-r--r--tdm/backend/auth.c1261
-rw-r--r--tdm/backend/bootman.c277
-rw-r--r--tdm/backend/choose.c1051
-rw-r--r--tdm/backend/client.c1857
-rw-r--r--tdm/backend/consolekit.c557
-rw-r--r--tdm/backend/consolekit.h36
-rw-r--r--tdm/backend/ctrl.c1035
-rw-r--r--tdm/backend/daemon.c78
-rw-r--r--tdm/backend/dm.c1669
-rw-r--r--tdm/backend/dm.h630
-rw-r--r--tdm/backend/dm_auth.h105
-rw-r--r--tdm/backend/dm_error.h58
-rw-r--r--tdm/backend/dm_socket.h72
-rw-r--r--tdm/backend/dpylist.c294
-rw-r--r--tdm/backend/error.c130
-rw-r--r--tdm/backend/genauth.c500
-rw-r--r--tdm/backend/getfd.c81
-rw-r--r--tdm/backend/getfd.h1
-rw-r--r--tdm/backend/greet.h278
-rw-r--r--tdm/backend/inifile.c255
-rw-r--r--tdm/backend/krb5auth.c248
-rw-r--r--tdm/backend/mitauth.c87
-rw-r--r--tdm/backend/netaddr.c219
-rw-r--r--tdm/backend/policy.c278
-rw-r--r--tdm/backend/printf.c872
-rw-r--r--tdm/backend/process.c762
-rw-r--r--tdm/backend/protodpy.c141
-rw-r--r--tdm/backend/reset.c111
-rw-r--r--tdm/backend/resource.c486
-rw-r--r--tdm/backend/rpcauth.c89
-rw-r--r--tdm/backend/server.c376
-rw-r--r--tdm/backend/session.c813
-rw-r--r--tdm/backend/sessreg.c307
-rw-r--r--tdm/backend/socket.c418
-rw-r--r--tdm/backend/streams.c127
-rw-r--r--tdm/backend/util.c637
-rw-r--r--tdm/backend/xdmauth.c267
-rw-r--r--tdm/backend/xdmcp.c1165
42 files changed, 18394 insertions, 0 deletions
diff --git a/tdm/backend/CMakeLists.txt b/tdm/backend/CMakeLists.txt
new file mode 100644
index 000000000..9f9d0430f
--- /dev/null
+++ b/tdm/backend/CMakeLists.txt
@@ -0,0 +1,48 @@
+#################################################
+#
+# (C) 2010-2011 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+# FIXME this is far from complete!!!
+
+include_directories(
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${DBUS_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${DBUS_LIBRARY_DIRS}
+)
+
+##### tdm (executable) ##########################
+
+add_custom_command( OUTPUT config.ci
+ COMMAND perl -w ${CMAKE_SOURCE_DIR}/tdm/confproc.pl ${CMAKE_SOURCE_DIR}/tdm/config.def config.ci
+ DEPENDS ${CMAKE_SOURCE_DIR}/tdm/confproc.pl ${CMAKE_SOURCE_DIR}/tdm/config.def )
+
+set_property( SOURCE auth.c APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config.ci )
+
+if( WITH_XDMCP )
+ set( XDMCP_LIBRARIES "Xdmcp" )
+else()
+ set( XDMCP_LIBRARIES "" )
+endif()
+
+tde_add_executable( tdm
+ SOURCES
+ access.c auth.c bootman.c choose.c client.c consolekit.c
+ ctrl.c daemon.c dm.c dpylist.c error.c genauth.c getfd.c
+ inifile.c krb5auth.c mitauth.c netaddr.c policy.c
+ process.c protodpy.c reset.c resource.c rpcauth.c
+ server.c session.c sessreg.c socket.c streams.c
+ util.c xdmauth.c xdmcp.c
+ LINK X11 ${XAU_LIBRARIES} ${DBUS_LIBRARIES} ${CRYPT_LIBRARY} ${PAM_LIBRARY} ${XDMCP_LIBRARIES}
+ DESTINATION ${BIN_INSTALL_DIR}
+)
diff --git a/tdm/backend/Imakefile b/tdm/backend/Imakefile
new file mode 100644
index 000000000..f3b4e0050
--- /dev/null
+++ b/tdm/backend/Imakefile
@@ -0,0 +1,203 @@
+/* well, we have no subdirs ...
+#define PassCDebugFlags 'CDEBUGFLAGS=$(CDEBUGFLAGS)'
+*/
+
+#ifdef DEBUG
+CDEBUGFLAGS := $(CDEBUGFLAGS) -g
+#endif
+
+#ifndef BuildBoth
+#define BuildBoth (defined(LinuxArchitecture) && !UseElfFormat)
+#endif
+
+#ifndef LinuxShadowSuite
+#define LinuxShadowSuite NO
+#endif
+
+#if FSUseSyslog
+LOG_DEFINES = -DUSE_SYSLOG
+#endif
+
+#ifdef NoXDMCP
+XDMCPLIB =
+#else
+XDMCP_DEFINES = -DXDMCP
+#endif
+
+#if HasXdmAuth
+XDMAUTH_DEFINES = -DHASXDMAUTH
+XDMAUTHOBJS = xdmauth.o
+XDMAUTHSRCS = xdmauth.c
+#endif
+
+#if HasSecureRPC
+RPC_DEFINES = -DSECURE_RPC
+RPCOBJS = rpcauth.o
+RPCSRCS = rpcauth.c
+RPCLIB = -lrpcsvc
+#endif
+
+#if HasKrbIV
+#if NOAFS
+KRBIV_DEFINES = KrbIVDefines -DNO_AFS
+#else
+KRBIV_DEFINES = KrbIVDefines
+#endif
+KRBIV_INCLUDES = KrbIVIncludes
+KRBIVLIB = KrbIVLibraries
+#endif
+
+#if HasKrb5
+KRB5_DEFINES = Krb5Defines
+KRB5_INCLUDE = Krb5Includes
+KRB5OBJS = krb5auth.o
+KRB5SRCS = krb5auth.c
+#endif
+
+/* This is correct for Linux and FreeBSD */
+#if HasPam
+PAM_LIBRARIES = PamLibraries
+PAM_DEFINES = -DUSE_PAM
+#endif
+
+#if HasPam
+#undef HasShadowPasswd
+#define HasShadowPasswd NO
+#undef HasLibCrypt
+#define HasLibCrypt NO
+#endif
+
+/*
+#if HasBSDAuth
+BSDAUTH_DEFINES = -DUSE_BSDAUTH
+#endif
+*/
+
+#if SystemV4 || HasShadowPasswd
+
+#if !LinuxShadowSuite
+PWD_DEFINES = -DUSESHADOW
+#else
+PWD_DEFINES = -DUSESHADOW -DSHADOWSUITE
+#endif
+
+#if !defined(i386IscArchitecture) && !defined(i386ScoArchitecture) && !defined(LinuxArchitecture) && !defined(NTOArchitecture) && !defined(SGIArchitecture)
+SYS_LIBRARIES3 = -lresolv
+#endif
+#if SystemV || defined(SequentArchitecture)
+SYS_LIBRARIES1 = -lsec
+#endif
+#if defined(LinuxArchitecture) && (!UseElfFormat || LinuxShadowSuite)
+SYS_LIBRARIES1 = -lshadow
+#endif
+
+#endif
+
+#if defined(UltrixArchitecture)
+SYS_LIBRARIES1 = -lauth
+#endif
+
+#if (defined(AIXArchitecture) && (OSMajorVersion >= 3))
+SYS_LIBRARIES1 = -ls
+#endif
+
+#if HasLibCrypt
+#ifdef SpecialLibCrypt
+CRYPT_LIBRARIES = SpecialLibCrypt
+#else
+CRYPT_LIBRARIES = -lcrypt
+#if defined(LynxOSArchitecture)
+CRYPT_DEFINES = -DHAS_CRYPT
+#endif
+#endif
+#endif
+
+#if HasBSD44Sockets
+SOCK_DEFINES = -DBSD44SOCKETS
+#endif
+
+#if defined(i386Architecture) || defined(AmigaArchitecture)
+FRAGILE_DEFINES = -DFRAGILE_DEV_MEM
+#endif
+
+#ifdef RandomDefines
+RANDOM_DEFINES = RandomDefines
+#elif defined(OpenBSDArchitecture)
+RANDOM_DEFINES = -DARC4_RANDOM
+#elif defined(LinuxArchitecture)
+RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\"
+#elif defined(NetBSDArchitecture) && \
+ ((OSMajorVersion > 1) || \
+ (OSMajorVersion == 1 && OSMinorVersion > 3))
+RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\"
+#endif
+
+
+#if HasSetUserContext
+USER_CONTEXT_DEFINES = -DHAS_SETUSERCONTEXT
+# XXX - only FreeBSD has this in libutil
+SYS_LIBRARIES1 = -lutil
+#endif
+
+#if HasSetProcTitle
+PROCTITLE_DEFINES = -DHAS_SETPROCTITLE
+#endif
+
+ SYS_LIBRARIES = $(SYS_LIBRARIES1) $(SYS_LIBRARIES2) $(SYS_LIBRARIES3)
+
+ INCLUDES = $(KRB5_INCLUDE)
+ DEPLIBS = $(DEPXLIB) $(DEPXAUTHLIB) $(DEPXDMCPLIB)
+ LOCAL_LIBRARIES = $(XLIB) $(XAUTHLIB) \
+ $(XDMCPLIB) $(RPCLIB) $(PAM_LIBRARIES) \
+ $(CRYPT_LIBRARIES) $(KRBIVLIB)
+
+ COMMSRCS = auth.c daemon.c server.c dpylist.c dm.c error.c \
+ netaddr.c reset.c resource.c protodpy.c policy.c \
+ session.c socket.c streams.c util.c xdmcp.c \
+ process.c mitauth.c \
+ genauth.c access.c choose.c consolekit.c \
+ $(XDMAUTHSRCS) $(RPCSRCS) $(KRB5SRCS)
+ COMMOBJS = auth.o daemon.o server.o dpylist.o dm.o error.o \
+ netaddr.o reset.o resource.o protodpy.o policy.o \
+ session.o socket.o streams.o util.o xdmcp.o \
+ process.o mitauth.o \
+ genauth.o access.o choose.o consolekit.o \
+ $(XDMAUTHOBJS) $(RPCOBJS) $(KRB5OBJS)
+
+ SRCS1 = $(COMMSRCS) client.c
+ OBJS1 = $(COMMOBJS) client.o
+
+#if BuildBoth
+ SRCS2 = $(COMMSRCS) clientsh.c
+ OBJS2 = $(COMMOBJS) clientsh.o
+
+ XDM_SHADOW = xdm-shadow
+#endif
+
+ PROGRAMS = xdm $(XDM_SHADOW)
+
+
+ OSMAJORVERSION = OSMajorVersion
+ OSMINORVERSION = OSMinorVersion
+ CONN_DEFINES = $(CONNECTION_FLAGS)
+ DEFINES = $(SIGNAL_DEFINES) $(LOG_DEFINES) \
+ $(CRYPT_DEFINES)$(PWD_DEFINES) \
+ $(BSDAUTH_DEFINES) $(PAM_DEFINES) $(USER_CONTEXT_DEFINES) \
+ $(XDMAUTH_DEFINES) $(RPC_DEFINES) $(KRB5_DEFINES) \
+ $(XDMCP_DEFINES) $(SOCK_DEFINES) $(CONN_DEFINES) \
+ $(FRAGILE_DEFINES) $(RANDOM_DEFINES) $(PROCTITLE_DEFINES) \
+ -DOSMAJORVERSION=$(OSMAJORVERSION) -DOSMINORVERSION=$(OSMINORVERSION) \
+ -Dconst=
+
+ComplexProgramTarget_1(xdm,$(LOCAL_LIBRARIES),NullParameter)
+#if BuildBoth
+NormalProgramTarget(xdm-shadow,$(OBJS2),$(DEPLIBS),$(LOCAL_LIBRARIES),-lshadow)
+InstallProgram(xdm-shadow,$(BINDIR))
+ObjectFromSpecialSource(clientsh,client,-DUSESHADOW)
+#endif
+
+#if defined(FreeBSDArchitecture) && (OSMajorVersion < 2)
+XCOMM only for daemon.c? it's used in some other places, too.
+SpecialCObjectRule(daemon,$(ICONFIGFILES),-UCSRG_BASED)
+#endif
+
diff --git a/tdm/backend/Makefile.am b/tdm/backend/Makefile.am
new file mode 100644
index 000000000..6b5779d51
--- /dev/null
+++ b/tdm/backend/Makefile.am
@@ -0,0 +1,47 @@
+# forcibly remove thread-related defines & flags
+AUTOMAKE_OPTIONS = foreign
+CPPFLAGS = $(USER_INCLUDES) $(X_INCLUDES) $(KRB4_INCS) $(KRB5_INCS) $(DBUS_INCS) -I.. -I../..
+LDFLAGS = $(USER_LDFLAGS) $(X_LDFLAGS) $(X_RPATH) $(KRB4_RPATH) $(KRB5_RPATH)
+LDADD = $(LIB_X11) -lXau $(LIBXDMCP) $(PASSWDLIBS) $(LIBSHADOW) $(LIBGEN) \
+ $(LIB_LIBS) $(KRB4_LIBS) $(KRB5_LIBS) $(DBUS_LIBS) $(LIBSOCKET) $(LIBRESOLV) \
+ $(LIBUCB) $(LIBUTIL) $(LIBPOSIX4)
+
+bin_PROGRAMS = tdm
+tdm_SOURCES = \
+ access.c \
+ auth.c \
+ bootman.c \
+ choose.c \
+ client.c \
+ consolekit.c \
+ ctrl.c \
+ daemon.c \
+ dm.c \
+ dpylist.c \
+ error.c \
+ genauth.c \
+ inifile.c \
+ krb5auth.c \
+ mitauth.c \
+ netaddr.c \
+ policy.c \
+ process.c \
+ protodpy.c \
+ reset.c \
+ resource.c \
+ rpcauth.c \
+ server.c \
+ session.c \
+ sessreg.c \
+ socket.c \
+ streams.c \
+ util.c \
+ xdmauth.c \
+ xdmcp.c
+
+EXTRA_DIST = printf.c
+
+noinst_HEADERS = dm.h dm_socket.h dm_error.h dm_auth.h greet.h
+
+# for unsermake (automake is handled by SUBDIRS in ../)
+tdm_COMPILE_FIRST = ../config.ci
diff --git a/tdm/backend/access.c b/tdm/backend/access.c
new file mode 100644
index 000000000..82736be57
--- /dev/null
+++ b/tdm/backend/access.c
@@ -0,0 +1,468 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2001,2004 Oswald Buddenhagen <[email protected]>
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+
+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
+ *
+ * Access control for XDMCP - keep a database of allowable display addresses
+ * and (potentially) a list of hosts to send ForwardQuery packets to
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <netdb.h>
+#if defined(IPv6) && defined(AF_INET6)
+# include <arpa/inet.h>
+#endif
+
+typedef struct {
+ short int type;
+ union {
+ char *aliasPattern;
+ char *hostPattern;
+ struct _displayAddress {
+ CARD16 connectionType;
+ ARRAY8 hostAddress;
+ } displayAddress;
+ } entry;
+} HostEntry;
+
+typedef struct {
+ short int iface;
+ short int mcasts;
+ short int nmcasts;
+} ListenEntry;
+
+typedef struct {
+ char *name;
+ short int hosts;
+ short int nhosts;
+} AliasEntry;
+
+typedef struct {
+ short int entries;
+ short int nentries;
+ short int hosts;
+ short int nhosts;
+ short int flags;
+} AclEntry;
+
+typedef struct {
+ HostEntry *hostList;
+ ListenEntry *listenList;
+ AliasEntry *aliasList;
+ AclEntry *acList;
+ short int nHosts, nListens, nAliases, nAcls;
+ CfgDep dep;
+} AccArr;
+
+static AccArr accData[1];
+
+
+static ARRAY8 localAddress;
+
+ARRAY8Ptr
+getLocalAddress( void )
+{
+ static int haveLocalAddress;
+
+ if (!haveLocalAddress) {
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai;
+
+ if (getaddrinfo( localHostname(), NULL, NULL, &ai )) {
+ XdmcpAllocARRAY8( &localAddress, 4 );
+ localAddress.data[0] = 127;
+ localAddress.data[1] = 0;
+ localAddress.data[2] = 0;
+ localAddress.data[3] = 1;
+ } else {
+ if (ai->ai_family == AF_INET) {
+ XdmcpAllocARRAY8( &localAddress, sizeof(struct in_addr) );
+ memcpy( localAddress.data,
+ &((struct sockaddr_in *)ai->ai_addr)->sin_addr,
+ sizeof(struct in_addr) );
+ } else /* if (ai->ai_family == AF_INET6) */ {
+ XdmcpAllocARRAY8( &localAddress, sizeof(struct in6_addr) );
+ memcpy( localAddress.data,
+ &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,
+ sizeof(struct in6_addr) );
+ }
+ freeaddrinfo( ai );
+#else
+ struct hostent *hostent;
+
+ if ((hostent = gethostbyname( localHostname() ))) {
+ XdmcpAllocARRAY8( &localAddress, hostent->h_length );
+ memmove( localAddress.data, hostent->h_addr, hostent->h_length );
+#endif
+ haveLocalAddress = 1;
+ }
+ }
+ return &localAddress;
+}
+
+
+void
+ScanAccessDatabase( int force )
+{
+ struct _displayAddress *da;
+ char *cptr;
+ int nChars, i;
+
+ Debug( "ScanAccessDatabase\n" );
+ if (Setjmp( cnftalk.errjmp ))
+ return; /* may memleak */
+ if (startConfig( GC_gXaccess, &accData->dep, force ) <= 0)
+ return;
+ if (accData->hostList)
+ free( accData->hostList );
+ accData->nHosts = GRecvInt();
+ accData->nListens = GRecvInt();
+ accData->nAliases = GRecvInt();
+ accData->nAcls = GRecvInt();
+ nChars = GRecvInt();
+ if (!(accData->hostList = (HostEntry *)
+ Malloc( accData->nHosts * sizeof(HostEntry) +
+ accData->nListens * sizeof(ListenEntry) +
+ accData->nAliases * sizeof(AliasEntry) +
+ accData->nAcls * sizeof(AclEntry) +
+ nChars )))
+ {
+ CloseGetter();
+ return;
+ }
+ accData->listenList = (ListenEntry *)(accData->hostList + accData->nHosts);
+ accData->aliasList = (AliasEntry *)(accData->listenList + accData->nListens);
+ accData->acList = (AclEntry *)(accData->aliasList + accData->nAliases);
+ cptr = (char *)(accData->acList + accData->nAcls);
+ for (i = 0; i < accData->nHosts; i++) {
+ switch ((accData->hostList[i].type = GRecvInt())) {
+ case HOST_ALIAS:
+ accData->hostList[i].entry.aliasPattern = cptr;
+ cptr += GRecvStrBuf( cptr );
+ break;
+ case HOST_PATTERN:
+ accData->hostList[i].entry.hostPattern = cptr;
+ cptr += GRecvStrBuf( cptr );
+ break;
+ case HOST_ADDRESS:
+ da = &accData->hostList[i].entry.displayAddress;
+ da->hostAddress.data = (unsigned char *)cptr;
+ cptr += (da->hostAddress.length = GRecvArrBuf( cptr ));
+ switch (GRecvInt())
+ {
+#ifdef AF_INET
+ case AF_INET:
+ da->connectionType = FamilyInternet;
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ da->connectionType = FamilyInternet6;
+ break;
+#endif
+#ifdef AF_DECnet
+ case AF_DECnet:
+ da->connectionType = FamilyDECnet;
+ break;
+#endif
+/*#ifdef AF_UNIX
+ case AF_UNIX:
+#endif*/
+ default:
+ da->connectionType = FamilyLocal;
+ break;
+ }
+ break;
+ case HOST_BROADCAST:
+ break;
+ default:
+ LogError( "Received unknown host type %d from config reader\n", accData->hostList[i].type );
+ return;
+ }
+ }
+ for (i = 0; i < accData->nListens; i++) {
+ accData->listenList[i].iface = GRecvInt();
+ accData->listenList[i].mcasts = GRecvInt();
+ accData->listenList[i].nmcasts = GRecvInt();
+ }
+ for (i = 0; i < accData->nAliases; i++) {
+ accData->aliasList[i].name = cptr;
+ cptr += GRecvStrBuf( cptr );
+ accData->aliasList[i].hosts = GRecvInt();
+ accData->aliasList[i].nhosts = GRecvInt();
+ }
+ for (i = 0; i < accData->nAcls; i++) {
+ accData->acList[i].entries = GRecvInt();
+ accData->acList[i].nentries = GRecvInt();
+ accData->acList[i].hosts = GRecvInt();
+ accData->acList[i].nhosts = GRecvInt();
+ accData->acList[i].flags = GRecvInt();
+ }
+}
+
+
+/* Returns non-0 if string is matched by pattern. Does case folding.
+ */
+static int
+patternMatch( const char *string, const char *pattern )
+{
+ int p, s;
+
+ if (!string)
+ string = "";
+
+ for (;;) {
+ s = *string++;
+ switch (p = *pattern++) {
+ case '*':
+ if (!*pattern)
+ return 1;
+ for (string--; *string; string++)
+ if (patternMatch( string, pattern ))
+ return 1;
+ return 0;
+ case '?':
+ if (s == '\0')
+ return 0;
+ break;
+ case '\0':
+ return s == '\0';
+ case '\\':
+ p = *pattern++;
+ /* fall through */
+ default:
+ if (tolower( p ) != tolower( s ))
+ return 0;
+ }
+ }
+}
+
+
+#define MAX_DEPTH 32
+
+static void
+scanHostlist( int fh, int nh,
+ ARRAY8Ptr clientAddress, CARD16 connectionType,
+ ChooserFunc function, char *closure,
+ int broadcast, int *haveLocalhost )
+{
+ HostEntry *h;
+ AliasEntry *a;
+ int na;
+
+ for (h = accData->hostList + fh; nh; nh--, h++) {
+ switch (h->type) {
+ case HOST_ALIAS:
+ for (a = accData->aliasList, na = accData->nAliases; na; na--, a++)
+ if (patternMatch( a->name, h->entry.aliasPattern )) /* XXX originally swapped, no wildcards in alias name matching */
+ scanHostlist( a->hosts, a->nhosts,
+ clientAddress, connectionType,
+ function, closure, broadcast,
+ haveLocalhost );
+ break;
+ case HOST_ADDRESS:
+ if (XdmcpARRAY8Equal( getLocalAddress(), &h->entry.displayAddress.hostAddress ))
+ *haveLocalhost = 1;
+ else if (function)
+ (*function)( connectionType, &h->entry.displayAddress.hostAddress, closure );
+ break;
+ case HOST_BROADCAST:
+ if (broadcast && function)
+ (*function)( FamilyBroadcast, 0, closure );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int
+scanEntrylist( int fh, int nh,
+ ARRAY8Ptr clientAddress, CARD16 connectionType,
+ char **clientName )
+{
+ HostEntry *h;
+ AliasEntry *a;
+ int na;
+
+ for (h = accData->hostList + fh; nh; nh--, h++) {
+ switch (h->type) {
+ case HOST_ALIAS:
+ for (a = accData->aliasList, na = accData->nAliases; na; na--, a++)
+ if (patternMatch( a->name, h->entry.aliasPattern ))
+ if (scanEntrylist( a->hosts, a->nhosts,
+ clientAddress, connectionType,
+ clientName ))
+ return 1;
+ break;
+ case HOST_PATTERN:
+ if (!*clientName)
+ *clientName = NetworkAddressToHostname( connectionType,
+ clientAddress );
+ if (patternMatch( *clientName, h->entry.hostPattern ))
+ return 1;
+ break;
+ case HOST_ADDRESS:
+ if (h->entry.displayAddress.connectionType == connectionType &&
+ XdmcpARRAY8Equal( &h->entry.displayAddress.hostAddress,
+ clientAddress ))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static AclEntry *
+matchAclEntry( ARRAY8Ptr clientAddress, CARD16 connectionType, int direct )
+{
+ AclEntry *e, *re;
+ char *clientName = 0;
+ int ne;
+
+ for (e = accData->acList, ne = accData->nAcls, re = 0; ne; ne--, e++)
+ if (!e->nhosts == direct)
+ if (scanEntrylist( e->entries, e->nentries,
+ clientAddress, connectionType,
+ &clientName ))
+ {
+ re = e;
+ break;
+ }
+ if (clientName)
+ free( clientName );
+ return re;
+}
+
+/*
+ * calls the given function for each valid indirect entry. Returns TRUE if
+ * the local host exists on any of the lists, else FALSE
+ */
+int
+ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress,
+ CARD16 connectionType,
+ ChooserFunc function, char *closure )
+{
+ AclEntry *e;
+ int haveLocalhost = 0;
+
+ e = matchAclEntry( clientAddress, connectionType, 0 );
+ if (e && !(e->flags & a_notAllowed)) {
+ if (e->flags & a_useChooser) {
+ ARRAY8Ptr choice;
+
+ choice = IndirectChoice( clientAddress, connectionType );
+ if (!choice || XdmcpARRAY8Equal( getLocalAddress(), choice ))
+ haveLocalhost = 1;
+ else
+ (*function)( connectionType, choice, closure );
+ } else
+ scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType,
+ function, closure, FALSE, &haveLocalhost );
+ }
+ return haveLocalhost;
+}
+
+int
+UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ AclEntry *e;
+
+ e = matchAclEntry( clientAddress, connectionType, 0 );
+ return e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser) &&
+ !IndirectChoice( clientAddress, connectionType );
+}
+
+void
+ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType,
+ ChooserFunc function, char *closure )
+{
+ AclEntry *e;
+ int haveLocalhost = 0;
+
+ e = matchAclEntry( clientAddress, connectionType, 0 );
+ if (e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser))
+ scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType,
+ function, closure, TRUE, &haveLocalhost );
+ if (haveLocalhost)
+ (*function)( connectionType, getLocalAddress(), closure );
+}
+
+/*
+ * returns TRUE if the given client is acceptable to the local host. The
+ * given display client is acceptable if it occurs without a host list.
+ */
+int
+AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType,
+ xdmOpCode type )
+{
+ AclEntry *e;
+
+ if (type == INDIRECT_QUERY)
+ return 1;
+
+ e = matchAclEntry( clientAddress, connectionType, 1 );
+ return e && !(e->flags & a_notAllowed) &&
+ (type != BROADCAST_QUERY || !(e->flags & a_notBroadcast));
+}
+
+void
+ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfunction,
+ void **closure )
+{
+ int i, j, ifc, mc, nmc;
+
+ for (i = 0; i < accData->nListens; i++) {
+ ifc = accData->listenList[i].iface;
+ (*listenfunction)( ifc < 0 ? 0 :
+ &accData->hostList[ifc].entry.displayAddress.hostAddress,
+ closure );
+ mc = accData->listenList[i].mcasts;
+ nmc = accData->listenList[i].nmcasts;
+ for (j = 0; j < nmc; j++, mc++)
+ (*mcastfunction)( &accData->hostList[mc].entry.displayAddress.hostAddress,
+ closure );
+ }
+}
+#endif /* XDMCP */
diff --git a/tdm/backend/auth.c b/tdm/backend/auth.c
new file mode 100644
index 000000000..b92881789
--- /dev/null
+++ b/tdm/backend/auth.c
@@ -0,0 +1,1261 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * maintain the authorization generation daemon
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#ifdef __OpenBSD__
+#include <pwd.h>
+#endif
+
+#include <sys/ioctl.h>
+
+#include "dm_socket.h"
+#ifdef DNETCONN
+# include <netdnet/dnetdb.h>
+#endif
+
+#if (defined(_POSIX_SOURCE) && !defined(_AIX) && !defined(__QNX__)) || defined(__hpux) || defined(__svr4__) /* XXX */
+# define NEED_UTSNAME
+# include <sys/utsname.h>
+#endif
+
+#ifdef __GNU__
+# include <netdb.h>
+# undef SIOCGIFCONF
+#else /* __GNU__ */
+# include <net/if.h>
+# ifdef __svr4__
+# include <netdb.h>
+# include <sys/sockio.h>
+# include <sys/stropts.h>
+# endif
+# ifdef __EMX__
+# define link rename
+# define chown(a,b,c)
+# include <io.h>
+# endif
+#endif /* __GNU__ */
+
+struct AuthProtocol {
+ unsigned short name_length;
+ const char *name;
+ void (*InitAuth)( unsigned short len, const char *name );
+ Xauth *(*GetAuth)( unsigned short len, const char *name );
+#ifdef XDMCP
+ void (*GetXdmcpAuth)( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName );
+#endif
+ int inited;
+};
+
+#ifdef XDMCP
+# define xdmcpauth(arg) , arg
+#else
+# define xdmcpauth(arg)
+#endif
+
+static struct AuthProtocol AuthProtocols[] = {
+{ (unsigned short)18, "MIT-MAGIC-COOKIE-1",
+ MitInitAuth, MitGetAuth xdmcpauth(NULL), 0
+},
+#ifdef HASXDMAUTH
+{ (unsigned short)19, "XDM-AUTHORIZATION-1",
+ XdmInitAuth, XdmGetAuth xdmcpauth(XdmGetXdmcpAuth), 0
+},
+#endif
+#ifdef SECURE_RPC
+{ (unsigned short)9, "SUN-DES-1",
+ SecureRPCInitAuth, SecureRPCGetAuth xdmcpauth(NULL), 0
+},
+#endif
+#ifdef K5AUTH
+{ (unsigned short)14, "MIT-KERBEROS-5",
+ Krb5InitAuth, Krb5GetAuth xdmcpauth(NULL), 0
+},
+#endif
+};
+
+static struct AuthProtocol *
+findProtocol( unsigned short name_length, const char *name )
+{
+ unsigned i;
+
+ for (i = 0; i < as(AuthProtocols); i++)
+ if (AuthProtocols[i].name_length == name_length &&
+ memcmp( AuthProtocols[i].name, name, name_length ) == 0)
+ {
+ return &AuthProtocols[i];
+ }
+ return (struct AuthProtocol *)0;
+}
+
+int
+ValidAuthorization( unsigned short name_length, const char *name )
+{
+ if (findProtocol( name_length, name ))
+ return TRUE;
+ return FALSE;
+}
+
+static Xauth *
+GenerateAuthorization( unsigned short name_length, const char *name )
+{
+ struct AuthProtocol *a;
+ Xauth *auth = 0;
+
+ Debug( "GenerateAuthorization %s\n", name );
+ if ((a = findProtocol( name_length, name ))) {
+ if (!a->inited) {
+ (*a->InitAuth)( name_length, name );
+ a->inited = TRUE;
+ }
+ auth = (*a->GetAuth)( name_length, name );
+ if (auth) {
+ Debug( "got %p (%d %.*s) %02[*hhx\n", auth,
+ auth->name_length, auth->name_length, auth->name,
+ auth->data_length, auth->data );
+ } else
+ Debug( "got (null)\n" );
+ } else
+ Debug( "unknown authorization %s\n", name );
+ return auth;
+}
+
+#ifdef XDMCP
+
+void
+SetProtoDisplayAuthorization( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName )
+{
+ struct AuthProtocol *a;
+ Xauth *auth;
+
+ a = findProtocol( authorizationNameLen, authorizationName );
+ pdpy->xdmcpAuthorization = pdpy->fileAuthorization = 0;
+ if (a) {
+ if (!a->inited) {
+ (*a->InitAuth)( authorizationNameLen, authorizationName );
+ a->inited = TRUE;
+ }
+ if (a->GetXdmcpAuth) {
+ (*a->GetXdmcpAuth)( pdpy, authorizationNameLen, authorizationName );
+ auth = pdpy->xdmcpAuthorization;
+ } else {
+ auth = (*a->GetAuth)( authorizationNameLen, authorizationName );
+ pdpy->fileAuthorization = auth;
+ pdpy->xdmcpAuthorization = 0;
+ }
+ if (auth)
+ Debug( "got %p (%d %.*s)\n", auth,
+ auth->name_length, auth->name_length, auth->name );
+ else
+ Debug( "got (null)\n" );
+ }
+}
+
+#endif /* XDMCP */
+
+void
+CleanUpFileName( const char *src, char *dst, int len )
+{
+ while (*src) {
+ if (--len <= 0)
+ break;
+ switch (*src & 0x7f) {
+ case '/':
+ *dst++ = '_';
+ break;
+ case '-':
+ *dst++ = '.';
+ break;
+ default:
+ *dst++ = (*src & 0x7f);
+ }
+ ++src;
+ }
+ *dst = '\0';
+}
+
+
+static FILE *
+fdOpenW( int fd )
+{
+ FILE *f;
+
+ if (fd >= 0) {
+ if ((f = fdopen( fd, "w" )))
+ return f;
+ close( fd );
+ }
+ return 0;
+}
+
+static FILE *
+mkTempFile( char *nambuf, int namelen )
+{
+ FILE *f;
+ int r;
+
+ for (r = 0; r < 100; r++) {
+ randomStr( nambuf + namelen );
+ if ((f = fdOpenW( open( nambuf, O_WRONLY | O_CREAT | O_EXCL, 0600 ) )))
+ return f;
+ if (errno != EEXIST)
+ break;
+ }
+ return 0;
+}
+
+#define NAMELEN 255
+
+static FILE *
+MakeServerAuthFile( struct display *d )
+{
+ FILE *f;
+ int i;
+ char cleanname[NAMELEN], nambuf[NAMELEN+128];
+
+ /*
+ * Some paranoid, but still not sufficient (DoS was still possible)
+ * checks used to be here. I removed all this stuff because
+ * a) authDir is supposed to be /var/run/xauth (=safe) or similar and
+ * b) even if it's not (say, /tmp), we create files safely (hopefully).
+ */
+ if (mkdir( authDir, 0755 ) < 0 && errno != EEXIST)
+ return 0;
+ CleanUpFileName( d->name, cleanname, NAMELEN - 8 );
+ i = sprintf( nambuf, "%s/A%s-", authDir, cleanname );
+ if ((f = mkTempFile( nambuf, i ))) {
+ StrDup( &d->authFile, nambuf );
+ return f;
+ }
+ return 0;
+}
+
+int
+SaveServerAuthorizations( struct display *d, Xauth **auths, int count )
+{
+ FILE *auth_file;
+ int i;
+
+ if (!d->authFile && d->clientAuthFile && *d->clientAuthFile)
+ StrDup( &d->authFile, d->clientAuthFile );
+ if (d->authFile) {
+ if (!(auth_file = fdOpenW( creat( d->authFile, 0600 ) ))) {
+ LogError( "Cannot open X server authorization file %s\n", d->authFile );
+ free( d->authFile );
+ d->authFile = NULL;
+ return FALSE;
+ }
+ } else {
+ if (!(auth_file = MakeServerAuthFile( d ))) {
+ LogError( "Cannot create X server authorization file\n" );
+ return FALSE;
+ }
+ }
+#ifdef __OpenBSD__
+ {
+ struct passwd *x11;
+ uid_t uid;
+ gid_t gid;
+ /* Give read capability to group _x11 */
+ x11 = getpwnam("_x11");
+ if (x11 == NULL) {
+ LogError("Can't find _x11 user\n");
+ uid = getuid();
+ gid = getgid();
+ } else {
+ uid = x11->pw_uid;
+ gid = x11->pw_gid;
+ }
+
+ fchown(fileno(auth_file), uid, gid);
+ }
+#endif
+
+ Debug( "file: %s auth: %p\n", d->authFile, auths );
+ for (i = 0; i < count; i++) {
+ /*
+ * User-based auths may not have data until
+ * a user logs in. In which case don't write
+ * to the auth file so xrdb and setup programs don't fail.
+ */
+ if (auths[i]->data_length > 0)
+ if (!XauWriteAuth( auth_file, auths[i] ) ||
+ fflush( auth_file ) == EOF)
+ {
+ fclose( auth_file );
+ LogError( "Cannot write X server authorization file %s\n",
+ d->authFile );
+ free( d->authFile );
+ d->authFile = NULL;
+ return FALSE;
+ }
+ }
+ fclose( auth_file );
+ return TRUE;
+}
+
+void
+SetLocalAuthorization( struct display *d )
+{
+ Xauth *auth, **auths;
+ int i, j;
+
+ if (d->authorizations)
+ {
+ for (i = 0; i < d->authNum; i++)
+ XauDisposeAuth( d->authorizations[i] );
+ free( (char *)d->authorizations );
+ d->authorizations = (Xauth **)NULL;
+ d->authNum = 0;
+ }
+ Debug( "SetLocalAuthorization %s, auths %[s\n", d->name, d->authNames );
+ if (!d->authNames)
+ return;
+ for (i = 0; d->authNames[i]; i++)
+ ;
+ d->authNameNum = i;
+ if (d->authNameLens)
+ free( (char *)d->authNameLens );
+ d->authNameLens = (unsigned short *)Malloc( d->authNameNum * sizeof(unsigned short) );
+ if (!d->authNameLens)
+ return;
+ for (i = 0; i < d->authNameNum; i++)
+ d->authNameLens[i] = strlen( d->authNames[i] );
+ auths = (Xauth **)Malloc( d->authNameNum * sizeof(Xauth *) );
+ if (!auths)
+ return;
+ j = 0;
+ for (i = 0; i < d->authNameNum; i++) {
+ auth = GenerateAuthorization( d->authNameLens[i], d->authNames[i] );
+ if (auth)
+ auths[j++] = auth;
+ }
+ if (SaveServerAuthorizations( d, auths, j )) {
+ d->authorizations = auths;
+ d->authNum = j;
+ } else {
+ for (i = 0; i < j; i++)
+ XauDisposeAuth( auths[i] );
+ free( (char *)auths );
+ }
+}
+
+/*
+ * Set the authorization to use for xdm's initial connection
+ * to the X server. Cannot use user-based authorizations
+ * because no one has logged in yet, so we don't have any
+ * user credentials.
+ * Well, actually we could use SUN-DES-1 because we tell the server
+ * to allow root in. This is bogus and should be fixed.
+ */
+void
+SetAuthorization( struct display *d )
+{
+ register Xauth **auth = d->authorizations;
+ int i;
+
+ for (i = 0; i < d->authNum; i++) {
+ if (auth[i]->name_length == 9 &&
+ memcmp( auth[i]->name, "SUN-DES-1", 9 ) == 0)
+ continue;
+ if (auth[i]->name_length == 14 &&
+ memcmp( auth[i]->name, "MIT-KERBEROS-5", 14 ) == 0)
+ continue;
+ XSetAuthorization( auth[i]->name, (int)auth[i]->name_length,
+ auth[i]->data, (int)auth[i]->data_length );
+ }
+}
+
+static int
+openFiles( const char *name, char *new_name, FILE **oldp, FILE **newp )
+{
+ strcat( strcpy( new_name, name ), "-n" );
+ if (!(*newp =
+ fdOpenW( creat( new_name, 0600 ) ))) {
+ Debug( "can't open new file %s\n", new_name );
+ return 0;
+ }
+ *oldp = fopen( name, "r" );
+ Debug( "opens succeeded %s %s\n", name, new_name );
+ return 1;
+}
+
+struct addrList {
+ struct addrList *next;
+ unsigned short family, address_length, number_length;
+ char data[1];
+};
+
+static struct addrList *addrs;
+
+static void
+initAddrs( void )
+{
+ addrs = 0;
+}
+
+static void
+doneAddrs( void )
+{
+ struct addrList *a, *n;
+ for (a = addrs; a; a = n) {
+ n = a->next;
+ free( a );
+ }
+}
+
+static void
+saveEntry( Xauth *auth )
+{
+ struct addrList *new;
+
+ if (!(new = Malloc( offsetof(struct addrList, data) +
+ auth->address_length + auth->number_length )))
+ return;
+ new->address_length = auth->address_length;
+ new->number_length = auth->number_length;
+ memcpy( new->data, auth->address, (int)auth->address_length );
+ memcpy( new->data + (int)auth->address_length,
+ auth->number, (int)auth->number_length );
+ new->family = auth->family;
+ new->next = addrs;
+ addrs = new;
+}
+
+static int
+checkEntry( Xauth *auth )
+{
+ struct addrList *a;
+
+ for (a = addrs; a; a = a->next)
+ if (a->family == auth->family &&
+ a->address_length == auth->address_length &&
+ !memcmp( a->data, auth->address, auth->address_length ) &&
+ a->number_length == auth->number_length &&
+ !memcmp( a->data + a->address_length,
+ auth->number, auth->number_length ))
+ return 1;
+ return 0;
+}
+
+static void
+writeAuth( FILE *file, Xauth *auth, int *ok )
+{
+ if (debugLevel & DEBUG_AUTH) /* normally too verbose */
+ Debug( "writeAuth: doWrite = %d\n"
+ "family: %d\n"
+ "addr: %02[*:hhx\n"
+ "number: %02[*:hhx\n"
+ "name: %02[*:hhx\n"
+ "data: %02[*:hhx\n",
+ ok != 0, auth->family,
+ auth->address_length, auth->address,
+ auth->number_length, auth->number,
+ auth->name_length, auth->name,
+ auth->data_length, auth->data );
+ if (ok && !XauWriteAuth( file, auth ))
+ *ok = FALSE;
+}
+
+static void
+writeAddr( int family, int addr_length, char *addr,
+ FILE *file, Xauth *auth, int *ok )
+{
+ auth->family = (unsigned short)family;
+ auth->address_length = addr_length;
+ auth->address = addr;
+ Debug( "writeAddr: writing and saving an entry\n" );
+ writeAuth( file, auth, ok );
+ if (!checkEntry( auth ))
+ saveEntry( auth );
+}
+
+static void
+DefineLocal( FILE *file, Xauth *auth, int *ok )
+{
+#if !defined(NEED_UTSNAME) || defined(__hpux)
+ char displayname[100];
+#endif
+#ifdef NEED_UTSNAME
+ struct utsname name;
+#endif
+
+ /* stolen from xinit.c */
+
+/* Make sure this produces the same string as _XGetHostname in lib/X/XlibInt.c.
+ * Otherwise, Xau will not be able to find your cookies in the Xauthority file.
+ *
+ * Note: POSIX says that the ``nodename'' member of utsname does _not_ have
+ * to have sufficient information for interfacing to the network,
+ * and so, you may be better off using gethostname (if it exists).
+ */
+
+#ifdef NEED_UTSNAME
+
+ /* hpux:
+ * Why not use gethostname()? Well, at least on my system, I've had to
+ * make an ugly kernel patch to get a name longer than 8 characters, and
+ * uname() lets me access to the whole string (it smashes release, you
+ * see), whereas gethostname() kindly truncates it for me.
+ */
+ uname( &name );
+ writeAddr( FamilyLocal, strlen( name.nodename ), name.nodename,
+ file, auth, ok );
+#endif
+
+#if !defined(NEED_UTSNAME) || defined(__hpux)
+ /* _AIX:
+ * In _AIX, _POSIX_SOURCE is defined, but uname gives only first
+ * field of hostname. Thus, we use gethostname instead.
+ */
+
+ /*
+ * For HP-UX, HP's Xlib expects a fully-qualified domain name, which
+ * is achieved by using gethostname(). For compatability, we must
+ * also still create the entry using uname() above.
+ */
+ displayname[0] = 0;
+ if (!gethostname( displayname, sizeof(displayname) ))
+ displayname[sizeof(displayname) - 1] = 0;
+
+# ifdef NEED_UTSNAME
+ /*
+ * If gethostname and uname both returned the same name,
+ * do not write a duplicate entry.
+ */
+ if (strcmp( displayname, name.nodename ))
+# endif
+ writeAddr( FamilyLocal, strlen( displayname ), displayname,
+ file, auth, ok );
+#endif
+}
+
+#ifdef SYSV_SIOCGIFCONF
+
+/* Deal with different SIOCGIFCONF ioctl semantics on SYSV, SVR4 */
+
+int
+ifioctl (int fd, int cmd, char *arg)
+{
+ struct strioctl ioc;
+ int ret;
+
+ bzero( (char *)&ioc, sizeof(ioc) );
+ ioc.ic_cmd = cmd;
+ ioc.ic_timout = 0;
+ if (cmd == SIOCGIFCONF) {
+ ioc.ic_len = ((struct ifconf *)arg)->ifc_len;
+ ioc.ic_dp = ((struct ifconf *)arg)->ifc_buf;
+ } else {
+ ioc.ic_len = sizeof(struct ifreq);
+ ioc.ic_dp = arg;
+ }
+ ret = ioctl( fd, I_STR, (char *)&ioc );
+ if (ret >= 0 && cmd == SIOCGIFCONF)
+ ((struct ifconf *)arg)->ifc_len = ioc.ic_len;
+ return (ret);
+}
+
+#endif /* SYSV_SIOCGIFCONF */
+
+#ifdef HAVE_GETIFADDRS
+# include <ifaddrs.h>
+
+static void
+DefineSelf( FILE *file, Xauth *auth, int *ok )
+{
+ struct ifaddrs *ifap, *ifr;
+ char *addr;
+ int family, len;
+
+ if (getifaddrs( &ifap ) < 0)
+ return;
+ for (ifr = ifap; ifr; ifr = ifr->ifa_next) {
+ if (!ifr->ifa_addr)
+ continue;
+ family = ConvertAddr( (char *)(ifr->ifa_addr), &len, &addr );
+ if (family == -1 || family == FamilyLocal)
+ continue;
+ /*
+ * don't write out 'localhost' entries, as
+ * they may conflict with other local entries.
+ * DefineLocal will always be called to add
+ * the local entry anyway, so this one can
+ * be tossed.
+ */
+ if (family == FamilyInternet &&
+ addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1)
+ {
+ Debug( "Skipping localhost address\n" );
+ continue;
+ }
+# if defined(IPv6) && defined(AF_INET6)
+ if (family == FamilyInternet6) {
+ if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) {
+ Debug( "Skipping IPv6 localhost address\n" );
+ continue;
+ }
+ /* Also skip XDM-AUTHORIZATION-1 */
+ if (auth->name_length == 19 &&
+ !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) {
+ Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" );
+ continue;
+ }
+ }
+# endif
+ writeAddr( family, len, addr, file, auth, ok );
+ }
+ freeifaddrs( ifap );
+}
+#else /* GETIFADDRS */
+
+#if defined(STREAMSCONN) && !defined(SYSV_SIOCGIFCONF) && !defined(WINTCP)
+
+#include <tiuser.h>
+
+/* Define this host for access control. Find all the hosts the OS knows about
+ * for this fd and add them to the selfhosts list.
+ * TLI version, written without sufficient documentation.
+ */
+static void
+DefineSelf( int fd, FILE *file, Xauth *auth, int *ok )
+{
+ struct netbuf netb;
+ char addrret[1024]; /* easier than t_alloc */
+
+ netb.maxlen = sizeof(addrret);
+ netb.buf = addrret;
+ if (t_getname( fd, &netb, LOCALNAME ) == -1)
+ t_error( "t_getname" );
+ /* what a kludge */
+ writeAddr( FamilyInternet, 4, netb.buf+4, file, auth, ok );
+}
+
+#else
+
+#ifdef WINTCP /* NCR with Wollongong TCP */
+
+#include <stropts.h>
+#include <tiuser.h>
+
+#include <sys/stream.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+static void
+DefineSelf( int fd, FILE *file, Xauth *auth, int *ok )
+{
+ /*
+ * The Wollongong drivers used by NCR SVR4/MP-RAS don't understand the
+ * socket IO calls that most other drivers seem to like. Because of
+ * this, this routine must be special cased for NCR. Eventually,
+ * this will be cleared up.
+ */
+
+ struct ipb ifnet;
+ struct in_ifaddr ifaddr;
+ struct strioctl str;
+ unsigned char *addr;
+ int len, ipfd;
+
+ if ((ipfd = open( "/dev/ip", O_RDWR, 0 )) < 0) {
+ LogError( "Trouble getting interface configuration\n" );
+ return;
+ }
+
+ /* Indicate that we want to start at the begining */
+ ifnet.ib_next = (struct ipb *)1;
+
+ while (ifnet.ib_next) {
+ str.ic_cmd = IPIOC_GETIPB;
+ str.ic_timout = 0;
+ str.ic_len = sizeof(struct ipb);
+ str.ic_dp = (char *)&ifnet;
+
+ if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) {
+ close( ipfd );
+ LogError( "Trouble getting interface configuration\n" );
+ return;
+ }
+
+ ifaddr.ia_next = (struct in_ifaddr *)ifnet.if_addrlist;
+ str.ic_cmd = IPIOC_GETINADDR;
+ str.ic_timout = 0;
+ str.ic_len = sizeof(struct in_ifaddr);
+ str.ic_dp = (char *)&ifaddr;
+
+ if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) {
+ close( ipfd );
+ LogError( "Trouble getting interface configuration\n" );
+ return;
+ }
+
+ /*
+ * Ignore the 127.0.0.1 entry.
+ */
+ if (IA_SIN( &ifaddr )->sin_addr.s_addr == htonl( 0x7f000001 ) )
+ continue;
+
+ writeAddr( FamilyInternet, 4, (char *)&(IA_SIN( &ifaddr )->sin_addr),
+ file, auth, ok );
+
+ }
+ close( ipfd );
+}
+
+#else /* WINTCP */
+
+/* Solaris provides an extended interface SIOCGLIFCONF. Other systems
+ * may have this as well, but the code has only been tested on Solaris
+ * so far, so we only enable it there. Other platforms may be added as
+ * needed.
+ *
+ * Test for Solaris commented out -- TSI @ UQV 2003.06.13
+ */
+#ifdef SIOCGLIFCONF
+/* #if defined(sun) */
+# define USE_SIOCGLIFCONF
+/* #endif */
+#endif
+
+#if defined(SIOCGIFCONF) || defined (USE_SIOCGLIFCONF)
+
+#if !defined(SYSV_SIOCGIFCONF) || defined(USE_SIOCGLIFCONF)
+# define ifioctl ioctl
+#endif
+
+#ifdef USE_SIOCGLIFCONF
+# define ifr_type struct lifreq
+#else
+# define ifr_type struct ifreq
+#endif
+
+/* Handle variable length ifreq in BNR2 and later */
+#ifdef VARIABLE_IFREQ
+# define ifr_size(p) (sizeof(struct ifreq) + \
+ (p->ifr_addr.sa_len > sizeof(p->ifr_addr) ? \
+ p->ifr_addr.sa_len - sizeof(p->ifr_addr) : 0))
+#else
+# define ifr_size(p) (sizeof(ifr_type))
+#endif
+
+#ifdef USE_SIOCGLIFCONF
+# define IFC_IOCTL_REQ SIOCGLIFCONF
+# define IFC_REQ(ifc) ifc.lifc_req
+# define IFC_LEN(ifc) ifc.lifc_len
+# define IFR_ADDR(ifr) ifr->lifr_addr
+# define IFR_NAME(ifr) ifr->lifr_name
+#else
+# define IFC_IOCTL_REQ SIOCGIFCONF
+# define IFC_REQ(ifc) ifc.ifc_req
+# define IFC_LEN(ifc) ifc.ifc_len
+# define IFR_ADDR(ifr) ifr->ifr_addr
+# define IFR_NAME(ifr) ifr->ifr_name
+#endif
+
+/* Define this host for access control. Find all the hosts the OS knows about
+ * for this fd and add them to the selfhosts list.
+ */
+static void
+DefineSelf( int fd, FILE *file, Xauth *auth, int *ok )
+{
+ char buf[2048], *cp, *cplim;
+ int len;
+ char *addr;
+ int family;
+ ifr_type *ifr;
+#ifdef USE_SIOCGLIFCONF
+ int n;
+ void * bufptr = buf;
+ size_t buflen = sizeof(buf);
+ struct lifconf ifc;
+# ifdef SIOCGLIFNUM
+ struct lifnum ifn;
+# endif
+#else
+ struct ifconf ifc;
+#endif
+
+#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
+ ifn.lifn_family = AF_UNSPEC;
+ ifn.lifn_flags = 0;
+ if (ioctl( fd, (int)SIOCGLIFNUM, (char *)&ifn ) < 0)
+ LogError( "Failed getting interface count\n" );
+ if (buflen < (ifn.lifn_count * sizeof(struct lifreq))) {
+ buflen = ifn.lifn_count * sizeof(struct lifreq);
+ bufptr = Malloc( buflen );
+ }
+#endif
+
+#ifdef USE_SIOCGLIFCONF
+ ifc.lifc_family = AF_UNSPEC;
+ ifc.lifc_flags = 0;
+ ifc.lifc_len = buflen;
+ ifc.lifc_buf = bufptr;
+#else
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+#endif
+ if (ifioctl (fd, IFC_IOCTL_REQ, (char *)&ifc) < 0) {
+ LogError( "Trouble getting network interface configuration\n" );
+#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
+ if (bufptr != buf)
+ free( bufptr );
+#endif
+ return;
+ }
+
+ cplim = (char *)IFC_REQ( ifc ) + IFC_LEN( ifc );
+
+ for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size (ifr)) {
+ ifr = (ifr_type *) cp;
+#ifdef DNETCONN
+ /*
+ * this is ugly but SIOCGIFCONF returns decnet addresses in
+ * a different form from other decnet calls
+ */
+ if (IFR_ADDR( ifr ).sa_family == AF_DECnet) {
+ len = sizeof(struct dn_naddr);
+ addr = (char *)IFR_ADDR( ifr ).sa_data;
+ family = FamilyDECnet;
+ } else
+#endif
+ {
+ family = ConvertAddr( (char *)&IFR_ADDR( ifr ), &len, &addr );
+ if (family < 0)
+ continue;
+
+ if (len == 0) {
+ Debug( "skipping zero length address\n" );
+ continue;
+ }
+ /*
+ * don't write out 'localhost' entries, as
+ * they may conflict with other local entries.
+ * DefineLocal will always be called to add
+ * the local entry anyway, so this one can
+ * be tossed.
+ */
+ if (family == FamilyInternet &&
+ addr[0] == 127 && addr[1] == 0 &&
+ addr[2] == 0 && addr[3] == 1)
+ {
+ Debug( "skipping localhost address\n" );
+ continue;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ if (family == FamilyInternet6) {
+ if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) {
+ Debug( "Skipping IPv6 localhost address\n" );
+ continue;
+ }
+ /* Also skip XDM-AUTHORIZATION-1 */
+ if (auth->name_length == 19 &&
+ !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) {
+ Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" );
+ continue;
+ }
+ }
+#endif
+ }
+ Debug( "DefineSelf: write network address, length %d\n", len );
+ writeAddr( family, len, addr, file, auth, ok );
+ }
+#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
+ if (bufptr != buf)
+ free( bufptr );
+#endif
+}
+
+#else /* SIOCGIFCONF */
+
+/* Define this host for access control. Find all the hosts the OS knows about
+ * for this fd and add them to the selfhosts list.
+ */
+static void
+DefineSelf( int fd, int file, int auth, int *ok )
+{
+ int len;
+ caddr_t addr;
+ int family;
+
+ struct utsname name;
+ register struct hostent *hp;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ } saddr;
+
+ struct sockaddr_in *inetaddr;
+
+ /* hpux:
+ * Why not use gethostname()? Well, at least on my system, I've had to
+ * make an ugly kernel patch to get a name longer than 8 characters, and
+ * uname() lets me access to the whole string (it smashes release, you
+ * see), whereas gethostname() kindly truncates it for me.
+ */
+ uname( &name );
+ if ((hp = gethostbyname( name.nodename ))) {
+ saddr.sa.sa_family = hp->h_addrtype;
+ inetaddr = (struct sockaddr_in *)(&(saddr.sa));
+ memmove( (char *)&(inetaddr->sin_addr), (char *)hp->h_addr,
+ (int)hp->h_length );
+ if ( (family = ConvertAddr( &(saddr.sa), &len, &addr )) >= 0)
+ writeAddr( FamilyInternet, sizeof(inetaddr->sin_addr),
+ (char *)(&inetaddr->sin_addr), file, auth, ok );
+ }
+}
+
+#endif /* SIOCGIFCONF else */
+#endif /* WINTCP else */
+#endif /* STREAMSCONN && !SYSV_SIOCGIFCONF else */
+#endif /* HAVE_GETIFADDRS */
+
+static void
+setAuthNumber( Xauth *auth, const char *name )
+{
+ char *colon;
+ char *dot;
+
+ Debug( "setAuthNumber %s\n", name );
+ colon = strrchr( name, ':' );
+ if (colon) {
+ ++colon;
+ dot = strchr( colon, '.' );
+ if (dot)
+ auth->number_length = dot - colon;
+ else
+ auth->number_length = strlen( colon );
+ if (!StrNDup( &auth->number, colon, auth->number_length ))
+ auth->number_length = 0;
+ Debug( "setAuthNumber: %s\n", auth->number );
+ }
+}
+
+static void
+writeLocalAuth( FILE *file, Xauth *auth, const char *name, int *ok )
+{
+#if defined(STREAMSCONN) || !defined(HAVE_GETIFADDRS)
+ int fd;
+#endif
+
+ Debug( "writeLocalAuth: %s %.*s\n", name, auth->name_length, auth->name );
+ setAuthNumber( auth, name );
+# ifdef STREAMSCONN
+ fd = t_open( "/dev/tcp", O_RDWR, 0 );
+ t_bind( fd, NULL, NULL );
+ DefineSelf( fd, file, auth, ok );
+ t_unbind( fd );
+ t_close( fd );
+# elif defined(HAVE_GETIFADDRS)
+ DefineSelf( file, auth, ok );
+# else
+# ifdef TCPCONN
+# if defined(IPv6) && defined(AF_INET6)
+ fd = socket( AF_INET6, SOCK_STREAM, 0 );
+ if (fd < 0)
+# endif
+ fd = socket( AF_INET, SOCK_STREAM, 0 );
+ DefineSelf( fd, file, auth, ok );
+ close( fd );
+# endif
+# ifdef DNETCONN
+ fd = socket( AF_DECnet, SOCK_STREAM, 0 );
+ DefineSelf( fd, file, auth, ok );
+ close( fd );
+# endif
+# endif /* HAVE_GETIFADDRS */
+ DefineLocal( file, auth, ok );
+}
+
+#ifdef XDMCP
+
+/*
+ * Call ConvertAddr(), and if it returns an IPv4 localhost, convert it
+ * to a local display name. Meets the _XTransConvertAddress's localhost
+ * hack.
+ */
+
+static int
+ConvertAuthAddr( char *saddr, int *len, char **addr )
+{
+ int ret = ConvertAddr( saddr, len, addr );
+ if (ret == FamilyInternet &&
+ ((struct in_addr *)*addr)->s_addr == htonl( 0x7F000001L ))
+ ret = FamilyLocal;
+ return ret;
+}
+
+static void
+writeRemoteAuth( FILE *file, Xauth *auth, XdmcpNetaddr peer, int peerlen,
+ const char *name, int *ok )
+{
+ int family = FamilyLocal;
+ char *addr;
+
+ Debug( "writeRemoteAuth: %s %.*s\n", name, auth->name_length, auth->name );
+ if (!peer || peerlen < 2)
+ return;
+ setAuthNumber( auth, name );
+ family = ConvertAuthAddr( peer, &peerlen, &addr );
+ Debug( "writeRemoteAuth: family %d\n", family );
+ if (family != FamilyLocal) {
+ Debug( "writeRemoteAuth: %d, %02[*:hhx\n",
+ family, peerlen, addr );
+ writeAddr( family, peerlen, addr, file, auth, ok );
+ } else
+ writeLocalAuth( file, auth, name, ok );
+}
+
+#endif /* XDMCP */
+
+#define NBSIZE 1024
+
+static void
+startUserAuth( char *buf, char *nbuf, FILE **old, FILE **new )
+{
+ const char *home;
+ int lockStatus;
+
+ initAddrs();
+ *new = 0;
+ if ((home = getEnv( userEnviron, "HOME" )) && strlen( home ) < NBSIZE - 12) {
+ sprintf( buf, "%s/.Xauthority", home );
+ Debug( "XauLockAuth %s\n", buf );
+ lockStatus = XauLockAuth( buf, 1, 2, 10 );
+ Debug( "lock is %d\n", lockStatus );
+ if (lockStatus == LOCK_SUCCESS)
+ if (!openFiles( buf, nbuf, old, new ))
+ XauUnlockAuth( buf );
+ }
+ if (!*new)
+ LogWarn( "Can't update authorization file in home dir %s\n", home );
+}
+
+static int
+endUserAuth( FILE *old, FILE *new, const char *nname, int ok )
+{
+ Xauth *entry;
+ struct stat statb;
+
+ if (old) {
+ if (fstat( fileno( old ), &statb ) != -1)
+ chmod( nname, (int)(statb.st_mode & 0777) );
+ /*SUPPRESS 560*/
+ while ((entry = XauReadAuth( old ))) {
+ if (!checkEntry( entry )) {
+ Debug( "writing an entry\n" );
+ writeAuth( new, entry, &ok );
+ }
+ XauDisposeAuth( entry );
+ }
+ fclose( old );
+ }
+ if (fclose( new ) == EOF)
+ ok = FALSE;
+ doneAddrs();
+ return ok;
+}
+
+static void
+undoUserAuth( const char *name, const char *new_name )
+{
+ LogWarn( "Can't save user authorization in home dir\n" );
+ unlink( new_name );
+ XauUnlockAuth( name );
+}
+
+static char *
+moveUserAuth( const char *name, char *new_name, char *envname )
+{
+ if (unlink( name ))
+ Debug( "unlink %s failed\n", name );
+ if (link( new_name, name )) {
+ Debug( "link failed %s %s\n", new_name, name );
+ LogError( "Can't move user authorization into place\n" );
+ envname = new_name;
+ } else {
+ Debug( "new authorization moved into place\n" );
+ unlink( new_name );
+ }
+ XauUnlockAuth( name );
+ return envname;
+}
+
+void
+SetUserAuthorization( struct display *d )
+{
+ FILE *old, *new;
+ char *name;
+ char *envname;
+ Xauth **auths;
+ int i, ok;
+ int magicCookie;
+ int data_len;
+ char name_buf[NBSIZE], new_name[NBSIZE + 2];
+
+ Debug( "SetUserAuthorization\n" );
+ auths = d->authorizations;
+ if (auths) {
+ startUserAuth( name_buf, new_name, &old, &new );
+ if (new) {
+ envname = 0;
+ name = name_buf;
+ } else {
+ fallback:
+ if (strlen( d->userAuthDir ) >= NBSIZE - 13)
+ return;
+ /*
+ * Note, that we don't lock the auth file here, as it's
+ * temporary - we can assume, that we are the only ones
+ * knowing about this file anyway.
+ */
+ i = sprintf( name_buf, "%s/.Xauth", d->userAuthDir );
+ new = mkTempFile( name_buf, i );
+ if (!new) {
+ LogError( "Can't create authorization file in %s\n",
+ d->userAuthDir );
+ return;
+ }
+ name = 0;
+ envname = name_buf;
+ old = 0;
+ }
+ ok = TRUE;
+ Debug( "%d authorization protocols for %s\n", d->authNum, d->name );
+ /*
+ * Write MIT-MAGIC-COOKIE-1 authorization first, so that
+ * R4 clients which only knew that, and used the first
+ * matching entry will continue to function
+ */
+ magicCookie = -1;
+ for (i = 0; i < d->authNum; i++) {
+ if (auths[i]->name_length == 18 &&
+ !memcmp( auths[i]->name, "MIT-MAGIC-COOKIE-1", 18 ))
+ {
+ magicCookie = i;
+ if ((d->displayType & d_location) == dLocal)
+ writeLocalAuth( new, auths[i], d->name, &ok );
+#ifdef XDMCP
+ else
+ writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data,
+ d->peer.length, d->name, &ok );
+#endif
+ break;
+ }
+ }
+ /* now write other authorizations */
+ for (i = 0; i < d->authNum; i++) {
+ if (i != magicCookie) {
+ data_len = auths[i]->data_length;
+ /* client will just use default Kerberos cache, so don't
+ * even write cache info into the authority file.
+ */
+ if (auths[i]->name_length == 14 &&
+ !strncmp( auths[i]->name, "MIT-KERBEROS-5", 14 ))
+ auths[i]->data_length = 0;
+ if ((d->displayType & d_location) == dLocal)
+ writeLocalAuth( new, auths[i], d->name, &ok );
+#ifdef XDMCP
+ else
+ writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data,
+ d->peer.length, d->name, &ok );
+#endif
+ auths[i]->data_length = data_len;
+ }
+ }
+ if (!endUserAuth( old, new, new_name, ok )) {
+ if (!name) {
+ LogError( "Can't save user authorization\n" );
+ return;
+ }
+ undoUserAuth( name, new_name );
+ initAddrs();
+ goto fallback;
+ }
+ if (name)
+ envname = moveUserAuth( name, new_name, envname );
+ if (envname) {
+ userEnviron = setEnv( userEnviron, "XAUTHORITY", envname );
+ systemEnviron = setEnv( systemEnviron, "XAUTHORITY", envname );
+ }
+ /* a chown() used to be here, but this code runs as user anyway */
+ }
+ Debug( "done SetUserAuthorization\n" );
+}
+
+void
+RemoveUserAuthorization( struct display *d )
+{
+ Xauth **auths;
+ FILE *old, *new;
+ int i;
+ char name[NBSIZE], new_name[NBSIZE + 2];
+
+ if (!(auths = d->authorizations))
+ return;
+ Debug( "RemoveUserAuthorization\n" );
+ startUserAuth( name, new_name, &old, &new );
+ if (new) {
+ for (i = 0; i < d->authNum; i++) {
+ if ((d->displayType & d_location) == dLocal)
+ writeLocalAuth( new, auths[i], d->name, 0 );
+#ifdef XDMCP
+ else
+ writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data,
+ d->peer.length, d->name, 0 );
+#endif
+ }
+ if (endUserAuth( old, new, new_name, TRUE ))
+ (void)moveUserAuth( name, new_name, 0 );
+ else
+ undoUserAuth( name, new_name );
+ }
+ Debug( "done RemoveUserAuthorization\n" );
+}
diff --git a/tdm/backend/bootman.c b/tdm/backend/bootman.c
new file mode 100644
index 000000000..291164ea7
--- /dev/null
+++ b/tdm/backend/bootman.c
@@ -0,0 +1,277 @@
+/*
+
+Copyright 2005 Stephan Kulow <[email protected]>
+Copyright 2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * Boot options
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+extern char **environ;
+
+static int
+getNull( char ***opts ATTR_UNUSED, int *def ATTR_UNUSED, int *cur ATTR_UNUSED )
+{
+ return BO_NOMAN;
+}
+
+static int
+setNull( const char *opt ATTR_UNUSED, SdRec *sdr ATTR_UNUSED )
+{
+ return BO_NOMAN;
+}
+
+static char *
+match( char *obuf, int *blen, const char *key, int klen )
+{
+ char *buf = obuf;
+ if (memcmp( buf, key, klen ) || !isspace( buf[klen] ))
+ return 0;
+ buf += klen + 1;
+ for (; isspace( *buf ); buf++);
+ if (!*buf)
+ return 0;
+ *blen -= buf - obuf;
+ return buf;
+}
+
+#define GRUB_MENU "/boot/grub/menu.lst"
+
+static char *grub;
+
+static int
+getGrub( char ***opts, int *def, int *cur )
+{
+ FILE *f;
+ char *ptr, *linp;
+ int len;
+ char line[1000];
+
+ if (!grub && !(grub = locate( "grub-set-default" )))
+ return BO_NOMAN;
+
+ *def = 0;
+ *cur = -1;
+ *opts = initStrArr( 0 );
+
+ if (!(f = fopen( GRUB_MENU, "r" )))
+ return errno == ENOENT ? BO_NOMAN : BO_IO;
+ while ((len = fGets( line, sizeof(line), f )) != -1) {
+ for (linp = line; isspace(*linp); linp++, len--);
+ if ((ptr = match( linp, &len, "default", 7 )))
+ *def = atoi( ptr );
+ else if ((ptr = match( linp, &len, "title", 5 ))) {
+ for (; isspace( ptr[len - 1] ); len--);
+ *opts = addStrArr( *opts, ptr, len );
+ }
+ }
+ fclose( f );
+
+ return BO_OK;
+}
+
+static int
+setGrub( const char *opt, SdRec *sdr )
+{
+ FILE *f;
+ char *ptr;
+ int len, i;
+ char line[1000];
+
+ if (!(f = fopen( GRUB_MENU, "r" )))
+ return errno == ENOENT ? BO_NOMAN : BO_IO;
+ for (i = 0; (len = fGets( line, sizeof(line), f )) != -1; )
+ if ((ptr = match( line, &len, "title", 5 ))) {
+ if (!strcmp( ptr, opt )) {
+ fclose( f );
+ sdr->osindex = i;
+ sdr->bmstamp = mTime( GRUB_MENU );
+ return BO_OK;
+ }
+ i++;
+ }
+ fclose( f );
+ return BO_NOENT;
+}
+
+static void
+commitGrub( void )
+{
+ char command[256];
+
+ if (sdRec.bmstamp != mTime( GRUB_MENU ) &&
+ setGrub( sdRec.osname, &sdRec ) != BO_OK)
+ return;
+
+ sprintf(command, "%s %d", grub, sdRec.osindex);
+ system(command);
+}
+
+static char *lilo;
+
+static int
+getLilo( char ***opts, int *def, int *cur )
+{
+ FILE *f;
+ int cdef, pid, len, ret = BO_OK;
+ static const char *args[5] = { 0, "-w", "-v", "-q", 0 };
+ char buf[256], next[256];
+
+ if (!lilo && !(lilo = locate( "lilo" )))
+ return BO_NOMAN;
+
+ args[0] = lilo;
+ if (!(f = pOpen( (char **)args, 'r', &pid )))
+ return BO_IO;
+ *opts = 0;
+ next[0] = 0;
+ for (;;) {
+ if ((len = fGets( buf, sizeof(buf), f)) == -1) {
+ ret = BO_NOMAN;
+ goto out;
+ }
+ if (!memcmp( buf, "Images:", 7 ))
+ break;
+#define Ldeflin " Default boot command line:"
+ if (!memcmp( buf, Ldeflin, strlen(Ldeflin) )) {
+ memcpy( next, buf + strlen(Ldeflin) + 2, len - strlen(Ldeflin) - 3 );
+ next[len - strlen(Ldeflin) - 3] = 0;
+ }
+ }
+ cdef = *def = 0;
+ *cur = -1;
+ *opts = initStrArr( 0 );
+ while ((len = fGets( buf, sizeof(buf), f)) != -1)
+ if (buf[0] == ' ' && buf[1] == ' ' && buf[2] != ' ') {
+ if (buf[len - 1] == '*') {
+ *def = cdef;
+ len--;
+ }
+ for (; buf[len - 1] == ' '; len--);
+ *opts = addStrArr( *opts, buf + 2, len - 2 );
+ if (!strcmp( (*opts)[cdef], next ))
+ *cur = cdef;
+ cdef++;
+ }
+ out:
+ if (pClose( f, pid )) {
+ if (*opts)
+ freeStrArr( *opts );
+ return BO_IO;
+ }
+ return ret;
+}
+
+static int
+setLilo( const char *opt, SdRec *sdr ATTR_UNUSED )
+{
+ char **opts;
+ int def, cur, ret, i;
+
+ if ((ret = getLilo( &opts, &def, &cur )) != BO_OK)
+ return ret;
+ if (!*opt)
+ opt = 0;
+ else {
+ for (i = 0; opts[i]; i++)
+ if (!strcmp( opts[i], opt ))
+ goto oke;
+ freeStrArr( opts );
+ return BO_NOENT;
+ }
+ oke:
+ freeStrArr( opts );
+ return BO_OK;
+}
+
+static void
+commitLilo( void )
+{
+ static const char *args[5] = { 0, "-w", "-R", 0, 0 };
+
+ args[0] = lilo;
+ args[3] = sdRec.osname;
+ runAndWait( (char **)args, environ );
+}
+
+static struct {
+ int (*get)( char ***, int *, int * );
+ int (*set)( const char *, SdRec * );
+ void (*commit)( void );
+} bootOpts[] = {
+ { getNull, setNull, 0 },
+ { getGrub, setGrub, commitGrub },
+ { getLilo, setLilo, commitLilo },
+};
+
+int
+getBootOptions( char ***opts, int *def, int *cur )
+{
+ return bootOpts[bootManager].get( opts, def, cur );
+}
+
+int
+setBootOption( const char *opt, SdRec *sdr )
+{
+ int ret;
+
+ if (sdr->osname) {
+ free( sdr->osname );
+ sdr->osname = 0;
+ }
+ if (opt) {
+ if ((ret = bootOpts[bootManager].set( opt, sdr )) != BO_OK)
+ return ret;
+ if (!StrDup( &sdr->osname, opt ))
+ return BO_IO; /* BO_NOMEM */
+ }
+ return BO_OK;
+}
+
+void
+commitBootOption( void )
+{
+ if (sdRec.osname) {
+ bootOpts[bootManager].commit();
+/*
+ free( sdRec.osname );
+ sdRec.osname = 0;
+*/
+ }
+}
+
diff --git a/tdm/backend/choose.c b/tdm/backend/choose.c
new file mode 100644
index 000000000..ead841228
--- /dev/null
+++ b/tdm/backend/choose.c
@@ -0,0 +1,1051 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2002-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * chooser backend
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <ctype.h>
+
+#ifdef __svr4__
+# include <sys/sockio.h>
+#endif
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#ifdef STREAMSCONN
+# ifdef WINTCP /* NCR with Wollongong TCP */
+# include <netinet/ip.h>
+# endif
+# include <stropts.h>
+# include <tiuser.h>
+# include <netconfig.h>
+# include <netdir.h>
+#endif
+
+#if !defined(__GNU__) && !defined(__hpux) /* XXX __hpux might be wrong */
+# include <net/if.h>
+#endif
+
+#include <netdb.h>
+
+typedef struct _IndirectUsers {
+ struct _IndirectUsers *next;
+ ARRAY8 client;
+ CARD16 connectionType;
+} IndirectUsersRec, *IndirectUsersPtr;
+
+static IndirectUsersPtr indirectUsers;
+
+int
+RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ IndirectUsersPtr i;
+
+ for (i = indirectUsers; i; i = i->next)
+ if (XdmcpARRAY8Equal( clientAddress, &i->client ) &&
+ connectionType == i->connectionType)
+ return 1;
+ if (!(i = (IndirectUsersPtr)Malloc( sizeof(IndirectUsersRec) )))
+ return 0;
+ if (!XdmcpCopyARRAY8( clientAddress, &i->client )) {
+ free( (char *)i );
+ return 0;
+ }
+ i->connectionType = connectionType;
+ i->next = indirectUsers;
+ indirectUsers = i;
+ return 1;
+}
+
+void
+ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ IndirectUsersPtr *i, ni;
+
+ for (i = &indirectUsers; *i; i = &(*i)->next)
+ if (XdmcpARRAY8Equal( clientAddress, &(*i)->client ) &&
+ connectionType == (*i)->connectionType)
+ {
+ ni = (*i)->next;
+ XdmcpDisposeARRAY8( &(*i)->client );
+ free( (char *)(*i) );
+ (*i) = ni;
+ break;
+ }
+}
+
+int
+IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ IndirectUsersPtr i;
+
+ for (i = indirectUsers; i; i = i->next)
+ if (XdmcpARRAY8Equal( clientAddress, &i->client ) &&
+ connectionType == i->connectionType)
+ return 1;
+ return 0;
+}
+
+typedef struct _Choices {
+ struct _Choices *next;
+ ARRAY8 client;
+ CARD16 connectionType;
+ ARRAY8 choice;
+ Time_t time;
+} ChoiceRec, *ChoicePtr;
+
+static ChoicePtr choices;
+
+ARRAY8Ptr
+IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ ChoicePtr c, next, prev;
+
+ prev = 0;
+ for (c = choices; c; c = next) {
+ next = c->next;
+ Debug( "choice checking timeout: %ld >? %d\n",
+ (long)(now - c->time), choiceTimeout );
+ if (now - c->time > (Time_t)choiceTimeout) {
+ Debug( "timeout choice %ld > %d\n",
+ (long)(now - c->time), choiceTimeout );
+ if (prev)
+ prev->next = next;
+ else
+ choices = next;
+ XdmcpDisposeARRAY8( &c->client );
+ XdmcpDisposeARRAY8( &c->choice );
+ free( (char *)c );
+ } else {
+ if (XdmcpARRAY8Equal( clientAddress, &c->client ) &&
+ connectionType == c->connectionType)
+ return &c->choice;
+ prev = c;
+ }
+ }
+ return 0;
+}
+
+int
+RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType,
+ ARRAY8Ptr choice )
+{
+ ChoicePtr c;
+ int insert;
+#if 0
+ int found = 0;
+#endif
+
+ Debug( "got indirect choice back\n" );
+ for (c = choices; c; c = c->next) {
+ if (XdmcpARRAY8Equal( clientAddress, &c->client ) &&
+ connectionType == c->connectionType) {
+#if 0
+ found = 1;
+#endif
+ break;
+ }
+ }
+#if 0
+ if (!found)
+ return 0;
+#endif
+
+ insert = 0;
+ if (!c) {
+ insert = 1;
+ c = (ChoicePtr)Malloc( sizeof(ChoiceRec) );
+ if (!c)
+ return 0;
+ c->connectionType = connectionType;
+ if (!XdmcpCopyARRAY8( clientAddress, &c->client )) {
+ free( (char *)c );
+ return 0;
+ }
+ } else
+ XdmcpDisposeARRAY8( &c->choice );
+ if (!XdmcpCopyARRAY8( choice, &c->choice )) {
+ XdmcpDisposeARRAY8( &c->client );
+ free( (char *)c );
+ return 0;
+ }
+ if (insert) {
+ c->next = choices;
+ choices = c;
+ }
+ c->time = now;
+ return 1;
+}
+
+#if 0
+static void
+RemoveIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType )
+{
+ ChoicePtr c, prev;
+
+ prev = 0;
+ for (c = choices; c; c = c->next) {
+ if (XdmcpARRAY8Equal( clientAddress, &c->client ) &&
+ connectionType == c->connectionType)
+ {
+ if (prev)
+ prev->next = c->next;
+ else
+ choices = c->next;
+ XdmcpDisposeARRAY8( &c->client );
+ XdmcpDisposeARRAY8( &c->choice );
+ free( (char *)c );
+ return;
+ }
+ prev = c;
+ }
+}
+#endif
+
+
+/* ####################### */
+
+
+typedef struct _hostAddr {
+ struct _hostAddr *next;
+ struct sockaddr *addr;
+ int addrlen;
+ xdmOpCode type;
+} HostAddr;
+
+static HostAddr *hostAddrdb;
+
+typedef struct _hostName {
+ struct _hostName *next;
+ unsigned willing:1, alive:1;
+ ARRAY8 hostname, status;
+ CARD16 connectionType;
+ ARRAY8 hostaddr;
+} HostName;
+
+static HostName *hostNamedb;
+
+static XdmcpBuffer directBuffer, broadcastBuffer;
+static XdmcpBuffer buffer;
+
+static int socketFD;
+#if defined(IPv6) && defined(AF_INET6)
+static int socket6FD;
+#endif
+
+
+static void
+doPingHosts()
+{
+ HostAddr *hosts;
+
+ for (hosts = hostAddrdb; hosts; hosts = hosts->next)
+#if defined(IPv6) && defined(AF_INET6)
+ XdmcpFlush( hosts->addr->sa_family == AF_INET6 ? socket6FD : socketFD,
+#else
+ XdmcpFlush( socketFD,
+#endif
+ hosts->type == QUERY ? &directBuffer : &broadcastBuffer,
+ (XdmcpNetaddr)hosts->addr, hosts->addrlen );
+}
+
+static int
+addHostname( ARRAY8Ptr hostname, ARRAY8Ptr status,
+ struct sockaddr *addr, int will )
+{
+ HostName **names, *name;
+ ARRAY8 hostAddr;
+ CARD16 connectionType;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ hostAddr.data =
+ (CARD8 *)& ((struct sockaddr_in *)addr)->sin_addr;
+ hostAddr.length = 4;
+ connectionType = FamilyInternet;
+ break;
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ hostAddr.data =
+ (CARD8 *)&((struct sockaddr_in6 *)addr)->sin6_addr;
+ hostAddr.length = 16;
+ connectionType = FamilyInternet6;
+ break;
+#endif
+ default:
+ hostAddr.data = (CARD8 *)"";
+ hostAddr.length = 0;
+ connectionType = FamilyLocal;
+ break;
+ }
+ for (names = &hostNamedb; *names; names = &(*names)->next) {
+ name = *names;
+ if (connectionType == name->connectionType &&
+ XdmcpARRAY8Equal( &hostAddr, &name->hostaddr ))
+ {
+ if (XdmcpARRAY8Equal( status, &name->status ))
+ return 0;
+ XdmcpDisposeARRAY8( &name->status );
+ XdmcpDisposeARRAY8( hostname );
+
+ GSendInt( G_Ch_ChangeHost );
+ goto gotold;
+ }
+ }
+ if (!(name = (HostName *)Malloc( sizeof(*name) )))
+ return 0;
+ if (hostname->length) {
+ switch (addr->sa_family) {
+ case AF_INET:
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+#endif
+ {
+ struct hostent *hostent;
+ char *host;
+
+ hostent = gethostbyaddr( (char *)hostAddr.data,
+ hostAddr.length, addr->sa_family );
+ if (hostent) {
+ XdmcpDisposeARRAY8( hostname );
+ host = hostent->h_name;
+ XdmcpAllocARRAY8( hostname, strlen( host ) );
+ memmove( hostname->data, host, hostname->length );
+ }
+ }
+ }
+ }
+ if (!XdmcpAllocARRAY8( &name->hostaddr, hostAddr.length )) {
+ free( (char *)name );
+ return 0;
+ }
+ memmove( name->hostaddr.data, hostAddr.data, hostAddr.length );
+ name->connectionType = connectionType;
+ name->hostname = *hostname;
+
+ *names = name;
+ name->next = 0;
+
+ GSendInt( G_Ch_AddHost );
+ gotold:
+ name->alive = 1;
+ name->willing = will;
+ name->status = *status;
+
+ GSendInt( (int)name ); /* just an id */
+ GSendNStr( (char *)name->hostname.data, name->hostname.length );
+ GSendNStr( (char *)name->status.data, name->status.length );
+ GSendInt( will );
+
+ return 1;
+}
+
+static void
+disposeHostname( HostName *host )
+{
+ XdmcpDisposeARRAY8( &host->hostname );
+ XdmcpDisposeARRAY8( &host->hostaddr );
+ XdmcpDisposeARRAY8( &host->status );
+ free( (char *)host );
+}
+
+static void
+emptyHostnames( void )
+{
+ HostName *host, *nhost;
+
+ for (host = hostNamedb; host; host = nhost) {
+ nhost = host->next;
+ disposeHostname( host );
+ }
+ hostNamedb = 0;
+}
+
+static void
+receivePacket( int sfd )
+{
+ XdmcpHeader header;
+ ARRAY8 authenticationName;
+ ARRAY8 hostname;
+ ARRAY8 status;
+ int saveHostname = 0;
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_storage addr;
+#else
+ struct sockaddr addr;
+#endif
+ int addrlen;
+
+ addrlen = sizeof(addr);
+ if (!XdmcpFill( sfd, &buffer, (XdmcpNetaddr)&addr, &addrlen ))
+ return;
+ if (!XdmcpReadHeader( &buffer, &header ))
+ return;
+ if (header.version != XDM_PROTOCOL_VERSION)
+ return;
+ hostname.data = 0;
+ status.data = 0;
+ authenticationName.data = 0;
+ switch (header.opcode) {
+ case WILLING:
+ if (XdmcpReadARRAY8( &buffer, &authenticationName ) &&
+ XdmcpReadARRAY8( &buffer, &hostname ) &&
+ XdmcpReadARRAY8( &buffer, &status )) {
+ if (header.length == 6 + authenticationName.length +
+ hostname.length + status.length) {
+ if (addHostname( &hostname, &status,
+ (struct sockaddr *)&addr, 1 ))
+ saveHostname = 1;
+ }
+ }
+ XdmcpDisposeARRAY8( &authenticationName );
+ break;
+ case UNWILLING:
+ if (XdmcpReadARRAY8( &buffer, &hostname ) &&
+ XdmcpReadARRAY8( &buffer, &status )) {
+ if (header.length == 4 + hostname.length + status.length) {
+ if (addHostname( &hostname, &status,
+ (struct sockaddr *)&addr, 0 ))
+ saveHostname = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (!saveHostname) {
+ XdmcpDisposeARRAY8( &hostname );
+ XdmcpDisposeARRAY8( &status );
+ }
+}
+
+static void
+addHostaddr( HostAddr **hosts, struct sockaddr *addr, int len, xdmOpCode type )
+{
+ HostAddr *host;
+
+ Debug( "adding host %[*hhu, type %d\n", len, addr, type );
+ for (host = *hosts; host; host = host->next)
+ if (host->type == type && host->addr->sa_family == addr->sa_family)
+ switch (addr->sa_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *na = (struct sockaddr_in *)addr;
+ struct sockaddr_in *oa = (struct sockaddr_in *)host->addr;
+ if (na->sin_port == oa->sin_port &&
+ na->sin_addr.s_addr == oa->sin_addr.s_addr)
+ return;
+ break;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *na = (struct sockaddr_in6 *)addr;
+ struct sockaddr_in6 *oa = (struct sockaddr_in6 *)host->addr;
+ if (na->sin6_port == oa->sin6_port &&
+ !memcmp( &na->sin6_addr, &oa->sin6_addr, 16 ))
+ return;
+ break;
+ }
+#endif
+ default: /* ... */
+ break;
+ }
+ Debug( " not dupe\n" );
+ if (!(host = (HostAddr *)Malloc( sizeof(*host) )))
+ return;
+ if (!(host->addr = (struct sockaddr *)Malloc( len ))) {
+ free( (char *)host );
+ return;
+ }
+ memcpy( (char *)host->addr, (char *)addr, len );
+ host->addrlen = len;
+ host->type = type;
+ host->next = *hosts;
+ *hosts = host;
+}
+
+static void
+registerHostaddr( struct sockaddr *addr, int len, xdmOpCode type )
+{
+ addHostaddr( &hostAddrdb, addr, len, type );
+}
+
+static void
+emptyPingHosts( void )
+{
+ HostAddr *host, *nhost;
+
+ for (host = hostAddrdb; host; host = nhost) {
+ nhost = host->next;
+ free( host->addr );
+ free( host );
+ }
+ hostAddrdb = 0;
+}
+
+/* Handle variable length ifreq in BNR2 and later */
+#ifdef VARIABLE_IFREQ
+# define ifr_size(p) \
+ (sizeof(struct ifreq) + \
+ (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \
+ p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0))
+#else
+# define ifr_size(p) (sizeof(struct ifreq))
+#endif
+
+#define IFC_REQ(ifc) ifc.ifc_req
+
+#ifndef SYSV_SIOCGIFCONF
+# define ifioctl ioctl
+#endif
+
+static void
+registerBroadcastForPing( void )
+{
+ struct sockaddr_in in_addr;
+
+#ifdef __GNU__
+ in_addr.sin_addr.s_addr = htonl( 0xFFFFFFFF );
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+ registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr),
+ BROADCAST_QUERY );
+#else /* __GNU__ */
+ struct ifconf ifc;
+ register struct ifreq *ifr;
+ struct sockaddr broad_addr;
+ char buf[2048], *cp, *cplim;
+# ifdef WINTCP /* NCR with Wollongong TCP */
+ int ipfd;
+ struct ifconf *ifcp;
+ struct strioctl ioc;
+ int n;
+
+ ifcp = (struct ifconf *)buf;
+ ifcp->ifc_buf = buf + 4;
+ ifcp->ifc_len = sizeof(buf) - 4;
+
+ if ((ipfd = open( "/dev/ip", O_RDONLY )) < 0) {
+ t_error( "RegisterBroadcastForPing() t_open(/dev/ip) failed" );
+ return;
+ }
+
+ ioc.ic_cmd = IPIOC_GETIFCONF;
+ ioc.ic_timout = 60;
+ ioc.ic_len = sizeof(buf);
+ ioc.ic_dp = (char *)ifcp;
+
+ if (ioctl( ipfd, (int)I_STR, (char *)&ioc ) < 0) {
+ perror( "RegisterBroadcastForPing() ioctl(I_STR(IPIOC_GETIFCONF)) failed" );
+ close( ipfd );
+ return;
+ }
+
+ for (ifr = ifcp->ifc_req, n = ifcp->ifc_len / sizeof(struct ifreq);
+ --n >= 0; ifr++)
+# else /* WINTCP */
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ifioctl(socketFD, (int)SIOCGIFCONF, (char *)&ifc) < 0)
+ return;
+
+ cplim = (char *)IFC_REQ( ifc ) + ifc.ifc_len;
+
+ for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size(ifr))
+# endif /* WINTCP */
+ {
+# ifndef WINTCP
+ ifr = (struct ifreq *)cp;
+# endif
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ broad_addr = ifr->ifr_addr;
+ ((struct sockaddr_in *)&broad_addr)->sin_addr.s_addr =
+ htonl( INADDR_BROADCAST );
+# ifdef SIOCGIFBRDADDR
+ {
+ struct ifreq broad_req;
+
+ broad_req = *ifr;
+# ifdef WINTCP /* NCR with Wollongong TCP */
+ ioc.ic_cmd = IPIOC_GETIFFLAGS;
+ ioc.ic_timout = 0;
+ ioc.ic_len = sizeof(broad_req);
+ ioc.ic_dp = (char *)&broad_req;
+
+ if (ioctl (ipfd, I_STR, (char *) &ioc ) != -1 &&
+# else /* WINTCP */
+ if (ifioctl( socketFD, SIOCGIFFLAGS, (char *)&broad_req ) !=
+ -1 &&
+# endif /* WINTCP */
+ (broad_req.ifr_flags & IFF_BROADCAST) &&
+ (broad_req.ifr_flags & IFF_UP))
+ {
+ broad_req = *ifr;
+# ifdef WINTCP /* NCR with Wollongong TCP */
+ ioc.ic_cmd = IPIOC_GETIFBRDADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_len = sizeof(broad_req);
+ ioc.ic_dp = (char *)&broad_req;
+
+ if (ioctl( ipfd, I_STR, (char *) &ioc) != -1 )
+# else /* WINTCP */
+ if (ifioctl( socketFD, SIOCGIFBRDADDR, (char *)&broad_req )
+ != -1)
+# endif /* WINTCP */
+ broad_addr = broad_req.ifr_addr;
+ else
+ continue;
+ } else
+ continue;
+ }
+# endif
+ in_addr = *((struct sockaddr_in *)&broad_addr);
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr),
+ BROADCAST_QUERY );
+ }
+#endif
+}
+
+static int
+makeSockAddrs( const char *name, HostAddr **hosts )
+{
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai, *nai, hints;
+ bzero( &hints, sizeof(hints) );
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo( name, stringify( XDM_UDP_PORT ), &hints, &ai ))
+ return 0;
+ for (nai = ai; nai; nai = nai->ai_next)
+ if ((nai->ai_family == AF_INET) || (nai->ai_family == AF_INET6))
+ addHostaddr( hosts, nai->ai_addr, nai->ai_addrlen,
+ (nai->ai_family == AF_INET ?
+ IN_MULTICAST( ((struct sockaddr_in *)nai->ai_addr)->sin_addr.s_addr ) :
+ IN6_IS_ADDR_MULTICAST( &((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr )) ?
+ BROADCAST_QUERY : QUERY );
+#else
+ struct sockaddr_in in_addr;
+ /* Per RFC 1123, check first for IP address in dotted-decimal form */
+ if ((in_addr.sin_addr.s_addr = inet_addr( name )) == (unsigned)-1) {
+ struct hostent *hostent;
+ if (!(hostent = gethostbyname( name )) ||
+ hostent->h_addrtype != AF_INET)
+ return 0;
+ memcpy( &in_addr.sin_addr, hostent->h_addr, 4 );
+ }
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ addHostaddr( hosts, (struct sockaddr *)&in_addr, sizeof(in_addr),
+# ifdef IN_MULTICAST
+ IN_MULTICAST( in_addr.sin_addr.s_addr ) ?
+ BROADCAST_QUERY :
+# endif
+ QUERY );
+#endif
+ return 1;
+}
+
+/*
+ * Register the address for this host.
+ * Called for interactively specified hosts.
+ * The special names "BROADCAST" and "*" look up all the broadcast
+ * addresses on the local host.
+ */
+
+static int
+registerForPing( const char *name )
+{
+ Debug( "manual host registration: %s\n", name );
+ if (!strcmp( name, "BROADCAST" ) || !strcmp( name, "*" ))
+ registerBroadcastForPing();
+ else if (!makeSockAddrs( name, &hostAddrdb ))
+ return 0;
+ return 1;
+}
+
+/*ARGSUSED*/
+static void
+AddChooserHost(
+ CARD16 connectionType,
+ ARRAY8Ptr addr,
+ char *closure ATTR_UNUSED )
+{
+ if (connectionType == FamilyBroadcast) {
+ registerBroadcastForPing();
+ return;
+ }
+ Debug( "internal host registration: %[*hhu, family %hx\n",
+ addr->length, addr->data, connectionType );
+ if (connectionType == FamilyInternet) {
+ struct sockaddr_in in_addr;
+ in_addr.sin_family = AF_INET;
+ memmove( &in_addr.sin_addr, addr->data, 4 );
+ in_addr.sin_port = htons( XDM_UDP_PORT );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+#endif
+ registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr),
+#ifdef IN_MULTICAST
+ IN_MULTICAST( in_addr.sin_addr.s_addr ) ?
+ BROADCAST_QUERY :
+#endif
+ QUERY );
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ else if (connectionType == FamilyInternet6) {
+ struct sockaddr_in6 in6_addr;
+ in6_addr.sin6_family = AF_INET6;
+ memmove( &in6_addr.sin6_addr, addr->data, 16 );
+ in6_addr.sin6_port = htons( XDM_UDP_PORT );
+#ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+#endif
+ registerHostaddr( (struct sockaddr *)&in6_addr, sizeof(in6_addr),
+ IN6_IS_ADDR_MULTICAST( &in6_addr.sin6_addr ) ?
+ BROADCAST_QUERY : QUERY );
+ }
+#endif
+}
+
+static ARRAYofARRAY8 AuthenticationNames;
+
+#if 0
+static void RegisterAuthenticationName( char *name, int namelen )
+{
+ ARRAY8Ptr authName;
+ if (!XdmcpReallocARRAYofARRAY8( &AuthenticationNames,
+ AuthenticationNames.length + 1 ))
+ return;
+ authName = &AuthenticationNames.data[AuthenticationNames.length - 1];
+ if (!XdmcpAllocARRAY8( authName, namelen ))
+ return;
+ memmove( authName->data, name, namelen );
+}
+#endif
+
+static int
+initXDMCP()
+{
+ XdmcpHeader header;
+#if 0
+ int i;
+#endif
+#ifndef STREAMSCONN
+#ifdef SO_BROADCAST
+ int soopts;
+#endif
+#endif
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.length = 1;
+#if 0
+ for (i = 0; i < (int)AuthenticationNames.length; i++)
+ header.length += 2 + AuthenticationNames.data[i].length;
+#endif
+
+ header.opcode = (CARD16)BROADCAST_QUERY;
+ XdmcpWriteHeader( &broadcastBuffer, &header );
+ XdmcpWriteARRAYofARRAY8( &broadcastBuffer, &AuthenticationNames );
+
+ header.opcode = (CARD16)QUERY;
+ XdmcpWriteHeader( &directBuffer, &header );
+ XdmcpWriteARRAYofARRAY8( &directBuffer, &AuthenticationNames );
+
+#if defined(STREAMSCONN)
+ if ((socketFD = t_open( "/dev/udp", O_RDWR, 0 )) < 0)
+ return 0;
+
+ if (t_bind( socketFD, NULL, NULL ) < 0) {
+ t_close( socketFD );
+ return 0;
+ }
+
+ /*
+ * This part of the code looks contrived. It will actually fit in nicely
+ * when the CLTS part of Xtrans is implemented.
+ */
+ {
+ struct netconfig *nconf;
+
+ if ((nconf = getnetconfigent( "udp" )) == NULL) {
+ t_unbind( socketFD );
+ t_close( socketFD );
+ return 0;
+ }
+
+ if (netdir_options( nconf, ND_SET_BROADCAST, socketFD, NULL )) {
+ freenetconfigent( nconf );
+ t_unbind( socketFD );
+ t_close( socketFD );
+ return 0;
+ }
+
+ freenetconfigent( nconf );
+ }
+#else
+ if ((socketFD = socket( AF_INET, SOCK_DGRAM, 0 )) < 0)
+ return 0;
+#if defined(IPv6) && defined(AF_INET6)
+ socket6FD = socket( AF_INET6, SOCK_DGRAM, 0 );
+#endif
+# ifdef SO_BROADCAST
+ soopts = 1;
+ if (setsockopt( socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts,
+ sizeof(soopts) ) < 0)
+ perror( "setsockopt" );
+# endif
+#endif
+
+ return 1;
+}
+
+static void ATTR_NORETURN
+chooseHost( int hid )
+{
+ HostName *h;
+#if defined(IPv6) && defined(AF_INET6)
+ char addr[64];
+#endif
+
+ for (h = hostNamedb; h; h = h->next)
+ if ((int)h == hid) {
+ /* XXX error handling */
+ GSet( &mstrtalk );
+ if ((td->displayType & d_location) == dLocal) {
+ GSendInt( D_RemoteHost );
+#if defined(IPv6) && defined(AF_INET6)
+ switch (h->connectionType) {
+ case FamilyInternet6:
+ inet_ntop( AF_INET6, h->hostaddr.data, addr, sizeof(addr) );
+ break;
+ default: /* FamilyInternet */
+ inet_ntop( AF_INET, h->hostaddr.data, addr, sizeof(addr) );
+ break;
+ }
+ GSendStr( addr );
+#else
+ GSendStr( inet_ntoa( *(struct in_addr *)h->hostaddr.data ) );
+#endif
+ CloseGreeter( FALSE );
+ SessionExit( EX_REMOTE );
+ } else {
+ GSendInt( D_ChooseHost );
+ GSendArr( td->clientAddr.length, (char *)td->clientAddr.data );
+ GSendInt( td->connectionType );
+ GSendArr( h->hostaddr.length, (char *)h->hostaddr.data );
+ CloseGreeter( FALSE );
+ goto bout;
+ }
+ break;
+ }
+/* LogError( "Internal error: chose unexisting host\n" ); */
+ bout:
+ SessionExit( EX_NORMAL );
+}
+
+static void
+directChooseHost( const char *name )
+{
+ HostAddr *hosts = 0;
+
+ if (!makeSockAddrs( name, &hosts ))
+ return;
+ GSendInt( G_Ch_Exit );
+ /* XXX error handling */
+ GSet( &mstrtalk );
+ if ((td->displayType & d_location) == dLocal) {
+ GSendInt( D_RemoteHost );
+ GSendStr( name );
+ CloseGreeter( FALSE );
+ SessionExit( EX_REMOTE );
+ } else {
+ GSendInt( D_ChooseHost );
+ GSendArr( td->clientAddr.length, (char *)td->clientAddr.data );
+ GSendInt( td->connectionType );
+ GSendArr( hosts->addrlen, (char *)hosts->addr );
+ CloseGreeter( FALSE );
+ SessionExit( EX_NORMAL );
+ }
+}
+
+#define PING_TRIES 3
+
+int
+DoChoose()
+{
+ HostName **hp, *h;
+ char *host, **hostp;
+ struct timeval *to, tnow, nextPing;
+ int pingTry, n, cmd;
+ FD_TYPE rfds;
+ static int xdmcpInited;
+
+ OpenGreeter();
+ GSendInt( G_Choose );
+ switch (cmd = CtrlGreeterWait( TRUE )) {
+ case G_Ready:
+ break;
+ default: /* error */
+ return cmd;
+ }
+
+ if (!xdmcpInited) {
+ if (!initXDMCP())
+ SessionExit( EX_UNMANAGE_DPY );
+ xdmcpInited = 1;
+ }
+ if ((td->displayType & d_location) == dLocal) {
+ /* XXX the config reader should do the lookup already */
+ for (hostp = td->chooserHosts; *hostp; hostp++)
+ if (!registerForPing( *hostp ))
+ LogError( "Unkown host %\"s specified for local chooser preload of display %s\n", *hostp, td->name );
+ } else
+ ForEachChooserHost( &td->clientAddr, td->connectionType,
+ AddChooserHost, 0 );
+
+ GSendInt( 0 ); /* entering async mode signal */
+
+ reping:
+ for (h = hostNamedb; h; h = h->next)
+ h->alive = 0;
+ pingTry = 0;
+ goto pingen;
+
+ for (;;) {
+ to = 0;
+ if (pingTry <= PING_TRIES) {
+ gettimeofday( &tnow, 0 );
+ if (nextPing.tv_sec < tnow.tv_sec ||
+ (nextPing.tv_sec == tnow.tv_sec &&
+ nextPing.tv_usec < tnow.tv_usec)) {
+ if (pingTry < PING_TRIES) {
+ pingen:
+ pingTry++;
+ doPingHosts();
+ gettimeofday( &tnow, 0 );
+ nextPing = tnow;
+ nextPing.tv_sec++;
+ } else {
+ for (hp = &hostNamedb; *hp; )
+ if (!(*hp)->alive) {
+ h = (*hp)->next;
+ disposeHostname( *hp );
+ GSendInt( G_Ch_RemoveHost );
+ GSendInt( (int)*hp ); /* just an id */
+ *hp = h;
+ } else
+ hp = &(*hp)->next;
+ goto noto;
+ }
+ }
+ to = &tnow;
+ tnow.tv_sec = nextPing.tv_sec - tnow.tv_sec;
+ tnow.tv_usec = nextPing.tv_usec - tnow.tv_usec;
+ if (tnow.tv_usec < 0) {
+ tnow.tv_usec += 1000000;
+ tnow.tv_sec--;
+ }
+ }
+ noto:
+ FD_ZERO( &rfds );
+ FD_SET( grtproc.pipe.rfd, &rfds );
+ FD_SET( socketFD, &rfds );
+#if defined(IPv6) && defined(AF_INET6)
+ if (socket6FD >= 0)
+ FD_SET( socket6FD, &rfds );
+#endif
+ n = grtproc.pipe.rfd;
+ if (socketFD > n)
+ n = socketFD;
+#if defined(IPv6) && defined(AF_INET6)
+ if (socket6FD > n)
+ n = socket6FD;
+#endif
+ if (select( n + 1, &rfds, 0, 0, to ) > 0) {
+ if (FD_ISSET( grtproc.pipe.rfd, &rfds ))
+ switch (cmd = CtrlGreeterWait( FALSE )) {
+ case -1:
+ break;
+ case G_Ch_Refresh:
+ goto reping;
+ case G_Ch_RegisterHost:
+ host = GRecvStr();
+ if (!registerForPing( host )) {
+ GSendInt( G_Ch_BadHost );
+ GSendStr( host );
+ }
+ free( host );
+ goto reping;
+ case G_Ch_DirectChoice:
+ host = GRecvStr();
+ directChooseHost( host );
+ GSendInt( G_Ch_BadHost );
+ GSendStr( host );
+ free( host );
+ break;
+ case G_Ready:
+ chooseHost( GRecvInt() );
+ /* NOTREACHED */
+ default:
+ emptyHostnames();
+ emptyPingHosts();
+ return cmd;
+ }
+ if (FD_ISSET( socketFD, &rfds ))
+ receivePacket( socketFD );
+#if defined(IPv6) && defined(AF_INET6)
+ if (socket6FD >= 0 && FD_ISSET( socket6FD, &rfds ))
+ receivePacket( socket6FD );
+#endif
+ }
+ }
+
+}
+
+#endif /* XDMCP */
diff --git a/tdm/backend/client.c b/tdm/backend/client.c
new file mode 100644
index 000000000..1dfd97849
--- /dev/null
+++ b/tdm/backend/client.c
@@ -0,0 +1,1857 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * user verification and session initiation.
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#ifdef SECURE_RPC
+# include <rpc/rpc.h>
+# include <rpc/key_prot.h>
+extern int key_setnet( struct key_netstarg *arg );
+#endif
+#ifdef K5AUTH
+# include <krb5/krb5.h>
+#endif
+#ifdef HAVE_SETUSERCONTEXT
+# include <login_cap.h>
+#endif
+#ifdef USE_PAM
+# ifdef HAVE_PAM_PAM_APPL_H
+# include <pam/pam_appl.h>
+# else
+# include <security/pam_appl.h>
+# endif
+#elif defined(_AIX) /* USE_PAM */
+# include <login.h>
+# include <usersec.h>
+extern int loginrestrictions( const char *Name, const int Mode, const char *Tty, char **Msg );
+extern int loginfailed( const char *User, const char *Host, const char *Tty );
+extern int loginsuccess( const char *User, const char *Host, const char *Tty, char **Msg );
+#else /* USE_PAM || _AIX */
+# ifdef KERBEROS
+# include <sys/param.h>
+# include <krb.h>
+# ifndef NO_AFS
+# include <kafs.h>
+# endif
+# endif
+/* for nologin */
+# include <sys/types.h>
+# include <unistd.h>
+/* for expiration */
+# include <time.h>
+#endif /* USE_PAM || _AIX */
+#ifdef HAVE_SHADOW
+# include <shadow.h>
+#endif
+#include <signal.h>
+
+#ifdef WITH_CONSOLE_KIT
+#include "consolekit.h"
+#endif
+
+#define AU_FAILED 0
+#define AU_SUCCESS 1
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+#else
+#define log_to_audit_system(l,h,d,s) do { ; } while (0)
+#endif
+
+/*
+ * Session data, mostly what struct verify_info was for
+ */
+char *curuser;
+char *curpass;
+char *curtype;
+char *newpass;
+char **userEnviron;
+char **systemEnviron;
+static int curuid;
+static int curgid;
+int cursource;
+
+char *dmrcuser;
+char *curdmrc;
+char *newdmrc;
+
+static struct passwd *p;
+#ifdef HAVE_SETUSERCONTEXT
+# ifdef HAVE_LOGIN_GETCLASS
+login_cap_t *lc;
+# else
+struct login_cap *lc;
+# endif
+#endif
+#ifdef USE_PAM
+static pam_handle_t *pamh;
+#elif defined(_AIX)
+static char tty[16], hostname[100];
+#else
+# ifdef USESHADOW
+static struct spwd *sp;
+# endif
+# ifdef KERBEROS
+static char krbtkfile[MAXPATHLEN];
+# endif
+#endif
+
+#define V_RET_AUTH \
+ do { \
+ PrepErrorGreet (); \
+ GSendInt (V_AUTH); \
+ return 0; \
+ } while(0)
+
+#define V_RET_FAIL(m) \
+ do { \
+ PrepErrorGreet (); \
+ GSendInt (V_MSG_ERR); \
+ GSendStr (m); \
+ GSendInt (V_FAIL); \
+ return 0; \
+ } while(0)
+
+#ifdef USE_PAM
+
+# ifdef PAM_MESSAGE_NONCONST
+typedef struct pam_message pam_message_type;
+typedef void *pam_gi_type;
+# else
+typedef const struct pam_message pam_message_type;
+typedef const void *pam_gi_type;
+# endif
+
+struct pam_data {
+ GConvFunc gconv;
+ int usecur;
+ int abort;
+};
+
+static int
+PAM_conv( int num_msg,
+ pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr )
+{
+ int count;
+ struct pam_response *reply;
+ struct pam_data *pd = (struct pam_data *)appdata_ptr;
+
+ if (!(reply = Calloc( num_msg, sizeof(*reply) )))
+ return PAM_CONV_ERR;
+
+ ReInitErrorLog();
+ Debug( "PAM_conv\n" );
+ for (count = 0; count < num_msg; count++)
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( msg[count]->msg );
+ continue;
+ case PAM_ERROR_MSG:
+ Debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg );
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( msg[count]->msg );
+ continue;
+ default:
+ /* could do better error handling here, but see below ... */
+ if (pd->usecur) {
+ switch (msg[count]->msg_style) {
+ /* case PAM_PROMPT_ECHO_ON: cannot happen */
+ case PAM_PROMPT_ECHO_OFF:
+ Debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg );
+ if (!curpass)
+ pd->gconv( GCONV_PASS, 0 );
+ StrDup( &reply[count].resp, curpass );
+ break;
+ default:
+ LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
+ goto conv_err;
+ }
+ } else {
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ Debug( " PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg );
+ reply[count].resp = pd->gconv( GCONV_NORMAL, msg[count]->msg );
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ Debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg );
+ reply[count].resp = pd->gconv( GCONV_HIDDEN, msg[count]->msg );
+ break;
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT:
+ Debug( " PAM_BINARY_PROMPT\n" );
+ reply[count].resp = pd->gconv( GCONV_BINARY, msg[count]->msg );
+ break;
+#endif
+ default:
+ LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
+ goto conv_err;
+ }
+ }
+ if (!reply[count].resp) {
+ Debug( " PAM_conv aborted\n" );
+ pd->abort = TRUE;
+ goto conv_err;
+ }
+ reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
+ }
+ Debug( " PAM_conv success\n" );
+ *resp = reply;
+ return PAM_SUCCESS;
+
+ conv_err:
+ for (; count >= 0; count--)
+ if (reply[count].resp)
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF: /* could wipe ... */
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT: /* ... that too ... */
+#endif
+ free( reply[count].resp );
+ break;
+ }
+ free( reply );
+ return PAM_CONV_ERR;
+}
+
+static int
+PAM_conv_null( int num_msg,
+ pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr ATTR_UNUSED )
+{
+ int count;
+ struct pam_response *reply;
+
+ if (!(reply = Calloc( num_msg, sizeof(*reply) )))
+ return PAM_CONV_ERR;
+
+ ReInitErrorLog();
+ Debug( "PAM_conv_null\n" );
+ for (count = 0; count < num_msg; count++) {
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
+ continue;
+ case PAM_ERROR_MSG:
+ LogError( "PAM error message: %s\n", msg[count]->msg );
+ continue;
+ default:
+ /* unknown */
+ Debug( " PAM_<%d>\n", msg[count]->msg_style );
+ free( reply );
+ return PAM_CONV_ERR;
+ }
+ reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
+ }
+ Debug( " PAM_conv_null success\n" );
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+# ifdef PAM_FAIL_DELAY
+static void
+fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
+ void *appdata_ptr ATTR_UNUSED )
+{}
+# endif
+
+ /**
+ * log_to_audit_system:
+ * @login: Name of user
+ * @hostname: Name of host machine
+ * @tty: Name of display
+ * @success: 1 for success, 0 for failure
+ *
+ * Logs the success or failure of the login attempt with the linux kernel
+ * audit system. The intent is to capture failed events where the user
+ * fails authentication or otherwise is not permitted to login. There are
+ * many other places where pam could potentially fail and cause login to
+ * fail, but these are system failures rather than the signs of an account
+ * being hacked.
+ *
+ * Returns nothing.
+ */
+
+#ifdef HAVE_LIBAUDIT
+static void
+log_to_audit_system (const char *loginname,
+ const char *hostname,
+ const char *tty,
+ int success)
+{
+ struct passwd *pw;
+ char buf[64];
+ int audit_fd;
+
+ audit_fd = audit_open();
+ if (loginname)
+ pw = getpwnam(loginname);
+ else {
+ loginname = "unknown";
+ pw = NULL;
+ }
+ Debug("log_to_audit %p %s\n", pw, loginname);
+
+ if (pw) {
+ snprintf(buf, sizeof(buf), "uid=%d", pw->pw_uid);
+ audit_log_user_message(audit_fd, AUDIT_USER_LOGIN,
+ buf, hostname, NULL, tty, (int)success);
+ } else {
+ snprintf(buf, sizeof(buf), "acct=%s", loginname);
+ audit_log_user_message(audit_fd, AUDIT_USER_LOGIN,
+ buf, hostname, NULL, tty, (int)success);
+ }
+ close(audit_fd);
+}
+#endif
+
+static int
+doPAMAuth( const char *psrv, struct pam_data *pdata )
+{
+ pam_gi_type pitem;
+ struct pam_conv pconv;
+ int pretc;
+
+ pdata->abort = FALSE;
+ pconv.conv = PAM_conv;
+ pconv.appdata_ptr = (void *)pdata;
+ Debug( " PAM service %s\n", psrv );
+ if ((pretc = pam_start( psrv, curuser, &pconv, &pamh )) != PAM_SUCCESS)
+ goto pam_bail2;
+ if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) {
+ pam_bail:
+ pam_end( pamh, pretc );
+ pamh = 0;
+ pam_bail2:
+ ReInitErrorLog();
+ LogError( "PAM error: %s\n", pam_strerror( 0, pretc ) );
+ V_RET_FAIL( 0 );
+ }
+ if ((td->displayType & d_location) == dForeign) {
+ char *cp = strchr( td->name, ':' );
+ *cp = 0;
+ pretc = pam_set_item( pamh, PAM_RHOST, td->name );
+ *cp = ':';
+ if (pretc != PAM_SUCCESS)
+ goto pam_bail;
+ }
+# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */
+ else if (pam_set_item( pamh, PAM_RHOST, 0 ) != PAM_SUCCESS)
+ goto pam_bail;
+# endif
+# ifdef PAM_FAIL_DELAY
+ pam_set_item( pamh, PAM_FAIL_DELAY, (void *)fail_delay );
+# endif
+ pretc = pam_putenv( pamh, "XDG_SESSION_CLASS=greeter" );
+ ReInitErrorLog();
+
+ Debug( " pam_authenticate() ...\n" );
+ pretc = pam_authenticate( pamh,
+ td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK );
+ ReInitErrorLog();
+ Debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pdata->abort) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ return 0;
+ }
+ if (!curuser) {
+ Debug( " asking PAM for user ...\n" );
+ pam_get_item( pamh, PAM_USER, &pitem );
+ ReInitErrorLog();
+ StrDup( &curuser, (const char *)pitem );
+ GSendInt( V_PUT_USER );
+ GSendStr( curuser );
+ }
+ if (pretc != PAM_SUCCESS) {
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ switch (pretc) {
+ case PAM_USER_UNKNOWN:
+ case PAM_AUTH_ERR:
+ case PAM_MAXTRIES: /* should handle this better ... */
+ case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_AUTH;
+ default:
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_FAIL( 0 );
+ }
+ }
+ return 1;
+}
+
+#endif /* USE_PAM */
+
+static int
+#if defined(USE_PAM) || defined(_AIX)
+AccNoPass( const char *un )
+{
+ struct passwd *pw = 0;
+# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */
+ struct spwd *spw;
+# endif
+#else
+AccNoPass( const char *un, struct passwd *pw )
+{
+#endif
+ struct group *gr;
+ char **fp;
+ int hg;
+
+ if (!*un)
+ return 0;
+
+ if (cursource != PWSRC_MANUAL)
+ return 1;
+
+ for (hg = 0, fp = td->noPassUsers; *fp; fp++)
+ if (**fp == '@')
+ hg = 1;
+ else if (!strcmp( un, *fp ))
+ return 1;
+ else if (!strcmp( "*", *fp )) {
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(pw = getpwnam( un )))
+ return 0;
+ if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
+ continue;
+# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */
+ if ((spw = getspnam( un )) &&
+ (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*'))
+ continue;
+# endif
+#endif
+ if (pw->pw_uid)
+ return 1;
+ }
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (hg && (pw || (pw = getpwnam( un )))) {
+#else
+ if (hg) {
+#endif
+ for (setgrent(); (gr = getgrent()); )
+ for (fp = td->noPassUsers; *fp; fp++)
+ if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) {
+ if (pw->pw_gid == gr->gr_gid) {
+ endgrent();
+ return 1;
+ }
+ for (; *gr->gr_mem; gr->gr_mem++)
+ if (!strcmp( un, *gr->gr_mem )) {
+ endgrent();
+ return 1;
+ }
+ }
+ endgrent();
+ }
+
+ return 0;
+}
+
+#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT)
+# define LC_RET0 do { login_close(lc); return 0; } while(0)
+#else
+# define LC_RET0 return 0
+#endif
+
+int
+Verify( GConvFunc gconv, int rootok )
+{
+#ifdef USE_PAM
+ const char *psrv;
+ struct pam_data pdata;
+ int pretc, pnopass;
+ char psrvb[64];
+#elif defined(_AIX)
+ char *msg, *curret;
+ int i, reenter;
+#else
+ struct stat st;
+ const char *nolg;
+ char *buf;
+ int fd;
+# ifdef HAVE_GETUSERSHELL
+ char *s;
+# endif
+# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
+ int tim, expir, warntime, quietlog;
+# endif
+#endif
+
+ Debug( "Verify ...\n" );
+
+#ifdef USE_PAM
+
+ pnopass = FALSE;
+ if (!strcmp( curtype, "classic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ if (AccNoPass( curuser )) {
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ pnopass = TRUE;
+ sprintf( psrvb, "%.31s-np", PAMService );
+ psrv = psrvb;
+ } else
+ psrv = PAMService;
+ } else
+ psrv = PAMService;
+ pdata.usecur = TRUE;
+ } else if (!strcmp( curtype, "pam" )) {
+ psrv = PAMService;
+ pdata.usecur = FALSE;
+ } else {
+ sprintf( psrvb, "%.31s-%.31s", PAMService, curtype );
+ psrv = psrvb;
+ pdata.usecur = FALSE;
+ }
+ pdata.gconv = gconv;
+ if (!doPAMAuth( psrv, &pdata ))
+ return 0;
+
+#elif defined(_AIX)
+
+ if ((td->displayType & d_location) == dForeign) {
+ char *tmpch;
+ strncpy( hostname, td->name, sizeof(hostname) - 1 );
+ hostname[sizeof(hostname)-1] = '\0';
+ if ((tmpch = strchr( hostname, ':' )))
+ *tmpch = '\0';
+ } else
+ hostname[0] = '\0';
+
+ /* tty names should only be 15 characters long */
+# if 0
+ for (i = 0; i < 15 && td->name[i]; i++) {
+ if (td->name[i] == ':' || td->name[i] == '.')
+ tty[i] = '_';
+ else
+ tty[i] = td->name[i];
+ }
+ tty[i] = '\0';
+# else
+ memcpy( tty, "/dev/xdm/", 9 );
+ for (i = 0; i < 6 && td->name[i]; i++) {
+ if (td->name[i] == ':' || td->name[i] == '.')
+ tty[9 + i] = '_';
+ else
+ tty[9 + i] = td->name[i];
+ }
+ tty[9 + i] = '\0';
+# endif
+
+ if (!strcmp( curtype, "classic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ if (AccNoPass( curuser )) {
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ Debug( "accepting despite empty password\n" );
+ goto done;
+ }
+ } else
+ if (!gconv( GCONV_PASS, 0 ))
+ return 0;
+ enduserdb();
+ msg = NULL;
+ if ((i = authenticate( curuser, curpass, &reenter, &msg ))) {
+ Debug( "authenticate() failed: %s\n", msg );
+ if (msg)
+ free( msg );
+ loginfailed( curuser, hostname, tty );
+ if (i == ENOENT || i == ESAD)
+ V_RET_AUTH;
+ else
+ V_RET_FAIL( 0 );
+ }
+ if (reenter) {
+ LogError( "authenticate() requests more data: %s\n", msg );
+ free( msg );
+ V_RET_FAIL( 0 );
+ }
+ } else if (!strcmp( curtype, "generic" ) || !strcmp(curtype, "pam")) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ for (curret = 0;;) {
+ msg = NULL;
+ if ((i = authenticate( curuser, curret, &reenter, &msg ))) {
+ Debug( "authenticate() failed: %s\n", msg );
+ if (msg)
+ free( msg );
+ loginfailed( curuser, hostname, tty );
+ if (i == ENOENT || i == ESAD)
+ V_RET_AUTH;
+ else
+ V_RET_FAIL( 0 );
+ }
+ if (curret)
+ free( curret );
+ if (!reenter)
+ break;
+ if (!(curret = gconv( GCONV_HIDDEN, msg )))
+ return 0;
+ free( msg );
+ }
+ } else {
+ LogError( "Unsupported authentication type %\"s requested\n", curtype );
+ V_RET_FAIL( 0 );
+ }
+ if (msg) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( msg );
+ free( msg );
+ }
+
+ done:
+
+#else
+
+ if (strcmp( curtype, "classic" )) {
+ LogError( "Unsupported authentication type %\"s requested\n", curtype );
+ V_RET_FAIL( 0 );
+ }
+
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+
+ if (!(p = getpwnam( curuser ))) {
+ Debug( "getpwnam() failed.\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
+ Debug( "account is locked\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+
+# ifdef USESHADOW
+ if ((sp = getspnam( curuser ))) {
+ p->pw_passwd = sp->sp_pwdp;
+ if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
+ Debug( "account is locked\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ } else
+ Debug( "getspnam() failed: %m. Are you root?\n" );
+# endif
+
+ if (!*p->pw_passwd) {
+ if (!td->allowNullPasswd) {
+ Debug( "denying user with empty password\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ goto nplogin;
+ }
+
+ if (AccNoPass( curuser, p )) {
+ nplogin:
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ Debug( "accepting password-less login\n" );
+ goto done;
+ }
+ } else
+ if (!gconv( GCONV_PASS, 0 ))
+ return 0;
+
+# ifdef KERBEROS
+ if (p->pw_uid) {
+ int ret;
+ char realm[REALM_SZ];
+
+ if (krb_get_lrealm( realm, 1 )) {
+ LogError( "Can't get KerberosIV realm.\n" );
+ V_RET_FAIL( 0 );
+ }
+
+ sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name );
+ krb_set_tkt_string( krbtkfile );
+ unlink( krbtkfile );
+
+ ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" );
+ if (ret == KSUCCESS) {
+ chown( krbtkfile, p->pw_uid, p->pw_gid );
+ Debug( "KerberosIV verify succeeded\n" );
+ goto done;
+ } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) {
+ LogError( "KerberosIV verification failure %\"s for %s\n",
+ krb_get_err_text( ret ), curuser );
+ krbtkfile[0] = '\0';
+ V_RET_FAIL( 0 );
+ }
+ Debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) );
+ }
+ krbtkfile[0] = '\0';
+# endif /* KERBEROS */
+
+# if defined(ultrix) || defined(__ultrix__)
+ if (authenticate_user( p, curpass, NULL ) < 0)
+# elif defined(HAVE_CRYPT)
+ if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd ))
+# else
+ if (strcmp( curpass, p->pw_passwd ))
+# endif
+ {
+ Debug( "password verify failed\n" );
+ V_RET_AUTH;
+ }
+
+ done:
+
+#endif /* !defined(USE_PAM) && !defined(_AIX) */
+
+ Debug( "restrict %s ...\n", curuser );
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(p = getpwnam( curuser ))) {
+ LogError( "getpwnam(%s) failed.\n", curuser );
+ V_RET_FAIL( 0 );
+ }
+#endif
+ if (!p->pw_uid) {
+ if (!rootok && !td->allowRootLogin)
+ V_RET_FAIL( "Root logins are not allowed" );
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ return 1; /* don't deny root to log in */
+ }
+
+#ifdef USE_PAM
+
+ Debug( " pam_acct_mgmt() ...\n" );
+ pretc = pam_acct_mgmt( pamh, 0 );
+ ReInitErrorLog();
+ Debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pretc == PAM_NEW_AUTHTOK_REQD) {
+ pdata.usecur = FALSE;
+ pdata.gconv = conv_interact;
+ /* pam will have output a message already, so no PrepErrorGreet () */
+ if (gconv != conv_interact || pnopass) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ GSendInt( V_CHTOK_AUTH );
+ /* this cannot auth the wrong user, as only classic auths get here */
+ while (!doPAMAuth( PAMService, &pdata ))
+ if (pdata.abort)
+ return 0;
+ GSendInt( V_PRE_OK );
+ } else
+ GSendInt( V_CHTOK );
+ for (;;) {
+ Debug( " pam_chauthtok() ...\n" );
+ pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK );
+ ReInitErrorLog();
+ Debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pdata.abort) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ return 0;
+ }
+ if (pretc == PAM_SUCCESS)
+ break;
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ /* effectively there is only PAM_AUTHTOK_ERR */
+ GSendInt( V_FAIL );
+ }
+ if (curpass)
+ free( curpass );
+ curpass = newpass;
+ newpass = 0;
+ } else if (pretc != PAM_SUCCESS) {
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_AUTH;
+ }
+
+#elif defined(_AIX) /* USE_PAM */
+
+ msg = NULL;
+ if (loginrestrictions( curuser,
+ ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
+ tty, &msg ) == -1)
+ {
+ Debug( "loginrestrictions() - %s\n", msg ? msg : "error" );
+ loginfailed( curuser, hostname, tty );
+ PrepErrorGreet();
+ if (msg) {
+ GSendInt( V_MSG_ERR );
+ GSendStr( msg );
+ }
+ GSendInt( V_AUTH );
+ return 0;
+ }
+ if (msg)
+ free( (void *)msg );
+
+#endif /* USE_PAM || _AIX */
+
+#ifndef _AIX
+
+# ifdef HAVE_SETUSERCONTEXT
+# ifdef HAVE_LOGIN_GETCLASS
+ lc = login_getclass( p->pw_class );
+# else
+ lc = login_getpwclass( p );
+# endif
+ if (!lc)
+ V_RET_FAIL( 0 );
+
+ p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell );
+# endif
+
+# ifndef USE_PAM
+
+/* restrict_expired */
+# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
+
+# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
+ if (sp)
+# endif
+ {
+
+# define DEFAULT_WARN (2L * 7L) /* Two weeks */
+
+ tim = time( NULL ) / 86400L;
+
+# ifdef HAVE_SETUSERCONTEXT
+ quietlog = login_getcapbool( lc, "hushlogin", 0 );
+ warntime = login_getcaptime( lc, "warnexpire",
+ DEFAULT_WARN * 86400L,
+ DEFAULT_WARN * 86400L ) / 86400L;
+# else
+ quietlog = 0;
+# ifdef USESHADOW
+ warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
+# else
+ warntime = DEFAULT_WARN;
+# endif
+# endif
+
+# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
+ if (p->pw_expire) {
+ expir = p->pw_expire / 86400L;
+# else
+ if (sp->sp_expire != -1) {
+ expir = sp->sp_expire;
+# endif
+ if (tim > expir) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Your account has expired;"
+ " please contact your system administrator" );
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (tim > (expir - warntime) && !quietlog) {
+ ASPrintf( &buf,
+ "Warning: your account will expire in %d day(s)",
+ expir - tim );
+ if (buf) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( buf );
+ free( buf );
+ }
+ }
+ }
+
+# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
+ if (p->pw_change) {
+ expir = p->pw_change / 86400L;
+# else
+ if (!sp->sp_lstchg) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are required to change your password immediately"
+ " (root enforced)" );
+ /* XXX todo password change */
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (sp->sp_max != -1) {
+ expir = sp->sp_lstchg + sp->sp_max;
+ if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Your account has expired;"
+ " please contact your system administrator" );
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+# endif
+ if (tim > expir) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are required to change your password immediately"
+ " (password aged)" );
+ /* XXX todo password change */
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (tim > (expir - warntime) && !quietlog) {
+ ASPrintf( &buf,
+ "Warning: your password will expire in %d day(s)",
+ expir - tim );
+ if (buf) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( buf );
+ free( buf );
+ }
+ }
+ }
+
+ }
+
+# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */
+
+/* restrict_nologin */
+# ifndef _PATH_NOLOGIN
+# define _PATH_NOLOGIN "/etc/nologin"
+# endif
+
+ if ((
+# ifdef HAVE_SETUSERCONTEXT
+ /* Do we ignore a nologin file? */
+ !login_getcapbool( lc, "ignorenologin", 0 )) &&
+ (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) ||
+# endif
+ !stat( (nolg = _PATH_NOLOGIN), &st )))
+ {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) {
+ if ((buf = Malloc( st.st_size + 1 ))) {
+ if (read( fd, buf, st.st_size ) == st.st_size) {
+ buf[st.st_size] = 0;
+ GSendStr( buf );
+ free( buf );
+ close( fd );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+ free( buf );
+ }
+ close( fd );
+ }
+ GSendStr( "Logins are not allowed at the moment.\nTry again later" );
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+
+/* restrict_time */
+# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK)
+ if (!auth_timeok( lc, time( NULL ) )) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are not allowed to login at the moment" );
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+# endif
+
+# ifdef HAVE_GETUSERSHELL
+ for (;;) {
+ if (!(s = getusershell())) {
+ Debug( "shell not in /etc/shells\n" );
+ endusershell();
+ V_RET_FAIL( "Your login shell is not listed in /etc/shells" );
+ /* Log the failed login attempt */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
+ }
+ if (!strcmp( s, p->pw_shell )) {
+ endusershell();
+ break;
+ }
+ }
+# endif
+
+# endif /* !USE_PAM */
+
+/* restrict_nohome */
+# ifdef HAVE_SETUSERCONTEXT
+ if (login_getcapbool( lc, "requirehome", 0 )) {
+ struct stat st;
+ if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Home folder not available" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+ }
+# endif
+
+#endif /* !_AIX */
+
+ return 1;
+
+}
+
+
+static const char *envvars[] = {
+ "TZ", /* SYSV and SVR4, but never hurts */
+#ifdef _AIX
+ "AUTHSTATE", /* for kerberos */
+#endif
+ NULL
+};
+
+
+#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
+static int num_saved_gids;
+static gid_t *saved_gids;
+
+static int
+saveGids( void )
+{
+ num_saved_gids = getgroups( 0, 0 );
+ if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids )))
+ return 0;
+ if (getgroups( num_saved_gids, saved_gids ) < 0) {
+ LogError( "saving groups failed: %m\n" );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+restoreGids( void )
+{
+ if (setgroups( num_saved_gids, saved_gids ) < 0) {
+ LogError( "restoring groups failed: %m\n" );
+ return 0;
+ }
+ if (setgid( p->pw_gid ) < 0) {
+ LogError( "restoring gid failed: %m\n" );
+ return 0;
+ }
+ return 1;
+}
+#endif /* USE_PAM && HAVE_INITGROUPS */
+
+static int
+resetGids( void )
+{
+#ifdef HAVE_INITGROUPS
+ if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) {
+ LogError( "restoring groups failed: %m\n" );
+ return 0;
+ }
+#endif
+ if (setgid( 0 ) < 0) {
+ LogError( "restoring gid failed: %m\n" );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+SetGid( const char *name, int gid )
+{
+ if (setgid( gid ) < 0) {
+ LogError( "setgid(%d) (user %s) failed: %m\n", gid, name );
+ return 0;
+ }
+#ifdef HAVE_INITGROUPS
+ if (initgroups( name, gid ) < 0) {
+ LogError( "initgroups for %s failed: %m\n", name );
+ setgid( 0 );
+ return 0;
+ }
+#endif /* QNX4 doesn't support multi-groups, no initgroups() */
+ return 1;
+}
+
+static int
+SetUid( const char *name, int uid )
+{
+ if (setuid( uid ) < 0) {
+ LogError( "setuid(%d) (user %s) failed: %m\n", uid, name );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+SetUser( const char *name, int uid, int gid )
+{
+ if (SetGid( name, gid )) {
+ if (SetUid( name, uid ))
+ return 1;
+ resetGids();
+ }
+ return 0;
+}
+
+#if defined(SECURE_RPC) || defined(K5AUTH)
+static void
+NukeAuth( int len, const char *name )
+{
+ int i;
+
+ for (i = 0; i < td->authNum; i++)
+ if (td->authorizations[i]->name_length == len &&
+ !memcmp( td->authorizations[i]->name, name, len ))
+ {
+ memcpy( &td->authorizations[i], &td->authorizations[i+1],
+ sizeof(td->authorizations[i]) * (--td->authNum - i) );
+ break;
+ }
+}
+#endif
+
+static void
+mergeSessionArgs( int cansave )
+{
+ char *mfname;
+ const char *fname;
+ int i, needsave;
+
+ mfname = 0;
+ fname = ".dmrc";
+ if ((!curdmrc || newdmrc) && *dmrcDir)
+ if (StrApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 ))
+ fname = mfname;
+ needsave = 0;
+ if (!curdmrc) {
+ curdmrc = iniLoad( fname );
+ if (!curdmrc) {
+ StrDup( &curdmrc, "[Desktop]\nSession=default\n" );
+ needsave = 1;
+ }
+ }
+ if (newdmrc) {
+ curdmrc = iniMerge( curdmrc, newdmrc );
+ needsave = 1;
+ }
+ if (needsave && cansave)
+ if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) {
+ for (i = 0; mfname[i]; i++)
+ if (mfname[i] == '/') {
+ mfname[i] = 0;
+ mkdir( mfname, 0755 );
+ mfname[i] = '/';
+ }
+ iniSave( curdmrc, mfname );
+ }
+ if (mfname)
+ free( mfname );
+}
+
+static int removeAuth;
+#ifdef USE_PAM
+static int removeSession;
+static int removeCreds;
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+int
+StartClient( const char *ck_session_cookie )
+#else
+int
+StartClient()
+#endif
+{
+ const char *home, *sessargs, *desksess;
+ char **env, *xma;
+ char **argv, *fname, *str;
+#ifdef USE_PAM
+ char **pam_env;
+# ifdef _AIX
+ char **saved_env;
+# endif
+ struct pam_conv pconv;
+ 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], *lname;
+ int i, pid, lfd;
+
+ 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 );
+ 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(NO_AFS)
+ if (krbtkfile[0] != '\0') {
+ if (k_hasafs()) {
+ if (k_setpag() == -1)
+ LogError( "setpag() for %s failed\n", curuser );
+ if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS)
+ LogError( "AFS Warning: %s\n", krb_get_err_text( ret ) );
+ }
+ }
+# endif /* KERBEROS && AFS */
+# endif /* _AIX */
+#endif /* !PAM */
+
+ curuid = p->pw_uid;
+ curgid = p->pw_gid;
+
+ env = baseEnv( curuser );
+ xma = 0;
+ if (td->ctrl.fpath && StrDup( &xma, td->ctrl.fpath )) {
+ if ((td->allowShutdown == SHUT_ALL ||
+ (td->allowShutdown == SHUT_ROOT && !curuser)) &&
+ StrApp( &xma, ",maysd", (char *)0 ))
+ {
+ if (td->allowNuke == SHUT_ALL ||
+ (td->allowNuke == SHUT_ROOT && !curuser))
+ StrApp( &xma, ",mayfn", (char *)0 );
+ StrApp( &xma, td->defSdMode == SHUT_FORCENOW ? ",fn" :
+ td->defSdMode == SHUT_TRYNOW ? ",tn" : ",sched",
+ (char *)0 );
+ }
+ if ((td->displayType & d_location) == dLocal && AnyReserveDisplays())
+ StrApp( &xma, ",rsvd", (char *)0 );
+ } else
+ StrDup( &xma, "true" );
+ 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 (cursource == PWSRC_AUTOLOGIN)
+ env = setEnv (env, "TDM_AUTOLOGIN", curuser);
+#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
+ if (krbtkfile[0] != '\0')
+ env = setEnv( env, "KRBTKFILE", krbtkfile );
+#endif
+#ifdef WITH_CONSOLE_KIT
+ if (ck_session_cookie != NULL) {
+ env = setEnv ( env, "XDG_SESSION_COOKIE", ck_session_cookie );
+ }
+#endif
+ userEnviron = inheritEnv( env, envvars );
+ env = systemEnv( p->pw_name );
+ 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
+ /* the greeter is gone by now ... */
+ pconv.conv = PAM_conv_null;
+ pconv.appdata_ptr = 0;
+ if ((pretc = pam_set_item( pamh, PAM_CONV, &pconv )) != PAM_SUCCESS) {
+ ReInitErrorLog();
+ LogError( "pam_set_item() for %s failed: %s\n",
+ curuser, pam_strerror( pamh, pretc ) );
+ return 0;
+ }
+ ReInitErrorLog();
+#endif
+
+#ifdef USE_PAM
+
+# ifdef HAVE_SETUSERCONTEXT
+ if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) {
+ LogError( "setusercontext(groups) for %s failed: %m\n",
+ curuser );
+ return 0;
+ }
+# else
+ if (!SetGid( curuser, curgid ))
+ return 0;
+# endif
+
+# ifdef _AIX
+ if (!(pam_env = initStrArr( 0 ))) {
+ resetGids();
+ return 0;
+ }
+ 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();
+# ifdef _AIX
+ 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())
+ return 0;
+# 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 */
+
+ /* Login succeeded */
+ log_to_audit_system (curuser, td->remoteHost, td->name, AU_SUCCESS);
+
+ removeAuth = 1;
+ chownCtrl( &td->ctrl, curuid );
+ endpwent();
+#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX)
+ endspent();
+#endif
+ ClearCloseOnFork( mstrtalk.pipe->wfd );
+ switch (pid = Fork()) {
+ case 0:
+
+ sessreg( td, getpid(), curuser, curuid );
+
+ if (source( systemEnviron, td->startup, td_setup )) {
+ LogError( "Cannot execute startup script %\"s\n", td->startup );
+ exit( 1 );
+ }
+
+ if (Setjmp( mstrtalk.errjmp ))
+ exit( 1 );
+ GSet( &mstrtalk );
+
+ setsid();
+ Signal( SIGINT, SIG_DFL );
+
+ /* 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 */
+# ifndef _AIX
+ 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 );
+ exit( 1 );
+ }
+# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
+# else
+# define D_LOGIN_SETLOGIN 0
+# endif
+
+# if defined(USE_PAM) && defined(HAVE_INITGROUPS)
+ if (!restoreGids())
+ exit( 1 );
+# endif
+
+# ifndef HAVE_SETUSERCONTEXT
+
+# ifdef USE_PAM
+ if (!SetUid( curuser, curuid ))
+ exit( 1 );
+# else
+ if (!SetUser( curuser, curuid, curgid ))
+ exit( 1 );
+# 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 )))
+ exit( 1 );
+
+ /*
+ * 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 );
+ exit( 1 );
+ }
+
+ 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 );
+ exit( 1 );
+ }
+
+ /*
+ * 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( "Can't set %s's process environment\n", curuser );
+ exit( 1 );
+ }
+ 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 "tdeinit" 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) {
+ GSendInt( D_ReLogin );
+ GSendStr( curuser );
+ GSendStr( curpass );
+ GSendStr( newdmrc );
+ }
+ if (curpass)
+ bzero( curpass, strlen( curpass ) );
+ SetUserAuthorization( td );
+ home = getEnv( userEnviron, "HOME" );
+ if (home) {
+ if (chdir( home ) < 0) {
+ LogError( "Cannot chdir to %s's home %s: %m, using /\n",
+ curuser, home );
+ home = 0;
+ userEnviron = setEnv( userEnviron, "HOME", "/" );
+ goto cdroot;
+ }
+ ASPrintf( &lname, td->clientLogFile, td->name );
+ if ((lfd = creat( lname, 0600 )) < 0) {
+ LogWarn( "Cannot create session log file %s: %m\n", lname );
+ free( lname );
+ goto tmperr;
+ }
+ } else {
+ cdroot:
+ chdir( "/" );
+ tmperr:
+ ASPrintf( &lname, "/tmp/xerr-%s-%s", curuser, td->name );
+ unlink( lname );
+ if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
+ LogError( "Cannot create fallback session log file %s: %m\n",
+ lname );
+ goto logerr;
+ }
+ }
+ dup2( lfd, 1 );
+ dup2( lfd, 2 );
+ close( lfd );
+ logerr:
+ free( lname );
+ if (!*dmrcDir)
+ mergeSessionArgs( home != 0 );
+ if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 )))
+ desksess = "failsafe"; /* only due to OOM */
+ GSendInt( D_User );
+ GSendInt( curuid );
+ GSendStr( curuser );
+ GSendStr( desksess );
+ close( mstrtalk.pipe->wfd );
+ 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] );
+ exit( 1 );
+ case -1:
+ RegisterCloseOnFork( mstrtalk.pipe->wfd );
+ LogError( "Forking session on %s failed: %m\n", td->name );
+ return 0;
+ default:
+ RegisterCloseOnFork( mstrtalk.pipe->wfd );
+ Debug( "StartSession, fork succeeded %d\n", pid );
+ return pid;
+ }
+}
+
+void
+SessionExit( int status )
+{
+ int pid;
+#ifdef USE_PAM
+ int pretc;
+#endif
+
+ Signal( SIGTERM, SIG_IGN );
+
+ if (removeAuth) {
+ if (source( systemEnviron, td->reset, td_setup ))
+ LogError( "Cannot execute reset script %\"s\n", td->reset );
+ sessreg( td, 0, 0, 0 );
+
+ switch ((pid = Fork())) {
+ case 0:
+#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
+ if (restoreGids() && SetUid( curuser, curuid ))
+#else
+ if (SetUser( curuser, curuid, curgid ))
+#endif
+
+ {
+ RemoveUserAuthorization( td );
+#ifdef K5AUTH
+ Krb5Destroy( td->name );
+#endif /* K5AUTH */
+#if !defined(USE_PAM) && !defined(_AIX)
+# ifdef KERBEROS
+ if (krbtkfile[0]) {
+ (void)dest_tkt();
+# ifndef NO_AFS
+ if (k_hasafs())
+ (void)k_unlog();
+# endif
+ }
+# endif
+#endif /* !USE_PAM && !_AIX*/
+ }
+ exit( 0 );
+ case -1:
+ LogError( "Cannot clean up session: fork() failed: %m" );
+ break;
+ default:
+ Wait4( pid );
+ break;
+ }
+ }
+
+#ifdef USE_PAM
+ if (removeCreds) {
+# ifdef HAVE_INITGROUPS
+ restoreGids();
+# endif
+ if (removeSession)
+ if ((pretc = pam_close_session( pamh, 0 )) != PAM_SUCCESS)
+ LogError( "pam_close_session() failed: %s\n",
+ pam_strerror( pamh, pretc ) );
+ if ((pretc = pam_setcred( pamh, PAM_DELETE_CRED )) != PAM_SUCCESS)
+ LogError( "pam_setcred(DELETE_CRED) failed: %s\n",
+ pam_strerror( pamh, pretc ) );
+ resetGids();
+ }
+ if (pamh) {
+ pam_end( pamh, PAM_SUCCESS );
+ ReInitErrorLog();
+ }
+#endif
+
+ /* make sure the server gets reset after the session is over */
+ if (td->serverPid >= 2) {
+ if (!td->terminateServer && td->resetSignal)
+ TerminateProcess( td->serverPid, td->resetSignal );
+ } else
+ ResetServer( td );
+ Debug( "display %s exiting with status %d\n", td->name, status );
+ exit( status );
+}
+
+int
+ReadDmrc()
+{
+ char *data, *fname = 0;
+ int len, pid, pfd[2], err;
+
+ if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser )))
+ return GE_NoUser;
+
+ if (*dmrcDir) {
+ if (!StrApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 ))
+ return GE_Error;
+ if (!(curdmrc = iniLoad( fname ))) {
+ free( fname );
+ return GE_Ok;
+ }
+ free( fname );
+ return GE_NoFile;
+ }
+
+ if (!StrApp( &fname, p->pw_dir, "/.dmrc", (char *)0 ))
+ return GE_Error;
+ if (pipe( pfd ))
+ return GE_Error;
+ if ((pid = Fork()) < 0) {
+ close( pfd[0] );
+ close( pfd[1] );
+ return GE_Error;
+ }
+ if (!pid) {
+ if (!SetUser( p->pw_name, p->pw_uid, p->pw_gid ))
+ exit( 0 );
+ if (!(data = iniLoad( fname ))) {
+ static const int m1 = -1;
+ write( pfd[1], &m1, sizeof(int) );
+ exit( 0 );
+ }
+ len = strlen( data );
+ write( pfd[1], &len, sizeof(int) );
+ write( pfd[1], data, len + 1 );
+ exit( 0 );
+ }
+ close( pfd[1] );
+ free( fname );
+ err = GE_Error;
+ if (Reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) {
+ if (len == -1)
+ err = GE_Denied;
+ else if ((curdmrc = Malloc( len + 1 ))) {
+ if (Reader( pfd[0], curdmrc, len + 1 ) == len + 1)
+ err = GE_Ok;
+ else {
+ free( curdmrc );
+ curdmrc = 0;
+ }
+ }
+ }
+ close( pfd[0] );
+ (void)Wait4( pid );
+ return err;
+}
diff --git a/tdm/backend/consolekit.c b/tdm/backend/consolekit.c
new file mode 100644
index 000000000..61d0b165e
--- /dev/null
+++ b/tdm/backend/consolekit.c
@@ -0,0 +1,557 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+
+ Copyright (C) 2006-2007 William Jon McCann <[email protected]>
+ Copyright (C) 2007 Kevin Kofler <[email protected]>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+#include "consolekit.h"
+
+
+#define CK_NAME "org.freedesktop.ConsoleKit"
+#define CK_PATH "/org/freedesktop/ConsoleKit"
+#define CK_INTERFACE "org.freedesktop.ConsoleKit"
+#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+
+static DBusConnection *private_connection = NULL;
+
+static void
+add_param_int (DBusMessageIter *iter_struct,
+ const char *key,
+ int value)
+{
+ DBusMessageIter iter_struct_entry;
+ DBusMessageIter iter_var;
+
+ dbus_message_iter_open_container (iter_struct,
+ DBUS_TYPE_STRUCT,
+ NULL,
+ &iter_struct_entry);
+
+ dbus_message_iter_append_basic (&iter_struct_entry,
+ DBUS_TYPE_STRING,
+ &key);
+
+ dbus_message_iter_open_container (&iter_struct_entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_INT32_AS_STRING,
+ &iter_var);
+
+ dbus_message_iter_append_basic (&iter_var,
+ DBUS_TYPE_INT32,
+ &value);
+
+ dbus_message_iter_close_container (&iter_struct_entry,
+ &iter_var);
+
+ dbus_message_iter_close_container (iter_struct, &iter_struct_entry);
+}
+
+static void
+add_param_boolean (DBusMessageIter *iter_struct,
+ const char *key,
+ int value)
+{
+ DBusMessageIter iter_struct_entry;
+ DBusMessageIter iter_var;
+
+ dbus_message_iter_open_container (iter_struct,
+ DBUS_TYPE_STRUCT,
+ NULL,
+ &iter_struct_entry);
+
+ dbus_message_iter_append_basic (&iter_struct_entry,
+ DBUS_TYPE_STRING,
+ &key);
+
+ dbus_message_iter_open_container (&iter_struct_entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BOOLEAN_AS_STRING,
+ &iter_var);
+
+ dbus_message_iter_append_basic (&iter_var,
+ DBUS_TYPE_BOOLEAN,
+ &value);
+
+ dbus_message_iter_close_container (&iter_struct_entry,
+ &iter_var);
+
+ dbus_message_iter_close_container (iter_struct, &iter_struct_entry);
+}
+
+static void
+add_param_string (DBusMessageIter *iter_struct,
+ const char *key,
+ const char *value)
+{
+ DBusMessageIter iter_struct_entry;
+ DBusMessageIter iter_var;
+
+ dbus_message_iter_open_container (iter_struct,
+ DBUS_TYPE_STRUCT,
+ NULL,
+ &iter_struct_entry);
+
+ dbus_message_iter_append_basic (&iter_struct_entry,
+ DBUS_TYPE_STRING,
+ &key);
+
+ dbus_message_iter_open_container (&iter_struct_entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_STRING_AS_STRING,
+ &iter_var);
+
+ dbus_message_iter_append_basic (&iter_var,
+ DBUS_TYPE_STRING,
+ &value);
+
+ dbus_message_iter_close_container (&iter_struct_entry,
+ &iter_var);
+
+ dbus_message_iter_close_container (iter_struct, &iter_struct_entry);
+}
+
+static int
+session_get_x11_display (DBusConnection *connection,
+ const char *ssid,
+ char **str)
+{
+ DBusError error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ const char *value;
+
+ if (str != NULL) {
+ *str = NULL;
+ }
+
+ message = dbus_message_new_method_call (CK_NAME,
+ ssid,
+ CK_SESSION_INTERFACE,
+ "GetX11Display");
+ if (message == NULL) {
+ Debug ("ConsoleKit: Couldn't allocate the D-Bus message");
+ return FALSE;
+ }
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error)) {
+ Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (connection);
+ dbus_message_unref (message);
+
+ if (reply == NULL) {
+ return FALSE;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &value);
+ if (str != NULL) {
+ *str = strdup (value);
+ }
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+static int
+session_unlock (DBusConnection *connection,
+ const char *ssid)
+{
+ DBusError error;
+ DBusMessage *message;
+ DBusMessage *reply;
+
+ Debug ("ConsoleKit: Unlocking session %s", ssid);
+ message = dbus_message_new_method_call (CK_NAME,
+ ssid,
+ CK_SESSION_INTERFACE,
+ "Unlock");
+ if (message == NULL) {
+ Debug ("ConsoleKit: Couldn't allocate the D-Bus message");
+ return FALSE;
+ }
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ dbus_message_unref (message);
+ dbus_message_unref (reply);
+ dbus_connection_flush (connection);
+
+ if (dbus_error_is_set (&error)) {
+ Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* from libhal */
+static char **
+get_path_array_from_iter (DBusMessageIter *iter,
+ int *num_elements)
+{
+ int count;
+ char **buffer;
+
+ count = 0;
+ buffer = (char **)malloc (sizeof (char *) * 8);
+
+ if (buffer == NULL)
+ goto oom;
+
+ buffer[0] = NULL;
+ while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_OBJECT_PATH) {
+ const char *value;
+ char *str;
+
+ if ((count % 8) == 0 && count != 0) {
+ buffer = realloc (buffer, sizeof (char *) * (count + 8));
+ if (buffer == NULL)
+ goto oom;
+ }
+
+ dbus_message_iter_get_basic (iter, &value);
+ str = strdup (value);
+ if (str == NULL)
+ goto oom;
+
+ buffer[count] = str;
+
+ dbus_message_iter_next (iter);
+ count++;
+ }
+
+ if ((count % 8) == 0) {
+ buffer = realloc (buffer, sizeof (char *) * (count + 1));
+ if (buffer == NULL)
+ goto oom;
+ }
+
+ buffer[count] = NULL;
+ if (num_elements != NULL)
+ *num_elements = count;
+ return buffer;
+
+oom:
+ LogWarn ("%s %d : error allocating memory\n", __FILE__, __LINE__);
+ return NULL;
+
+}
+
+static char **
+get_sessions_for_user (DBusConnection *connection,
+ const char *user,
+ const char *x11_display)
+{
+ DBusError error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter iter_reply;
+ DBusMessageIter iter_array;
+ struct passwd *pwent;
+ char **sessions;
+
+ sessions = NULL;
+ message = NULL;
+ reply = NULL;
+
+ pwent = getpwnam (user);
+
+ dbus_error_init (&error);
+ message = dbus_message_new_method_call (CK_NAME,
+ CK_MANAGER_PATH,
+ CK_MANAGER_INTERFACE,
+ "GetSessionsForUser");
+ if (message == NULL) {
+ Debug ("ConsoleKit: Couldn't allocate the D-Bus message");
+ goto out;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter,
+ DBUS_TYPE_UINT32,
+ &pwent->pw_uid);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ dbus_connection_flush (connection);
+
+ if (dbus_error_is_set (&error)) {
+ Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message);
+ goto out;
+ }
+
+ if (reply == NULL) {
+ Debug ("ConsoleKit: No reply for GetSessionsForUser");
+ goto out;
+ }
+
+ dbus_message_iter_init (reply, &iter_reply);
+ if (dbus_message_iter_get_arg_type (&iter_reply) != DBUS_TYPE_ARRAY) {
+ Debug ("ConsoleKit: Wrong reply for GetSessionsForUser - expecting an array.");
+ goto out;
+ }
+
+ dbus_message_iter_recurse (&iter_reply, &iter_array);
+ sessions = get_path_array_from_iter (&iter_array, NULL);
+
+ out:
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+
+ return sessions;
+}
+
+void
+unlock_ck_session (const char *user,
+ const char *x11_display)
+{
+ DBusError error;
+ DBusConnection *connection;
+ char **sessions;
+ int i;
+
+ Debug ("ConsoleKit: Unlocking session for %s on %s", user, x11_display);
+
+ dbus_error_init (&error);
+ connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (connection == NULL) {
+ Debug ("ConsoleKit: Failed to connect to the D-Bus daemon: %s", error.message);
+ dbus_error_free (&error);
+ return;
+ }
+
+ sessions = get_sessions_for_user (connection, user, x11_display);
+ if (sessions == NULL || sessions[0] == NULL) {
+ Debug ("ConsoleKit: no sessions found");
+ return;
+ }
+
+ for (i = 0; sessions[i] != NULL; i++) {
+ char *ssid;
+ char *xdisplay;
+
+ ssid = sessions[i];
+ session_get_x11_display (connection, ssid, &xdisplay);
+ Debug ("ConsoleKit: session %s has DISPLAY %s", ssid, xdisplay);
+
+ if (xdisplay != NULL
+ && x11_display != NULL
+ && strcmp (xdisplay, x11_display) == 0) {
+ int res;
+
+ res = session_unlock (connection, ssid);
+ if (! res) {
+ LogError ("ConsoleKit: Unable to unlock %s", ssid);
+ }
+ }
+
+ free (xdisplay);
+ }
+
+ freeStrArr (sessions);
+}
+
+char *
+open_ck_session (struct passwd *pwent,
+ struct display *d)
+{
+ DBusConnection *connection;
+ DBusError error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter iter_struct;
+ char *cookie;
+
+ cookie = NULL;
+
+ if (pwent == NULL) {
+ Debug ("ConsoleKit: NULL user passed as parameter");
+ return NULL;
+ }
+
+ Debug ("ConsoleKit: Opening session for %s", pwent->pw_name);
+
+ dbus_error_init (&error);
+ connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &error);
+ private_connection = connection;
+
+ if (connection == NULL) {
+ Debug ("ConsoleKit: Failed to connect to the D-Bus daemon: %s", error.message);
+ dbus_error_free (&error);
+ return NULL;
+ }
+
+ dbus_connection_set_exit_on_disconnect (connection, FALSE);
+ /* FIXME: What to do about these?
+ dbus_connection_set_watch_functions( connection,
+ dbusAddWatch,
+ dbusRemoveWatch,
+ dbusToggleWatch,
+ data, 0 );
+ dbus_connection_set_timeout_functions( connection,
+ dbusAddTimeout,
+ dbusRemoveTimeout,
+ dbusToggleTimeout,
+ data, 0 );
+ dbus_connection_set_wakeup_main_function( connection,
+ dbusWakeupMain,
+ data, 0 ); */
+
+ dbus_error_init (&error);
+ message = dbus_message_new_method_call (CK_NAME,
+ CK_MANAGER_PATH,
+ CK_MANAGER_INTERFACE,
+ "OpenSessionWithParameters");
+ if (message == NULL) {
+ Debug ("ConsoleKit: Couldn't allocate the D-Bus message");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &iter_struct);
+
+ add_param_int (&iter_struct, "user", pwent->pw_uid);
+ add_param_string (&iter_struct, "x11-display", d->name);
+ add_param_boolean (&iter_struct, "is-local", ((d->displayType & d_location) == dLocal));
+#ifdef XDMCP
+ if ((d->displayType & d_location) != dLocal) {
+ add_param_string (&iter_struct, "remote-host-name", d->remoteHost);
+ }
+#endif
+
+#ifdef HAVE_VTS
+ if (d->serverVT > 0) {
+ char device[20];
+
+ /* FIXME: how does xorg construct this */
+ sprintf(device, "/dev/tty%d", d->serverVT);
+ add_param_string (&iter_struct, "x11-display-device", device);
+ }
+#endif
+
+ dbus_message_iter_close_container (&iter, &iter_struct);
+
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error)) {
+ Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ if (reply != NULL) {
+ const char *value;
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &value);
+ cookie = strdup (value);
+ dbus_message_unref (reply);
+ }
+
+ return cookie;
+}
+
+void
+close_ck_session (const char *cookie)
+{
+ DBusError error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ if (cookie == NULL) {
+ return;
+ }
+
+ if (private_connection == NULL) {
+ return;
+ }
+
+ dbus_error_init (&error);
+ message = dbus_message_new_method_call (CK_NAME,
+ CK_MANAGER_PATH,
+ CK_MANAGER_INTERFACE,
+ "CloseSession");
+ if (message == NULL) {
+ Debug ("ConsoleKit: Couldn't allocate the D-Bus message");
+ return;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter,
+ DBUS_TYPE_STRING,
+ &cookie);
+
+ reply = dbus_connection_send_with_reply_and_block (private_connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error)) {
+ Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (private_connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ dbus_connection_close (private_connection);
+ private_connection = NULL;
+}
diff --git a/tdm/backend/consolekit.h b/tdm/backend/consolekit.h
new file mode 100644
index 000000000..e385e3f91
--- /dev/null
+++ b/tdm/backend/consolekit.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+
+ Copyright (C) 2006 William Jon McCann <[email protected]>
+ Copyright (C) 2007 Kevin Kofler <[email protected]>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef __CONSOLE_KIT_H
+#define __CONSOLE_KIT_H
+
+#include <pwd.h>
+
+struct display;
+
+char * open_ck_session (struct passwd *pwent,
+ struct display *display);
+void close_ck_session (const char *cookie);
+void unlock_ck_session (const char *user,
+ const char *x11_display);
+
+#endif /* __CONSOLE_KIT_H */
diff --git a/tdm/backend/ctrl.c b/tdm/backend/ctrl.c
new file mode 100644
index 000000000..4acd6d293
--- /dev/null
+++ b/tdm/backend/ctrl.c
@@ -0,0 +1,1035 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001-2005 Oswald Buddenhagen <[email protected]>
+
+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 "dm.h"
+#include "dm_socket.h"
+#include "dm_error.h"
+
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+
+#ifdef __linux__
+#include <linux/vt.h>
+#endif
+#include "getfd.h"
+
+static void
+acceptSock( CtrlRec *cr )
+{
+ struct cmdsock *cs;
+ int fd;
+
+ if ((fd = accept( cr->fd, 0, 0 )) < 0) {
+ bust:
+ LogError( "Error accepting command connection\n" );
+ return;
+ }
+ if (!(cs = Malloc( sizeof(*cs) ))) {
+ close( fd );
+ goto bust;
+ }
+ cs->sock.fd = fd;
+ cs->sock.buffer = 0;
+ cs->sock.buflen = 0;
+ cs->next = cr->css;
+ cr->css = cs;
+ fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK );
+ RegisterCloseOnFork( fd );
+ RegisterInput( fd );
+}
+
+static void
+nukeSock( struct cmdsock *cs )
+{
+ UnregisterInput( cs->sock.fd );
+ CloseNClearCloseOnFork( cs->sock.fd );
+ if (cs->sock.buffer)
+ free( cs->sock.buffer );
+ free( cs );
+}
+
+#ifdef HONORS_SOCKET_PERMS
+static CtrlRec ctrl = { 0, 0, -1, 0, 0, { -1, 0, 0 } };
+#else
+static CtrlRec ctrl = { 0, 0, 0, -1, 0, 0, { -1, 0, 0 } };
+
+static int mkTempDir( char *dir )
+{
+ int i, l = strlen( dir ) - 6;
+
+ for (i = 0; i < 100; i++) {
+ randomStr( dir + l );
+ if (!mkdir( dir, 0700 ))
+ return True;
+ if (errno != EEXIST)
+ break;
+ }
+ return False;
+}
+#endif
+
+void
+openCtrl( struct display *d )
+{
+ CtrlRec *cr;
+ const char *dname;
+ char *sockdir;
+ struct sockaddr_un sa;
+
+ if (!*fifoDir)
+ return;
+ if (d) {
+ cr = &d->ctrl, dname = d->name;
+ if (!memcmp( dname, "localhost:", 10 ))
+ dname += 9;
+ } else
+ cr = &ctrl, dname = 0;
+ if (cr->fifo.fd < 0) {
+ if (mkdir( fifoDir, 0755 )) {
+ if (errno != EEXIST) {
+ LogError( "mkdir %\"s failed; no control FiFos will be available\n",
+ fifoDir );
+ return;
+ }
+ } else
+ chmod( fifoDir, 0755 ); /* override umask */
+ StrApp( &cr->fpath, fifoDir, dname ? "/xdmctl-" : "/xdmctl",
+ dname, (char *)0 );
+ if (cr->fpath) {
+ unlink( cr->fpath );
+ if (mkfifo( cr->fpath, 0 ) < 0)
+ LogError( "Cannot create control FiFo %\"s\n", cr->fpath );
+ else {
+ cr->gid = fifoGroup;
+ if (!d)
+ chown( cr->fpath, -1, fifoGroup );
+ chmod( cr->fpath, 0620 );
+ if ((cr->fifo.fd = open( cr->fpath, O_RDWR | O_NONBLOCK )) >= 0) {
+ RegisterCloseOnFork( cr->fifo.fd );
+ RegisterInput( cr->fifo.fd );
+ goto fifok;
+ }
+ unlink( cr->fpath );
+ LogError( "Cannot open control FiFo %\"s\n", cr->fpath );
+ }
+ free( cr->fpath );
+ cr->fpath = 0;
+ }
+ }
+ fifok:
+ if (cr->fd < 0) {
+ /* fifoDir is created above already */
+ sockdir = 0;
+ StrApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl",
+ dname, (char *)0 );
+ if (sockdir) {
+ StrApp( &cr->path, sockdir, "/socket", (char *)0 );
+ if (cr->path) {
+ if (strlen( cr->path ) >= sizeof(sa.sun_path))
+ LogError( "path %\"s too long; no control sockets will be available\n",
+ cr->path );
+#ifdef HONORS_SOCKET_PERMS
+ else if (mkdir( sockdir, 0700 ) && errno != EEXIST)
+ LogError( "mkdir %\"s failed; no control sockets will be available\n",
+ sockdir );
+ else if (unlink( cr->path ) && errno != ENOENT)
+ LogError( "unlink %\"s failed: %m; control socket will not be available\n",
+ cr->path );
+ else {
+#else
+ else if (unlink( sockdir ) && errno != ENOENT)
+ LogError( "unlink %\"s failed: %m; control socket will not be available\n",
+ sockdir );
+ else if (!StrApp( &cr->realdir, sockdir, "-XXXXXX", (char *)0))
+ ;
+ else if (!mkTempDir( cr->realdir )) {
+ LogError( "mkdir %\"s failed: %m; control socket will not be available\n",
+ cr->realdir );
+ free( cr->realdir );
+ cr->realdir = 0;
+ } else if (symlink( cr->realdir, sockdir )) {
+ LogError( "symlink %\"s => %\"s failed: %m; control socket will not be available\n",
+ sockdir, cr->realdir );
+ rmdir( cr->realdir );
+ free( cr->realdir );
+ cr->realdir = 0;
+ } else {
+ chown( sockdir, 0, d ? 0 : fifoGroup );
+ chmod( sockdir, 0750 );
+#endif
+ if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
+ LogError( "Cannot create control socket\n" );
+ else {
+ sa.sun_family = AF_UNIX;
+ strcpy( sa.sun_path, cr->path );
+ if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
+ if (!listen( cr->fd, 5 )) {
+#ifdef HONORS_SOCKET_PERMS
+ chmod( cr->path, 0660 );
+ if (!d)
+ chown( cr->path, -1, fifoGroup );
+ chmod( sockdir, 0755 );
+#else
+ chmod( cr->path, 0666 );
+#endif
+ RegisterCloseOnFork( cr->fd );
+ RegisterInput( cr->fd );
+ free( sockdir );
+ return;
+ }
+ unlink( cr->path );
+ LogError( "Cannot listen on control socket %\"s\n",
+ cr->path );
+ } else
+ LogError( "Cannot bind control socket %\"s\n",
+ cr->path );
+ close( cr->fd );
+ cr->fd = -1;
+ }
+#ifdef HONORS_SOCKET_PERMS
+ rmdir( sockdir );
+#else
+ unlink( sockdir );
+ rmdir( cr->realdir );
+ free( cr->realdir );
+ cr->realdir = 0;
+#endif
+ }
+ free( cr->path );
+ cr->path = 0;
+ }
+ free( sockdir );
+ }
+ }
+}
+
+void
+closeCtrl( struct display *d )
+{
+ CtrlRec *cr = d ? &d->ctrl : &ctrl;
+
+ if (cr->fd >= 0) {
+ UnregisterInput( cr->fd );
+ CloseNClearCloseOnFork( cr->fd );
+ cr->fd = -1;
+ unlink( cr->path );
+ *strrchr( cr->path, '/' ) = 0;
+#ifdef HONORS_SOCKET_PERMS
+ rmdir( cr->path );
+#else
+ unlink( cr->path );
+ rmdir( cr->realdir );
+ free( cr->realdir );
+ cr->realdir = 0;
+#endif
+ free( cr->path );
+ cr->path = 0;
+ while (cr->css) {
+ struct cmdsock *cs = cr->css;
+ cr->css = cs->next;
+ nukeSock( cs );
+ }
+ }
+ if (cr->fifo.fd >= 0) {
+ UnregisterInput( cr->fifo.fd );
+ CloseNClearCloseOnFork( cr->fifo.fd );
+ cr->fifo.fd = -1;
+ unlink( cr->fpath );
+ free( cr->fpath );
+ cr->fpath = 0;
+ if (cr->fifo.buffer)
+ free( cr->fifo.buffer );
+ cr->fifo.buffer = 0;
+ cr->fifo.buflen = 0;
+ }
+}
+
+void
+chownCtrl( CtrlRec *cr, int uid )
+{
+ if (cr->fpath)
+ chown( cr->fpath, uid, -1 );
+ if (cr->path)
+#ifdef HONORS_SOCKET_PERMS
+ chown( cr->path, uid, -1 );
+#else
+ chown( cr->realdir, uid, -1 );
+#endif
+}
+
+void
+updateCtrl( void )
+{
+ unsigned ffl, slc;
+
+ ffl = 0;
+ if (ctrl.path)
+ for (ffl = strlen( ctrl.path ), slc = 2; ;)
+ if (ctrl.path[--ffl] == '/')
+ if (!--slc)
+ break;
+ if (ffl != strlen( fifoDir ) || memcmp( fifoDir, ctrl.path, ffl ) ||
+ ctrl.gid != fifoGroup)
+ {
+ closeCtrl( 0 );
+ openCtrl( 0 );
+ }
+}
+
+
+static void
+fLog( struct display *d, int fd, const char *sts, const char *msg, ... )
+{
+ char *fmsg, *otxt;
+ const char *what;
+ int olen;
+ va_list va;
+
+ va_start( va, msg );
+ VASPrintf( &fmsg, msg, va );
+ va_end( va );
+ if (!fmsg)
+ return;
+ if (fd >= 0) {
+ olen = ASPrintf( &otxt, "%s\t%\\s\n", sts, fmsg );
+ if (otxt) {
+ Writer( fd, otxt, olen );
+ free( otxt );
+ }
+ what = "socket";
+ } else
+ what = "FiFo";
+ if (d)
+ Debug( "control %s for %s: %s - %s", what, d->name, sts, fmsg );
+ else
+ Debug( "global control %s: %s - %s", what, sts, fmsg );
+ free( fmsg );
+}
+
+
+static char *
+unQuote( const char *str )
+{
+ char *ret, *adp;
+
+ if (!(ret = Malloc( strlen( str ) + 1 )))
+ return 0;
+ for (adp = ret; *str; str++, adp++)
+ if (*str == '\\')
+ switch (*++str) {
+ case 0: str--; /* fallthrough */
+ case '\\': *adp = '\\'; break;
+ case 'n': *adp = '\n'; break;
+ case 't': *adp = '\t'; break;
+ default: *adp++ = '\\'; *adp = *str; break;
+ }
+ else
+ *adp = *str;
+ *adp = 0;
+ return ret;
+}
+
+static void
+str_cat_l( char **bp, const char *str, int max )
+{
+ int dnl = StrNLen( str, max );
+ memcpy( *bp, str, dnl );
+ *bp += dnl;
+}
+
+static void
+str_cat( char **bp, const char *str )
+{
+ int dnl = strlen( str );
+ memcpy( *bp, str, dnl );
+ *bp += dnl;
+}
+
+static void
+sd_cat( char **bp, SdRec *sdr )
+{
+ if (sdr->how == SHUT_HALT)
+ str_cat( bp, "halt," );
+ else
+ str_cat( bp, "reboot," );
+ if (sdr->start == TO_INF)
+ str_cat( bp, "0," );
+ else
+ *bp += sprintf( *bp, "%d,", sdr->start );
+ if (sdr->timeout == TO_INF)
+ str_cat( bp, "-1," );
+ else
+ *bp += sprintf( *bp, "%d,", sdr->timeout );
+ if (sdr->force == SHUT_ASK)
+ str_cat( bp, "ask" );
+ else if (sdr->force == SHUT_FORCE)
+ str_cat( bp, "force" );
+ else if (sdr->force == SHUT_FORCEMY)
+ str_cat( bp, "forcemy" );
+ else
+ str_cat( bp, "cancel" );
+ *bp += sprintf( *bp, ",%d,%s", sdr->uid, sdr->osname ? sdr->osname : "-" );
+}
+
+static void
+emitXSessC( struct display *di, struct display *d, void *ctx )
+{
+ char *dname, *bp;
+ char cbuf[1024];
+
+ bp = cbuf;
+ *bp++ = '\t';
+ dname = di->name;
+ if (!memcmp( dname, "localhost:", 10 ))
+ dname += 9;
+ str_cat_l( &bp, dname, sizeof(cbuf)/2 );
+ *bp++ = ',';
+#ifdef HAVE_VTS
+ if (di->serverVT)
+ bp += sprintf( bp, "vt%d", di->serverVT );
+#endif
+ *bp++ = ',';
+#ifdef XDMCP
+ if (di->status == remoteLogin) {
+ *bp++ = ',';
+ str_cat_l( &bp, di->remoteHost, sizeof(cbuf)/3 );
+ } else
+#endif
+ {
+ if (di->userName)
+ str_cat_l( &bp, di->userName, sizeof(cbuf)/5 );
+ *bp++ = ',';
+ if (di->sessName)
+ str_cat_l( &bp, di->sessName, sizeof(cbuf)/5 );
+ }
+ *bp++ = ',';
+ if (di == d)
+ *bp++ = '*';
+ if (di->userSess >= 0 &&
+ (d ? (d->userSess != di->userSess &&
+ (d->allowNuke == SHUT_NONE ||
+ (d->allowNuke == SHUT_ROOT && d->userSess))) :
+ !fifoAllowNuke))
+ *bp++ = '!';
+ Writer( (int)ctx, cbuf, bp - cbuf );
+}
+
+static void
+emitTTYSessC( STRUCTUTMP *ut, struct display *d, void *ctx )
+{
+ struct passwd *pw;
+ char *bp;
+ int vt, l;
+ char cbuf[sizeof(ut->ut_line) + sizeof(ut->ut_user) + sizeof(ut->ut_host) + 16];
+ char user[sizeof(ut->ut_user) + 1];
+
+#ifndef BSD_UTMP
+ if (ut->ut_type != USER_PROCESS)
+ l = 0;
+ else
+#endif
+ {
+ l = StrNLen( ut->ut_user, sizeof(ut->ut_user) );
+ memcpy( user, ut->ut_user, l );
+ }
+ user[l] = 0;
+ bp = cbuf;
+ *bp++ = '\t';
+ str_cat_l( &bp, ut->ut_line, sizeof(ut->ut_line) );
+ *bp++ = ',';
+ if (*ut->ut_host) {
+ *bp++ = '@';
+ str_cat_l( &bp, ut->ut_host, sizeof(ut->ut_host) );
+ }
+#ifdef HAVE_VTS
+ else if ((vt = TTYtoVT( ut->ut_line )))
+ bp += sprintf( bp, "vt%d", vt );
+#endif
+ *bp++ = ',';
+ str_cat( &bp, user );
+ *bp++ = ',';
+ /* blank: session type unknown */
+ *bp++ = ',';
+ /* blank: certainly not querying display */
+ *bp++ = 't';
+ if (*user &&
+ (d ? ((d->allowNuke == SHUT_NONE ||
+ (d->allowNuke == SHUT_ROOT && d->userSess)) &&
+ (!(pw = getpwnam( user )) || d->userSess != (int)pw->pw_uid)) :
+ !fifoAllowNuke))
+ *bp++ = '!';
+ Writer( (int)ctx, cbuf, bp - cbuf );
+}
+
+static void
+processCtrl( const char *string, int len, int fd, struct display *d )
+{
+#define Reply(t) Writer (fd, t, strlen (t))
+
+ struct display *di;
+ const char *word;
+ char **ar, **ap, *args, *bp;
+ SdRec sdr;
+ char cbuf[1024];
+
+ if (!(ar = initStrArr( 0 )))
+ return;
+ for (word = string; ; string++, len--)
+ if (!len || *string == '\t') {
+ if (!(ar = addStrArr( ar, word, string - word )))
+ return;
+ if (!len)
+ break;
+ word = string + 1;
+ }
+ word = fd >= 0 ? "socket" : "FiFo";
+ if (d)
+ Debug( "control %s for %s received %'[s\n", word, d->name, ar );
+ else
+ Debug( "global control %s received %'[s\n", word, ar );
+ if (ar[0]) {
+ if (fd >= 0 && !strcmp( ar[0], "caps" )) {
+ if (ar[1])
+ goto exce;
+ Reply( "ok\ttdm\tlist\t" );
+ if (bootManager != BO_NONE)
+ Reply( "bootoptions\t" );
+ if (d) {
+ if ((d->displayType & d_location) == dLocal)
+#ifdef HAVE_VTS
+ Reply( "local\tactivate\t" );
+#else
+ Reply( "local\t" );
+#endif
+ if (d->allowShutdown != SHUT_NONE) {
+ if (d->allowShutdown == SHUT_ROOT && d->userSess)
+ Reply( "shutdown root\t" );
+ else
+ Reply( "shutdown\t" );
+ Reply( "shutdown ask\t" );
+ if (d->allowNuke != SHUT_NONE) {
+ if (d->allowNuke == SHUT_ROOT && d->userSess)
+ Reply( "nuke root\t" );
+ else
+ Reply( "nuke\t" );
+ }
+ }
+ if ((d->displayType & d_location) == dLocal &&
+ AnyReserveDisplays())
+ Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t",
+ idleReserveDisplays() ) );
+ Reply( "lock\tsuicide\n" );
+ } else {
+ if (fifoAllowShutdown) {
+ Reply( "shutdown\t" );
+ if (fifoAllowNuke)
+ Reply( "nuke\t" );
+ }
+ if (AnyReserveDisplays())
+ Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t",
+ idleReserveDisplays() ) );
+#ifdef HAVE_VTS
+ Reply( "login\tactivate\n" );
+#else
+ Reply( "login\n" );
+#endif
+ }
+ goto bust;
+ } else if (fd >= 0 && !strcmp( ar[0], "list" )) {
+ int flags = lstRemote | lstTTY;
+ if (ar[1]) {
+ if (!strcmp( ar[1], "all" ))
+ flags = lstRemote | lstPassive | lstTTY;
+ else if (!strcmp( ar[1], "alllocal" ))
+ flags = lstPassive | lstTTY;
+ else {
+ fLog( d, fd, "bad", "invalid list scope %\"s", ar[1] );
+ goto bust;
+ }
+ if (ar[2])
+ goto exce;
+ }
+ Reply( "ok" );
+ ListSessions( flags, d, (void *)fd, emitXSessC, emitTTYSessC );
+ Reply( "\n" );
+ goto bust;
+ } else if (fd >= 0 && !strcmp( ar[0], "activevt" )) {
+#ifdef HAVE_VTS
+ Reply( "ok" );
+ int vt_fd = getfd(NULL);
+ if (vt_fd > 0) {
+ struct vt_stat vtstat;
+ if (!ioctl(vt_fd, VT_GETSTATE, &vtstat)) {
+ Writer( fd, cbuf, sprintf( cbuf, "\t%d", vtstat.v_active ) );
+ }
+ }
+ Reply( "\n" );
+#else
+ Reply( "notsup\tvirtual terminal support not available\n" );
+#endif
+ goto bust;
+ } else if (!strcmp( ar[0], "reserve" )) {
+ int lt = 60; /* XXX make default timeout configurable? */
+ if (ar[1]) {
+ lt = strtol( ar[1], &bp, 10 );
+ if (lt < 15 || *bp) {
+ fLog( d, fd, "bad", "invalid timeout %\"s", ar[1] );
+ goto bust;
+ }
+ if (ar[2])
+ goto exce;
+ }
+ if (d && (d->displayType & d_location) != dLocal) {
+ fLog( d, fd, "perm", "display is not local" );
+ goto bust;
+ }
+ if (!StartReserveDisplay( lt )) {
+ fLog( d, fd, "noent", "no reserve display available" );
+ goto bust;
+ }
+#ifdef HAVE_VTS
+ } else if (!strcmp( ar[0], "activate" )) {
+ int vt;
+ if (!ar[1])
+ goto miss;
+ if (ar[2])
+ goto exce;
+ if (d && (d->displayType & d_location) != dLocal) {
+ fLog( d, fd, "perm", "display is not local" );
+ goto bust;
+ }
+ if (ar[1][0] != 'v' || ar[1][1] != 't' ||
+ (vt = atoi( ar[1] + 2 )) <= 0)
+ {
+ if (!(di = FindDisplayByName( ar[1] ))) {
+ fLog( d, fd, "noent", "display not found" );
+ goto bust;
+ }
+ if ((di->displayType & d_location) != dLocal) {
+ fLog( d, fd, "inval", "target display is not local" );
+ goto bust;
+ }
+ if (!di->serverVT) {
+ fLog( d, fd, "noent", "target display has no VT assigned" );
+ goto bust;
+ }
+ vt = di->serverVT;
+ }
+ if (!activateVT( vt )) {
+ fLog( d, fd, "inval", "VT switch failed" );
+ goto bust;
+ }
+#endif
+ } else if (!strcmp( ar[0], "shutdown" )) {
+ ap = ar;
+ if (!*++ap)
+ goto miss;
+ sdr.force = SHUT_CANCEL;
+ sdr.osname = 0;
+ if (!strcmp( *ap, "status" )) {
+ if (fd < 0)
+ goto bust;
+ if (*++ap)
+ goto exce;
+ bp = cbuf;
+ *bp++ = 'o';
+ *bp++ = 'k';
+ if (sdRec.how) {
+ str_cat( &bp, "\tglobal," );
+ sd_cat( &bp, &sdRec );
+ }
+ if (d && d->hstent->sdRec.how) {
+ str_cat( &bp, "\tlocal," );
+ sd_cat( &bp, &d->hstent->sdRec );
+ }
+ *bp++ = '\n';
+ Writer( fd, cbuf, bp - cbuf );
+ goto bust;
+ } else if (!strcmp( *ap, "cancel" )) {
+ sdr.how = 0;
+ sdr.start = 0;
+ if (ap[1]) {
+ if (!d)
+ goto exce;
+ if (!strcmp( *++ap, "global" ))
+ sdr.start = TO_INF;
+ else if (strcmp( *ap, "local" )) {
+ fLog( d, fd, "bad", "invalid cancel scope %\"s", *ap );
+ goto bust;
+ }
+ }
+ } else {
+ if (!strcmp( *ap, "reboot" ))
+ sdr.how = SHUT_REBOOT;
+ else if (!strcmp( *ap, "halt" ))
+ sdr.how = SHUT_HALT;
+ else {
+ fLog( d, fd, "bad", "invalid type %\"s", *ap );
+ goto bust;
+ }
+ sdr.uid = -1;
+ if (!*++ap)
+ goto miss;
+ if (**ap == '=') {
+ switch (setBootOption( *ap + 1, &sdr )) {
+ case BO_NOMAN:
+ fLog( d, fd, "notsup", "boot options unavailable" );
+ goto bust;
+ case BO_NOENT:
+ fLog( d, fd, "noent", "no such boot option" );
+ goto bust;
+ case BO_IO:
+ fLog( d, fd, "io", "io error" );
+ goto bust;
+ }
+ if (!*++ap)
+ goto miss;
+ }
+ sdr.start = strtol( *ap, &bp, 10 );
+ if (bp != *ap && !*bp) {
+ if (**ap == '+')
+ sdr.start += now;
+ if (!*++ap)
+ goto miss;
+ sdr.timeout = strtol( *ap, &bp, 10 );
+ if (bp == *ap || *bp) {
+ fLog( d, fd, "bad", "invalid timeout %\"s", ar[3] );
+ goto bust;
+ }
+ if (**ap == '+')
+ sdr.timeout += sdr.start ? sdr.start : now;
+ if (sdr.timeout < 0)
+ sdr.timeout = TO_INF;
+ else {
+ if (!*++ap)
+ goto miss;
+ if (!strcmp( *ap, "force" ))
+ sdr.force = SHUT_FORCE;
+ else if (d && !strcmp( *ap, "forcemy" ))
+ sdr.force = SHUT_FORCEMY;
+ else if (strcmp( *ap, "cancel" )) {
+ fLog( d, fd, "bad", "invalid timeout action %\"s",
+ *ap );
+ goto bust;
+ }
+ }
+ } else {
+ sdr.timeout = 0;
+ if (d && !strcmp( *ap, "ask" ))
+ sdr.force = SHUT_ASK;
+ else if (!strcmp( *ap, "forcenow" ))
+ sdr.force = SHUT_FORCE;
+ else if (!strcmp( *ap, "schedule" ))
+ sdr.timeout = TO_INF;
+ else if (strcmp( *ap, "trynow" )) {
+ fLog( d, fd, "bad", "invalid mode %\"s", *ap );
+ goto bust;
+ }
+ }
+ }
+ if (*++ap)
+ goto exce;
+ if (d) {
+ sdr.uid = d->userSess >= 0 ? d->userSess : 0;
+ if (d->allowShutdown == SHUT_NONE ||
+ (d->allowShutdown == SHUT_ROOT && sdr.uid &&
+ sdr.force != SHUT_ASK))
+ {
+ fLog( d, fd, "perm", "shutdown forbidden" );
+ goto bust;
+ }
+ if (!sdr.how && !sdr.start) {
+ if (d->hstent->sdRec.osname)
+ free( d->hstent->sdRec.osname );
+ d->hstent->sdRec = sdr;
+ } else {
+ if (sdRec.how && sdRec.force == SHUT_FORCE &&
+ ((d->allowNuke == SHUT_NONE && sdRec.uid != sdr.uid) ||
+ (d->allowNuke == SHUT_ROOT && sdr.uid)))
+ {
+ fLog( d, fd, "perm", "overriding forced shutdown forbidden" );
+ goto bust;
+ }
+ if (sdr.force == SHUT_FORCE &&
+ (d->allowNuke == SHUT_NONE ||
+ (d->allowNuke == SHUT_ROOT && sdr.uid)))
+ {
+ fLog( d, fd, "perm", "forced shutdown forbidden" );
+ goto bust;
+ }
+ if (!sdr.start) {
+ if (d->hstent->sdRec.osname)
+ free( d->hstent->sdRec.osname );
+ d->hstent->sdRec = sdr;
+ } else {
+ if (!sdr.how)
+ cancelShutdown();
+ else {
+ if (sdRec.osname)
+ free( sdRec.osname );
+ sdRec = sdr;
+ }
+ }
+ }
+ } else {
+ if (!fifoAllowShutdown) {
+ fLog( d, fd, "perm", "shutdown forbidden" );
+ goto bust;
+ }
+ if (sdRec.how && sdRec.force == SHUT_FORCE &&
+ sdRec.uid != -1 && !fifoAllowNuke)
+ {
+ fLog( d, fd, "perm", "overriding forced shutdown forbidden" );
+ goto bust;
+ }
+ if (!sdr.how)
+ cancelShutdown();
+ else {
+ if (sdr.force != SHUT_CANCEL) {
+ if (!fifoAllowNuke) {
+ fLog( d, fd, "perm", "forced shutdown forbidden" );
+ goto bust;
+ }
+ } else {
+ if (!sdr.start && !sdr.timeout && AnyActiveDisplays()) {
+ fLog( d, fd, "busy", "user sessions running" );
+ goto bust;
+ }
+ }
+ sdr.uid = -1;
+ if (sdRec.osname)
+ free( sdRec.osname );
+ sdRec = sdr;
+ }
+ }
+ } else if (fd >= 0 && !strcmp( ar[0], "listbootoptions" )) {
+ char **opts;
+ int def, cur, i, j;
+
+ if (ar[1])
+ goto exce;
+ switch (getBootOptions( &opts, &def, &cur )) {
+ case BO_NOMAN:
+ fLog( d, fd, "notsup", "boot options unavailable" );
+ goto bust;
+ case BO_IO:
+ fLog( d, fd, "io", "io error" );
+ goto bust;
+ }
+ Reply( "ok\t" );
+ for (i = 0; opts[i]; i++) {
+ bp = cbuf;
+ if (i)
+ *bp++ = ' ';
+ for (j = 0; opts[i][j]; j++)
+ if (opts[i][j] == ' ') {
+ *bp++ = '\\';
+ *bp++ = 's';
+ } else
+ *bp++ = opts[i][j];
+ Writer( fd, cbuf, bp - cbuf );
+ }
+ freeStrArr( opts );
+ Writer( fd, cbuf, sprintf( cbuf, "\t%d\t%d\n", def, cur ) );
+ goto bust;
+ } else if (d) {
+ if (!strcmp( ar[0], "lock" )) {
+ if (ar[1])
+ goto exce;
+ d->hstent->lock = 1;
+ } else if (!strcmp( ar[0], "unlock" )) {
+ if (ar[1])
+ goto exce;
+ d->hstent->lock = 0;
+ } else if (!strcmp( ar[0], "suicide" )) {
+ if (ar[1])
+ goto exce;
+ if (d->status == running && d->pid != -1) {
+ TerminateProcess( d->pid, SIGTERM );
+ d->status = raiser;
+ }
+ } else {
+ fLog( d, fd, "nosys", "unknown command" );
+ goto bust;
+ }
+ } else {
+ if (!strcmp( ar[0], "login" )) {
+ int nuke;
+ if (arrLen( ar ) < 5) {
+ miss:
+ fLog( d, fd, "bad", "missing argument(s)" );
+ goto bust;
+ }
+ if (!(di = FindDisplayByName( ar[1] ))) {
+ fLog( d, fd, "noent", "display %s not found", ar[1] );
+ goto bust;
+ }
+ if (ar[5]) {
+ if (!(args = unQuote( ar[5] ))) {
+ fLog( d, fd, "nomem", "out of memory" );
+ goto bust;
+ }
+ if (ar[6]) {
+ free( args );
+ exce:
+ fLog( d, fd, "bad", "excess argument(s)" );
+ goto bust;
+ }
+ setNLogin( di, ar[3], ar[4], args, 2 );
+ free( args );
+ } else
+ setNLogin( di, ar[3], ar[4], 0, 2 );
+ nuke = !strcmp( ar[2], "now" );
+ switch (di->status) {
+ case running:
+ if (di->pid != -1 && (di->userSess < 0 || nuke)) {
+ TerminateProcess( di->pid, SIGTERM );
+ di->status = raiser;
+ }
+ break;
+ case remoteLogin:
+ if (di->serverPid != -1 && nuke)
+ TerminateProcess( di->serverPid, di->termSignal );
+ break;
+ case reserve:
+ di->status = notRunning;
+ break;
+ case textMode:
+#ifndef HAVE_VTS
+ SwitchToX( di );
+#endif
+ break;
+ default:
+ break;
+ }
+ } else {
+ fLog( d, fd, "nosys", "unknown command" );
+ goto bust;
+ }
+ }
+ if (fd >= 0)
+ Reply( "ok\n" );
+ }
+ bust:
+ freeStrArr( ar );
+}
+
+static int
+handleChan( struct display *d, struct bsock *cs, int fd, FD_TYPE *reads )
+{
+ char *bufp, *nbuf, *obuf, *eol;
+ int len, bl, llen;
+ char buf[1024];
+
+ bl = cs->buflen;
+ obuf = cs->buffer;
+ if (bl <= 0 && FD_ISSET( cs->fd, reads )) {
+ FD_CLR( cs->fd, reads );
+ bl = -bl;
+ memcpy( buf, obuf, bl );
+ if ((len = Reader( cs->fd, buf + bl, sizeof(buf) - bl )) <= 0)
+ return -1;
+ bl += len;
+ bufp = buf;
+ } else {
+ len = 0;
+ bufp = obuf;
+ }
+ if (bl > 0) {
+ if ((eol = memchr( bufp, '\n', bl ))) {
+ llen = eol - bufp + 1;
+ bl -= llen;
+ if (bl) {
+ if (!(nbuf = Malloc( bl )))
+ return -1;
+ memcpy( nbuf, bufp + llen, bl );
+ } else
+ nbuf = 0;
+ cs->buffer = nbuf;
+ cs->buflen = bl;
+ processCtrl( bufp, llen - 1, fd, d );
+ if (obuf)
+ free( obuf );
+ return 1;
+ } else if (!len) {
+ if (fd >= 0)
+ cs->buflen = -bl;
+ else
+ fLog( d, -1, "bad", "unterminated command" );
+ }
+ }
+ return 0;
+}
+
+int
+handleCtrl( FD_TYPE *reads, struct display *d )
+{
+ CtrlRec *cr = d ? &d->ctrl : &ctrl;
+ struct cmdsock *cs, **csp;
+
+ if (cr->fifo.fd >= 0) {
+ switch (handleChan( d, &cr->fifo, -1, reads )) {
+ case -1:
+ if (cr->fifo.buffer)
+ free( cr->fifo.buffer );
+ cr->fifo.buflen = 0;
+ break;
+ case 1:
+ return 1;
+ default:
+ break;
+ }
+ }
+ if (cr->fd >= 0 && FD_ISSET( cr->fd, reads ))
+ acceptSock( cr );
+ else {
+ for (csp = &cr->css; (cs = *csp); ) {
+ switch (handleChan( d, &cs->sock, cs->sock.fd, reads )) {
+ case -1:
+ *csp = cs->next;
+ nukeSock( cs );
+ continue;
+ case 1:
+ return 1;
+ default:
+ break;
+ }
+ csp = &cs->next;
+ }
+ }
+ return 0;
+}
diff --git a/tdm/backend/daemon.c b/tdm/backend/daemon.c
new file mode 100644
index 000000000..79d9e47ff
--- /dev/null
+++ b/tdm/backend/daemon.c
@@ -0,0 +1,78 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000,2001,2003 Oswald Buddenhagen <[email protected]>
+
+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
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+void
+BecomeDaemon( void )
+{
+ int pfd[2];
+
+ /*
+ * fork so that the process goes into the background automatically. Also
+ * has a nice side effect of having the child process get inherited by
+ * init (pid 1).
+ * Create a pipe and block on it, so the parent knows when the child is
+ * done with detaching. This eliminates the possibility that the child
+ * might get killed when the init script that's running xdm exits.
+ */
+
+ if (pipe( pfd ))
+ pfd[0] = pfd[1] = -1; /* so what ...? */
+ switch (fork ()) {
+ case 0:
+ /* child */
+ break;
+ case -1:
+ /* error */
+ LogError( "Daemon fork failed: %m\n" );
+ break;
+
+ default:
+ /* parent */
+ close( pfd[1] );
+ read( pfd[0], &pfd[1] /* dummy */, 1 );
+ exit( 0 );
+ }
+
+ /* don't use daemon() - it doesn't buy us anything but an additional fork */
+
+ setsid();
+
+ close( pfd[0] );
+ close( pfd[1] ); /* tell parent that we're done with detaching */
+
+ chdir( "/" );
+}
diff --git a/tdm/backend/dm.c b/tdm/backend/dm.c
new file mode 100644
index 000000000..cd672f39a
--- /dev/null
+++ b/tdm/backend/dm.c
@@ -0,0 +1,1669 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <[email protected]>
+
+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 "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
+
+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)
+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 __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, *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)
+ 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) {
+ 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] );
+
+ 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;
+ }
+#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() */
+ write( signalFds[1], &buf, 1 );
+#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 int active_vts;
+
+static int
+GetBusyVTs( void )
+{
+ struct vt_stat vtstat;
+ int con;
+
+ if (active_vts == -1) {
+ vtstat.v_state = 0;
+ if ((con = open( "/dev/console", O_RDONLY )) >= 0) {
+ ioctl( con, VT_GETSTATE, &vtstat );
+ close( con );
+ }
+ active_vts = vtstat.v_state;
+ }
+ 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 < 16)
+ 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 >= 16)
+ continue;
+ } else {
+ if (++tvt >= 16)
+ 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 << 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)
+ char *p;
+ int left;
+#endif
+
+ ASPrintf( &prog, "%s: %s", prog, name );
+ ReInitErrorLog();
+#ifdef HAVE_SETPROCTITLE
+ setproctitle( "%s", name );
+#elif !defined(NOXDMTITLE)
+ p = Title;
+ left = TitleLen;
+
+ *p++ = '-';
+ --left;
+ while (*name && left > 0) {
+ *p++ = *name++;
+ --left;
+ }
+ while (left > 0) {
+ *p++ = '\0';
+ --left;
+ }
+#endif
+}
diff --git a/tdm/backend/dm.h b/tdm/backend/dm.h
new file mode 100644
index 000000000..c05d4c865
--- /dev/null
+++ b/tdm/backend/dm.h
@@ -0,0 +1,630 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * global xdm core declarations
+ */
+
+#ifndef _DM_H_
+#define _DM_H_ 1
+
+#define WITH_CONSOLE_KIT
+
+#include "greet.h"
+#include <config.ci>
+
+#include <X11/X.h> /* FamilyInternet6 */
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+#include <X11/Xmd.h>
+#include <X11/Xauth.h>
+#include <X11/Intrinsic.h>
+
+#include <sys/param.h>
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#include <time.h>
+#define Time_t time_t
+
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef XDMCP
+# if defined(__osf__)
+/* someone somewhere defines QUERY under Tru64 which confuses Xdmcp.h */
+# undef QUERY
+# endif
+# include <X11/Xdmcp.h>
+#endif
+
+#ifndef PATH_MAX
+# ifdef MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+# else
+# define PATH_MAX 1024
+# endif
+#endif
+
+#include <sys/wait.h>
+#define waitCode(w) (WIFEXITED(w) ? WEXITSTATUS(w) : 0)
+#define waitSig(w) (WIFSIGNALED(w) ? WTERMSIG(w) : 0)
+#ifdef WCOREDUMP
+# define waitCore(w) (WCOREDUMP(w))
+#else
+# define waitCore(w) 0 /* not in POSIX. so what? */
+#endif
+typedef int waitType;
+
+#define waitCompose(sig,core,code) ((sig) * 256 + (core) * 128 + (code))
+#define waitVal(w) waitCompose(waitSig(w), waitCore(w), waitCode(w))
+#define WaitCode(w) ((w) & 0x7f)
+#define WaitCore(w) (((w) >> 7) & 1)
+#define WaitSig(w) (((w) >> 8) & 0xff)
+
+#include <sys/time.h>
+#define FD_TYPE fd_set
+
+#include <setjmp.h>
+#if defined(__EMX__) || (defined(__NetBSD__) && defined(__sparc__)) /* XXX netbsd? */
+# define Setjmp(e) setjmp(e)
+# define Longjmp(e,v) longjmp(e,v)
+# define Jmp_buf jmp_buf
+#else
+# define Setjmp(e) sigsetjmp(e,1)
+# define Longjmp(e,v) siglongjmp(e,v)
+# define Jmp_buf sigjmp_buf
+#endif
+
+#include <utmp.h>
+#ifdef HAVE_UTMPX
+# include <utmpx.h>
+# define STRUCTUTMP struct utmpx
+# define UTMPNAME utmpxname
+# define SETUTENT setutxent
+# define GETUTENT getutxent
+# define PUTUTLINE pututxline
+# define ENDUTENT endutxent
+# define LASTLOG lastlogx
+# define ut_time ut_tv.tv_sec
+# define ll_time ll_tv.tv_sec
+#else
+# define STRUCTUTMP struct utmp
+# define UTMPNAME utmpname
+# define SETUTENT setutent
+# define GETUTENT getutent
+# define PUTUTLINE pututline
+# define ENDUTENT endutent
+# define LASTLOG lastlog
+#endif
+#ifndef HAVE_STRUCT_UTMP_UT_USER
+# define ut_user ut_name
+#endif
+#ifndef WTMP_FILE
+# ifdef _PATH_WTMPX
+# define WTMP_FILE _PATH_WTMPX
+# elif defined(_PATH_WTMP)
+# define WTMP_FILE _PATH_WTMP
+# else
+# define WTMP_FILE "/usr/adm/wtmp"
+# endif
+#endif
+#ifndef UTMP_FILE
+# ifdef _PATH_UTMPX
+# define UTMP_FILE _PATH_UTMPX
+# elif defined(_PATH_UTMP)
+# define UTMP_FILE _PATH_UTMP
+# else
+# define UTMP_FILE "/etc/utmp"
+# endif
+#endif
+
+#ifdef HAVE_NETCONFIG_H
+# define STREAMSCONN
+#else
+# define UNIXCONN
+# define TCPCONN
+# ifdef FamilyInternet6
+# define IPv6
+# endif
+# ifdef HAVE_NETDNET_DN_H
+# define DNETCONN
+# endif
+#endif
+
+#if !defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM)
+# define NEED_ENTROPY
+#endif
+
+typedef struct GPipe {
+ int wfd, rfd;
+ char *who;
+} GPipe;
+
+typedef struct GTalk {
+ GPipe *pipe;
+ Jmp_buf errjmp;
+} GTalk;
+
+typedef struct GProc {
+ GPipe pipe;
+ int pid;
+} GProc;
+
+typedef enum displayStatus { notRunning = 0, running, zombie, phoenix, raiser,
+ textMode, reserve, remoteLogin } DisplayStatus;
+
+typedef enum serverStatus { ignore = 0, awaiting, starting,
+ terminated, killed, pausing } ServerStatus;
+
+typedef struct RcStr {
+ struct RcStr *next;
+ char *str;
+ int cnt;
+} RcStr;
+
+typedef struct CfgDep {
+ RcStr *name;
+ long time;
+} CfgDep;
+
+typedef struct CfgArr {
+ char *data; /* config value array; allocated */
+ long *idx; /* config index array; alias */
+ CfgDep dep; /* filestamp */
+ int numCfgEnt; /* number of config entries */
+} CfgArr;
+
+struct bsock {
+ int fd;
+ int buflen;
+ char *buffer;
+};
+
+struct cmdsock {
+ struct cmdsock *next;
+ struct bsock sock; /* buffered fd of the socket */
+};
+
+typedef struct {
+ struct cmdsock *css; /* open connections */
+
+ char *path; /* filename of the socket */
+#ifndef HONORS_SOCKET_PERMS
+ char *realdir; /* real dirname of the socket */
+#endif
+ int fd; /* fd of the socket */
+ int gid; /* owner group of the socket */
+
+ char *fpath; /* filename of the fifo */
+ struct bsock fifo; /* buffered fd of the fifo */
+} CtrlRec;
+
+struct display {
+ struct display *next;
+ struct disphist *hstent; /* display history entry */
+
+ /* basic display information */
+ char *name; /* DISPLAY name -- also referenced in hstent */
+ char *class2; /* display class (may be NULL) */
+ int displayType; /* location/origin/lifetime */
+ CfgArr cfg; /* config data array */
+
+ /* display state */
+ DisplayStatus status; /* current status */
+ int zstatus; /* substatus while zombie */
+ int pid; /* process id of child */
+ int serverPid; /* process id of server (-1 if none) */
+#ifdef HAVE_VTS
+ int serverVT; /* server VT (0 = none, -1 = pending) */
+ struct display *follower; /* on exit, hand VT to this display */
+#endif
+ ServerStatus serverStatus; /* X server startup state */
+ Time_t lastStart; /* time of last display start */
+ int startTries; /* current start try */
+ int stillThere; /* state during HUP processing */
+ int userSess; /* -1=nobody, otherwise uid */
+ char *userName;
+ char *sessName;
+ CtrlRec ctrl; /* command socket & fifo */
+ GPipe pipe; /* comm master <-> slave */
+ GPipe gpipe; /* comm master <-> greeter */
+#ifdef XDMCP
+ char *remoteHost; /* for X -query type remote login */
+ /* XDMCP state */
+ unsigned sessionID; /* ID of active session */
+ ARRAY8 peer; /* display peer address */
+ ARRAY8 from; /* XDMCP port of display */
+ unsigned displayNumber; /* numerical part of name */
+ int useChooser; /* Run the chooser for this display */
+ ARRAY8 clientAddr; /* for chooser picking */
+ unsigned connectionType; /* ... */
+ int xdmcpFd;
+#endif
+
+ CONF_CORE_LOCAL_DEFS
+
+ int idleTimeout; /* abort login after that time */
+
+ unsigned short *authNameLens; /* authorization protocol name lens */
+
+ /* information potentially derived from resources */
+ int authNameNum; /* number of protocol names */
+ Xauth **authorizations; /* authorization data */
+ int authNum; /* number of authorizations */
+ char *authFile; /* file to store authorization in */
+};
+
+typedef struct {
+ unsigned how:2, /* 0=none 1=reboot 2=halt (SHUT_*) */
+ force:2;
+ int uid;
+ int start;
+ int timeout;
+ char *osname;
+ time_t bmstamp;
+ int osindex;
+} SdRec;
+
+struct disphist {
+ struct disphist *next;
+ char *name;
+ Time_t lastExit; /* time of last display exit */
+ unsigned rLogin:2, /* 0=nothing 1=relogin 2=login */
+ lock:1, /* screen locker running */
+ goodExit:1; /* was the last exit "peaceful"? */
+ SdRec sdRec;
+ char *nuser, *npass, *nargs;
+};
+
+#ifdef XDMCP
+
+#define PROTO_TIMEOUT (30 * 60) /* 30 minutes should be long enough */
+
+struct protoDisplay {
+ struct protoDisplay *next;
+ XdmcpNetaddr address; /* UDP address */
+ int addrlen; /* UDP address length */
+ unsigned long date; /* creation date */
+ CARD16 displayNumber;
+ CARD16 connectionType;
+ ARRAY8 connectionAddress;
+ CARD32 sessionID;
+ Xauth *fileAuthorization;
+ Xauth *xdmcpAuthorization;
+ ARRAY8 authenticationName;
+ ARRAY8 authenticationData;
+ XdmAuthKeyRec key;
+};
+#endif /* XDMCP */
+
+/* status code for RStopDisplay */
+#define DS_RESTART 0
+#define DS_TEXTMODE 1
+#define DS_RESERVE 2
+#define DS_REMOTE 3
+#define DS_REMOVE 4
+
+/* command codes dpy process -> master process */
+#define D_User 1
+#define D_ReLogin 2
+#define D_ChooseHost 4
+#define D_RemoteHost 5
+#define D_XConnOk 6
+
+extern int debugLevel;
+
+CONF_CORE_GLOBAL_DECLS
+
+/* in daemon.c */
+void BecomeDaemon( void );
+
+/* in dm.c */
+extern char *prog, *progpath;
+extern time_t now;
+extern SdRec sdRec;
+void StartDisplay( struct display *d );
+void StartDisplayP2( struct display *d );
+void StopDisplay( struct display *d );
+void SetTitle( const char *name );
+void SwitchToX( struct display *d );
+void setNLogin( struct display *d,
+ const char *nuser, const char *npass, char *nargs,
+ int rl );
+void cancelShutdown( void );
+int TTYtoVT( const char *tty );
+int activateVT( int vt );
+
+/* in ctrl.c */
+void openCtrl( struct display *d );
+void closeCtrl( struct display *d );
+int handleCtrl( FD_TYPE *reads, struct display *d );
+void chownCtrl( CtrlRec *cr, int uid );
+void updateCtrl( void );
+
+/* in dpylist.c */
+extern struct display *displays; /* that's ugly ... */
+int AnyDisplaysLeft( void );
+void ForEachDisplay( void (*f)( struct display * ) );
+#ifdef HAVE_VTS
+void ForEachDisplayRev( void (*f)( struct display * ) );
+#endif
+void RemoveDisplay( struct display *old );
+struct display
+ *FindDisplayByName( const char *name ),
+#ifdef XDMCP
+ *FindDisplayBySessionID( CARD32 sessionID ),
+ *FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber ),
+#endif /* XDMCP */
+ *FindDisplayByPid( int pid ),
+ *FindDisplayByServerPid( int serverPid ),
+ *NewDisplay( const char *name );
+int AnyActiveDisplays( void );
+int AnyRunningDisplays( void );
+int AnyReserveDisplays( void );
+int idleReserveDisplays( void );
+int AllLocalDisplaysLocked( struct display *dp );
+int StartReserveDisplay( int lt );
+void ReapReserveDisplays( void );
+
+/* in reset.c */
+void pseudoReset( void );
+
+/* in resource.c */
+char **FindCfgEnt( struct display *d, int id );
+int InitResources( char **argv );
+int LoadDMResources( int force );
+int LoadDisplayResources( struct display *d );
+void ScanServers( void );
+void CloseGetter( void );
+int startConfig( int what, CfgDep *dep, int force );
+RcStr *newStr( char *str );
+void delStr( RcStr *str );
+extern GTalk cnftalk;
+
+/* in session.c */
+extern struct display *td;
+extern const char *td_setup;
+char **baseEnv( const char *user );
+char **inheritEnv( char **env, const char **what );
+char **systemEnv( const char *user );
+int source( char **env, const char *file, const char *arg );
+void ManageSession( struct display *d );
+
+extern GTalk mstrtalk, grttalk;
+extern GProc grtproc;
+void OpenGreeter( void );
+int CloseGreeter( int force );
+int CtrlGreeterWait( int wreply );
+void PrepErrorGreet( void );
+char *conv_interact( int what, const char *prompt );
+
+/* process.c */
+typedef void (*SIGFUNC)( int );
+SIGFUNC Signal( int, SIGFUNC Handler );
+
+void RegisterInput( int fd );
+void UnregisterInput( int fd );
+void RegisterCloseOnFork( int fd );
+void ClearCloseOnFork( int fd );
+void CloseNClearCloseOnFork( int fd );
+int Fork( void );
+int Wait4( int pid );
+void execute( char **argv, char **env );
+int runAndWait( char **args, char **env );
+FILE *pOpen( char **what, char m, int *pid );
+int pClose( FILE *f, int pid );
+char *locate( const char *exe );
+void TerminateProcess( int pid, int sig );
+
+void GSet( GTalk *talk); /* call before GOpen! */
+int GFork( GPipe *pajp, const char *pname, char *cname,
+ GPipe *ogp, char *cgname );
+void GClosen( GPipe *pajp );
+int GOpen( GProc *proc,
+ char **argv, const char *what, char **env, char *cname,
+ GPipe *gp );
+int GClose( GProc *proc, GPipe *gp, int force );
+
+void GSendInt( int val );
+int GRecvInt( void );
+int GRecvCmd( int *cmd );
+void GSendArr( int len, const char *data );
+char *GRecvArr( int *len );
+int GRecvStrBuf( char *buf );
+int GRecvArrBuf( char *buf );
+void GSendStr( const char *buf );
+void GSendNStr( const char *buf, int len ); /* exact len, buf != 0 */
+void GSendStrN( const char *buf, int len ); /* maximal len */
+char *GRecvStr( void );
+void GSendArgv( char **argv );
+void GSendStrArr( int len, char **data );
+char **GRecvStrArr( int *len );
+char **GRecvArgv( void );
+
+/* client.c */
+#define GCONV_NORMAL 0
+#define GCONV_HIDDEN 1
+#define GCONV_USER 2
+#define GCONV_PASS 3
+#define GCONV_PASS_ND 4
+#define GCONV_BINARY 5
+typedef char *(*GConvFunc)( int what, const char *prompt );
+int Verify( GConvFunc gconv, int rootok );
+#ifdef WITH_CONSOLE_KIT
+int StartClient( const char *ck_session_cookie );
+#else
+int StartClient( void );
+#endif
+void SessionExit( int status ) ATTR_NORETURN;
+int ReadDmrc( void );
+extern char **userEnviron, **systemEnviron;
+extern char *curuser, *curpass, *curtype, *newpass,
+ *dmrcuser, *curdmrc, *newdmrc;
+extern int cursource;
+#define PWSRC_MANUAL 0
+#define PWSRC_AUTOLOGIN 1
+#define PWSRC_RELOGIN 2
+
+/* server.c */
+char **PrepServerArgv( struct display *d, const char *args );
+void StartServer( struct display *d );
+void AbortStartServer( struct display *d );
+void StartServerSuccess( void );
+void StartServerFailed( void );
+void StartServerTimeout( void );
+extern struct display *startingServer;
+extern time_t serverTimeout;
+
+void WaitForServer( struct display *d );
+void ResetServer( struct display *d );
+int PingServer(struct display *d );
+extern Display *dpy;
+
+/* in util.c */
+void *Calloc( size_t nmemb, size_t size );
+void *Malloc( size_t size );
+void *Realloc( void *ptr, size_t size );
+void WipeStr( char *str );
+int StrCmp( const char *s1, const char *s2 );
+#ifdef HAVE_STRNLEN
+# define StrNLen(s, m) strnlen(s, m)
+#else
+int StrNLen( const char *s, int max );
+#endif
+int StrNDup( char **dst, const char *src, int len );
+int StrDup( char **dst, const char *src );
+int arrLen( char **arr );
+void freeStrArr( char **arr );
+char **initStrArr( char **arr );
+char **xCopyStrArr( int rn, char **arr );
+/* Note: the following functions free the old data even in case of failure */
+int ReStrN( char **dst, const char *src, int len );
+int ReStr( char **dst, const char *src );
+int StrApp( char **dst, ... );
+char **addStrArr( char **arr, const char *str, int len );
+char **parseArgs( char **argv, const char *string );
+/* End note */
+char **setEnv( char **e, const char *name, const char *value );
+char **putEnv( const char *string, char **env );
+const char *getEnv( char **e, const char *name );
+const char *localHostname( void );
+int Reader( int fd, void *buf, int len );
+int Writer( int fd, const void *buf, int len );
+int fGets( char *buf, int max, FILE *f );
+void randomStr( char *s );
+time_t mTime( const char *fn );
+void ListSessions( int flags, struct display *d, void *ctx,
+ void (*emitXSess)( struct display *, struct display *, void * ),
+ void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) );
+
+/* in inifile.c */
+char *iniLoad( const char *fname );
+int iniSave( const char *data, const char *fname );
+char *iniEntry( char *data, const char *section, const char *key, const char *value );
+char *iniMerge( char *data, const char *newdata );
+
+/* in bootman.c */
+int getBootOptions( char ***opts, int *def, int *cur );
+int setBootOption( const char *opt, SdRec *sdr );
+void commitBootOption( void );
+
+/* in netaddr.c */
+char *NetaddrAddress( char *netaddrp, int *lenp );
+char *NetaddrPort( char *netaddrp, int *lenp );
+int ConvertAddr( char *saddr, int *len, char **addr );
+int NetaddrFamily( char *netaddrp );
+int addressEqual( char *a1, int len1, char *a2, int len2 );
+
+#ifdef XDMCP
+
+/* in xdmcp.c */
+char *NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress );
+void SendFailed( struct display *d, const char *reason );
+void init_session_id( void );
+
+/* in policy.c */
+struct sockaddr;
+ARRAY8Ptr Accept( struct sockaddr *from, int fromlen, CARD16 displayNumber );
+ARRAY8Ptr ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames );
+int CheckAuthentication( struct protoDisplay *pdpy, ARRAY8Ptr displayID, ARRAY8Ptr name, ARRAY8Ptr data );
+int SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName, ARRAYofARRAY8Ptr authorizationNames );
+int SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes, ARRAYofARRAY8Ptr connectionAddresses );
+int Willing( ARRAY8Ptr addr, CARD16 connectionType, ARRAY8Ptr authenticationName, ARRAY8Ptr status, xdmOpCode type );
+
+/* in protodpy.c */
+void DisposeProtoDisplay( struct protoDisplay *pdpy );
+
+struct protoDisplay *FindProtoDisplay( XdmcpNetaddr address, int addrlen,
+ CARD16 displayNumber );
+struct protoDisplay *NewProtoDisplay( XdmcpNetaddr address, int addrlen,
+ CARD16 displayNumber,
+ CARD16 connectionType,
+ ARRAY8Ptr connectionAddress,
+ CARD32 sessionID );
+
+#define FamilyBroadcast 0xffff
+typedef void (*ChooserFunc)( CARD16 connectionType, ARRAY8Ptr addr, char *closure );
+typedef void (*ListenFunc)( ARRAY8Ptr addr, void **closure );
+
+/* in access.c */
+ARRAY8Ptr getLocalAddress( void );
+int AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType, xdmOpCode type );
+int ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure );
+void ScanAccessDatabase( int force );
+int UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType );
+void ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure );
+void ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfcuntion, void **closure );
+
+/* in choose.c */
+ARRAY8Ptr IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType );
+int IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType );
+int RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType );
+void ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType );
+int RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, ARRAY8Ptr choice );
+int DoChoose( void );
+
+/* socket.c or streams.c */
+void UpdateListenSockets( void );
+int AnyListenSockets( void );
+int ProcessListenSockets( FD_TYPE *reads );
+
+/* in xdmcp.c */
+void ProcessRequestSocket( int fd );
+
+#endif /* XDMCP */
+
+/* in sessreg.c */
+void sessreg( struct display *d, int pid, const char *user, int uid );
+
+#endif /* _DM_H_ */
diff --git a/tdm/backend/dm_auth.h b/tdm/backend/dm_auth.h
new file mode 100644
index 000000000..28725ee8d
--- /dev/null
+++ b/tdm/backend/dm_auth.h
@@ -0,0 +1,105 @@
+/************************************************************
+
+Copyright 1998 by Thomas E. Dickey <[email protected]>
+
+ All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization.
+
+********************************************************/
+
+#ifndef _DM_AUTH_H_
+#define _DM_AUTH_H_ 1
+
+#include "dm.h"
+
+void MitInitAuth( unsigned short name_len, const char *name );
+Xauth *MitGetAuth( unsigned short namelen, const char *name );
+
+#ifdef HASXDMAUTH
+void XdmInitAuth( unsigned short name_len, const char *name );
+Xauth *XdmGetAuth( unsigned short namelen, const char *name );
+# ifdef XDMCP
+void XdmGetXdmcpAuth( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName );
+int XdmCheckAuthentication( struct protoDisplay *pdpy,
+ ARRAY8Ptr displayID,
+ ARRAY8Ptr authenticationName,
+ ARRAY8Ptr authenticationData );
+# else
+# define XdmGetXdmcpAuth NULL
+# endif
+#endif
+
+#ifdef SECURE_RPC
+void SecureRPCInitAuth( unsigned short name_len, const char *name );
+Xauth *SecureRPCGetAuth( unsigned short name_len, const char *name );
+#endif
+
+#ifdef K5AUTH
+void Krb5InitAuth( unsigned short name_len, const char *name );
+Xauth *Krb5GetAuth( unsigned short name_len, const char *name );
+
+Xauth *Krb5GetAuthFor( unsigned short name_len, const char *name, const char *dname );
+char *Krb5Init( const char *user, const char *passwd, const char *dname );
+void Krb5Destroy( const char *dname );
+#endif
+
+/* auth.c */
+int ValidAuthorization( unsigned short name_length, const char *name );
+
+
+#ifdef XDMCP
+
+void
+SetProtoDisplayAuthorization( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName );
+
+#endif /* XDMCP */
+
+int SaveServerAuthorizations( struct display *d, Xauth **auths, int count );
+void CleanUpFileName( const char *src, char *dst, int len );
+void RemoveUserAuthorization( struct display *d );
+void SetAuthorization( struct display *d );
+void SetLocalAuthorization( struct display *d );
+void SetUserAuthorization( struct display *d );
+
+/* genauth.c */
+int GenerateAuthData( char *auth, int len );
+#ifdef NEED_ENTROPY
+void AddPreGetEntropy( void );
+void AddOtherEntropy( void );
+void AddTimerEntropy( void );
+#endif
+
+#ifdef HAVE_ARC4RANDOM
+# define secureRandom() arc4random()
+#else
+int secureRandom( void );
+#endif
+
+#endif /* _DM_AUTH_H_ */
diff --git a/tdm/backend/dm_error.h b/tdm/backend/dm_error.h
new file mode 100644
index 000000000..3570c18fc
--- /dev/null
+++ b/tdm/backend/dm_error.h
@@ -0,0 +1,58 @@
+/************************************************************
+
+Copyright 1998 by Thomas E. Dickey <[email protected]>
+
+ All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization.
+
+********************************************************/
+
+
+#ifndef _DM_ERROR_H_
+#define _DM_ERROR_H_ 1
+
+#include "greet.h"
+
+#include <stdarg.h>
+
+void GDebug( const char *fmt, ... );
+void Debug( const char *fmt, ... );
+void LogInfo( const char *fmt, ... );
+void LogWarn( const char *fmt, ... );
+void LogError( const char *fmt, ... );
+void LogPanic( const char *fmt, ... ) ATTR_NORETURN;
+void LogOutOfMem( void );
+void Panic( const char *mesg ) ATTR_NORETURN;
+void InitErrorLog( const char *errorLogFile );
+#ifdef USE_SYSLOG
+void ReInitErrorLog( void );
+#else
+# define ReInitErrorLog() while(0)
+#endif
+int ASPrintf( char **strp, const char *fmt, ... );
+int VASPrintf( char **strp, const char *fmt, va_list args );
+
+#endif /* _DM_ERROR_H_ */
diff --git a/tdm/backend/dm_socket.h b/tdm/backend/dm_socket.h
new file mode 100644
index 000000000..56a39fd0e
--- /dev/null
+++ b/tdm/backend/dm_socket.h
@@ -0,0 +1,72 @@
+/************************************************************
+
+Copyright 1998 by Thomas E. Dickey <[email protected]>
+Copyright 2002-2004 Oswald Buddenhagen <[email protected]>
+
+ All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization.
+
+********************************************************/
+
+#ifndef _DM_SOCKET_H_
+#define _DM_SOCKET_H_ 1
+
+#ifndef __Lynx__
+# include <sys/socket.h>
+#else
+# include <socket.h>
+#endif
+
+#ifdef TCPCONN
+# include <netinet/in.h>
+#endif
+
+#ifdef UNIXCONN
+# ifndef __Lynx__
+# include <sys/un.h>
+# else
+# include <un.h>
+# endif
+#endif
+
+#ifdef DNETCONN
+# include <netdnet/dn.h>
+#endif
+
+#if (defined(__svr4__) && !defined(__sun__)) && defined(SIOCGIFCONF)
+# define SYSV_SIOCGIFCONF
+int ifioctl( int fd, int cmd, char *arg );
+#else
+# define ifioctl ioctl
+#endif
+
+#ifdef BSD
+# if (BSD >= 199103)
+# define VARIABLE_IFREQ
+# endif
+#endif
+
+#endif /* _DM_SOCKET_H_ */
diff --git a/tdm/backend/dpylist.c b/tdm/backend/dpylist.c
new file mode 100644
index 000000000..b512293f7
--- /dev/null
+++ b/tdm/backend/dpylist.c
@@ -0,0 +1,294 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * a simple linked list of known displays
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+struct display *displays;
+static struct disphist *disphist;
+
+int
+AnyDisplaysLeft( void )
+{
+ return displays != (struct display *)0;
+}
+
+int
+AnyActiveDisplays( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (d->status == remoteLogin || d->userSess >= 0)
+ return 1;
+ return 0;
+}
+
+int
+AnyRunningDisplays( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ switch (d->status) {
+ case notRunning:
+ case textMode:
+ case reserve:
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+int
+AnyReserveDisplays( void )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if ((d->displayType & d_lifetime) == dReserve)
+ return 1;
+ return 0;
+}
+
+int
+idleReserveDisplays( void )
+{
+ struct display *d;
+ int cnt = 0;
+
+ for (d = displays; d; d = d->next)
+ if (d->status == reserve)
+ cnt++;
+ return cnt;
+}
+
+int
+StartReserveDisplay( int lt )
+{
+ struct display *d, *rd;
+
+ for (rd = 0, d = displays; d; d = d->next)
+ if (d->status == reserve)
+ rd = d;
+ if (rd) {
+ rd->idleTimeout = lt;
+ rd->status = notRunning;
+ return 1;
+ }
+ return 0;
+}
+
+void
+ForEachDisplay( void (*f)( struct display * ) )
+{
+ struct display *d, *next;
+
+ for (d = displays; d; d = next) {
+ next = d->next;
+ (*f)( d );
+ }
+}
+
+#ifdef HAVE_VTS
+static void
+_forEachDisplayRev( struct display *d, void (*f)( struct display * ) )
+{
+ if (d) {
+ if (d->next)
+ _forEachDisplayRev( d->next, f );
+ (*f)( d );
+ }
+}
+
+void
+ForEachDisplayRev( void (*f)( struct display * ) )
+{
+ _forEachDisplayRev( displays, f );
+}
+#endif
+
+struct display *
+FindDisplayByName( const char *name )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (!strcmp( name, d->name ))
+ return d;
+ return 0;
+}
+
+struct display *
+FindDisplayByPid( int pid )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (pid == d->pid)
+ return d;
+ return 0;
+}
+
+struct display *
+FindDisplayByServerPid( int serverPid )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (serverPid == d->serverPid)
+ return d;
+ return 0;
+}
+
+#ifdef XDMCP
+
+struct display *
+FindDisplayBySessionID( CARD32 sessionID )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if (sessionID == d->sessionID)
+ return d;
+ return 0;
+}
+
+struct display *
+FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber )
+{
+ struct display *d;
+
+ for (d = displays; d; d = d->next)
+ if ((d->displayType & d_origin) == dFromXDMCP &&
+ d->displayNumber == displayNumber &&
+ addressEqual( (XdmcpNetaddr)d->from.data, d->from.length,
+ addr, addrlen ))
+ return d;
+ return 0;
+}
+
+#endif /* XDMCP */
+
+#define IfFree(x) if (x) free( (char *)x )
+
+void
+RemoveDisplay( struct display *old )
+{
+ struct display *d, **dp;
+ int i;
+
+ for (dp = &displays; (d = *dp); dp = &(*dp)->next) {
+ if (d == old) {
+ Debug( "Removing display %s\n", d->name );
+ *dp = d->next;
+ IfFree( d->class2 );
+ IfFree( d->cfg.data );
+ delStr( d->cfg.dep.name );
+#ifdef XDMCP
+ IfFree( d->remoteHost );
+#endif
+ if (d->authorizations) {
+ for (i = 0; i < d->authNum; i++)
+ XauDisposeAuth( d->authorizations[i] );
+ free( (char *)d->authorizations );
+ }
+ if (d->authFile) {
+ (void)unlink( d->authFile );
+ free( d->authFile );
+ }
+ IfFree( d->authNameLens );
+#ifdef XDMCP
+ XdmcpDisposeARRAY8( &d->peer );
+ XdmcpDisposeARRAY8( &d->from );
+ XdmcpDisposeARRAY8( &d->clientAddr );
+#endif
+ free( (char *)d );
+ break;
+ }
+ }
+}
+
+static struct disphist *
+FindHist( const char *name )
+{
+ struct disphist *hstent;
+
+ for (hstent = disphist; hstent; hstent = hstent->next)
+ if (!strcmp( hstent->name, name ))
+ return hstent;
+ return 0;
+}
+
+struct display *
+NewDisplay( const char *name )
+{
+ struct display *d;
+ struct disphist *hstent;
+
+ if (!(hstent = FindHist( name ))) {
+ if (!(hstent = Calloc( 1, sizeof(*hstent) )))
+ return 0;
+ if (!StrDup( &hstent->name, name )) {
+ free( hstent );
+ return 0;
+ }
+ hstent->next = disphist; disphist = hstent;
+ }
+
+ if (!(d = (struct display *)Calloc( 1, sizeof(*d) )))
+ return 0;
+ d->next = displays;
+ d->hstent = hstent;
+ d->name = hstent->name;
+ /* initialize fields (others are 0) */
+ d->pid = -1;
+ d->serverPid = -1;
+ d->ctrl.fd = -1;
+ d->ctrl.fifo.fd = -1;
+ d->pipe.rfd = -1;
+ d->pipe.wfd = -1;
+ d->gpipe.rfd = -1;
+ d->gpipe.wfd = -1;
+ d->userSess = -1;
+#ifdef XDMCP
+ d->xdmcpFd = -1;
+#endif
+ displays = d;
+ Debug( "created new display %s\n", d->name );
+ return d;
+}
diff --git a/tdm/backend/error.c b/tdm/backend/error.c
new file mode 100644
index 000000000..93ec40e70
--- /dev/null
+++ b/tdm/backend/error.c
@@ -0,0 +1,130 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * Log display manager errors to a file as
+ * we generally do not have a terminal to talk to
+ * or use syslog if it exists
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <unistd.h>
+#include <stdio.h>
+
+#define PRINT_QUOTES
+#define PRINT_ARRAYS
+#define LOG_DEBUG_MASK DEBUG_CORE
+#define LOG_PANIC_EXIT 1
+#define NEED_ASPRINTF
+#define STATIC
+#include "printf.c"
+
+void
+GDebug( const char *fmt, ... )
+{
+ va_list args;
+
+ if (debugLevel & DEBUG_HLPCON) {
+ va_start( args, fmt );
+ Logger( DM_DEBUG, fmt, args );
+ va_end( args );
+ }
+}
+
+void
+Panic( const char *mesg )
+{
+ int fd = open( "/dev/console", O_WRONLY );
+ write( fd, "xdm panic: ", 11 );
+ write( fd, mesg, strlen( mesg ) );
+ write( fd, "\n", 1 );
+#ifdef USE_SYSLOG
+ ReInitErrorLog();
+ syslog( LOG_ALERT, "%s", mesg );
+#endif
+ exit( 1 );
+}
+
+#ifdef USE_SYSLOG
+void
+ReInitErrorLog()
+{
+ if (!(debugLevel & DEBUG_NOSYSLOG))
+ InitLog();
+}
+#endif
+
+void
+InitErrorLog( const char *errorLogFile )
+{
+ int fd;
+ char buf[128];
+
+#ifdef USE_SYSLOG
+ ReInitErrorLog();
+#endif
+ /* We do this independently of using syslog, as we cannot redirect
+ * the output of external programs to syslog.
+ */
+ if (!errorLogFile || strcmp( errorLogFile, "-" )) {
+ if (!errorLogFile) {
+ sprintf( buf, "/var/log/%s.log", prog );
+ errorLogFile = buf;
+ }
+ if ((fd = open( errorLogFile, O_CREAT | O_APPEND | O_WRONLY, 0666 )) < 0)
+ LogError( "Cannot open log file %s\n", errorLogFile );
+ else {
+#ifdef USE_SYSLOG
+# ifdef USE_PAM
+# define PAMLOG " PAM logs messages related to authentication to authpriv.*."
+# else
+# define PAMLOG
+# endif
+# define WARNMSG \
+ "********************************************************************************\n" \
+ "Note that your system uses syslog. All of tdm's internally generated messages\n" \
+ "(i.e., not from libraries and external programs/scripts it uses) go to the\n" \
+ "daemon.* syslog facility; check your syslog configuration to find out to which\n" \
+ "file(s) it is logged." PAMLOG "\n" \
+ "********************************************************************************\n\n"
+ if (!lseek( fd, 0, SEEK_END ))
+ write( fd, WARNMSG, sizeof(WARNMSG) - 1 );
+#endif
+ dup2( fd, 1 );
+ close( fd );
+ dup2( 1, 2 );
+ }
+ }
+}
+
diff --git a/tdm/backend/genauth.c b/tdm/backend/genauth.c
new file mode 100644
index 000000000..6da95cce0
--- /dev/null
+++ b/tdm/backend/genauth.c
@@ -0,0 +1,500 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2003-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#ifdef NEED_ENTROPY
+
+# include <signal.h>
+
+/* ####################################################################### */
+
+/*
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * Copyright (c) 2001-2002 Damien Miller. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dm_socket.h"
+
+#include <string.h>
+
+#ifndef INADDR_LOOPBACK
+# define INADDR_LOOPBACK 0x7F000001U
+#endif
+
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+static int
+getPrngdBytes( char *buf, int len,
+ unsigned short tcp_port, const char *socket_path )
+{
+ int fd, addr_len, rval, errors;
+ char msg[2];
+ struct sockaddr *addr;
+ struct sockaddr_in addr_in;
+ struct sockaddr_un addr_un;
+ int af;
+ SIGFUNC old_sigpipe;
+
+ if (tcp_port) {
+ memset( &addr_in, 0, sizeof(addr_in) );
+ af = addr_in.sin_family = AF_INET;
+ addr_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ addr_in.sin_port = htons( tcp_port );
+ addr_len = sizeof(addr_in);
+ addr = (struct sockaddr *)&addr_in;
+ } else if (*socket_path) {
+ unsigned spl = strlen( socket_path );
+ if (spl >= sizeof(addr_un.sun_path)) {
+ LogError( "get_random_prngd: "
+ "Random pool path is too long\n" );
+ return -1;
+ }
+ af = addr_un.sun_family = AF_UNIX;
+ strncpy( addr_un.sun_path, socket_path,
+ sizeof(addr_un.sun_path) );
+ addr_len = offsetof( struct sockaddr_un, sun_path ) + spl + 1;
+ addr = (struct sockaddr *)&addr_un;
+ } else
+ return -1;
+
+ old_sigpipe = Signal( SIGPIPE, SIG_IGN );
+
+ errors = 0;
+ rval = -1;
+reopen:
+ if ((fd = socket( af, SOCK_STREAM, 0 )) < 0) {
+ LogError( "Couldn't create socket: %m\n" );
+ goto done;
+ }
+
+ if (connect( fd, (struct sockaddr *)addr, addr_len )) {
+ if (af == AF_INET)
+ LogError( "Couldn't connect to PRNGD port %d: %m\n",
+ tcp_port );
+ else
+ LogError( "Couldn't connect to PRNGD socket %\"s: %m\n",
+ socket_path );
+ goto done;
+ }
+
+ /* Send blocking read request to PRNGD */
+ msg[0] = 0x02;
+ msg[1] = len;
+
+ if (Writer( fd, msg, sizeof(msg) ) != sizeof(msg)) {
+ if (errno == EPIPE && errors < 10) {
+ close( fd );
+ errors++;
+ goto reopen;
+ }
+ LogError( "Couldn't write to PRNGD socket: %m\n" );
+ goto done;
+ }
+
+ if (Reader( fd, buf, len ) != len) {
+ if (errno == EPIPE && errors < 10) {
+ close( fd );
+ errors++;
+ goto reopen;
+ }
+ LogError( "Couldn't read from PRNGD socket: %m\n" );
+ goto done;
+ }
+
+ rval = 0;
+done:
+ Signal( SIGPIPE, old_sigpipe );
+ if (fd != -1)
+ close( fd );
+ return rval;
+}
+
+/* ####################################################################### */
+
+/*
+ * Stolen from the Linux kernel.
+ *
+ * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+static unsigned epool[32], erotate, eadd_ptr;
+
+static void
+add_entropy( unsigned const *in, int nwords )
+{
+ static unsigned const twist_table[8] = {
+ 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
+ unsigned i, w;
+ int new_rotate;
+
+ while (nwords--) {
+ w = *in++;
+ w = (w<<erotate | w>>(32-erotate)) & 0xffffffff;
+ i = eadd_ptr = (eadd_ptr - 1) & 31;
+ new_rotate = erotate + 14;
+ if (i)
+ new_rotate = erotate + 7;
+ erotate = new_rotate & 31;
+ w ^= epool[(i + 26) & 31];
+ w ^= epool[(i + 20) & 31];
+ w ^= epool[(i + 14) & 31];
+ w ^= epool[(i + 7) & 31];
+ w ^= epool[(i + 1) & 31];
+ w ^= epool[i];
+ epool[i] = (w >> 3) ^ twist_table[w & 7];
+ }
+}
+
+/* ####################################################################### */
+
+/*
+ * This code implements something close to the MD5 message-digest
+ * algorithm. This code is based on code written by Colin Plumb
+ * in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ */
+
+/* The four core functions - F1 is optimized somewhat */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1 (z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define pmd5_step(f, w, x, y, z, data, s) \
+ (w += (f(x, y, z) + data) & 0xffffffff, w = w<<s | w>>(32-s), w += x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.
+ */
+static void
+pmd5_hash( unsigned *out, unsigned const in[16] )
+{
+ unsigned a, b, c, d;
+
+ a = out[0];
+ b = out[1];
+ c = out[2];
+ d = out[3];
+
+ pmd5_step( F1, a, b, c, d, in[0] + 0xd76aa478, 7 );
+ pmd5_step( F1, d, a, b, c, in[1] + 0xe8c7b756, 12 );
+ pmd5_step( F1, c, d, a, b, in[2] + 0x242070db, 17 );
+ pmd5_step( F1, b, c, d, a, in[3] + 0xc1bdceee, 22 );
+ pmd5_step( F1, a, b, c, d, in[4] + 0xf57c0faf, 7 );
+ pmd5_step( F1, d, a, b, c, in[5] + 0x4787c62a, 12 );
+ pmd5_step( F1, c, d, a, b, in[6] + 0xa8304613, 17 );
+ pmd5_step( F1, b, c, d, a, in[7] + 0xfd469501, 22 );
+ pmd5_step( F1, a, b, c, d, in[8] + 0x698098d8, 7 );
+ pmd5_step( F1, d, a, b, c, in[9] + 0x8b44f7af, 12 );
+ pmd5_step( F1, c, d, a, b, in[10] + 0xffff5bb1, 17 );
+ pmd5_step( F1, b, c, d, a, in[11] + 0x895cd7be, 22 );
+ pmd5_step( F1, a, b, c, d, in[12] + 0x6b901122, 7 );
+ pmd5_step( F1, d, a, b, c, in[13] + 0xfd987193, 12 );
+ pmd5_step( F1, c, d, a, b, in[14] + 0xa679438e, 17 );
+ pmd5_step( F1, b, c, d, a, in[15] + 0x49b40821, 22 );
+
+ pmd5_step( F2, a, b, c, d, in[1] + 0xf61e2562, 5 );
+ pmd5_step( F2, d, a, b, c, in[6] + 0xc040b340, 9 );
+ pmd5_step( F2, c, d, a, b, in[11] + 0x265e5a51, 14 );
+ pmd5_step( F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20 );
+ pmd5_step( F2, a, b, c, d, in[5] + 0xd62f105d, 5 );
+ pmd5_step( F2, d, a, b, c, in[10] + 0x02441453, 9 );
+ pmd5_step( F2, c, d, a, b, in[15] + 0xd8a1e681, 14 );
+ pmd5_step( F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20 );
+ pmd5_step( F2, a, b, c, d, in[9] + 0x21e1cde6, 5 );
+ pmd5_step( F2, d, a, b, c, in[14] + 0xc33707d6, 9 );
+ pmd5_step( F2, c, d, a, b, in[3] + 0xf4d50d87, 14 );
+ pmd5_step( F2, b, c, d, a, in[8] + 0x455a14ed, 20 );
+ pmd5_step( F2, a, b, c, d, in[13] + 0xa9e3e905, 5 );
+ pmd5_step( F2, d, a, b, c, in[2] + 0xfcefa3f8, 9 );
+ pmd5_step( F2, c, d, a, b, in[7] + 0x676f02d9, 14 );
+ pmd5_step( F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20 );
+
+ pmd5_step( F3, a, b, c, d, in[5] + 0xfffa3942, 4 );
+ pmd5_step( F3, d, a, b, c, in[8] + 0x8771f681, 11 );
+ pmd5_step( F3, c, d, a, b, in[11] + 0x6d9d6122, 16 );
+ pmd5_step( F3, b, c, d, a, in[14] + 0xfde5380c, 23 );
+ pmd5_step( F3, a, b, c, d, in[1] + 0xa4beea44, 4 );
+ pmd5_step( F3, d, a, b, c, in[4] + 0x4bdecfa9, 11 );
+ pmd5_step( F3, c, d, a, b, in[7] + 0xf6bb4b60, 16 );
+ pmd5_step( F3, b, c, d, a, in[10] + 0xbebfbc70, 23 );
+ pmd5_step( F3, a, b, c, d, in[13] + 0x289b7ec6, 4 );
+ pmd5_step( F3, d, a, b, c, in[0] + 0xeaa127fa, 11 );
+ pmd5_step( F3, c, d, a, b, in[3] + 0xd4ef3085, 16 );
+ pmd5_step( F3, b, c, d, a, in[6] + 0x04881d05, 23 );
+ pmd5_step( F3, a, b, c, d, in[9] + 0xd9d4d039, 4 );
+ pmd5_step( F3, d, a, b, c, in[12] + 0xe6db99e5, 11 );
+ pmd5_step( F3, c, d, a, b, in[15] + 0x1fa27cf8, 16 );
+ pmd5_step( F3, b, c, d, a, in[2] + 0xc4ac5665, 23 );
+
+ pmd5_step( F4, a, b, c, d, in[0] + 0xf4292244, 6 );
+ pmd5_step( F4, d, a, b, c, in[7] + 0x432aff97, 10 );
+ pmd5_step( F4, c, d, a, b, in[14] + 0xab9423a7, 15 );
+ pmd5_step( F4, b, c, d, a, in[5] + 0xfc93a039, 21 );
+ pmd5_step( F4, a, b, c, d, in[12] + 0x655b59c3, 6 );
+ pmd5_step( F4, d, a, b, c, in[3] + 0x8f0ccc92, 10 );
+ pmd5_step( F4, c, d, a, b, in[10] + 0xffeff47d, 15 );
+ pmd5_step( F4, b, c, d, a, in[1] + 0x85845dd1, 21 );
+ pmd5_step( F4, a, b, c, d, in[8] + 0x6fa87e4f, 6 );
+ pmd5_step( F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10 );
+ pmd5_step( F4, c, d, a, b, in[6] + 0xa3014314, 15 );
+ pmd5_step( F4, b, c, d, a, in[13] + 0x4e0811a1, 21 );
+ pmd5_step( F4, a, b, c, d, in[4] + 0xf7537e82, 6 );
+ pmd5_step( F4, d, a, b, c, in[11] + 0xbd3af235, 10 );
+ pmd5_step( F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15 );
+ pmd5_step( F4, b, c, d, a, in[9] + 0xeb86d391, 21 );
+
+ out[0] += a;
+ out[1] += b;
+ out[2] += c;
+ out[3] += d;
+}
+
+/* ####################################################################### */
+
+
+static int
+sumFile( const char *name, int len, int whence, long offset )
+{
+ int fd, i, cnt, readlen = 0;
+ unsigned char buf[0x1000];
+
+ if ((fd = open( name, O_RDONLY )) < 0) {
+ Debug( "cannot open entropy source %\"s: %m\n", name );
+ return -1;
+ }
+ lseek( fd, offset, whence );
+ while (readlen < len) {
+ if (!(cnt = read( fd, buf, sizeof(buf) )))
+ break;
+ if (cnt < 0) {
+ close( fd );
+ Debug( "cannot read entropy source %\"s: %m\n", name );
+ return -1;
+ }
+ readlen += cnt;
+ if (sizeof(unsigned) == 4)
+ add_entropy( (unsigned *)buf, (cnt + 3) / 4 );
+ else {
+ unsigned buf2[sizeof(buf) / 4];
+ for (i = 0; i < cnt; i += 8) {
+ buf2[i / 4] = *(unsigned *)(buf + i) & 0xffffffff;
+ buf2[i / 4 + 1] = *(unsigned *)(buf + i) >> 32;
+ }
+ add_entropy( buf2, (cnt + 3) / 4 );
+ }
+ }
+ close( fd );
+ Debug( "read %d bytes from entropy source %\"s\n", readlen, name );
+ return readlen;
+}
+
+void
+AddTimerEntropy( void )
+{
+ struct timeval now;
+ gettimeofday( &now, 0 );
+ add_entropy( (unsigned *)&now, sizeof(now)/sizeof(unsigned) );
+}
+
+#define BSIZ 0x10000
+
+void
+AddOtherEntropy( void )
+{
+ AddTimerEntropy();
+ /* XXX -- setup-specific ... use some common ones */
+ sumFile( "/var/log/messages", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/syslog", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/debug", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/kern.log", 0x1000, SEEK_END, -0x1000 );
+ sumFile( "/var/log/daemon.log", 0x1000, SEEK_END, -0x1000 );
+/* root hardly ever has an own box ... maybe pick a random mailbox instead? eek ...
+ sumFile( "/var/spool/mail/root", 0x1000, SEEK_END, -0x1000 );
+*/
+}
+
+void
+AddPreGetEntropy( void )
+{
+ static long offset;
+ int readlen;
+
+ AddTimerEntropy();
+ if ((readlen = sumFile( randomFile, BSIZ, SEEK_SET, offset )) == BSIZ) {
+ offset += readlen;
+#if defined(__i386__) || defined(amiga)
+ if (!strcmp( randomFile, "/dev/mem" )) {
+ if (offset == 0xa0000) /* skip 640kB-1MB ROM mappings */
+ offset = 0x100000;
+ else if (offset == 0xf00000) /* skip 15-16MB memory hole */
+ offset = 0x1000000;
+ }
+#endif
+ return;
+ } else if (readlen >= 0 && offset) {
+ if ((offset = sumFile( randomFile, BSIZ, SEEK_SET, 0 )) == BSIZ)
+ return;
+ }
+ LogError( "Cannot read randomFile %\"s; "
+ "X cookies may be easily guessable\n", randomFile );
+}
+#endif
+
+/* ONLY 8 or 16 bytes! */
+/* auth MUST be sizeof(unsigned)-aligned! */
+int
+GenerateAuthData( char *auth, int len )
+{
+#ifdef HAVE_ARC4RANDOM
+ int i;
+ unsigned *rnd = (unsigned *)auth;
+ if (sizeof(unsigned) == 4)
+ for (i = 0; i < len; i += 4)
+ rnd[i / 4] = arc4random();
+ else
+ for (i = 0; i < len; i += 8)
+ rnd[i / 8] = arc4random() | (arc4random() << 32);
+ return 1;
+#else
+ int fd;
+ const char *rd = randomDevice;
+# ifdef DEV_RANDOM
+ if (!*rd)
+ rd = DEV_RANDOM;
+# else
+ if (*rd) {
+# endif
+ if ((fd = open( rd, O_RDONLY )) >= 0) {
+ if (read( fd, auth, len ) == len) {
+ close( fd );
+ return 1;
+ }
+ close( fd );
+ LogError( "Cannot read randomDevice %\"s: %m\n", rd );
+ } else
+ LogError( "Cannot open randomDevice %\"s: %m\n", rd );
+# ifdef DEV_RANDOM
+ return 0;
+# else
+ }
+
+ if (!getPrngdBytes( auth, len, prngdPort, prngdSocket ))
+ return 1;
+
+ {
+ unsigned *rnd = (unsigned *)auth;
+ unsigned tmp[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
+ AddPreGetEntropy();
+ pmd5_hash( tmp, epool );
+ add_entropy( tmp, 1 );
+ pmd5_hash( tmp, epool + 16 );
+ add_entropy( tmp + 2, 1 );
+ if (sizeof(unsigned) == 4)
+ memcpy( auth, tmp, len );
+ else {
+ int i;
+ for (i = 0; i < len; i += 8)
+ rnd[i / 8] = tmp[i / 4] | (tmp[i / 4 + 1] << 32);
+ }
+ }
+ return 1;
+# endif
+#endif
+}
+
+#ifndef HAVE_ARC4RANDOM
+int
+secureRandom( void )
+{
+ int rslt;
+ GenerateAuthData( (char *)&rslt, sizeof(int) );
+ return rslt & 0x7fffffff;
+}
+#endif \ No newline at end of file
diff --git a/tdm/backend/getfd.c b/tdm/backend/getfd.c
new file mode 100644
index 000000000..307b2501c
--- /dev/null
+++ b/tdm/backend/getfd.c
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#ifdef __linux__
+#include <linux/kd.h>
+#endif
+#include "getfd.h"
+
+/*
+ * getfd.c
+ *
+ * Get an fd for use with kbd/console ioctls.
+ * We try several things because opening /dev/console will fail
+ * if someone else used X (which does a chown on /dev/console).
+ */
+
+static int
+is_a_console(int fd) {
+ char arg;
+
+ arg = 0;
+#ifdef __OpenBSD__
+ return arg;
+#else
+ return (ioctl(fd, KDGKBTYPE, &arg) == 0
+ && ((arg == KB_101) || (arg == KB_84)));
+#endif
+}
+
+static int
+open_a_console(const char *fnam) {
+ int fd;
+
+ /*
+ * For ioctl purposes we only need some fd and permissions
+ * do not matter. But setfont:activatemap() does a write.
+ */
+ fd = open(fnam, O_RDWR);
+ if (fd < 0 && errno == EACCES)
+ fd = open(fnam, O_WRONLY);
+ if (fd < 0 && errno == EACCES)
+ fd = open(fnam, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ if (!is_a_console(fd)) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int getfd() {
+ int fd;
+
+ fd = open_a_console("/dev/tty");
+ if (fd >= 0)
+ return fd;
+
+ fd = open_a_console("/dev/tty0");
+ if (fd >= 0)
+ return fd;
+
+ fd = open_a_console("/dev/vc/0");
+ if (fd >= 0)
+ return fd;
+
+ fd = open_a_console("/dev/console");
+ if (fd >= 0)
+ return fd;
+
+ for (fd = 0; fd < 3; fd++)
+ if (is_a_console(fd))
+ return fd;
+
+ // "Couldnt get a file descriptor referring to the console
+ return -1;
+}
+
diff --git a/tdm/backend/getfd.h b/tdm/backend/getfd.h
new file mode 100644
index 000000000..b0b33a892
--- /dev/null
+++ b/tdm/backend/getfd.h
@@ -0,0 +1 @@
+extern int getfd();
diff --git a/tdm/backend/greet.h b/tdm/backend/greet.h
new file mode 100644
index 000000000..985edc29c
--- /dev/null
+++ b/tdm/backend/greet.h
@@ -0,0 +1,278 @@
+/*
+
+Copyright 2001-2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * interface to xdm's external greeter and config reader
+ */
+
+#ifndef GREET_H
+#define GREET_H
+
+#include <config.h>
+
+#define DEBUG_CORE 0x01
+#define DEBUG_CONFIG 0x02
+#define DEBUG_GREET 0x04
+#define DEBUG_HLPCON 0x08
+#define DEBUG_WSESS 0x10
+#define DEBUG_WCONFIG 0x20
+#define DEBUG_WGREET 0x40
+#define DEBUG_NOSYSLOG 0x80
+#define DEBUG_AUTH 0x100
+#define DEBUG_VALGRIND 0x400
+#define DEBUG_STRACE 0x800
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+# define ATTR_UNUSED __attribute__((unused))
+# define ATTR_NORETURN __attribute__((noreturn))
+# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
+#else
+# define ATTR_UNUSED
+# define ATTR_NORETURN
+# define ATTR_PRINTFLIKE(fmt,var)
+#endif
+
+#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0])))
+
+#define __stringify(x) #x
+#define stringify(x) __stringify(x)
+
+/*
+ * Exit codes for fork()ed session process, greeter, and config reader
+ */
+#define EX_NORMAL 30 /* do whatever seems appropriate */
+#define EX_REMANAGE_DPY 31 /* force remanage; same as EX_NORMAL, but cannot return to reserve mode immediately */
+#define EX_UNMANAGE_DPY 32 /* force deletion */
+#define EX_RESERVER_DPY 33 /* force server termination */
+#define EX_AL_RESERVER_DPY 34 /* reserver; maybe, auto-(re-)login */
+#define EX_OPENFAILED_DPY 35 /* XOpenDisplay failed, retry */
+#define EX_RESERVE 37 /* put in reserve mode */
+#ifdef XDMCP
+#define EX_REMOTE 38 /* start -query-ing X-server */
+#define EX_MAX EX_REMOTE
+#else
+#define EX_MAX EX_RESERVE
+#endif
+
+/*
+ * Command codes core -> greeter
+ */
+#define G_Greet 1 /* get login; bidi */
+#define G_ErrorGreet 2 /* print failed auto-login */
+#ifdef XDMCP
+#define G_Choose 3 /* run chooser; bidi */
+# define G_Ch_AddHost 301
+# define G_Ch_ChangeHost 302
+# define G_Ch_RemoveHost 303
+# define G_Ch_BadHost 304
+# define G_Ch_Exit 305
+#endif
+#define G_SessMan 4 /* start "session manager" */
+#define G_ConfShutdown 5 /* confirm forced shutdown */
+#define G_GreetTimed 6 /* get login; timed login permitted */
+
+#ifdef XDMCP
+#define G_Ch_Refresh 10 /* XXX change */
+#define G_Ch_RegisterHost 11 /* str name XXX change */
+#define G_Ch_DirectChoice 12 /* str name XXX change */
+#endif
+
+/*
+ * Status/command codes greeter -> core
+ */
+#define G_Ready 0 /* nop */
+#define G_Cancel 1 /* abort login, etc. */
+
+#define G_DGreet 2 /* get login */
+#ifdef XDMCP
+#define G_DChoose 3 /* run chooser */
+#endif
+
+#define G_Shutdown 101 /* 5*int, string; async */
+# define SHUT_REBOOT 1 /* how */
+# define SHUT_HALT 2
+# define SHUT_CONSOLE -1 /* pseudo-code */
+# define SHUT_SCHEDULE 0 /* when; config only */
+# define SHUT_TRYNOW 1
+# define SHUT_FORCENOW 2
+# define SHUT_CANCEL 0 /* force */
+# define SHUT_FORCEMY 1
+# define SHUT_FORCE 2
+# define SHUT_ASK 3
+# define TO_INF 0x7fffffff
+#define G_SessionExit 102 /* int code; async */
+#define G_GetCfg 103 /* int what; int sts, <variable> */
+#define G_SetupDpy 104 /* ; int <syncer> */
+#define G_ReadDmrc 105 /* str user; int sts - curdmrc */
+#define G_GetDmrc 106 /* str key; str value - curdmrc */
+/*#define G_ResetDmrc 107*/ /* ; async - newdmrc */
+#define G_PutDmrc 108 /* str key, str value; async - newdmrc */
+#define G_Verify 109 /* str type; ..., int V_ret */
+#define G_VerifyRootOK 110 /* str type; ..., int V_ret */
+#define G_List 111 /* int flags; ?*(str,str,[int,]str,str,int), int 0 */
+# define lstRemote 1
+# define lstPassive 2
+# define lstTTY 4
+# define isSelf 1
+# define isTTY 2
+#define G_QueryShutdown 112 /* ; 5*int; string */
+#define G_Activate 113 /* int vt; async */
+#define G_ListBootOpts 114 /* ; int sts, [argv opts, int dflt, int cur] */
+# define BO_OK 0
+# define BO_NOMAN -1
+# define BO_NOENT -2
+# define BO_IO -3
+#define G_Console 116 /* ; async */
+#define G_AutoLogin 117 /* ; async */
+
+/*
+ * Command codes core -> config reader
+ */
+#define GC_Files 1 /* get file list */
+#define GC_GetConf 2 /* get a config group */
+# define GC_gGlobal 1 /* get global config array */
+#ifdef XDMCP
+# define GC_gXaccess 3 /* get Xaccess equivalent */
+#endif
+# define GC_gDisplay 4 /* get per-display config array */
+
+/*
+ * Error code core -> greeter
+ */
+#define GE_Ok 0
+#define GE_NoFkt 1 /* no such function (only for extensions!) */
+#define GE_Error 2 /* internal error, like OOM */
+/* for config reading */
+#define GE_NoEnt 10 /* no such config entry */
+#define GE_BadType 11 /* unknown config entry type */
+/* for dmrc reading */
+#define GE_NoUser 20 /* no such user */
+#define GE_NoFile 21 /* no such file */
+#define GE_Denied 22 /* permission denied */
+
+/*
+ * Log levels.
+ * Used independently in core, greeter & config reader.
+ */
+#define DM_DEBUG 0
+#define DM_INFO 1
+#define DM_WARN 2
+#define DM_ERR 3
+#define DM_PANIC 4
+
+/*
+ * Status codes from Verify
+ */
+/* terminal status codes */
+#define V_OK 0
+#define V_FAIL 10 /* whatever, already reported with V_MSG_* */
+#define V_AUTH 11 /* authentication failed */
+/* non-terminal status codes */
+#define V_MSG_INFO 110 /* info message attached */
+#define V_MSG_ERR 111 /* error message attached (null for generic) */
+#define V_PUT_USER 112 /* user name attached; only with pam & no user send */
+#define V_CHTOK 113 /* password expired; change now */
+#define V_CHTOK_AUTH 114 /* password expired; change now, but authenticate first */
+#define V_PRE_OK 115 /* authentication succeeded, continue with password change */
+/* queries */
+#define V_GET_TEXT 200 /* str prompt, int echo, int ndelay; str return, int tag */
+# define V_IS_SECRET 1
+# define V_IS_USER 2
+# define V_IS_PASSWORD 4
+# define V_IS_OLDPASSWORD 8
+# define V_IS_NEWPASSWORD 16
+#define V_GET_BINARY 201 /* array prompt, int ndelay; array return */
+
+/*
+ * Config/Runtime data keys
+ */
+#define C_WHO_MASK 0x00ff0000 /* Non-zero for proprietary extensions (see manufacturer table [to be written]) */
+#define C_TYPE_MASK 0x0f000000 /* Type of the value */
+# define C_TYPE_INT 0x00000000 /* Integer */
+# define C_TYPE_STR 0x01000000 /* String */
+# define C_TYPE_ARGV 0x02000000 /* 0-terminated Array of Strings */
+# define C_TYPE_ARR 0x03000000 /* Array (only when XDCMP is enabled) */
+#define C_PRIVATE 0xf0000000 /* Private, don't make it visible to interfaces! */
+
+/* display variables */
+#define C_isLocal (C_TYPE_INT | 0x200)
+#define C_hasConsole (C_TYPE_INT | 0x201)
+#define C_isAuthorized (C_TYPE_INT | 0x202)
+
+/**
+ ** for struct display
+ **/
+
+#define d_location 1
+#define dLocal 1 /* server runs on local host */
+#define dForeign 0 /* server runs on remote host */
+
+#define d_lifetime 6
+#define dPermanent 4 /* display restarted when session exits */
+#define dReserve 2 /* display not restarted when session exits */
+#define dTransient 0 /* display removed when session exits */
+
+#ifdef XDMCP
+#define d_origin 8
+#else
+#define d_origin 0 /* clever, huh? :) */
+#endif
+#define dFromXDMCP 8 /* started with XDMCP */
+#define dFromFile 0 /* started via entry in servers file */
+
+#ifdef XDMCP
+/**
+ ** for xdmcp acls
+ **/
+
+/*
+ * flags in acl entries
+ */
+#define a_notAllowed 1 /* both direct and indirect */
+#define a_notBroadcast 2 /* only direct */
+#define a_useChooser 2 /* only indirect */
+
+/*
+ * type of host entries
+ */
+#define HOST_ALIAS 0
+#define HOST_ADDRESS 1
+#define HOST_PATTERN 2
+#define HOST_BROADCAST 3
+
+#endif
+
+#endif /* GREET_H */
diff --git a/tdm/backend/inifile.c b/tdm/backend/inifile.c
new file mode 100644
index 000000000..b5426de75
--- /dev/null
+++ b/tdm/backend/inifile.c
@@ -0,0 +1,255 @@
+/*
+
+Copyright 2003 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * load, save and manipulate ini-style config files
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+char *
+iniLoad( const char *fname )
+{
+ char *data;
+ int fd, len;
+ struct stat st;
+
+ if ((fd = open( fname, O_RDONLY | O_NONBLOCK )) < 0) {
+ Debug( "cannot open ini-file %\"s: %m", fname );
+ return 0;
+ }
+ if (fstat( fd, &st ) || !S_ISREG( st.st_mode )) {
+ LogWarn( "Ini-file %\"s is no regular file\n", fname );
+ close( fd );
+ return 0;
+ }
+ if (st.st_size >= 0x10000) {
+ LogWarn( "Ini-file %\"s is too big\n", fname );
+ close( fd );
+ return 0;
+ }
+ len = st.st_size;
+ if (!(data = Malloc( len + 2 ))) {
+ close( fd );
+ return 0;
+ }
+ if (read( fd, data, len ) != len) {
+ Debug( "cannot read ini-file %\"s: %m", fname );
+ free( data );
+ close( fd );
+ return 0;
+ }
+ close( fd );
+ if (data[len - 1] != '\n')
+ data[len++] = '\n';
+ data[len] = 0;
+ return data;
+}
+
+int
+iniSave( const char *data, const char *fname )
+{
+ int fd, cnt, len;
+
+ if ((fd = open( fname, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600 )) < 0) {
+ Debug( "cannot create ini-file %\"s: %m", fname );
+ return 0;
+ }
+ len = strlen( data );
+ if ((cnt = write( fd, data, len )) == len) {
+ close( fd );
+ return 1;
+ }
+ if (cnt == -1)
+ Debug( "cannot write ini-file %\"s: %m", fname );
+ else
+ Debug( "cannot write ini-file %\"s: partial write", fname );
+ close( fd );
+ return 0;
+}
+
+#define apparr(d,s,n) do { memcpy (d, s, n); d += n; } while(0)
+#define appbyte(d,b) *d++ = b
+
+char *
+iniEntry( char *data, const char *section, const char *key, const char *value )
+{
+ char *p = data, *secinsert = 0, *pastinsert = 0, *cb, *ce, *ndata;
+ const char *t;
+ int insect = 0, ll, sl, kl, vl, len, nlen;
+
+ if (p) {
+ while (*p) {
+ for (; *p == ' ' || *p == '\t'; p++);
+ if (*p == '\n') {
+ p++;
+ continue;
+ }
+ if (*p == '[') {
+ for (t = section; *++p == *t; t++);
+ insect = !*t && *p == ']';
+ } else if (insect) {
+ for (t = key; *p == *t; t++, p++);
+ for (; *p == ' ' || *p == '\t'; p++);
+ if (!*t && *p == '=') {
+ for (p++; *p == ' ' || *p == '\t'; p++);
+ cb = p;
+ while (*p++ != '\n');
+ ce = p;
+ if (value) {
+ ll = sl = kl = 0;
+ len = (ce - data) + strlen( ce );
+ goto insert;
+ } else {
+ for (ce--; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--);
+ if (!StrNDup( &p, cb, ce - cb ))
+ return 0;
+ return p;
+ }
+ }
+ }
+ for (; *p != '\n'; p++);
+ p++;
+ if (insect)
+ secinsert = p;
+ else
+ pastinsert = p;
+ }
+ }
+ if (!value)
+ return 0;
+ len = p - data;
+ if (secinsert) {
+ ce = cb = secinsert;
+ sl = ll = 0;
+ } else {
+ sl = strlen( section ) + 3;
+ if (pastinsert) {
+ ce = cb = pastinsert;
+ ll = 1;
+ } else {
+ ce = cb = data;
+ ll = 0;
+ }
+ }
+ kl = strlen( key ) + 1;
+ insert:
+ vl = strlen( value );
+ nlen = len - (ce - cb) + ll + sl + kl + vl + 1;
+ if (!(p = ndata = Malloc( nlen + 1 )))
+ return data;
+ apparr( p, data, cb - data );
+ if (kl) {
+ if (sl) {
+ if (ll)
+ appbyte( p, '\n' );
+ appbyte( p, '[' );
+ apparr( p, section, sl - 3 );
+ appbyte( p, ']' );
+ appbyte( p, '\n' );
+ }
+ apparr( p, key, kl - 1 );
+ appbyte( p, '=' );
+ }
+ apparr( p, value, vl );
+ appbyte( p, '\n' );
+ if (data) {
+ apparr( p, ce, len - (ce - data) );
+ free( data );
+ }
+ appbyte( p, 0 );
+ return ndata;
+}
+
+char *
+iniMerge( char *data, const char *newdata )
+{
+ const char *p, *cb, *ce;
+ char *section = 0, *key, *value;
+
+ if (!newdata)
+ return data;
+ for (p = newdata;;) {
+ for (; *p == ' ' || *p == '\t'; p++);
+ if (!*p)
+ break;
+ if (*p == '\n') {
+ p++;
+ continue;
+ }
+ if (*p == '#') {
+ for (p++; *p != '\n'; p++)
+ if (!*p)
+ goto bail;
+ p++;
+ continue;
+ }
+ if (*p == '[') {
+ cb = ++p;
+ for (; *p != ']'; p++)
+ if (!*p || *p == '\n') /* missing ] */
+ goto bail;
+ if (!ReStrN( &section, cb, p - cb ))
+ break;
+ p++;
+ } else {
+ cb = p;
+ for (; *p != '='; p++)
+ if (!*p || *p == '\n') /* missing = */
+ goto bail;
+ for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--);
+ if (!StrNDup( &key, cb, ce - cb ))
+ break;
+ for (p++; *p == ' ' || *p == '\t'; p++);
+ cb = p;
+ for (; *p && *p != '\n'; p++);
+ for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--);
+ if (!StrNDup( &value, cb, ce - cb ))
+ break;
+ if (section)
+ data = iniEntry( data, section, key, value );
+ free( value );
+ free( key );
+ }
+ }
+ bail:
+ if (section)
+ free( section );
+ return data;
+}
diff --git a/tdm/backend/krb5auth.c b/tdm/backend/krb5auth.c
new file mode 100644
index 000000000..16b640a35
--- /dev/null
+++ b/tdm/backend/krb5auth.c
@@ -0,0 +1,248 @@
+/*
+
+Copyright 1994, 1998 The Open Group
+Copyright 2003 Oswald Buddenhagen <[email protected]>
+
+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: Stephen Gildea, The Open Group
+ *
+ * generate Kerberos Version 5 authorization records
+ */
+
+#include <config.h>
+
+#ifdef K5AUTH
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <krb5/krb5.h>
+
+static krb5_context ctx;
+
+/*ARGSUSED*/
+void
+Krb5InitAuth( unsigned short name_len ATTR_UNUSED, const char *name ATTR_UNUSED )
+{
+ if (krb5_init_context( &ctx ))
+ LogError( "Error while initializing Krb5 context\n" );
+}
+
+/*
+ * Returns malloc'ed string that is the credentials cache name.
+ * name should be freed by caller.
+ */
+static char *
+Krb5CCacheName( const char *dname )
+{
+ char *name;
+ const char *tmpdir;
+ int dnl, nl;
+
+ tmpdir = getenv( "TMPDIR" );
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ dnl = strlen( dname );
+ name = Malloc( strlen( tmpdir ) + dnl + 20 );
+ if (!name)
+ return NULL;
+ nl = sprintf( name, "FILE:%s/K5C", tmpdir );
+ CleanUpFileName( dname, name + nl, dnl + 1 );
+ return name;
+}
+
+Xauth *
+Krb5GetAuthFor( unsigned short namelen, const char *name, const char *dname )
+{
+ Xauth *new;
+ char *filename;
+
+ if (!(new = (Xauth *)Malloc( sizeof(*new) )))
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+
+ if (dname) {
+ if (!(filename = Krb5CCacheName( dname ))) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->data = 0;
+ if (!StrApp( &new->data, "UU:", filename, (char *)0 )) {
+ free( filename );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ free( filename );
+ new->data_length = strlen( new->data );
+ } else {
+ new->data = NULL;
+ new->data_length = 0;
+ }
+
+ if (!(new->name = (char *)Malloc( namelen ))) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( new->name, name, namelen );
+ new->name_length = namelen;
+ return new;
+}
+
+
+Xauth *
+Krb5GetAuth( unsigned short namelen, const char *name )
+{
+ return Krb5GetAuthFor( namelen, name, NULL );
+}
+
+
+static krb5_error_code
+Krb5DisplayCCache( const char *dname, krb5_ccache *ccache_return, char **name )
+{
+ char *ccname;
+ krb5_error_code code;
+
+ if (!(ccname = Krb5CCacheName( dname )))
+ return ENOMEM;
+ Debug( "resolving Kerberos cache %s\n", ccname );
+ if ((code = krb5_cc_resolve( ctx, ccname, ccache_return )) || !name)
+ free( ccname );
+ else
+ *name = ccname;
+ return code;
+}
+
+char *
+Krb5Init( const char *user, const char *passwd, const char *dname )
+{
+ krb5_error_code code;
+ krb5_get_init_creds_opt options;
+ krb5_principal me;
+ krb5_creds my_creds;
+ krb5_ccache ccache;
+ char *ccname;
+
+ if (!ctx)
+ return 0;
+
+ if ((code = krb5_parse_name( ctx, user, &me ))) {
+ LogError( "%s while parsing Krb5 user %\"s\n",
+ error_message( code ), user );
+ return 0;
+ }
+
+ krb5_get_init_creds_opt_init( &options );
+ /*krb5_get_init_creds_opt_set_tkt_life (&options, 60*60*8);*/ /* 8 hours */
+
+ if ((code = krb5_get_init_creds_password( ctx, &my_creds,
+ me, /* principal */
+ (char * /* for MIT */) passwd,
+ 0, /* prompter */
+ 0, /* prompter ctx */
+ 0, /* start time delta */
+ 0, /* service */
+ &options )))
+ {
+ char *my_name = NULL;
+ int code2 = krb5_unparse_name( ctx, me, &my_name );
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ LogError( "Password incorrect for Krb5 principal %\"s\n",
+ code2 ? user : my_name );
+ else
+ LogError( "%s while getting initial Krb5 credentials for %\"s\n",
+ error_message( code ), code2 ? user : my_name );
+ if (my_name)
+ free( my_name );
+ goto err3;
+ }
+
+ if ((code = Krb5DisplayCCache( dname, &ccache, &ccname ))) {
+ LogError( "%s while getting Krb5 ccache for %\"s\n",
+ error_message( code ), dname );
+ goto err2;
+ }
+
+ if ((code = krb5_cc_initialize( ctx, ccache, me ))) {
+ LogError( "%s while initializing Krb5 cache %\"s\n",
+ error_message( code ), ccname );
+ goto err1;
+ }
+
+ if ((code = krb5_cc_store_cred( ctx, ccache, &my_creds ))) {
+ LogError( "%s while storing Krb5 credentials to cache %\"s\n",
+ error_message( code ), ccname );
+ err1:
+ krb5_cc_close( ctx, ccache );
+ free( ccname );
+ err2:
+ krb5_free_cred_contents( ctx, &my_creds );
+ err3:
+ krb5_free_principal( ctx, me );
+ return 0;
+ }
+
+ krb5_cc_close( ctx, ccache );
+ krb5_free_cred_contents( ctx, &my_creds );
+ krb5_free_principal( ctx, me );
+ return ccname;
+}
+
+void
+Krb5Destroy( const char *dname )
+{
+ krb5_error_code code;
+ krb5_ccache ccache;
+
+ if (!ctx)
+ return;
+
+ if ((code = Krb5DisplayCCache( dname, &ccache, 0 )))
+ LogError( "%s while getting Krb5 ccache to destroy\n",
+ error_message( code ) );
+ else {
+ if ((code = krb5_cc_destroy( ctx, ccache ))) {
+ if (code == KRB5_FCC_NOFILE)
+ Debug( "no Kerberos ccache file found to destroy\n" );
+ else
+ LogError( "%s while destroying Krb5 credentials cache\n",
+ error_message( code ) );
+ } else
+ Debug( "kerberos ccache destroyed\n" );
+ }
+}
+
+#endif
diff --git a/tdm/backend/mitauth.c b/tdm/backend/mitauth.c
new file mode 100644
index 000000000..fd18d41df
--- /dev/null
+++ b/tdm/backend/mitauth.c
@@ -0,0 +1,87 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2003 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * generate authorization keys
+ * for MIT-MAGIC-COOKIE-1 type authorization
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+
+#define AUTH_DATA_LEN 16 /* bytes of authorization data */
+static char auth_name[256];
+
+void
+MitInitAuth( unsigned short name_len, const char *name )
+{
+ if (name_len > 256)
+ name_len = 256;
+ memmove( auth_name, name, name_len );
+}
+
+Xauth *
+MitGetAuth( unsigned short namelen, const char *name )
+{
+ Xauth *new;
+ new = (Xauth *)Malloc( sizeof(Xauth) );
+
+ if (!new)
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+
+ new->data = (char *)Malloc( AUTH_DATA_LEN );
+ if (!new->data) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->name = (char *)Malloc( namelen );
+ if (!new->name) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( (char *)new->name, name, namelen );
+ new->name_length = namelen;
+ if (!GenerateAuthData( new->data, AUTH_DATA_LEN )) {
+ free( (char *)new->name );
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->data_length = AUTH_DATA_LEN;
+ return new;
+}
diff --git a/tdm/backend/netaddr.c b/tdm/backend/netaddr.c
new file mode 100644
index 000000000..349a53528
--- /dev/null
+++ b/tdm/backend/netaddr.c
@@ -0,0 +1,219 @@
+/*
+
+Copyright 1991, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+
+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
+ *
+ * netaddr.c - Interpretation of XdmcpNetaddr object.
+ */
+
+#include "dm.h"
+#include "dm_socket.h"
+#include "dm_error.h"
+
+/* given an char *, returns the socket protocol family used,
+ e.g., AF_INET */
+
+int
+NetaddrFamily( char *netaddrp )
+{
+#ifdef STREAMSCONN
+ short family = *(short *)netaddrp;
+ return family;
+#else
+ return ((struct sockaddr *)netaddrp)->sa_family;
+#endif
+}
+
+
+/* given an char *, returns a pointer to the TCP/UDP port used
+ and sets *lenp to the length of the address
+ or 0 if not using TCP or UDP. */
+
+char *
+NetaddrPort( char *netaddrp, int *lenp )
+{
+#ifdef STREAMSCONN
+ *lenp = 2;
+ return netaddrp+2;
+#else
+ switch (NetaddrFamily( netaddrp ))
+ {
+ case AF_INET:
+ *lenp = 2;
+ return (char *)&(((struct sockaddr_in *)netaddrp)->sin_port);
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ *lenp = 2;
+ return (char *)&(((struct sockaddr_in6 *)netaddrp)->sin6_port);
+#endif
+ default:
+ *lenp = 0;
+ return NULL;
+ }
+#endif
+}
+
+
+/* given an char *, returns a pointer to the network address
+ and sets *lenp to the length of the address */
+
+char *
+NetaddrAddress( char *netaddrp, int *lenp )
+{
+#ifdef STREAMSCONN
+ *lenp = 4;
+ return netaddrp+4;
+#else
+ switch (NetaddrFamily( netaddrp )) {
+#ifdef UNIXCONN
+ case AF_UNIX:
+ *lenp = strlen( ((struct sockaddr_un *)netaddrp)->sun_path );
+ return (char *)(((struct sockaddr_un *)netaddrp)->sun_path);
+#endif
+#ifdef TCPCONN
+ case AF_INET:
+ *lenp = sizeof(struct in_addr);
+ return (char *)&(((struct sockaddr_in *)netaddrp)->sin_addr);
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct in6_addr *a = &(((struct sockaddr_in6 *)netaddrp)->sin6_addr);
+ if (IN6_IS_ADDR_V4MAPPED( a )) {
+ *lenp = sizeof(struct in_addr);
+ return ((char *)&(a->s6_addr))+12;
+ } else {
+ *lenp = sizeof(struct in6_addr);
+ return (char *)&(a->s6_addr);
+ }
+ }
+#endif
+#endif
+#ifdef DNETCONN
+ case AF_DECnet:
+ *lenp = sizeof(struct dn_naddr);
+ return (char *)&(((struct sockaddr_dn *)netaddrp)->sdn_add);
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+#endif
+ default:
+ *lenp = 0;
+ return NULL;
+ }
+#endif /* STREAMSCONN else */
+}
+
+
+/* given an char *, sets *addr to the network address used and
+ sets *len to the number of bytes in addr.
+ Returns the X protocol family used, e.g., FamilyInternet */
+
+int
+ConvertAddr( char *saddr, int *len, char **addr )
+{
+ int retval;
+
+ if (len == NULL)
+ return -1;
+ *addr = NetaddrAddress( saddr, len );
+#ifdef STREAMSCONN
+ /* kludge */
+ if (NetaddrFamily( saddr ) == 2)
+ retval = FamilyInternet;
+#else
+ switch (NetaddrFamily( saddr )) {
+#ifdef AF_UNSPEC
+ case AF_UNSPEC:
+ retval = FamilyLocal;
+ break;
+#endif
+#ifdef AF_UNIX
+#ifndef __hpux
+ case AF_UNIX:
+ retval = FamilyLocal;
+ break;
+#endif
+#endif
+#ifdef TCPCONN
+ case AF_INET:
+ retval = FamilyInternet;
+ break;
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ if (*len == sizeof(struct in_addr))
+ retval = FamilyInternet;
+ else
+ retval = FamilyInternet6;
+ break;
+#endif
+#endif
+#ifdef DNETCONN
+ case AF_DECnet:
+ retval = FamilyDECnet;
+ break;
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+ retval = FamilyChaos;
+ break;
+#endif
+ default:
+ retval = -1;
+ break;
+ }
+#endif /* STREAMSCONN else */
+ Debug( "ConvertAddr returning %d for family %d\n", retval,
+ NetaddrFamily( saddr ) );
+ return retval;
+}
+
+#ifdef XDMCP
+int
+addressEqual( char *a1, int len1, char *a2, int len2 )
+{
+ int partlen1, partlen2;
+ char *part1, *part2;
+
+ if (len1 != len2)
+ return FALSE;
+ if (NetaddrFamily( a1 ) != NetaddrFamily( a2 ))
+ return FALSE;
+ part1 = NetaddrPort( a1, &partlen1 );
+ part2 = NetaddrPort( a2, &partlen2 );
+ if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0)
+ return FALSE;
+ part1 = NetaddrAddress( a1, &partlen1 );
+ part2 = NetaddrAddress( a2, &partlen2 );
+ if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0)
+ return FALSE;
+ return TRUE;
+}
+#endif
diff --git a/tdm/backend/policy.c b/tdm/backend/policy.c
new file mode 100644
index 000000000..cabee7088
--- /dev/null
+++ b/tdm/backend/policy.c
@@ -0,0 +1,278 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * policy.c. Implement site-dependent policy for XDMCP connections
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_socket.h"
+
+static ARRAY8 noAuthentication = { (CARD16)0, (CARD8Ptr) 0 };
+
+typedef struct _XdmAuth {
+ ARRAY8 authentication;
+ ARRAY8 authorization;
+} XdmAuthRec, *XdmAuthPtr;
+
+static XdmAuthRec auth[] = {
+#ifdef HASXDMAUTH
+{ {(CARD16)20, (CARD8 *)"XDM-AUTHENTICATION-1"},
+ {(CARD16)19, (CARD8 *)"XDM-AUTHORIZATION-1"},
+},
+#endif
+{ {(CARD16)0, (CARD8 *)0},
+ {(CARD16)0, (CARD8 *)0},
+}
+};
+
+#define NumAuth as(auth)
+
+ARRAY8Ptr
+ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames )
+{
+ int i, j;
+
+ for (i = 0; i < (int)authenticationNames->length; i++)
+ for (j = 0; j < NumAuth; j++)
+ if (XdmcpARRAY8Equal( &authenticationNames->data[i],
+ &auth[j].authentication ))
+ return &authenticationNames->data[i];
+ return &noAuthentication;
+}
+
+int
+CheckAuthentication(
+ struct protoDisplay *pdpy ATTR_UNUSED,
+ ARRAY8Ptr displayID ATTR_UNUSED,
+ ARRAY8Ptr name ATTR_UNUSED,
+ ARRAY8Ptr data ATTR_UNUSED )
+{
+#ifdef HASXDMAUTH
+ if (name->length && !memcmp( (char *)name->data, "XDM-AUTHENTICATION-1", 20 ))
+ return XdmCheckAuthentication( pdpy, displayID, name, data );
+#endif
+ return TRUE;
+}
+
+int
+SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName,
+ ARRAYofARRAY8Ptr authorizationNames )
+{
+ int i, j;
+
+ for (j = 0; j < NumAuth; j++)
+ if (XdmcpARRAY8Equal( authenticationName,
+ &auth[j].authentication ))
+ break;
+ if (j < NumAuth)
+ for (i = 0; i < (int)authorizationNames->length; i++)
+ if (XdmcpARRAY8Equal( &authorizationNames->data[i],
+ &auth[j].authorization ))
+ return i;
+ for (i = 0; i < (int)authorizationNames->length; i++)
+ if (ValidAuthorization( authorizationNames->data[i].length,
+ (char *)authorizationNames->data[i].data ))
+ return i;
+ return -1;
+}
+
+
+/*#define WILLING_INTERNAL*/
+
+#ifdef WILLING_INTERNAL
+/* Report the loadavg to chooser. Nice feature ...
+ *
+ * Wed Mar 10 1999 -- Steffen Hansen
+ */
+static void
+Willing_msg( char *mbuf )
+{
+#ifdef __linux__
+ int fd;
+ int numcpu;
+ const char *fail_msg = "Willing to manage";
+ FILE *f;
+ float load[3];
+ float mhz = 0.0;
+ char buf[1024];
+
+ fd = open( "/proc/loadavg", O_RDONLY );
+ if (fd == -1) {
+ sprintf( mbuf, fail_msg );
+ return;
+ } else if (read( fd, buf, 100 ) < 4) {
+ close( fd );
+ sprintf( mbuf, fail_msg );
+ return;
+ }
+ close( fd );
+
+ sscanf( buf, "%f %f %f", &load[0], &load[1], &load[2] );
+ sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)",
+ load[0], load[1], load[2] );
+
+ numcpu = 0;
+
+ if (!(f = fopen( "/proc/cpuinfo", "r" )))
+ return;
+
+ while (fGets( buf, sizeof(buf), f ) != -1) {
+ float m;
+ if (sscanf( buf, "cpu MHz : %f", &m )) {
+ numcpu++;
+ mhz = m;
+ }
+ }
+
+ fclose( f );
+
+ if (numcpu) {
+ if (numcpu > 1)
+ sprintf( buf, " %d*%0.0f MHz", numcpu, mhz );
+ else
+ sprintf( buf, " %0.0f MHz", mhz );
+
+ strncat( mbuf, buf, 256 );
+
+ mbuf[255] = 0;
+ }
+#elif HAVE_GETLOADAVG /* !__linux__ */
+#ifdef __GNUC__
+# warning This code is untested...
+#endif
+ double load[3];
+ getloadavg( load, 3 );
+ sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)", load[0],
+ load[1], load[2] );
+#else /* !__linux__ && !GETLOADAVG */
+ strcpy( mbuf, "Willing to manage" );
+#endif
+}
+#endif
+
+/*ARGSUSED*/
+int
+Willing( ARRAY8Ptr addr, CARD16 connectionType,
+ ARRAY8Ptr authenticationName ATTR_UNUSED,
+ ARRAY8Ptr status, xdmOpCode type )
+{
+ int ret;
+ char statusBuf[256];
+ static time_t lastscan;
+
+ if (autoRescan && lastscan + 15 < now) {
+ lastscan = now;
+ ScanAccessDatabase( FALSE );
+ }
+ ret = AcceptableDisplayAddress( addr, connectionType, type );
+ if (!ret)
+ sprintf( statusBuf, "Display not authorized to connect" );
+ else {
+ if (*willing) {
+ FILE *fd;
+ int len, ok = 0;
+ if ((fd = popen( willing, "r" ))) {
+ for (;;) {
+ if ((len = fGets( statusBuf, sizeof(statusBuf), fd )) != -1) {
+ if (len) {
+ ok = 1;
+ break;
+ }
+ }
+ if (feof( fd ) || errno != EINTR)
+ break;
+ }
+ pclose( fd );
+ }
+ if (!ok)
+ sprintf( statusBuf, "Willing, but %.*s failed",
+ sizeof(statusBuf) - 21, willing );
+ } else
+#ifdef WILLING_INTERNAL
+ Willing_msg( statusBuf );
+#else
+ strcpy( statusBuf, "Willing to manage" );
+#endif
+ }
+ status->length = strlen( statusBuf );
+ status->data = (CARD8Ptr) Malloc( status->length );
+ if (!status->data)
+ status->length = 0;
+ else
+ memmove( status->data, statusBuf, status->length );
+ return ret;
+}
+
+/*ARGSUSED*/
+ARRAY8Ptr
+Accept( struct sockaddr *from ATTR_UNUSED, int fromlen ATTR_UNUSED,
+ CARD16 displayNumber ATTR_UNUSED )
+{
+ return 0;
+}
+
+/*ARGSUSED*/
+int
+SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes,
+ ARRAYofARRAY8Ptr connectionAddresses ATTR_UNUSED )
+{
+ int i;
+
+ /*
+ * Select one supported connection type
+ */
+
+ for (i = 0; i < connectionTypes->length; i++) {
+ switch (connectionTypes->data[i]) {
+ case FamilyLocal:
+#if defined(TCPCONN)
+ case FamilyInternet:
+# if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+# endif /* IPv6 */
+#endif /* TCPCONN */
+#if defined(DNETCONN)
+ case FamilyDECnet:
+#endif /* DNETCONN */
+ return i;
+ }
+ } /* for */
+ return -1;
+}
+
+#endif /* XDMCP */
diff --git a/tdm/backend/printf.c b/tdm/backend/printf.c
new file mode 100644
index 000000000..90780d510
--- /dev/null
+++ b/tdm/backend/printf.c
@@ -0,0 +1,872 @@
+/*
+
+Copyright 2001,2002,2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * printf.c - working horse of error.c
+ */
+
+/*
+ * NOTE: this file is meant to be included, not linked,
+ * so it can be used in the helper programs without much voodoo.
+ */
+
+/* ########## printf core implementation with some extensions ########## */
+/*
+ * How to use the extensions:
+ * - put ' or " in the flags field to quote a string with this char and
+ * escape special characters (only available, if PRINT_QUOTES is defined)
+ * - put \\ in the flags field to quote special characters and leading and
+ * trailing spaces (only available, if PRINT_QUOTES is defined)
+ * - arrays (only available, if PRINT_ARRAYS is defined)
+ * - the array modifier [ comes after the maximal field width specifier
+ * - the array length can be specified literally, with the '*' modifier
+ * (in which case an argument is expected) or will be automatically
+ * determined (stop values are -1 for ints and 0 for strings)
+ * - these modifiers expect their argument to be an in-line string quoted
+ * with an arbitrary character:
+ * - (, ) -> array pre-/suf-fix; default ""
+ * - <, > -> element pre-/suf-fix; default ""
+ * - | -> element separator; default " "
+ * - these modifiers expect no argument:
+ * - : -> print '<number of elements>: ' before an array
+ * - , -> short for |','
+ * - { -> short for ('{')' }'<' '|''
+ * - the pointer to the array is the last argument to the format
+ * - the %m conversion from syslog() is supported
+ */
+
+/**************************************************************
+ * Partially stolen from OpenSSH's OpenBSD compat directory.
+ * (C) Patrick Powell, Brandon Long, Thomas Roessler,
+ * Michael Elkins, Ben Lindstrom
+ **************************************************************/
+
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UPCASE (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+#define DP_F_SQUOTE (1 << 7)
+#define DP_F_DQUOTE (1 << 8)
+#define DP_F_BACKSL (1 << 9)
+#define DP_F_ARRAY (1 << 10)
+#define DP_F_COLON (1 << 11)
+
+/* Conversion Flags */
+#define DP_C_INT 0
+#define DP_C_BYTE 1
+#define DP_C_SHORT 2
+#define DP_C_LONG 3
+#define DP_C_STR 10
+
+typedef void (*OutCh)( void *bp, char c );
+
+
+static void
+fmtint( OutCh dopr_outch, void *bp,
+ long value, int base, int min, int max, int flags )
+{
+ const char *ctab;
+ unsigned long uvalue;
+ int signvalue = 0;
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ char convert[20];
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if (!(flags & DP_F_UNSIGNED)) {
+ if (value < 0) {
+ signvalue = '-';
+ uvalue = -value;
+ } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+
+ ctab = (flags & DP_F_UPCASE) ? "0123456789ABCDEF" : "0123456789abcdef";
+ do {
+ convert[place++] = ctab[uvalue % (unsigned)base];
+ uvalue = uvalue / (unsigned)base;
+ } while (uvalue);
+
+ zpadlen = max - place;
+ spadlen = min - (max > place ? max : place) -
+ (signvalue ? 1 : 0) - ((flags & DP_F_NUM) ? 2 : 0);
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (spadlen < 0)
+ spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = zpadlen > spadlen ? zpadlen : spadlen;
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch( bp, ' ' );
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch( bp, signvalue );
+
+ /* Prefix */
+ if (flags & DP_F_NUM) {
+ dopr_outch( bp, '0' );
+ dopr_outch( bp, 'x' );
+ }
+
+ /* Zeros */
+ if (zpadlen > 0)
+ while (zpadlen > 0) {
+ dopr_outch( bp, '0' );
+ --zpadlen;
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch( bp, convert[--place] );
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch( bp, ' ' );
+ ++spadlen;
+ }
+}
+
+typedef struct {
+ const char *str;
+ size_t len;
+} str_t;
+
+static void
+putstr( OutCh dopr_outch, void *bp, str_t *st )
+{
+ size_t pt;
+
+ for (pt = 0; pt < st->len; pt++)
+ dopr_outch( bp, st->str[pt] );
+}
+
+static str_t _null_parents = { "(null)", 6 };
+#ifdef PRINT_ARRAYS
+static str_t _null_dparents = { "((null))", 8 };
+#endif
+#if defined(PRINT_QUOTES) || defined(PRINT_ARRAYS)
+static str_t _null_caps = { "NULL", 4 };
+#endif
+
+static void
+fmtstr( OutCh dopr_outch, void *bp,
+ const char *value, int flags, int min, int max )
+{
+ int padlen, strln, curcol;
+#ifdef PRINT_QUOTES
+ int lastcol = 0;
+#endif
+ char ch;
+
+ if (!value) {
+#ifdef PRINT_QUOTES
+ if (flags & (DP_F_SQUOTE | DP_F_DQUOTE))
+ putstr( dopr_outch, bp, &_null_caps );
+ else
+#endif
+ putstr( dopr_outch, bp, &_null_parents );
+ return;
+ }
+
+ for (strln = 0; (unsigned)strln < (unsigned)max && value[strln]; strln++);
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ for (; padlen > 0; padlen--)
+ dopr_outch( bp, ' ' );
+#ifdef PRINT_QUOTES
+# if 0 /* gcc's flow analyzer is not the smartest ... */
+ lastcol = 0;
+# endif
+ if (flags & DP_F_SQUOTE)
+ dopr_outch( bp, '\'' );
+ else if (flags & DP_F_DQUOTE)
+ dopr_outch( bp, '"');
+ else if (flags & DP_F_BACKSL)
+ for (lastcol = strln; lastcol && value[lastcol - 1] == ' '; lastcol--);
+#endif
+ for (curcol = 0; curcol < strln; curcol++) {
+ ch = value[curcol];
+#ifdef PRINT_QUOTES
+ if (flags & (DP_F_SQUOTE | DP_F_DQUOTE | DP_F_BACKSL)) {
+ switch (ch) {
+ case '\r': ch = 'r'; break;
+ case '\n': ch = 'n'; break;
+ case '\t': ch = 't'; break;
+ case '\a': ch = 'a'; break;
+ case '\b': ch = 'b'; break;
+ case '\v': ch = 'v'; break;
+ case '\f': ch = 'f'; break;
+ default:
+ if (ch < 32 ||
+ ((unsigned char)ch >= 0x7f && (unsigned char)ch < 0xa0))
+ {
+ dopr_outch( bp, '\\' );
+ fmtint( dopr_outch, bp, (unsigned char)ch, 8, 3, 3, DP_F_ZERO );
+ continue;
+ } else {
+ if ((ch == '\'' && (flags & DP_F_SQUOTE)) ||
+ (ch == '"' && (flags & DP_F_DQUOTE) ) ||
+ (ch == ' ' && (flags & DP_F_BACKSL) &&
+ (!curcol || curcol >= lastcol)) ||
+ ch == '\\')
+ dopr_outch( bp, '\\' );
+ dopr_outch( bp, ch );
+ continue;
+ }
+ }
+ dopr_outch( bp, '\\' );
+ }
+#endif
+ dopr_outch( bp, ch );
+ }
+#ifdef PRINT_QUOTES
+ if (flags & DP_F_SQUOTE)
+ dopr_outch( bp, '\'' );
+ else if (flags & DP_F_DQUOTE)
+ dopr_outch( bp, '"' );
+#endif
+ for (; padlen < 0; padlen++)
+ dopr_outch( bp, ' ' );
+}
+
+static void
+DoPr( OutCh dopr_outch, void *bp, const char *format, va_list args )
+{
+ const char *strvalue;
+#ifdef PRINT_ARRAYS
+ str_t arpr, arsf, arepr, aresf, aresp, *arp;
+ void *arptr;
+#endif
+ unsigned long value;
+ int radix = 0, min, max, flags, cflags, errn;
+#ifdef PRINT_ARRAYS
+ int arlen = 0;
+ unsigned aridx;
+ char sch;
+#endif
+ char ch;
+#define NCHR if (!(ch = *format++)) return
+
+#if 0 /* gcc's flow analyzer is not the smartest ... */
+# ifdef PRINT_ARRAYS
+ arlen = 0;
+# endif
+ radix = 0;
+#endif
+ errn = errno;
+ for (;;) {
+ for (;;) {
+ NCHR;
+ if (ch == '%')
+ break;
+ dopr_outch (bp, ch);
+ }
+ flags = cflags = min = 0;
+ max = -1;
+ for (;;) {
+ NCHR;
+ switch (ch) {
+ case '#': flags |= DP_F_NUM; continue;
+ case '-': flags |= DP_F_MINUS; continue;
+ case '+': flags |= DP_F_PLUS; continue;
+ case ' ': flags |= DP_F_SPACE; continue;
+ case '0': flags |= DP_F_ZERO; continue;
+#ifdef PRINT_QUOTES
+ case '"': flags |= DP_F_DQUOTE; continue;
+ case '\'': flags |= DP_F_SQUOTE; continue;
+ case '\\': flags |= DP_F_BACKSL; continue;
+#endif
+ }
+ break;
+ }
+ for (;;) {
+ if (isdigit( (unsigned char)ch )) {
+ min = 10 * min + (ch - '0');
+ NCHR;
+ continue;
+ } else if (ch == '*') {
+ min = va_arg( args, int );
+ NCHR;
+ }
+ break;
+ }
+ if (ch == '.') {
+ max = 0;
+ for (;;) {
+ NCHR;
+ if (isdigit( (unsigned char)ch )) {
+ max = 10 * max + (ch - '0');
+ continue;
+ } else if (ch == '*') {
+ max = va_arg( args, int );
+ NCHR;
+ }
+ break;
+ }
+ }
+#ifdef PRINT_ARRAYS
+ if (ch == '[') {
+ flags |= DP_F_ARRAY;
+ arlen = -1;
+ arpr.len = arsf.len = arepr.len = aresf.len = 0;
+ aresp.len = 1, aresp.str = " ";
+ for (;;) {
+ NCHR;
+ if (isdigit( (unsigned char)ch )) {
+ arlen = 0;
+ for (;;) {
+ arlen += (ch - '0');
+ NCHR;
+ if (!isdigit( (unsigned char)ch ))
+ break;
+ arlen *= 10;
+ }
+ }
+ switch (ch) {
+ case ':': flags |= DP_F_COLON; continue;
+ case '*': arlen = va_arg( args, int ); continue;
+ case '(': arp = &arpr; goto rar;
+ case ')': arp = &arsf; goto rar;
+ case '<': arp = &arepr; goto rar;
+ case '>': arp = &aresf; goto rar;
+ case '|': arp = &aresp;
+ rar:
+ NCHR;
+ sch = ch;
+ arp->str = format;
+ do {
+ NCHR;
+ } while (ch != sch);
+ arp->len = format - arp->str - 1;
+ continue;
+ case ',':
+ aresp.len = 1, aresp.str = ",";
+ continue;
+ case '{':
+ aresp.len = 0, arpr.len = arepr.len = 1, arsf.len = 2;
+ arpr.str = "{", arepr.str = " ", arsf.str = " }";
+ continue;
+ }
+ break;
+ }
+ }
+#endif
+ for (;;) {
+ switch (ch) {
+ case 'h':
+ cflags = DP_C_SHORT;
+ NCHR;
+ if (ch == 'h') {
+ cflags = DP_C_BYTE;
+ NCHR;
+ }
+ continue;
+ case 'l':
+ cflags = DP_C_LONG;
+ NCHR;
+ continue;
+ }
+ break;
+ }
+ switch (ch) {
+ case '%':
+ dopr_outch( bp, ch );
+ break;
+ case 'm':
+ fmtstr( dopr_outch, bp, strerror( errn ), flags, min, max );
+ break;
+ case 'c':
+ dopr_outch( bp, va_arg( args, int ) );
+ break;
+ case 's':
+#ifdef PRINT_ARRAYS
+ cflags = DP_C_STR;
+ goto printit;
+#else
+ strvalue = va_arg( args, char * );
+ fmtstr( dopr_outch, bp, strvalue, flags, min, max );
+ break;
+#endif
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ case 'd':
+ case 'i':
+ radix = 10;
+ goto printit;
+ case 'X':
+ flags |= DP_F_UPCASE;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ radix = 16;
+ printit:
+#ifdef PRINT_ARRAYS
+ if (flags & DP_F_ARRAY) {
+ if (!(arptr = va_arg( args, void * )))
+ putstr( dopr_outch, bp,
+ arpr.len ? &_null_caps : &_null_dparents );
+ else {
+ if (arlen == -1) {
+ arlen = 0;
+ switch (cflags) {
+ case DP_C_STR: while (((char **)arptr)[arlen]) arlen++; break;
+ case DP_C_BYTE: while (((unsigned char *)arptr)[arlen] != (unsigned char)-1) arlen++; break;
+ case DP_C_SHORT: while (((unsigned short int *)arptr)[arlen] != (unsigned short int)-1) arlen++; break;
+ case DP_C_LONG: while (((unsigned long int *)arptr)[arlen] != (unsigned long int)-1) arlen++; break;
+ default: while (((unsigned int *)arptr)[arlen] != (unsigned int)-1) arlen++; break;
+ }
+ }
+ if (flags & DP_F_COLON) {
+ fmtint( dopr_outch, bp, (long)arlen, 10, 0, -1, DP_F_UNSIGNED );
+ dopr_outch( bp, ':' );
+ dopr_outch( bp, ' ' );
+ }
+ putstr( dopr_outch, bp, &arpr );
+ for (aridx = 0; aridx < (unsigned)arlen; aridx++) {
+ if (aridx)
+ putstr( dopr_outch, bp, &aresp );
+ putstr( dopr_outch, bp, &arepr );
+ if (cflags == DP_C_STR) {
+ strvalue = ((char **)arptr)[aridx];
+ fmtstr( dopr_outch, bp, strvalue, flags, min, max );
+ } else {
+ if (flags & DP_F_UNSIGNED) {
+ switch (cflags) {
+ case DP_C_BYTE: value = ((unsigned char *)arptr)[aridx]; break;
+ case DP_C_SHORT: value = ((unsigned short int *)arptr)[aridx]; break;
+ case DP_C_LONG: value = ((unsigned long int *)arptr)[aridx]; break;
+ default: value = ((unsigned int *)arptr)[aridx]; break;
+ }
+ } else {
+ switch (cflags) {
+ case DP_C_BYTE: value = ((signed char *)arptr)[aridx]; break;
+ case DP_C_SHORT: value = ((short int *)arptr)[aridx]; break;
+ case DP_C_LONG: value = ((long int *)arptr)[aridx]; break;
+ default: value = ((int *)arptr)[aridx]; break;
+ }
+ }
+ fmtint( dopr_outch, bp, value, radix, min, max, flags );
+ }
+ putstr( dopr_outch, bp, &aresf );
+ }
+ putstr( dopr_outch, bp, &arsf );
+ }
+ } else {
+ if (cflags == DP_C_STR) {
+ strvalue = va_arg( args, char * );
+ fmtstr( dopr_outch, bp, strvalue, flags, min, max );
+ } else {
+#endif
+ if (flags & DP_F_UNSIGNED) {
+ switch (cflags) {
+ case DP_C_LONG: value = va_arg( args, unsigned long int ); break;
+ default: value = va_arg( args, unsigned int ); break;
+ }
+ } else {
+ switch (cflags) {
+ case DP_C_LONG: value = va_arg( args, long int ); break;
+ default: value = va_arg( args, int ); break;
+ }
+ }
+ fmtint( dopr_outch, bp, value, radix, min, max, flags );
+#ifdef PRINT_ARRAYS
+ }
+ }
+#endif
+ break;
+ case 'p':
+ value = (long)va_arg( args, void * );
+ fmtint( dopr_outch, bp, value, 16, sizeof(long) * 2 + 2,
+ max, flags | DP_F_UNSIGNED | DP_F_ZERO | DP_F_NUM );
+ break;
+ }
+ }
+}
+
+/* ########## end of printf core implementation ########## */
+
+
+/*
+ * Logging function for xdm and helper programs.
+ */
+#ifndef NO_LOGGER
+
+#include <stdio.h>
+#include <time.h>
+
+#ifdef USE_SYSLOG
+# include <syslog.h>
+# ifdef LOG_NAME
+# define InitLog() openlog(LOG_NAME, LOG_PID, LOG_DAEMON)
+# else
+# define InitLog() openlog(prog, LOG_PID, LOG_DAEMON)
+# endif
+static int lognums[] = { LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT };
+#else
+# define InitLog() while(0)
+#endif
+
+static const char *lognams[] = { "debug", "info", "warning", "error", "panic" };
+
+static void
+logTime( char *dbuf )
+{
+ time_t tim;
+ (void)time( &tim );
+ strftime( dbuf, 20, "%b %e %H:%M:%S", localtime( &tim ) );
+}
+
+#if defined(LOG_DEBUG_MASK) || defined(USE_SYSLOG)
+STATIC int debugLevel;
+#endif
+
+#define OOMSTR "Out of memory. Expect problems.\n"
+
+STATIC void
+LogOutOfMem( void )
+{
+ static time_t last;
+ time_t tnow;
+
+ time( &tnow );
+ if (last + 100 > tnow) { /* don't log bursts */
+ last = tnow;
+ return;
+ }
+ last = tnow;
+#ifdef USE_SYSLOG
+ if (!(debugLevel & DEBUG_NOSYSLOG))
+ syslog( LOG_CRIT, OOMSTR );
+ else
+#endif
+ {
+ int el;
+ char dbuf[24], sbuf[128];
+ logTime( dbuf );
+ el = sprintf( sbuf, "%s "
+#ifdef LOG_NAME
+ LOG_NAME "[%ld]: " OOMSTR, dbuf,
+#else
+ "%s[%ld]: " OOMSTR, dbuf, prog,
+#endif
+ (long)getpid() );
+ write( 2, sbuf, el );
+ }
+}
+
+typedef struct {
+ char *buf;
+ int clen, blen, type;
+ char lmbuf[128];
+} OCLBuf;
+
+static void
+OutChLFlush( OCLBuf *oclbp )
+{
+ if (oclbp->clen) {
+#ifdef USE_SYSLOG
+ if (!(debugLevel & DEBUG_NOSYSLOG))
+ syslog( lognums[oclbp->type], "%.*s", oclbp->clen, oclbp->buf );
+ else
+#endif
+ {
+ oclbp->buf[oclbp->clen] = '\n';
+ write( 2, oclbp->buf, oclbp->clen + 1 );
+ }
+ oclbp->clen = 0;
+ }
+}
+
+static void
+OutChL( void *bp, char c )
+{
+ OCLBuf *oclbp = (OCLBuf *)bp;
+ char *nbuf;
+ int nlen;
+
+ if (c == '\n')
+ OutChLFlush( oclbp );
+ else {
+ if (oclbp->clen >= oclbp->blen - 1) {
+ if (oclbp->buf == oclbp->lmbuf) {
+ OutChLFlush( oclbp );
+ oclbp->buf = 0;
+ oclbp->blen = 0;
+ }
+ nlen = oclbp->blen * 3 / 2 + 128;
+ nbuf = Realloc( oclbp->buf, nlen );
+ if (nbuf) {
+ oclbp->buf = nbuf;
+ oclbp->blen = nlen;
+ } else {
+ OutChLFlush( oclbp );
+ oclbp->buf = oclbp->lmbuf;
+ oclbp->blen = sizeof(oclbp->lmbuf);
+ }
+ }
+#ifdef USE_SYSLOG
+ if (!oclbp->clen && (debugLevel & DEBUG_NOSYSLOG)) {
+#else
+ if (!oclbp->clen) {
+#endif
+ char dbuf[24];
+ logTime( dbuf );
+ oclbp->clen = sprintf( oclbp->buf, "%s "
+#ifdef LOG_NAME
+ LOG_NAME "[%ld] %s: ", dbuf,
+#else
+ "%s[%ld] %s: ", dbuf, prog,
+#endif
+ (long)getpid(), lognams[oclbp->type] );
+ }
+ oclbp->buf[oclbp->clen++] = c;
+ }
+}
+
+static void
+Logger( int type, const char *fmt, va_list args )
+{
+ OCLBuf oclb;
+
+ oclb.buf = 0;
+ oclb.blen = oclb.clen = 0;
+ oclb.type = type;
+ DoPr( OutChL, &oclb, fmt, args );
+ /* no flush, every message is supposed to be \n-terminated */
+ if (oclb.buf && oclb.buf != oclb.lmbuf)
+ free( oclb.buf );
+}
+
+#ifdef LOG_DEBUG_MASK
+STATIC void
+Debug( const char *fmt, ... )
+{
+ if (debugLevel & LOG_DEBUG_MASK) {
+ va_list args;
+ int olderrno = errno;
+ va_start( args, fmt );
+ Logger( DM_DEBUG, fmt, args );
+ va_end( args );
+ errno = olderrno;
+ }
+}
+#endif
+
+#ifndef LOG_NO_INFO
+STATIC void
+LogInfo( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_INFO, fmt, args );
+ va_end( args );
+}
+#endif
+
+#ifndef LOG_NO_WARN
+STATIC void
+LogWarn( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_WARN, fmt, args );
+ va_end( args );
+}
+#endif
+
+#ifndef LOG_NO_ERROR
+STATIC void
+LogError( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_ERR, fmt, args );
+ va_end( args );
+}
+#endif
+
+#ifdef LOG_PANIC_EXIT
+STATIC void
+LogPanic( const char *fmt, ... )
+{
+ va_list args;
+
+ va_start( args, fmt );
+ Logger( DM_PANIC, fmt, args );
+ va_end( args );
+ exit( LOG_PANIC_EXIT );
+}
+#endif
+
+#endif /* NO_LOGGER */
+
+#ifdef NEED_FDPRINTF
+
+typedef struct {
+ char *buf;
+ int clen, blen, tlen;
+} OCFBuf;
+
+static void
+OutCh_OCF( void *bp, char c )
+{
+ OCFBuf *ocfbp = (OCFBuf *)bp;
+ char *nbuf;
+ int nlen;
+
+ ocfbp->tlen++;
+ if (ocfbp->clen >= ocfbp->blen) {
+ if (ocfbp->blen < 0)
+ return;
+ nlen = ocfbp->blen * 3 / 2 + 100;
+ nbuf = Realloc( ocfbp->buf, nlen );
+ if (!nbuf) {
+ free( ocfbp->buf );
+ ocfbp->blen = -1;
+ ocfbp->buf = 0;
+ ocfbp->clen = 0;
+ return;
+ }
+ ocfbp->blen = nlen;
+ ocfbp->buf = nbuf;
+ }
+ ocfbp->buf[ocfbp->clen++] = c;
+}
+
+STATIC int
+FdPrintf( int fd, const char *fmt, ... )
+{
+ va_list args;
+ OCFBuf ocfb = { 0, 0, 0, -1 };
+
+ va_start( args, fmt );
+ DoPr( OutCh_OCF, &ocfb, fmt, args );
+ va_end( args );
+ if (ocfb.buf) {
+ Debug( "FdPrintf %\".*s to %d\n", ocfb.clen, ocfb.buf, fd );
+ (void)write( fd, ocfb.buf, ocfb.clen );
+ free( ocfb.buf );
+ }
+ return ocfb.tlen;
+}
+
+#endif /* NEED_FDPRINTF */
+
+#ifdef NEED_ASPRINTF
+
+typedef struct {
+ char *buf;
+ int clen, blen, tlen;
+} OCABuf;
+
+static void
+OutCh_OCA( void *bp, char c )
+{
+ OCABuf *ocabp = (OCABuf *)bp;
+ char *nbuf;
+ int nlen;
+
+ ocabp->tlen++;
+ if (ocabp->clen >= ocabp->blen) {
+ if (ocabp->blen < 0)
+ return;
+ nlen = ocabp->blen * 3 / 2 + 100;
+ nbuf = Realloc( ocabp->buf, nlen );
+ if (!nbuf) {
+ free( ocabp->buf );
+ ocabp->blen = -1;
+ ocabp->buf = 0;
+ ocabp->clen = 0;
+ return;
+ }
+ ocabp->blen = nlen;
+ ocabp->buf = nbuf;
+ }
+ ocabp->buf[ocabp->clen++] = c;
+}
+
+STATIC int
+VASPrintf( char **strp, const char *fmt, va_list args )
+{
+ OCABuf ocab = { 0, 0, 0, -1 };
+
+ DoPr( OutCh_OCA, &ocab, fmt, args );
+ OutCh_OCA( &ocab, 0 );
+ *strp = Realloc( ocab.buf, ocab.clen );
+ if (!*strp)
+ *strp = ocab.buf;
+ return ocab.tlen;
+}
+
+STATIC int
+ASPrintf( char **strp, const char *fmt, ... )
+{
+ va_list args;
+ int len;
+
+ va_start( args, fmt );
+ len = VASPrintf( strp, fmt, args );
+ va_end( args );
+ return len;
+}
+
+#endif /* NEED_ASPRINTF */
diff --git a/tdm/backend/process.c b/tdm/backend/process.c
new file mode 100644
index 000000000..f9d34fe7f
--- /dev/null
+++ b/tdm/backend/process.c
@@ -0,0 +1,762 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * subdaemon and external process management and communication
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <unistd.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+extern char **environ;
+
+
+SIGFUNC Signal( int sig, SIGFUNC handler )
+{
+#ifndef __EMX__
+ struct sigaction sigact, osigact;
+ sigact.sa_handler = handler;
+ sigemptyset( &sigact.sa_mask );
+# ifdef SA_RESTART
+ sigact.sa_flags = SA_RESTART;
+# else
+ sigact.sa_flags = 0;
+# endif
+ sigaction( sig, &sigact, &osigact );
+ return osigact.sa_handler;
+#else
+ return signal( sig, handler );
+#endif
+}
+
+
+void
+TerminateProcess( int pid, int sig )
+{
+ kill( pid, sig );
+#ifdef SIGCONT
+ kill( pid, SIGCONT );
+#endif
+}
+
+
+static FD_TYPE CloseMask;
+static int max = -1;
+
+void
+RegisterCloseOnFork( int fd )
+{
+ FD_SET( fd, &CloseMask );
+ if (fd > max)
+ max = fd;
+}
+
+void
+ClearCloseOnFork( int fd )
+{
+ FD_CLR( fd, &CloseMask );
+}
+
+void
+CloseNClearCloseOnFork( int fd )
+{
+ close( fd );
+ FD_CLR( fd, &CloseMask );
+}
+
+static void
+CloseOnFork( void )
+{
+ int fd;
+
+ for (fd = 0; fd <= max; fd++)
+ if (FD_ISSET( fd, &CloseMask ))
+ close( fd );
+ FD_ZERO( &CloseMask );
+ max = -1;
+}
+
+int
+Fork()
+{
+ int pid;
+
+ sigset_t ss, oss;
+ sigfillset( &ss );
+ sigprocmask( SIG_SETMASK, &ss, &oss );
+
+ if (!(pid = fork())) {
+#ifdef SIGCHLD
+ (void)Signal( SIGCHLD, SIG_DFL );
+#endif
+ (void)Signal( SIGTERM, SIG_DFL );
+ (void)Signal( SIGINT, SIG_IGN ); /* for -nodaemon */
+ (void)Signal( SIGPIPE, SIG_DFL );
+ (void)Signal( SIGALRM, SIG_DFL );
+ (void)Signal( SIGHUP, SIG_DFL );
+ sigemptyset( &ss );
+ sigprocmask( SIG_SETMASK, &ss, NULL );
+ CloseOnFork();
+ return 0;
+ }
+
+ sigprocmask( SIG_SETMASK, &oss, 0 );
+
+ return pid;
+}
+
+int
+Wait4( int pid )
+{
+ waitType result;
+
+ while (waitpid( pid, &result, 0 ) < 0)
+ if (errno != EINTR) {
+ Debug( "Wait4(%d) failed: %m\n", pid );
+ return 0;
+ }
+ return waitVal( result );
+}
+
+
+void
+execute( char **argv, char **env )
+{
+ Debug( "execute: %[s ; %[s\n", argv, env );
+ execve( argv[0], argv, env );
+ /*
+ * In case this is a shell script which hasn't been
+ * made executable (or this is a SYSV box), do
+ * a reasonable thing
+ */
+ if (errno != ENOENT) {
+ char **newargv;
+ FILE *f;
+ int nu;
+ char program[1024];
+
+ /*
+ * emulate BSD kernel behaviour -- read
+ * the first line; check if it starts
+ * with "#!", in which case it uses
+ * the rest of the line as the name of
+ * program to run. Else use "/bin/sh".
+ */
+ if (!(f = fopen( argv[0], "r" )))
+ return;
+ if (!fGets( program, sizeof(program), f )) {
+ fclose( f );
+ return;
+ }
+ fclose( f );
+ if (!strncmp( program, "#!", 2 ))
+ newargv = parseArgs( 0, program + 2 );
+ else
+ newargv = addStrArr( 0, "/bin/sh", 7 );
+ if (!newargv)
+ return;
+ nu = arrLen( newargv );
+ if (!(argv = xCopyStrArr( nu, argv )))
+ return;
+ memcpy( argv, newargv, sizeof(char *) * nu );
+ Debug( "shell script execution: %[s\n", argv );
+ execve( argv[0], argv, env );
+ }
+}
+
+int
+runAndWait( char **args, char **env )
+{
+ int pid, ret;
+
+ switch (pid = Fork()) {
+ case 0:
+ execute( args, env );
+ LogError( "Can't execute %\"s: %m\n", args[0] );
+ exit( 127 );
+ case -1:
+ LogError( "Can't fork to execute %\"s: %m\n", args[0] );
+ return 1;
+ }
+ ret = Wait4( pid );
+ return waitVal( ret );
+}
+
+FILE *
+pOpen( char **what, char m, int *pid )
+{
+ int dp[2];
+
+ if (pipe( dp ))
+ return 0;
+ switch ((*pid = Fork())) {
+ case 0:
+ if (m == 'r')
+ dup2( dp[1], 1 );
+ else
+ dup2( dp[0], 0 );
+ close( dp[0] );
+ close( dp[1] );
+ execute( what, environ );
+ LogError( "Can't execute %\"s: %m\n", what[0] );
+ exit( 127 );
+ case -1:
+ close( dp[0] );
+ close( dp[1] );
+ LogError( "Can't fork to execute %\"s: %m\n", what[0] );
+ return 0;
+ }
+ if (m == 'r') {
+ close( dp[1] );
+ return fdopen( dp[0], "r" );
+ } else {
+ close( dp[0] );
+ return fdopen( dp[1], "w" );
+ }
+}
+
+int
+pClose( FILE *f, int pid )
+{
+ fclose( f );
+ return Wait4( pid );
+}
+
+char *
+locate( const char *exe )
+{
+ int len;
+ char *path, *name, *thenam, nambuf[PATH_MAX+1];
+ char *pathe;
+
+ if (!(path = getenv( "PATH" ))) {
+ LogError( "Can't execute %'s: $PATH not set.\n", exe );
+ return 0;
+ }
+ len = strlen( exe );
+ name = nambuf + PATH_MAX - len;
+ memcpy( name, exe, len + 1 );
+ *--name = '/';
+ do {
+ if (!(pathe = strchr( path, ':' )))
+ pathe = path + strlen( path );
+ len = pathe - path;
+ if (len && !(len == 1 && *path == '.')) {
+ thenam = name - len;
+ if (thenam >= nambuf) {
+ memcpy( thenam, path, len );
+ if (!access( thenam, X_OK )) {
+ StrDup( &name, thenam );
+ return name;
+ }
+ }
+ }
+ path = pathe;
+ } while (*path++ != '\0');
+ LogError( "Can't execute %'s: not in $PATH.\n", exe );
+ return 0;
+}
+
+
+static GTalk *curtalk;
+
+void
+GSet( GTalk *tlk )
+{
+ curtalk = tlk;
+}
+
+int
+GFork( GPipe *pajp, const char *pname, char *cname,
+ GPipe *ogp, char *cgname )
+{
+ int opipe[2], ipipe[2], ogpipe[2], igpipe[2], pid;
+
+ if (pipe( opipe ))
+ goto badp1;
+ if (pipe( ipipe ))
+ goto badp2;
+ if (ogp) {
+ if (pipe( ogpipe ))
+ goto badp3;
+ if (pipe( igpipe )) {
+ close( ogpipe[0] );
+ close( ogpipe[1] );
+ badp3:
+ close( ipipe[0] );
+ close( ipipe[1] );
+ badp2:
+ close( opipe[0] );
+ close( opipe[1] );
+ badp1:
+ LogError( "Cannot start %s, pipe() failed", cname );
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ }
+ RegisterCloseOnFork( opipe[1] );
+ RegisterCloseOnFork( ipipe[0] );
+ if (ogp) {
+ RegisterCloseOnFork( ogpipe[1] );
+ RegisterCloseOnFork( igpipe[0] );
+ }
+ switch (pid = Fork()) {
+ case -1:
+ close( opipe[0] );
+ close( ipipe[1] );
+ CloseNClearCloseOnFork( opipe[1] );
+ CloseNClearCloseOnFork( ipipe[0] );
+ if (ogp) {
+ close( ogpipe[0] );
+ close( igpipe[1] );
+ CloseNClearCloseOnFork( ogpipe[1] );
+ CloseNClearCloseOnFork( igpipe[0] );
+ }
+ LogError( "Cannot start %s, fork() failed\n", cname );
+ if (cname)
+ free( cname );
+ return -1;
+ case 0:
+ pajp->wfd = ipipe[1];
+ RegisterCloseOnFork( ipipe[1] );
+ pajp->rfd = opipe[0];
+ RegisterCloseOnFork( opipe[0] );
+ pajp->who = (char *)pname;
+ if (ogp) {
+ ogp->wfd = igpipe[1];
+ RegisterCloseOnFork( igpipe[1] );
+ ogp->rfd = ogpipe[0];
+ RegisterCloseOnFork( ogpipe[0] );
+ ogp->who = (char *)pname;
+ }
+ break;
+ default:
+ close( opipe[0] );
+ close( ipipe[1] );
+ pajp->rfd = ipipe[0];
+ pajp->wfd = opipe[1];
+ pajp->who = cname;
+ if (ogp) {
+ close( ogpipe[0] );
+ close( igpipe[1] );
+ ogp->rfd = igpipe[0];
+ ogp->wfd = ogpipe[1];
+ ogp->who = cgname;
+ }
+ break;
+ }
+ return pid;
+}
+
+int
+GOpen( GProc *proc, char **argv, const char *what, char **env, char *cname,
+ GPipe *gp )
+{
+ char **margv;
+ int pip[2];
+ char coninfo[32];
+
+/* ### GSet (proc->pipe); */
+ if (proc->pid) {
+ LogError( "%s already running\n", cname );
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ if (!(margv = xCopyStrArr( 1, argv ))) {
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ if (!StrApp( margv, progpath, what, (char *)0 )) {
+ free( margv );
+ if (cname)
+ free( cname );
+ return -1;
+ }
+ if (pipe( pip )) {
+ LogError( "Cannot start %s, pipe() failed\n", cname );
+ if (cname)
+ free( cname );
+ goto fail;
+ }
+ if (gp) {
+ ClearCloseOnFork( gp->rfd );
+ ClearCloseOnFork( gp->wfd );
+ }
+ proc->pid = GFork( &proc->pipe, 0, cname, 0, 0 );
+ if (proc->pid) {
+ close( pip[1] );
+ if (gp) {
+ RegisterCloseOnFork( gp->rfd );
+ RegisterCloseOnFork( gp->wfd );
+ }
+ }
+ switch (proc->pid) {
+ case -1:
+ fail1:
+ close( pip[0] );
+ fail:
+ free( margv[0] );
+ free( margv );
+ return -1;
+ case 0:
+ (void)Signal( SIGPIPE, SIG_IGN );
+ close( pip[0] );
+ fcntl( pip[1], F_SETFD, FD_CLOEXEC );
+ if (gp)
+ sprintf( coninfo, "CONINFO=%d %d %d %d",
+ proc->pipe.rfd, proc->pipe.wfd, gp->rfd, gp->wfd );
+ else
+ sprintf( coninfo, "CONINFO=%d %d",
+ proc->pipe.rfd, proc->pipe.wfd );
+ env = putEnv( coninfo, env );
+ if (debugLevel & DEBUG_VALGRIND) {
+ char **nmargv = xCopyStrArr( 1, margv );
+ nmargv[0] = locate( "valgrind" );
+ execute( nmargv, env );
+ } else if (debugLevel & DEBUG_STRACE) {
+ char **nmargv = xCopyStrArr( 1, margv );
+ nmargv[0] = locate( "strace" );
+ execute( nmargv, env );
+ } else
+ execute( margv, env );
+ write( pip[1], "", 1 );
+ exit( 1 );
+ default:
+ (void)Signal( SIGPIPE, SIG_IGN );
+ if (Reader( pip[0], coninfo, 1 )) {
+ Wait4( proc->pid );
+ LogError( "Cannot execute %\"s (%s)\n", margv[0], cname );
+ GClosen (&proc->pipe);
+ goto fail1;
+ }
+ close( pip[0] );
+ Debug( "started %s (%\"s), pid %d\n", cname, margv[0], proc->pid );
+ free( margv[0] );
+ free( margv );
+ GSendInt( debugLevel );
+ return 0;
+ }
+}
+
+static void
+iGClosen( GPipe *pajp )
+{
+ CloseNClearCloseOnFork( pajp->rfd );
+ CloseNClearCloseOnFork( pajp->wfd );
+ pajp->rfd = pajp->wfd = -1;
+}
+
+void
+GClosen (GPipe *pajp)
+{
+ iGClosen( pajp );
+ if (pajp->who)
+ free( pajp->who );
+ pajp->who = 0;
+}
+
+int
+GClose (GProc *proc, GPipe *gp, int force)
+{
+ int ret;
+
+ if (!proc->pid) {
+ Debug( "whoops, GClose while helper not running\n" );
+ return 0;
+ }
+ iGClosen( &proc->pipe );
+ if (gp)
+ GClosen (gp);
+ if (force)
+ TerminateProcess( proc->pid, SIGTERM );
+ ret = Wait4( proc->pid );
+ proc->pid = 0;
+ if (WaitSig( ret ) ? WaitSig( ret ) != SIGTERM :
+ (WaitCode( ret ) < EX_NORMAL || WaitCode( ret ) > EX_MAX))
+ LogError( "Abnormal termination of %s, code %d, signal %d\n",
+ proc->pipe.who, WaitCode( ret ), WaitSig( ret ) );
+ Debug( "closed %s\n", proc->pipe.who );
+ if (proc->pipe.who)
+ free( proc->pipe.who );
+ proc->pipe.who = 0;
+ return ret;
+}
+
+static void ATTR_NORETURN
+GErr( void )
+{
+ Longjmp( curtalk->errjmp, 1 );
+}
+
+static void
+GRead( void *buf, int len )
+{
+ if (Reader( curtalk->pipe->rfd, buf, len ) != len) {
+ LogError( "Cannot read from %s\n", curtalk->pipe->who );
+ GErr();
+ }
+}
+
+static void
+GWrite( const void *buf, int len )
+{
+ if (Writer( curtalk->pipe->wfd, buf, len ) != len) {
+ LogError( "Cannot write to %s\n", curtalk->pipe->who );
+ GErr();
+ }
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ if ((debugLevel & DEBUG_HLPCON))
+ sched_yield();
+#endif
+}
+
+void
+GSendInt( int val )
+{
+ GDebug( "sending int %d (%#x) to %s\n", val, val, curtalk->pipe->who );
+ GWrite( &val, sizeof(val) );
+}
+
+int
+GRecvInt()
+{
+ int val;
+
+ GDebug( "receiving int from %s ...\n", curtalk->pipe->who );
+ GRead( &val, sizeof(val) );
+ GDebug( " -> %d (%#x)\n", val, val );
+ return val;
+}
+
+int
+GRecvCmd( int *cmd )
+{
+ GDebug( "receiving command from %s ...\n", curtalk->pipe->who );
+ if (Reader( curtalk->pipe->rfd, cmd, sizeof(*cmd) ) == sizeof(*cmd)) {
+ GDebug( " -> %d\n", *cmd );
+ return 1;
+ }
+ GDebug( " -> no data\n" );
+ return 0;
+}
+
+void
+GSendArr( int len, const char *data )
+{
+ GDebug( "sending array[%d] %02[*{hhx to %s\n",
+ len, len, data, curtalk->pipe->who );
+ GWrite( &len, sizeof(len) );
+ GWrite( data, len );
+}
+
+static char *
+iGRecvArr( int *rlen )
+{
+ int len;
+ char *buf;
+
+ GRead( &len, sizeof(len) );
+ *rlen = len;
+ GDebug( " -> %d bytes\n", len );
+ if (!len)
+ return (char *)0;
+ if (!(buf = Malloc( len )))
+ GErr();
+ GRead( buf, len );
+ return buf;
+}
+
+char *
+GRecvArr( int *rlen )
+{
+ char *buf;
+
+ GDebug( "receiving array from %s ...\n", curtalk->pipe->who );
+ buf = iGRecvArr( rlen );
+ GDebug( " -> %02[*{hhx\n", *rlen, buf );
+ return buf;
+}
+
+static int
+iGRecvArrBuf( char *buf )
+{
+ int len;
+
+ GRead( &len, sizeof(len) );
+ GDebug( " -> %d bytes\n", len );
+ if (len)
+ GRead( buf, len );
+ return len;
+}
+
+int
+GRecvArrBuf( char *buf )
+{
+ int len;
+
+ GDebug( "receiving already allocated array from %s ...\n",
+ curtalk->pipe->who );
+ len = iGRecvArrBuf( buf );
+ GDebug( " -> %02[*{hhx\n", len, buf );
+ return len;
+}
+
+int
+GRecvStrBuf( char *buf )
+{
+ int len;
+
+ GDebug( "receiving already allocated string from %s ...\n",
+ curtalk->pipe->who );
+ len = iGRecvArrBuf( buf );
+ GDebug( " -> %\".*s\n", len, buf );
+ return len;
+}
+
+void
+GSendStr( const char *buf )
+{
+ int len;
+
+ GDebug( "sending string %\"s to %s\n", buf, curtalk->pipe->who );
+ if (buf) {
+ len = strlen( buf ) + 1;
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+ } else
+ GWrite( &buf, sizeof(int) );
+}
+
+void
+GSendNStr( const char *buf, int len )
+{
+ int tlen = len + 1;
+ GDebug( "sending string %\".*s to %s\n", len, buf, curtalk->pipe->who );
+ GWrite( &tlen, sizeof(tlen) );
+ GWrite( buf, len );
+ GWrite( "", 1 );
+}
+
+void
+GSendStrN( const char *buf, int len )
+{
+ if (buf)
+ GSendNStr( buf, StrNLen( buf, len ) );
+ else
+ GSendStr( buf );
+}
+
+char *
+GRecvStr()
+{
+ int len;
+ char *buf;
+
+ GDebug( "receiving string from %s ...\n", curtalk->pipe->who );
+ buf = iGRecvArr( &len );
+ GDebug( " -> %\".*s\n", len, buf );
+ return buf;
+}
+
+static void
+iGSendStrArr( int num, char **data )
+{
+ char **cdata;
+
+ GWrite( &num, sizeof(num) );
+ for (cdata = data; --num >= 0; cdata++)
+ GSendStr( *cdata );
+}
+
+/*
+void
+GSendStrArr (int num, char **data)
+{
+ GDebug( "sending string array[%d] to %s\n", num, curtalk->pipe->who );
+ iGSendStrArr( num, data );
+}
+*/
+
+char **
+GRecvStrArr( int *rnum )
+{
+ int num;
+ char **argv, **cargv;
+
+ GDebug( "receiving string array from %s ...\n", curtalk->pipe->who );
+ GRead( &num, sizeof(num) );
+ GDebug( " -> %d strings\n", num );
+ *rnum = num;
+ if (!num)
+ return (char **)0;
+ if (!(argv = Malloc( num * sizeof(char *) )))
+ GErr();
+ for (cargv = argv; --num >= 0; cargv++)
+ *cargv = GRecvStr();
+ return argv;
+}
+
+void
+GSendArgv( char **argv )
+{
+ int num;
+
+ if (argv) {
+ for (num = 0; argv[num]; num++);
+ GDebug( "sending argv[%d] to %s ...\n", num, curtalk->pipe->who );
+ iGSendStrArr( num + 1, argv );
+ } else {
+ GDebug( "sending NULL argv to %s\n", curtalk->pipe->who );
+ GWrite( &argv, sizeof(int) );
+ }
+}
+
+char **
+GRecvArgv()
+{
+ int num;
+
+ return GRecvStrArr( &num );
+}
+
diff --git a/tdm/backend/protodpy.c b/tdm/backend/protodpy.c
new file mode 100644
index 000000000..08c38fbd1
--- /dev/null
+++ b/tdm/backend/protodpy.c
@@ -0,0 +1,141 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+
+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
+ *
+ * manage a collection of proto-displays. These are displays for
+ * which sessionID's have been generated, but no session has been
+ * started.
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+
+static struct protoDisplay *protoDisplays;
+
+struct protoDisplay *
+FindProtoDisplay(
+ XdmcpNetaddr address,
+ int addrlen,
+ CARD16 displayNumber )
+{
+ struct protoDisplay *pdpy;
+
+ Debug( "FindProtoDisplay\n" );
+ for (pdpy = protoDisplays; pdpy; pdpy=pdpy->next) {
+ if (pdpy->displayNumber == displayNumber &&
+ addressEqual( address, addrlen, pdpy->address, pdpy->addrlen ))
+ {
+ return pdpy;
+ }
+ }
+ return (struct protoDisplay *)0;
+}
+
+static void
+TimeoutProtoDisplays (void)
+{
+ struct protoDisplay *pdpy, *next;
+
+ for (pdpy = protoDisplays; pdpy; pdpy = next) {
+ next = pdpy->next;
+ if (pdpy->date < (unsigned long)(now - PROTO_TIMEOUT))
+ DisposeProtoDisplay( pdpy );
+ }
+}
+
+struct protoDisplay *
+NewProtoDisplay( XdmcpNetaddr address, int addrlen, CARD16 displayNumber,
+ CARD16 connectionType, ARRAY8Ptr connectionAddress,
+ CARD32 sessionID )
+{
+ struct protoDisplay *pdpy;
+
+ Debug( "NewProtoDisplay\n" );
+ TimeoutProtoDisplays ();
+ pdpy = (struct protoDisplay *)Malloc( sizeof(*pdpy) );
+ if (!pdpy)
+ return NULL;
+ pdpy->address = (XdmcpNetaddr)Malloc( addrlen );
+ if (!pdpy->address) {
+ free( (char *)pdpy );
+ return NULL;
+ }
+ pdpy->addrlen = addrlen;
+ memmove( pdpy->address, address, addrlen );
+ pdpy->displayNumber = displayNumber;
+ pdpy->connectionType = connectionType;
+ pdpy->date = now;
+ if (!XdmcpCopyARRAY8( connectionAddress, &pdpy->connectionAddress )) {
+ free( (char *)pdpy->address );
+ free( (char *)pdpy );
+ return NULL;
+ }
+ pdpy->sessionID = sessionID;
+ pdpy->fileAuthorization = (Xauth *)NULL;
+ pdpy->xdmcpAuthorization = (Xauth *)NULL;
+ pdpy->next = protoDisplays;
+ protoDisplays = pdpy;
+ return pdpy;
+}
+
+void
+DisposeProtoDisplay( pdpy )
+ struct protoDisplay *pdpy;
+{
+ struct protoDisplay *p, *prev;
+
+ prev = 0;
+ for (p = protoDisplays; p; p=p->next) {
+ if (p == pdpy)
+ break;
+ prev = p;
+ }
+ if (!p)
+ return;
+ if (prev)
+ prev->next = pdpy->next;
+ else
+ protoDisplays = pdpy->next;
+ bzero( &pdpy->key, sizeof(pdpy->key) );
+ if (pdpy->fileAuthorization)
+ XauDisposeAuth( pdpy->fileAuthorization );
+ if (pdpy->xdmcpAuthorization)
+ XauDisposeAuth( pdpy->xdmcpAuthorization );
+ XdmcpDisposeARRAY8( &pdpy->connectionAddress );
+ free( (char *)pdpy->address );
+ free( (char *)pdpy );
+}
+
+#endif /* XDMCP */
diff --git a/tdm/backend/reset.c b/tdm/backend/reset.c
new file mode 100644
index 000000000..2c2100870
--- /dev/null
+++ b/tdm/backend/reset.c
@@ -0,0 +1,111 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+
+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
+ *
+ * pseudoReset -- pretend to reset the server by killing all clients
+ * with windows. It will reset the server most of the time, unless
+ * a client remains connected with no windows.
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <X11/Xlib.h>
+
+#include <signal.h>
+
+/*ARGSUSED*/
+static int
+ignoreErrors( Display *dspl ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED )
+{
+ Debug( "ignoring error\n" );
+ return 0;
+}
+
+/*
+ * this is mostly bogus -- but quite useful. I wish the protocol
+ * had some way of enumerating and identifying clients, that way
+ * this code wouldn't have to be this kludgy.
+ */
+
+static void
+killWindows( Window window )
+{
+ Window root, parent, *children;
+ unsigned int child, nchildren = 0;
+
+ while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren )
+ && nchildren > 0)
+ {
+ for (child = 0; child < nchildren; child++) {
+ Debug( "XKillClient %p\n", children[child] );
+ XKillClient( dpy, children[child] );
+ }
+ XFree( (char *)children );
+ }
+}
+
+static Jmp_buf resetJmp;
+
+/* ARGSUSED */
+static void
+abortReset( int n ATTR_UNUSED )
+{
+ Longjmp( resetJmp, 1 );
+}
+
+/*
+ * this display connection better not have any windows...
+ */
+
+void
+pseudoReset()
+{
+ int screen;
+
+ if (Setjmp( resetJmp )) {
+ LogError( "pseudoReset timeout\n" );
+ } else {
+ (void)Signal( SIGALRM, abortReset );
+ (void)alarm( 30 );
+ XSetErrorHandler( ignoreErrors );
+ for (screen = 0; screen < ScreenCount (dpy); screen++) {
+ Debug( "pseudoReset screen %d\n", screen );
+ killWindows( RootWindow( dpy, screen ) );
+ }
+ Debug( "before XSync\n" );
+ XSync( dpy, False );
+ (void)alarm( 0 );
+ }
+ Signal( SIGALRM, SIG_DFL );
+ XSetErrorHandler( (XErrorHandler)0 );
+ Debug( "pseudoReset done\n" );
+}
diff --git a/tdm/backend/resource.c b/tdm/backend/resource.c
new file mode 100644
index 000000000..f17db78ec
--- /dev/null
+++ b/tdm/backend/resource.c
@@ -0,0 +1,486 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * obtain configuration data
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+
+
+static char **originalArgv;
+
+static GProc getter;
+GTalk cnftalk;
+
+static void
+OpenGetter()
+{
+ GSet( &cnftalk );
+ if (!getter.pid) {
+ if (GOpen( &getter,
+ originalArgv, "_config", 0, strdup( "config reader" ),
+ 0 ))
+ LogPanic( "Cannot run config reader\n" );
+ Debug( "getter now ready\n" );
+ }
+}
+
+void
+CloseGetter()
+{
+ if (getter.pid) {
+ GSet( &cnftalk );
+ (void)GClose (&getter, 0, 0);
+ Debug( "getter now closed\n" );
+ }
+}
+
+/*
+ * ref-counted, unique-instance strings
+ */
+static RcStr *strs;
+
+/*
+ * make a ref-counted string of the argument. the new string will
+ * have a ref-count of 1. the passed string pointer is no longer valid.
+ */
+RcStr *
+newStr( char *str )
+{
+ RcStr *cs;
+
+ for (cs = strs; cs; cs = cs->next)
+ if (!strcmp( str, cs->str )) {
+ free( str );
+ cs->cnt++;
+ return cs;
+ }
+ if (!(cs = Malloc( sizeof(*cs) )))
+ return 0;
+ cs->cnt = 1;
+ cs->str = str;
+ cs->next = strs;
+ strs = cs;
+ return cs;
+}
+
+/*
+ * decrement ref-count and delete string when count drops to 0.
+ */
+void
+delStr( RcStr *str )
+{
+ RcStr **cs;
+
+ if (!str || --str->cnt)
+ return;
+ for (cs = &strs; *cs; cs = &((*cs)->next))
+ if (str == *cs) {
+ *cs = (*cs)->next;
+ free( (*cs)->str );
+ free( *cs );
+ break;
+ }
+}
+
+
+typedef struct CfgFile {
+ RcStr *name;
+ int depidx;
+ long deptime;
+} CfgFile;
+
+static int numCfgFiles;
+static CfgFile *cfgFiles;
+
+static int cfgMapT[] = {
+ GC_gGlobal,
+ GC_gDisplay,
+#ifdef XDMCP
+ GC_gXaccess,
+#endif
+};
+static int cfgMap[as(cfgMapT)];
+
+static int
+GetDeps()
+{
+ int ncf, i, dep, ret;
+ CfgFile *cf;
+
+ OpenGetter();
+ GSendInt( GC_Files );
+ ncf = GRecvInt();
+ if (!(cf = Malloc( ncf * sizeof(*cf) ))) {
+ CloseGetter();
+ return 0;
+ }
+ for (i = 0; i < ncf; i++) {
+ cf[i].name = newStr( GRecvStr() );
+ if ((dep = cf[i].depidx = GRecvInt()) != -1)
+ cf[i].deptime = mTime( cf[dep].name->str );
+ }
+ if (cfgFiles) {
+ for (i = 0; i < numCfgFiles; i++)
+ delStr( cfgFiles[i].name );
+ free( cfgFiles );
+ }
+ ret = 1;
+ cfgFiles = cf;
+ numCfgFiles = ncf;
+ for (i = 0; i < as(cfgMapT); i++) {
+ GSendInt( cfgMapT[i] );
+ if ((cfgMap[i] = GRecvInt()) < 0) {
+ LogError( "Config reader does not support config cathegory %#x\n",
+ cfgMapT[i] );
+ ret = 0;
+ }
+ }
+ GSendInt( -1 );
+ return ret;
+}
+
+static int
+checkDep( int idx )
+{
+ int dep;
+
+ if ((dep = cfgFiles[idx].depidx) == -1)
+ return 0;
+ if (checkDep( dep ))
+ return 1;
+ return mTime( cfgFiles[dep].name->str ) != cfgFiles[idx].deptime;
+}
+
+static int
+needsReScan( int what, CfgDep *dep )
+{
+ int widx, idx;
+ long mt;
+
+ for (widx = 0; cfgMapT[widx] != what; widx++);
+ idx = cfgMap[widx];
+ if (checkDep( idx )) {
+ if (!GetDeps())
+ return -1;
+ idx = cfgMap[widx];
+ }
+ mt = mTime( cfgFiles[idx].name->str );
+ if (dep->name != cfgFiles[idx].name) {
+ if (dep->name)
+ delStr( dep->name );
+ dep->name = cfgFiles[idx].name;
+ dep->name->cnt++;
+ dep->time = mt;
+ return 1;
+ } else if (dep->time != mt) {
+ dep->time = mt;
+ return 1;
+ } else
+ return 0;
+}
+
+int
+startConfig( int what, CfgDep *dep, int force )
+{
+ int ret;
+
+ if ((ret = needsReScan( what, dep )) < 0 || (!ret && !force))
+ return ret;
+ OpenGetter();
+ GSendInt( GC_GetConf );
+ GSendInt( what );
+ GSendStr( dep->name->str );
+ return 1;
+}
+
+static void
+LoadResources( CfgArr *conf )
+{
+ char **vptr, **pptr, *cptr;
+ long *iptr, i, id, nu, j, nptr, nint, nchr;
+
+ if (conf->data)
+ free( conf->data );
+ conf->numCfgEnt = GRecvInt();
+ nptr = GRecvInt();
+ nint = GRecvInt();
+ nchr = GRecvInt();
+ if (!(conf->data = Malloc( conf->numCfgEnt *
+ (sizeof(long) +
+ sizeof(char *)) +
+ nptr * sizeof(char *) +
+ nint * sizeof(long) +
+ nchr )))
+ {
+ CloseGetter();
+ return;
+ }
+ vptr = (char **)conf->data;
+ pptr = vptr + conf->numCfgEnt;
+ conf->idx = (long *)(pptr + nptr);
+ iptr = conf->idx + conf->numCfgEnt;
+ cptr = (char *)(iptr + nint);
+ for (i = 0; i < conf->numCfgEnt; i++) {
+ id = GRecvInt();
+ conf->idx[i] = id;
+ switch (id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ vptr[i] = (char *)((unsigned long)GRecvInt());
+ break;
+ case C_TYPE_STR:
+ vptr[i] = cptr;
+ cptr += GRecvStrBuf( cptr );
+ break;
+ case C_TYPE_ARGV:
+ nu = GRecvInt();
+ vptr[i] = (char *)pptr;
+ for (j = 0; j < nu; j++) {
+ *pptr++ = cptr;
+ cptr += GRecvStrBuf( cptr );
+ }
+ *pptr++ = (char *)0;
+ break;
+ default:
+ LogError( "Config reader supplied unknown data type in id %#x\n",
+ id );
+ break;
+ }
+ }
+}
+
+static void
+ApplyResource( int id, char **src, char **dst )
+{
+ switch (id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ *(int *)dst = *(long *)src;
+ break;
+ case C_TYPE_STR:
+ case C_TYPE_ARGV:
+ *dst = *src;
+ break;
+ }
+}
+
+
+#define boffset(f) XtOffsetOf(struct display, f)
+
+/* no global variables exported currently
+struct globEnts {
+ int id;
+ char **off;
+} globEnt[] = {
+};
+ */
+
+/* no per-display variables exported currently
+struct dpyEnts {
+ int id;
+ int off;
+} dpyEnt[] = {
+};
+ */
+
+CfgArr cfg;
+
+char **
+FindCfgEnt( struct display *d, int id )
+{
+ int i;
+
+/* no global variables exported currently
+ for (i = 0; i < as(globEnt); i++)
+ if (globEnt[i].id == id)
+ return globEnt[i].off;
+ */
+ for (i = 0; i < cfg.numCfgEnt; i++)
+ if (cfg.idx[i] == id)
+ return ((char **)cfg.data) + i;
+ if (d) {
+/* no per-display variables exported currently
+ for (i = 0; i < as(dpyEnt); i++)
+ if (dpyEnt[i].id == id)
+ return (char **)(((char *)d) + dpyEnt[i].off);
+ */
+ for (i = 0; i < d->cfg.numCfgEnt; i++)
+ if (d->cfg.idx[i] == id)
+ return ((char **)d->cfg.data) + i;
+ }
+ Debug( "unknown config entry %#x requested\n", id );
+ return (char **)0;
+}
+
+
+CONF_CORE_GLOBAL_DEFS
+
+struct globVals {
+ int id;
+ char **off;
+} globVal[] = {
+CONF_CORE_GLOBALS
+};
+
+int
+LoadDMResources( int force )
+{
+ int i, ret;
+ char **ent;
+
+ if (Setjmp( cnftalk.errjmp ))
+ return -1; /* may memleak, but we probably have to abort anyway */
+ if ((ret = startConfig( GC_gGlobal, &cfg.dep, force )) <= 0)
+ return ret;
+ LoadResources( &cfg );
+/* Debug( "manager resources: %[*x\n",
+ cfg.numCfgEnt, ((char **)cfg.data) + cfg.numCfgEnt );*/
+ ret = 1;
+ for (i = 0; i < as(globVal); i++) {
+ if (!(ent = FindCfgEnt( 0, globVal[i].id )))
+ ret = -1;
+ else
+ ApplyResource( globVal[i].id, ent, globVal[i].off );
+ }
+ if (ret < 0)
+ LogError( "Internal error: config reader supplied incomplete data\n" );
+ return ret;
+}
+
+
+struct dpyVals {
+ int id;
+ int off;
+} dpyVal[] = {
+CONF_CORE_LOCALS
+};
+
+int
+LoadDisplayResources( struct display *d )
+{
+ int i, ret;
+ char **ent;
+
+ if (Setjmp( cnftalk.errjmp ))
+ return -1; /* may memleak */
+ if ((ret = startConfig( GC_gDisplay, &d->cfg.dep, FALSE )) <= 0)
+ return ret;
+ GSendStr( d->name );
+ GSendStr( d->class2 );
+ LoadResources( &d->cfg );
+/* Debug( "display(%s, %s) resources: %[*x\n", d->name, d->class2,
+ d->cfg.numCfgEnt, ((char **)d->cfg.data) + d->cfg.numCfgEnt );*/
+ ret = 1;
+ for (i = 0; i < as(dpyVal); i++) {
+ if (!(ent = FindCfgEnt( d, dpyVal[i].id )))
+ ret = -1;
+ else
+ ApplyResource( dpyVal[i].id, ent,
+ (char **)(((char *)d) + dpyVal[i].off) );
+ }
+ if (ret < 0)
+ LogError( "Internal error: config reader supplied incomplete data\n" );
+ return ret;
+}
+
+int
+InitResources( char **argv )
+{
+ originalArgv = argv;
+ cnftalk.pipe = &getter.pipe;
+ if (Setjmp( cnftalk.errjmp ))
+ return 0; /* may memleak */
+ return GetDeps();
+}
+
+static void
+addServers( char **srv, int bType )
+{
+ char *name, *class2;
+ const char *dtx, *cls;
+ struct display *d;
+
+ for (; *srv; srv++) {
+ if ((cls = strchr( *srv, '_' ))) {
+ if (!StrNDup( &name, *srv, cls - *srv ))
+ return;
+ if (!StrDup( &class2, cls )) {
+ free( name );
+ return;
+ }
+ } else {
+ if (!StrDup( &name, *srv ))
+ return;
+ class2 = 0;
+ }
+ if ((d = FindDisplayByName( name ))) {
+ if (d->class2)
+ free( d->class2 );
+ dtx = "existing";
+ } else {
+ if (!(d = NewDisplay( name ))) {
+ free( name );
+ if (class2)
+ free( class2 );
+ return;
+ }
+ dtx = "new";
+ }
+ d->stillThere = 1;
+ d->class2 = class2;
+ d->displayType = (*name == ':' ? dLocal : dForeign) | bType;
+ if ((bType & d_lifetime) == dReserve) {
+ if (d->status == notRunning)
+ d->status = reserve;
+ } else {
+ if (d->status == reserve)
+ d->status = notRunning;
+ }
+ Debug( "found %s %s%s display: %s %s\n", dtx,
+ ((d->displayType & d_location) == dLocal) ? "local" : "foreign",
+ ((d->displayType & d_lifetime) == dReserve) ? " reserve" : "",
+ d->name, d->class2 );
+ free( name );
+ }
+}
+
+void
+ScanServers( void )
+{
+ Debug( "ScanServers\n" );
+ addServers( staticServers, dFromFile | dPermanent );
+ addServers( reserveServers, dFromFile | dReserve );
+}
+
diff --git a/tdm/backend/rpcauth.c b/tdm/backend/rpcauth.c
new file mode 100644
index 000000000..1186f72c2
--- /dev/null
+++ b/tdm/backend/rpcauth.c
@@ -0,0 +1,89 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+
+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
+ *
+ * generate SecureRPC authorization records
+ */
+
+#include <config.h>
+
+#ifdef SECURE_RPC
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+
+/*ARGSUSED*/
+void
+SecureRPCInitAuth( unsigned short name_len ATTR_UNUSED,
+ const char *name ATTR_UNUSED )
+{
+}
+
+Xauth *
+SecureRPCGetAuth( unsigned short namelen, const char *name )
+{
+ Xauth *new;
+ char key[MAXNETNAMELEN+1];
+
+ new = (Xauth *)Malloc( sizeof(*new) );
+ if (!new)
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+
+ getnetname( key );
+ Debug( "system netname %s\n", key );
+ new->data_length = strlen( key );
+ new->data = (char *)Malloc( new->data_length );
+ if (!new->data) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->name = (char *)Malloc( namelen );
+ if (!new->name) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( new->name, name, namelen );
+ new->name_length = namelen;
+ memmove( new->data, key, new->data_length );
+ return new;
+}
+
+#endif
diff --git a/tdm/backend/server.c b/tdm/backend/server.c
new file mode 100644
index 000000000..e78d8a66c
--- /dev/null
+++ b/tdm/backend/server.c
@@ -0,0 +1,376 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001,2003,2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * server.c - manage the X server
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <X11/Xlib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+
+struct display *startingServer;
+time_t serverTimeout = TO_INF;
+
+char **
+PrepServerArgv( struct display *d, const char *args )
+{
+ char **argv;
+#ifdef HAVE_VTS
+ char vtstr[8];
+#endif
+
+ if (!(argv = parseArgs( 0, d->serverCmd )) ||
+ !(argv = parseArgs( argv, args )) ||
+ !(argv = addStrArr( argv, d->name, -1 )))
+ exit( 47 );
+#ifdef HAVE_VTS
+ if (d->serverVT &&
+ !(argv = addStrArr( argv, vtstr,
+ sprintf( vtstr, "vt%d", d->serverVT ) )))
+ exit( 47 );
+#endif
+ return argv;
+}
+
+static void
+StartServerOnce( void )
+{
+ struct display *d = startingServer;
+ char **argv;
+ int pid;
+
+ Debug( "StartServerOnce for %s, try %d\n", d->name, ++d->startTries );
+ d->serverStatus = starting;
+ switch (pid = Fork()) {
+ case 0:
+ argv = PrepServerArgv( d, d->serverArgsLocal );
+ if (d->authFile) {
+ if (!(argv = addStrArr( argv, "-auth", 5 )) ||
+ !(argv = addStrArr( argv, d->authFile, -1 )))
+ exit( 47 );
+ }
+ Debug( "exec %\"[s\n", argv );
+ /*
+ * give the server SIGUSR1 ignored,
+ * it will notice that and send SIGUSR1
+ * when ready
+ */
+ (void)Signal( SIGUSR1, SIG_IGN );
+ (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) {
+ 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] );
+
+ 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( 47 );
+ case -1:
+ LogError( "X server fork failed\n" );
+ StartServerFailed();
+ break;
+ default:
+ Debug( "X server forked, pid %d\n", pid );
+ d->serverPid = pid;
+ serverTimeout = d->serverTimeout + now;
+ break;
+ }
+}
+
+void
+StartServer( struct display *d )
+{
+ startingServer = d;
+ d->startTries = 0;
+ StartServerOnce();
+}
+
+void
+AbortStartServer( struct display *d )
+{
+ if (startingServer == d)
+ {
+ if (d->serverStatus != ignore)
+ {
+ d->serverStatus = ignore;
+ serverTimeout = TO_INF;
+ Debug( "aborting X server start\n" );
+ }
+ startingServer = 0;
+ }
+}
+
+void
+StartServerSuccess()
+{
+ struct display *d = startingServer;
+ d->serverStatus = ignore;
+ serverTimeout = TO_INF;
+ Debug( "X server ready, starting session\n" );
+ StartDisplayP2( d );
+}
+
+void
+StartServerFailed()
+{
+ struct display *d = startingServer;
+ if (!d->serverAttempts || d->startTries < d->serverAttempts) {
+ d->serverStatus = pausing;
+ serverTimeout = d->openDelay + now;
+ } else {
+ d->serverStatus = ignore;
+ serverTimeout = TO_INF;
+ startingServer = 0;
+ LogError( "X server for display %s can't be started,"
+ " session disabled\n", d->name );
+ StopDisplay( d );
+ }
+}
+
+void
+StartServerTimeout()
+{
+ struct display *d = startingServer;
+ switch (d->serverStatus) {
+ case ignore:
+ case awaiting:
+ break; /* cannot happen */
+ case starting:
+ LogError( "X server startup timeout, terminating\n" );
+ kill( d->serverPid, d->termSignal );
+ d->serverStatus = d->termSignal == SIGKILL ? killed : terminated;
+ serverTimeout = d->serverTimeout + now;
+ break;
+ case terminated:
+ LogInfo( "X server termination timeout, killing\n" );
+ kill( d->serverPid, SIGKILL );
+ d->serverStatus = killed;
+ serverTimeout = 10 + now;
+ break;
+ case killed:
+ LogInfo( "X server is stuck in D state; leaving it alone\n" );
+ StartServerFailed();
+ break;
+ case pausing:
+ StartServerOnce();
+ break;
+ }
+}
+
+
+Display *dpy;
+
+/*
+ * this code is complicated by some TCP failings. On
+ * many systems, the connect will occasionally hang forever,
+ * this trouble is avoided by setting up a timeout to Longjmp
+ * out of the connect (possibly leaving piles of garbage around
+ * inside Xlib) and give up, terminating the server.
+ */
+
+static Jmp_buf openAbort;
+
+/* ARGSUSED */
+static void
+abortOpen( int n ATTR_UNUSED )
+{
+ Longjmp( openAbort, 1 );
+}
+
+#ifdef XDMCP
+
+#ifdef STREAMSCONN
+# include <tiuser.h>
+#endif
+
+static void
+GetRemoteAddress( struct display *d, int fd )
+{
+ char buf[512];
+ int len = sizeof(buf);
+#ifdef STREAMSCONN
+ struct netbuf netb;
+#endif
+
+ XdmcpDisposeARRAY8( &d->peer );
+#ifdef STREAMSCONN
+ netb.maxlen = sizeof(buf);
+ netb.buf = buf;
+ t_getname( fd, &netb, REMOTENAME );
+ len = 8;
+ /* lucky for us, t_getname returns something that looks like a sockaddr */
+#else
+ getpeername( fd, (struct sockaddr *)buf, (void *)&len );
+#endif
+ if (len && XdmcpAllocARRAY8( &d->peer, len ))
+ memmove( (char *)d->peer.data, buf, len );
+ Debug( "got remote address %s %d\n", d->name, d->peer.length );
+}
+
+#endif /* XDMCP */
+
+static int
+openErrorHandler( Display *dspl ATTR_UNUSED )
+{
+ LogError( "IO Error in XOpenDisplay\n" );
+ exit( EX_OPENFAILED_DPY );
+ /*NOTREACHED*/
+ return (0);
+}
+
+void
+WaitForServer( struct display *d )
+{
+ volatile int i;
+ /* static int i; */
+
+ i = 0;
+ do {
+ (void)Signal( SIGALRM, abortOpen );
+ (void)alarm( (unsigned)d->openTimeout );
+ if (!Setjmp( openAbort )) {
+ Debug( "before XOpenDisplay(%s)\n", d->name );
+ errno = 0;
+ (void)XSetIOErrorHandler( openErrorHandler );
+ dpy = XOpenDisplay( d->name );
+#ifdef STREAMSCONN
+ {
+ /* For some reason, the next XOpenDisplay we do is
+ going to fail, so we might as well get that out
+ of the way. There is something broken here. */
+ Display *bogusDpy = XOpenDisplay( d->name );
+ Debug( "bogus XOpenDisplay %s\n",
+ bogusDpy ? "succeeded" : "failed" );
+ if (bogusDpy) XCloseDisplay( bogusDpy ); /* just in case */
+ }
+#endif
+ (void)alarm( (unsigned)0 );
+ (void)Signal( SIGALRM, SIG_DFL );
+ (void)XSetIOErrorHandler( (int (*)( Display * )) 0 );
+ Debug( "after XOpenDisplay(%s)\n", d->name );
+ if (dpy) {
+#ifdef XDMCP
+ if ((d->displayType & d_location) == dForeign)
+ GetRemoteAddress( d, ConnectionNumber( dpy ) );
+#endif
+ RegisterCloseOnFork( ConnectionNumber( dpy ) );
+ return;
+ }
+ Debug( "OpenDisplay(%s) attempt %d failed: %m\n", d->name, i + 1 );
+ sleep( (unsigned)d->openDelay );
+ } else {
+ LogError( "Hung in XOpenDisplay(%s), aborting\n", d->name );
+ (void)Signal( SIGALRM, SIG_DFL );
+ break;
+ }
+ } while (++i < d->openRepeat);
+ LogError( "Cannot connect to %s, giving up\n", d->name );
+ exit( EX_OPENFAILED_DPY );
+}
+
+
+void
+ResetServer( struct display *d )
+{
+ if (dpy && (d->displayType & d_origin) != dFromXDMCP)
+ pseudoReset();
+}
+
+
+static Jmp_buf pingTime;
+
+static void
+PingLost( void )
+{
+ Longjmp( pingTime, 1 );
+}
+
+/* ARGSUSED */
+static int
+PingLostIOErr( Display *dspl ATTR_UNUSED )
+{
+ PingLost();
+ return 0;
+}
+
+/* ARGSUSED */
+static void
+PingLostSig( int n ATTR_UNUSED )
+{
+ PingLost();
+}
+
+int
+PingServer( struct display *d )
+{
+ int (*oldError)( Display * );
+ void (*oldSig)( int );
+ int oldAlarm;
+
+ oldError = XSetIOErrorHandler( PingLostIOErr );
+ oldAlarm = alarm( 0 );
+ oldSig = Signal( SIGALRM, PingLostSig );
+ (void)alarm( d->pingTimeout * 60 );
+ if (!Setjmp( pingTime )) {
+ Debug( "ping X server\n" );
+ XSync( dpy, 0 );
+ } else {
+ Debug( "X server dead\n" );
+ (void)alarm( 0 );
+ (void)Signal( SIGALRM, SIG_DFL );
+ XSetIOErrorHandler( oldError );
+ return 0;
+ }
+ (void)alarm( 0 );
+ (void)Signal( SIGALRM, oldSig );
+ (void)alarm( oldAlarm );
+ Debug( "X server alive\n" );
+ XSetIOErrorHandler( oldError );
+ return 1;
+}
diff --git a/tdm/backend/session.c b/tdm/backend/session.c
new file mode 100644
index 000000000..9a12ce312
--- /dev/null
+++ b/tdm/backend/session.c
@@ -0,0 +1,813 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * subdaemon event loop, etc.
+ */
+
+#include "dm.h"
+#include "dm_error.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+
+#ifdef WITH_CONSOLE_KIT
+#include "consolekit.h"
+#endif
+
+struct display *td;
+const char *td_setup = "auto";
+
+static void DeleteXloginResources( void );
+static void LoadXloginResources( void );
+static void SetupDisplay( const char *arg );
+
+
+static Jmp_buf pingTime;
+
+/* ARGSUSED */
+static void
+catchAlrm( int n ATTR_UNUSED )
+{
+ Longjmp( pingTime, 1 );
+}
+
+static Jmp_buf tenaciousClient;
+
+/* ARGSUSED */
+static void
+waitAbort( int n ATTR_UNUSED )
+{
+ Longjmp( tenaciousClient, 1 );
+}
+
+static void
+AbortClient( int pid )
+{
+ int sig = SIGTERM;
+ volatile int i;
+ int retId;
+
+ for (i = 0; i < 4; i++) {
+ if (kill( -pid, sig ) == -1) {
+ switch (errno) {
+ case EPERM:
+ LogError( "Can't kill client\n" );
+ case EINVAL:
+ case ESRCH:
+ return;
+ }
+ }
+ if (!Setjmp( tenaciousClient )) {
+ (void)Signal( SIGALRM, waitAbort );
+ (void)alarm( (unsigned)10 );
+ retId = wait( (waitType *)0 );
+ (void)alarm( (unsigned)0 );
+ (void)Signal( SIGALRM, SIG_DFL );
+ if (retId == pid)
+ break;
+ } else
+ (void)Signal( SIGALRM, SIG_DFL );
+ sig = SIGKILL;
+ }
+}
+
+
+static char *
+conv_auto( int what, const char *prompt ATTR_UNUSED )
+{
+ switch (what) {
+ case GCONV_USER:
+ return curuser;
+ case GCONV_PASS:
+ case GCONV_PASS_ND:
+ return curpass;
+ default:
+ LogError( "Unknown authentication data type requested for autologin.\n" );
+ return 0;
+ }
+}
+
+static void
+DoAutoLogon( void )
+{
+ ReStr( &curuser, td->autoUser );
+ ReStr( &curpass, td->autoPass );
+ ReStr( &curtype, "classic" );
+ cursource = PWSRC_AUTOLOGIN;
+}
+
+static int
+AutoLogon( Time_t tdiff )
+{
+ Debug( "autoLogon, tdiff = %d, rLogin = %d, goodexit = %d, nuser = %s\n",
+ tdiff, td->hstent->rLogin, td->hstent->goodExit, td->hstent->nuser );
+ if (td->hstent->rLogin == 2 ||
+ (td->hstent->rLogin == 1 &&
+ tdiff <= 0 && !td->hstent->goodExit && !td->hstent->lock))
+ {
+ curuser = td->hstent->nuser;
+ td->hstent->nuser = 0;
+ curpass = td->hstent->npass;
+ td->hstent->npass = 0;
+ newdmrc = td->hstent->nargs;
+ td->hstent->nargs = 0;
+ ReStr( &curtype, "classic" );
+ cursource = (td->hstent->rLogin == 1) ? PWSRC_RELOGIN : PWSRC_MANUAL;
+ return 1;
+ } else if (*td->autoUser && !td->autoDelay &&
+ ((tdiff > 0 && ((td->displayType & d_lifetime) == dTransient ||
+ !td->hstent->lastExit)) ||
+ td->autoAgain))
+ {
+ unsigned int lmask;
+ Window dummy1, dummy2;
+ int dummy3, dummy4, dummy5, dummy6;
+ XQueryPointer( dpy, DefaultRootWindow( dpy ),
+ &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
+ &lmask );
+ if (lmask & ShiftMask)
+ return 0;
+ DoAutoLogon();
+ return 1;
+ }
+ return 0;
+}
+
+
+static const struct {
+ int vcode, echo, ndelay;
+} grqs[] = {
+ { V_GET_TEXT, TRUE, FALSE },
+ { V_GET_TEXT, FALSE, FALSE },
+ { V_GET_TEXT, TRUE, FALSE },
+ { V_GET_TEXT, FALSE, FALSE },
+ { V_GET_TEXT, FALSE, TRUE },
+ { V_GET_BINARY, 0, 0 }
+};
+
+char *
+conv_interact( int what, const char *prompt )
+{
+ char *ret;
+ int tag;
+
+ GSendInt( grqs[what].vcode );
+ if (what == GCONV_BINARY) {
+ unsigned const char *up = (unsigned const char *)prompt;
+ int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
+ GSendArr( len, prompt );
+ GSendInt( FALSE ); /* ndelay */
+ return GRecvArr( &len );
+ } else {
+ GSendStr( prompt );
+ GSendInt( grqs[what].echo );
+ GSendInt( grqs[what].ndelay );
+ ret = GRecvStr();
+ if (ret) {
+ tag = GRecvInt();
+ switch (what) {
+ case GCONV_USER:
+ /* assert(tag & V_IS_USER); */
+ if (curuser)
+ free( curuser );
+ curuser = ret;
+ break;
+ case GCONV_PASS:
+ case GCONV_PASS_ND:
+ /* assert(tag & V_IS_PASSWORD); */
+ if (curpass)
+ free( curpass );
+ curpass = ret;
+ break;
+ default:
+ if (tag & V_IS_USER)
+ ReStr( &curuser, ret );
+ else if (tag & V_IS_PASSWORD)
+ ReStr( &curpass, ret );
+ else if (tag & V_IS_NEWPASSWORD)
+ ReStr( &newpass, ret );
+ else if (tag & V_IS_OLDPASSWORD)
+ ReStr( &ret, curpass );
+ }
+ }
+ return ret;
+ }
+}
+
+static int greeter;
+GProc grtproc;
+GTalk grttalk;
+
+GTalk mstrtalk; /* make static; see dm.c */
+
+int
+CtrlGreeterWait( int wreply )
+{
+ int i, cmd, type, rootok;
+ char *name, *pass, **avptr;
+#ifdef XDMCP
+ ARRAY8Ptr aptr;
+#endif
+
+ if (Setjmp( mstrtalk.errjmp )) {
+ CloseGreeter( TRUE );
+ SessionExit( EX_UNMANAGE_DPY );
+ }
+
+ while (GRecvCmd( &cmd )) {
+ switch (cmd)
+ {
+ case G_Ready:
+ Debug( "G_Ready\n" );
+ return 0;
+ case G_GetCfg:
+ /*Debug ("G_GetCfg\n");*/
+ type = GRecvInt();
+ /*Debug (" index %#x\n", type);*/
+ if (type == C_isLocal)
+ i = (td->displayType & d_location) == dLocal;
+ else if (type == C_hasConsole)
+#ifdef HAVE_VTS
+ i = *consoleTTYs != 0;
+#else
+ i = td->console != 0;
+#endif
+ else if (type == C_isAuthorized)
+ i = td->authorizations != 0;
+ else
+ goto normal;
+ GSendInt( GE_Ok );
+ /*Debug (" -> bool %d\n", i);*/
+ GSendInt( i );
+ break;
+ normal:
+ if (!(avptr = FindCfgEnt( td, type ))) {
+ /*Debug (" -> not found\n");*/
+ GSendInt( GE_NoEnt );
+ break;
+ }
+ switch (type & C_TYPE_MASK) {
+ default:
+ /*Debug (" -> unknown type\n");*/
+ GSendInt( GE_BadType );
+ break;
+ case C_TYPE_INT:
+ case C_TYPE_STR:
+ case C_TYPE_ARGV:
+#ifdef XDMCP
+ case C_TYPE_ARR:
+#endif
+ GSendInt( GE_Ok );
+ switch (type & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ /*Debug (" -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr);*/
+ GSendInt( *(long *)avptr );
+ break;
+ case C_TYPE_STR:
+ /*Debug (" -> string %\"s\n", *avptr);*/
+ GSendStr( *avptr );
+ break;
+ case C_TYPE_ARGV:
+ /*Debug (" -> sending argv %\"[{s\n", *(char ***)avptr);*/
+ GSendArgv( *(char ***)avptr );
+ break;
+#ifdef XDMCP
+ case C_TYPE_ARR:
+ aptr = *(ARRAY8Ptr *)avptr;
+ /*Debug (" -> sending array %02[*:hhx\n",
+ aptr->length, aptr->data);*/
+ GSendArr( aptr->length, (char *)aptr->data );
+ break;
+#endif
+ }
+ break;
+ }
+ break;
+ case G_ReadDmrc:
+ Debug( "G_ReadDmrc\n" );
+ name = GRecvStr();
+ Debug( " user %\"s\n", name );
+ if (StrCmp( dmrcuser, name )) {
+ if (curdmrc) { free( curdmrc ); curdmrc = 0; }
+ if (dmrcuser)
+ free( dmrcuser );
+ dmrcuser = name;
+ i = ReadDmrc();
+ Debug( " -> status %d\n", i );
+ GSendInt( i );
+ Debug( " => %\"s\n", curdmrc );
+ } else {
+ if (name)
+ free( name );
+ Debug( " -> status " stringify( GE_Ok ) "\n" );
+ GSendInt( GE_Ok );
+ Debug( " => keeping old\n" );
+ }
+ break;
+ case G_GetDmrc:
+ Debug( "G_GetDmrc\n" );
+ name = GRecvStr();
+ Debug( " key %\"s\n", name );
+ pass = iniEntry( curdmrc, "Desktop", name, 0 );
+ Debug( " -> %\"s\n", pass );
+ GSendStr( pass );
+ if (pass)
+ free( pass );
+ free( name );
+ break;
+/* case G_ResetDmrc:
+ Debug ("G_ResetDmrc\n");
+ if (newdmrc) { free (newdmrc); newdmrc = 0; }
+ break; */
+ case G_PutDmrc:
+ Debug( "G_PutDmrc\n" );
+ name = GRecvStr();
+ Debug( " key %\"s\n", name );
+ pass = GRecvStr();
+ Debug( " value %\"s\n", pass );
+ newdmrc = iniEntry( newdmrc, "Desktop", name, pass );
+ free( pass );
+ free( name );
+ break;
+ case G_VerifyRootOK:
+ Debug( "G_VerifyRootOK\n" );
+ rootok = TRUE;
+ goto doverify;
+ case G_Verify:
+ Debug( "G_Verify\n" );
+ rootok = FALSE;
+ doverify:
+ if (curuser) { free( curuser ); curuser = 0; }
+ if (curpass) { free( curpass ); curpass = 0; }
+ if (curtype) free( curtype );
+ curtype = GRecvStr();
+ Debug( " type %\"s\n", curtype );
+ cursource = PWSRC_MANUAL;
+ if (Verify( conv_interact, rootok )) {
+ Debug( " -> return success\n" );
+ GSendInt( V_OK );
+ } else
+ Debug( " -> failure returned\n" );
+ break;
+ case G_AutoLogin:
+ Debug( "G_AutoLogin\n" );
+ DoAutoLogon();
+ if (Verify( conv_auto, FALSE )) {
+ Debug( " -> return success\n" );
+ GSendInt( V_OK );
+ } else
+ Debug( " -> failure returned\n" );
+ break;
+ case G_SetupDpy:
+ Debug( "G_SetupDpy\n" );
+ SetupDisplay( 0 );
+ td_setup = 0;
+ GSendInt( 0 );
+ break;
+ default:
+ return cmd;
+ }
+ if (!wreply)
+ return -1;
+ }
+ Debug( "lost connection to greeter\n" );
+ return -2;
+}
+
+void
+OpenGreeter()
+{
+ char *name, **env;
+ static Time_t lastStart;
+ int cmd;
+ Cursor xcursor;
+
+ GSet( &grttalk );
+ if (greeter)
+ return;
+ if (time( 0 ) < lastStart + 10) /* XXX should use some readiness indicator instead */
+ SessionExit( EX_UNMANAGE_DPY );
+ greeter = 1;
+ ASPrintf( &name, "greeter for display %s", td->name );
+ Debug( "starting %s\n", name );
+
+ /* Hourglass cursor */
+ if ((xcursor = XCreateFontCursor( dpy, XC_watch ))) {
+ XDefineCursor( dpy, DefaultRootWindow( dpy ), xcursor );
+ XFreeCursor( dpy, xcursor );
+ }
+ XFlush( dpy );
+
+ /* Load system default Resources (if any) */
+ LoadXloginResources();
+
+ grttalk.pipe = &grtproc.pipe;
+ env = systemEnv( (char *)0 );
+ if (GOpen( &grtproc, (char **)0, "_greet", env, name, &td->gpipe ))
+ SessionExit( EX_UNMANAGE_DPY );
+ freeStrArr( env );
+ if ((cmd = CtrlGreeterWait( TRUE ))) {
+ if (cmd != -2)
+ LogError( "Received unknown or unexpected command %d from greeter\n", cmd );
+ CloseGreeter( TRUE );
+ SessionExit( EX_UNMANAGE_DPY );
+ }
+ Debug( "%s ready\n", name );
+ time( &lastStart );
+}
+
+int
+CloseGreeter( int force )
+{
+ int ret;
+
+ if (!greeter)
+ return EX_NORMAL;
+ greeter = 0;
+ ret = GClose (&grtproc, 0, force);
+ Debug( "greeter for %s stopped\n", td->name );
+ if (WaitCode( ret ) > EX_NORMAL && WaitCode( ret ) <= EX_MAX) {
+ Debug( "greeter-initiated session exit, code %d\n", WaitCode( ret ) );
+ SessionExit( WaitCode( ret ) );
+ }
+ return ret;
+}
+
+void
+PrepErrorGreet()
+{
+ if (!greeter) {
+ OpenGreeter();
+ GSendInt( G_ErrorGreet );
+ GSendStr( curuser );
+ }
+}
+
+static Jmp_buf idleTOJmp;
+
+/* ARGSUSED */
+static void
+IdleTOJmp( int n ATTR_UNUSED )
+{
+ Longjmp( idleTOJmp, 1 );
+}
+
+
+static Jmp_buf abortSession;
+
+/* ARGSUSED */
+static void
+catchTerm( int n ATTR_UNUSED )
+{
+ Signal( SIGTERM, SIG_IGN );
+ Longjmp( abortSession, EX_AL_RESERVER_DPY );
+}
+
+/*
+ * We need our own error handlers because we can't be sure what exit code Xlib
+ * will use, and our Xlib does exit(1) which matches EX_REMANAGE_DPY, which
+ * can cause a race condition leaving the display wedged. We need to use
+ * EX_RESERVER_DPY for IO errors, to ensure that the manager waits for the
+ * server to terminate. For other X errors, we should give up.
+ */
+
+/*ARGSUSED*/
+static int
+IOErrorHandler( Display *dspl ATTR_UNUSED )
+{
+ LogError( "Fatal X server IO error: %m\n" );
+ /* The only X interaction during the session are pings, and those
+ have an own IOErrorHandler -> not EX_AL_RESERVER_DPY */
+ Longjmp( abortSession, EX_RESERVER_DPY );
+ /*NOTREACHED*/
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+ErrorHandler( Display *dspl ATTR_UNUSED, XErrorEvent *event )
+{
+ LogError( "X error\n" );
+ if (event->error_code == BadImplementation)
+ Longjmp( abortSession, EX_UNMANAGE_DPY );
+ return 0;
+}
+
+void
+ManageSession( struct display *d )
+{
+ int ex, cmd;
+ volatile int clientPid = 0;
+ volatile Time_t tdiff = 0;
+#ifdef WITH_CONSOLE_KIT
+ char *ck_session_cookie;
+#endif
+
+
+ td = d;
+ Debug( "ManageSession %s\n", d->name );
+ if ((ex = Setjmp( abortSession ))) {
+ CloseGreeter( TRUE );
+ if (clientPid)
+ AbortClient( clientPid );
+ SessionExit( ex );
+ /* NOTREACHED */
+ }
+ (void)XSetIOErrorHandler( IOErrorHandler );
+ (void)XSetErrorHandler( ErrorHandler );
+ (void)Signal( SIGTERM, catchTerm );
+
+ (void)Signal( SIGHUP, SIG_IGN );
+
+ if (Setjmp( grttalk.errjmp ))
+ Longjmp( abortSession, EX_RESERVER_DPY ); /* EX_RETRY_ONCE */
+
+#ifdef XDMCP
+ if (d->useChooser)
+ DoChoose();
+ /* NOTREACHED */
+#endif
+
+ if (d->hstent->sdRec.how) {
+ OpenGreeter();
+ GSendInt( G_ConfShutdown );
+ GSendInt( d->hstent->sdRec.how );
+ GSendInt( d->hstent->sdRec.uid );
+ GSendStr( d->hstent->sdRec.osname );
+ if ((cmd = CtrlGreeterWait( TRUE )) != G_Ready) {
+ LogError( "Received unknown command %d from greeter\n", cmd );
+ CloseGreeter( TRUE );
+ }
+ goto regreet;
+ }
+
+ tdiff = time( 0 ) - td->hstent->lastExit - td->openDelay;
+ if (AutoLogon( tdiff )) {
+ if (!Verify( conv_auto, FALSE ))
+ goto gcont;
+ if (greeter)
+ GSendInt( V_OK );
+ } else {
+ regreet:
+ OpenGreeter();
+ if (Setjmp( idleTOJmp )) {
+ CloseGreeter( TRUE );
+ SessionExit( EX_NORMAL );
+ }
+ Signal( SIGALRM, IdleTOJmp );
+ alarm( td->idleTimeout );
+#ifdef XDMCP
+ if (((d->displayType & d_location) == dLocal) &&
+ d->loginMode >= LOGIN_DEFAULT_REMOTE)
+ goto choose;
+#endif
+ for (;;) {
+ Debug( "ManageSession, greeting, tdiff = %d\n", tdiff );
+ GSendInt( (*td->autoUser && td->autoDelay &&
+ (tdiff > 0 || td->autoAgain)) ?
+ G_GreetTimed : G_Greet );
+ gcont:
+ cmd = CtrlGreeterWait( TRUE );
+#ifdef XDMCP
+ recmd:
+ if (cmd == G_DChoose) {
+ choose:
+ cmd = DoChoose();
+ goto recmd;
+ }
+ if (cmd == G_DGreet)
+ continue;
+#endif
+ alarm( 0 );
+ if (cmd == G_Ready)
+ break;
+ if (cmd == -2)
+ CloseGreeter( FALSE );
+ else {
+ LogError( "Received unknown command %d from greeter\n", cmd );
+ CloseGreeter( TRUE );
+ }
+ goto regreet;
+ }
+ }
+
+ if (CloseGreeter( FALSE ) != EX_NORMAL)
+ goto regreet;
+
+ DeleteXloginResources();
+
+ if (td_setup)
+ SetupDisplay( td_setup );
+
+#ifdef WITH_CONSOLE_KIT
+ ck_session_cookie = open_ck_session (getpwnam(curuser), d);
+ if (!(clientPid = StartClient(ck_session_cookie))) {
+#else
+ if (!(clientPid = StartClient())) {
+#endif
+ LogError( "Client start failed\n" );
+ SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */
+ }
+ Debug( "client Started\n" );
+
+ /*
+ * Wait for session to end,
+ */
+ for (;;) {
+ if (!Setjmp( pingTime )) {
+ (void)Signal( SIGALRM, catchAlrm );
+ (void)alarm( d->pingInterval * 60 ); /* may be 0 */
+ (void)Wait4( clientPid );
+ (void)alarm( 0 );
+ break;
+ } else {
+ (void)alarm( 0 );
+ if (!PingServer( d ))
+ catchTerm( SIGTERM );
+ }
+ }
+
+#ifdef WITH_CONSOLE_KIT
+ if (ck_session_cookie != NULL) {
+ close_ck_session (ck_session_cookie);
+ free (ck_session_cookie);
+ }
+#endif
+
+ /*
+ * Sometimes the Xsession somehow manages to exit before
+ * a server crash is noticed - so we sleep a bit and wait
+ * for being killed.
+ */
+ if (!PingServer( d )) {
+ Debug( "X server dead upon session exit.\n" );
+ if ((d->displayType & d_location) == dLocal)
+ sleep( 10 );
+ SessionExit( EX_AL_RESERVER_DPY );
+ }
+ SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */
+}
+
+static int xResLoaded;
+
+void
+LoadXloginResources()
+{
+ char **args;
+ char **env;
+
+ if (!xResLoaded && td->resources[0] && access( td->resources, 4 ) == 0) {
+ env = systemEnv( (char *)0 );
+ if ((args = parseArgs( (char **)0, td->xrdb )) &&
+ (args = addStrArr( args, td->resources, -1 )))
+ {
+ Debug( "loading resource file: %s\n", td->resources );
+ (void)runAndWait( args, env );
+ freeStrArr( args );
+ }
+ freeStrArr( env );
+ xResLoaded = TRUE;
+ }
+}
+
+void
+SetupDisplay( const char *arg )
+{
+ char **env;
+
+ env = systemEnv( (char *)0 );
+ (void)source( env, td->setup, arg );
+ freeStrArr( env );
+}
+
+void
+DeleteXloginResources()
+{
+ int i;
+ Atom prop;
+
+ if (!xResLoaded)
+ return;
+ xResLoaded = FALSE;
+ prop = XInternAtom( dpy, "SCREEN_RESOURCES", True );
+ XDeleteProperty( dpy, RootWindow( dpy, 0 ), XA_RESOURCE_MANAGER );
+ if (prop)
+ for (i = ScreenCount(dpy); --i >= 0; )
+ XDeleteProperty( dpy, RootWindow( dpy, i ), prop );
+ XSync( dpy, 0 );
+}
+
+
+int
+source( char **env, const char *file, const char *arg )
+{
+ char **args;
+ int ret;
+
+ if (file && file[0]) {
+ Debug( "source %s\n", file );
+ if (!(args = parseArgs( (char **)0, file )))
+ return waitCompose( 0,0,3 );
+ if (arg && !(args = addStrArr( args, arg, -1 )))
+ return waitCompose( 0,0,3 );
+ ret = runAndWait( args, env );
+ freeStrArr( args );
+ return ret;
+ }
+ return 0;
+}
+
+char **
+inheritEnv( char **env, const char **what )
+{
+ char *value;
+
+ for (; *what; ++what)
+ if ((value = getenv( *what )))
+ env = setEnv( env, *what, value );
+ return env;
+}
+
+char **
+baseEnv( const char *user )
+{
+ char **env;
+
+ env = 0;
+
+#ifdef _AIX
+ /* we need the tags SYSENVIRON: and USRENVIRON: in the call to setpenv() */
+ env = setEnv( env, "SYSENVIRON:", 0 );
+#endif
+
+ if (user) {
+ env = setEnv( env, "USER", user );
+#ifdef _AIX
+ env = setEnv( env, "LOGIN", user );
+#endif
+ env = setEnv( env, "LOGNAME", user );
+ }
+
+#ifdef _AIX
+ env = setEnv( env, "USRENVIRON:", 0 );
+#endif
+
+ env = inheritEnv( env, (const char **)exportList );
+
+ env = setEnv( env, "DISPLAY",
+ memcmp( td->name, "localhost:", 10 ) ?
+ td->name : td->name + 9 );
+
+ if (td->ctrl.path)
+ env = setEnv( env, "DM_CONTROL", fifoDir );
+
+ return env;
+}
+
+char **
+systemEnv( const char *user )
+{
+ char **env;
+
+ env = baseEnv( user );
+ if (td->authFile)
+ env = setEnv( env, "XAUTHORITY", td->authFile );
+ env = setEnv( env, "PATH", td->systemPath );
+ env = setEnv( env, "SHELL", td->systemShell );
+ return env;
+}
diff --git a/tdm/backend/sessreg.c b/tdm/backend/sessreg.c
new file mode 100644
index 000000000..b507f8141
--- /dev/null
+++ b/tdm/backend/sessreg.c
@@ -0,0 +1,307 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2005 Oswald Buddenhagen <[email protected]>
+
+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 OPEN GROUP 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 The Open Group 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 Open Group.
+
+*/
+
+/*
+
+ Author: Keith Packard, MIT X Consortium
+ Lastlog support and dynamic utmp entry allocation
+ by Andreas Stolcke <[email protected]>
+
+*/
+
+#define _FILE_OFFSET_BITS 64
+#include "dm.h"
+#include "dm_error.h"
+
+#if defined(__svr4__) || defined(__Lynx__) || defined(__QNX__) || defined(__APPLE__) || defined(_SEQUENT_) /*|| defined(USE_PAM)*/
+# define NO_LASTLOG
+#endif
+
+#ifndef NO_LASTLOG
+# ifdef HAVE_LASTLOG_H
+# include <lastlog.h>
+# endif
+# ifndef LLOG_FILE
+# ifdef _PATH_LASTLOGX
+# define LLOG_FILE _PATH_LASTLOGX
+# elif defined(_PATH_LASTLOG)
+# define LLOG_FILE _PATH_LASTLOG
+# else
+# define LLOG_FILE "/usr/adm/lastlog"
+# endif
+# endif
+#endif
+
+#if !defined(__svr4__) && !defined(__QNX__)
+# define SESSREG_HOST
+#endif
+
+#ifdef BSD
+# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+/* *BSD doesn't like a ':0' type entry in utmp */
+# define NO_UTMP
+# endif
+#endif
+
+#ifdef BSD_UTMP
+# ifndef TTYS_FILE
+# define TTYS_FILE "/etc/ttys"
+# endif
+#endif
+
+#ifdef _AIX
+# define UTL_PFX "xdm/"
+# define UTL_OFF strlen(UTL_PFX)
+#else
+# define UTL_OFF 0
+#endif
+
+#ifndef BSD_UTMP
+static unsigned
+crc32s( const unsigned char *str )
+{
+ int b;
+ unsigned crc = 0xffffffff, by;
+
+ for (; *str; str++) {
+ by = (crc & 255) ^ *str;
+ for (b = 0; b < 8; b++)
+ by = (by >> 1) ^ (-(by & 1) & 0xedb88320);
+ crc = (crc >> 8) ^ by;
+ }
+ return crc;
+}
+#endif
+
+void
+sessreg( struct display *d, int pid, const char *user, int uid )
+{
+ char *dot, *colon;
+ int left, clen;
+#ifdef BSD_UTMP
+ FILE *ttys;
+ int utmp, slot, freeslot;
+ STRUCTUTMP entry;
+#else
+ unsigned crc, i;
+#endif
+ int wtmp, c;
+#ifndef NO_LASTLOG
+ int llog;
+ struct LASTLOG ll;
+#endif
+ STRUCTUTMP ut_ent;
+
+ if (!d->useSessReg)
+ return;
+
+ bzero( &ut_ent, sizeof(ut_ent) );
+
+ if (pid) {
+ strncpy( ut_ent.ut_user, user, sizeof(ut_ent.ut_user) );
+#ifndef BSD_UTMP
+ ut_ent.ut_pid = pid;
+ ut_ent.ut_type = USER_PROCESS;
+ } else {
+ ut_ent.ut_type = DEAD_PROCESS;
+#endif
+ }
+ ut_ent.ut_time = time( 0 );
+
+ colon = strchr( d->name, ':' );
+ clen = strlen( colon );
+ if (clen > (int)(sizeof(ut_ent.ut_line) - UTL_OFF) - 2)
+ return; /* uhm, well ... */
+ if (colon == d->name) {
+#ifndef BSD_UTMP
+ strncpy( ut_ent.ut_id, d->name, sizeof(ut_ent.ut_id) );
+#endif
+ left = 0;
+ } else {
+#ifdef SESSREG_HOST
+# ifndef BSD_UTMP
+ if (pid)
+# endif
+ {
+ if (colon - d->name > (int)sizeof(ut_ent.ut_host)) {
+ ut_ent.ut_host[0] = '~';
+ memcpy( ut_ent.ut_host + 1,
+ colon - (sizeof(ut_ent.ut_host) - 1),
+ sizeof(ut_ent.ut_host) - 1 );
+ } else
+ memcpy( ut_ent.ut_host, d->name, colon - d->name );
+ }
+#endif
+#ifndef BSD_UTMP
+ crc = crc32s( d->name );
+ ut_ent.ut_id[0] = crc % 26 + 'A';
+ crc /= 26;
+ for (i = 1; i < sizeof(ut_ent.ut_id); i++) {
+ c = crc % 62;
+ crc /= 62;
+ ut_ent.ut_id[i] = c < 26 ? c + 'A' :
+ c < 52 ? c - 26 + 'a' : c - 52 + '0';
+ }
+#endif
+ left = sizeof(ut_ent.ut_line) - UTL_OFF - clen;
+ if (colon - d->name <= left) {
+ clen += colon - d->name;
+ colon = d->name;
+ left = 0;
+ } else {
+ dot = strchr( d->name, '.' );
+ if (dot && dot - d->name < left) {
+ memcpy( ut_ent.ut_line + UTL_OFF, d->name, left - 1 );
+ ut_ent.ut_line[UTL_OFF + left - 1] = '~';
+ } else {
+ memcpy( ut_ent.ut_line + UTL_OFF, d->name, left/2 - 1 );
+ ut_ent.ut_line[UTL_OFF + left/2 - 1] = '~';
+ if (dot) {
+ memcpy( ut_ent.ut_line + UTL_OFF + left/2,
+ dot - (left - left/2 - 1),
+ left - left/2 - 1 );
+ ut_ent.ut_line[UTL_OFF + left - 1] = '~';
+ } else
+ memcpy( ut_ent.ut_line + UTL_OFF + left/2,
+ colon - (left - left/2), left - left/2 );
+ }
+ }
+ }
+#ifdef UTL_PFX
+ memcpy( ut_ent.ut_line, UTL_PFX, UTL_OFF );
+#endif
+ memcpy( ut_ent.ut_line + UTL_OFF + left, colon, clen );
+
+#ifndef NO_UTMP
+# ifdef BSD_UTMP
+ if ((utmp = open( UTMP_FILE, O_RDWR )) < 0)
+ Debug( "cannot open utmp file " UTMP_FILE ": %m\n" );
+ else {
+
+ slot = 1;
+ if (pid) {
+ if (!(ttys = fopen( TTYS_FILE, "r" )))
+ LogWarn( "Cannot open tty file " TTYS_FILE ": %m\n" );
+ else {
+ int column0 = 1;
+ while ((c = getc( ttys )) != EOF)
+ if (c == '\n') {
+ slot++;
+ column0 = 1;
+ } else
+ column0 = 0;
+ if (!column0)
+ slot++;
+ fclose( ttys );
+ }
+ }
+ freeslot = -1;
+ lseek( utmp, slot * sizeof(entry), SEEK_SET );
+ while (read( utmp, (char *)&entry, sizeof(entry) ) == sizeof(entry)) {
+ if (!strncmp( entry.ut_line, ut_ent.ut_line,
+ sizeof(entry.ut_line) ))
+# ifdef SESSREG_HOST
+ if (!strncmp( entry.ut_host, ut_ent.ut_host,
+ sizeof(entry.ut_host) ))
+# endif
+ goto found;
+ if (freeslot < 0 && *entry.ut_user == '\0')
+ freeslot = slot;
+ slot++;
+ }
+ if (!pid) {
+ Debug( "utmp entry for display %s vanished\n", d->name );
+ goto skip;
+ }
+ if (freeslot >= 0)
+ slot = freeslot;
+ found:
+
+# ifdef SESSREG_HOST
+ if (!pid)
+ bzero( ut_ent.ut_host, sizeof(ut_ent.ut_host) );
+# endif
+ lseek( utmp, slot * sizeof(ut_ent), SEEK_SET );
+ if (write( utmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent))
+ LogError( "Cannot write utmp file " UTMP_FILE ": %m\n" );
+ skip:
+ close( utmp );
+ }
+# else
+ UTMPNAME( UTMP_FILE );
+ SETUTENT();
+ PUTUTLINE( &ut_ent );
+ ENDUTENT();
+# endif
+#endif
+
+ if ((wtmp = open( WTMP_FILE, O_WRONLY|O_APPEND )) < 0)
+ Debug( "cannot open wtmp file " WTMP_FILE ": %m\n" );
+ else {
+ if (write( wtmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent))
+ LogError( "Cannot write wtmp file " WTMP_FILE ": %m\n" );
+ close( wtmp );
+ }
+
+#ifndef NO_LASTLOG
+ if (pid) {
+ bzero( (char *)&ll, sizeof(ll) );
+ ll.ll_time = ut_ent.ut_time;
+ memcpy( ll.ll_line, ut_ent.ut_line, sizeof(ll.ll_line) );
+ memcpy( ll.ll_host, ut_ent.ut_host, sizeof(ll.ll_host) );
+# ifdef HAVE_UTMPX
+ updlastlogx( LLOG_FILE, uid, &ll );
+# else
+ if ((llog = open( LLOG_FILE, O_RDWR )) < 0)
+ Debug( "cannot open lastlog file " LLOG_FILE ": %m\n" );
+ else {
+ lseek( llog, (off_t)uid * sizeof(ll), SEEK_SET );
+ if (write( llog, (char *)&ll, sizeof(ll) ) != sizeof(ll))
+ LogError( "Cannot write llog file " WTMP_FILE ": %m\n" );
+ close( llog );
+ }
+# endif
+ }
+#else
+ (void)uid;
+#endif
+
+#ifdef UTL_PFX
+ {
+ char tmp[sizeof("/dev/") + sizeof(ut_ent.ut_line)];
+ mkdir( "/dev/" UTL_PFX, 0755 );
+ chmod( "/dev/" UTL_PFX, 0755 );
+ sprintf( tmp, "/dev/%.*s", sizeof(ut_ent.ut_line), ut_ent.ut_line );
+ if (pid)
+ close( creat( tmp, 0644 ) );
+ else
+ unlink( tmp );
+ }
+#endif
+}
diff --git a/tdm/backend/socket.c b/tdm/backend/socket.c
new file mode 100644
index 000000000..677a3d32f
--- /dev/null
+++ b/tdm/backend/socket.c
@@ -0,0 +1,418 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+Copyright 2002,2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * socket.c - Support for BSD sockets
+ */
+
+#include "dm.h"
+
+#if defined(XDMCP) && !defined(STREAMSCONN)
+
+#include "dm_error.h"
+#include "dm_socket.h"
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+static int c_request_port;
+
+static int
+CreateListeningSocket( struct sockaddr *sock_addr, int salen )
+{
+ int fd;
+#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+ int on = 0;
+#endif
+ const char *addrstring = "unknown";
+#if defined(IPv6) && defined(AF_INET6)
+ char addrbuf[INET6_ADDRSTRLEN];
+#endif
+
+ if (!request_port)
+ return -1;
+
+ if (debugLevel & DEBUG_CORE) {
+#if defined(IPv6) && defined(AF_INET6)
+ void *ipaddr;
+ if (sock_addr->sa_family == AF_INET6)
+ ipaddr = & ((struct sockaddr_in6 *)sock_addr)->sin6_addr;
+ else
+ ipaddr = & ((struct sockaddr_in *)sock_addr)->sin_addr;
+ addrstring =
+ inet_ntop( sock_addr->sa_family, ipaddr, addrbuf, sizeof(addrbuf) );
+
+#else
+ addrstring = inet_ntoa( ((struct sockaddr_in *)sock_addr)->sin_addr );
+#endif
+
+ Debug( "creating socket to listen on port %d of address %s\n",
+ request_port, addrstring );
+ }
+
+ if ((fd = socket( sock_addr->sa_family, SOCK_DGRAM, 0 )) == -1) {
+ LogError( "XDMCP socket creation failed, errno %d\n", errno );
+ return -1;
+ }
+#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+ setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on) );
+#endif
+
+ if (bind( fd, sock_addr, salen ) == -1) {
+ LogError( "error %d binding socket address %d\n", errno, request_port );
+ close( fd );
+ return -1;
+ }
+
+ RegisterCloseOnFork( fd );
+ RegisterInput( fd );
+ return fd;
+}
+
+struct socklist {
+ struct socklist *next;
+ struct socklist *mcastgroups;
+ struct sockaddr *addr;
+ int salen;
+ int addrlen;
+ int fd;
+ int ref; /* referenced bit - see UpdateListenSockets */
+};
+
+static struct socklist *listensocks;
+
+static void
+DestroyListeningSocket( struct socklist *s )
+{
+ struct socklist *g, *n;
+
+ if (s->fd >= 0) {
+ CloseNClearCloseOnFork( s->fd );
+ UnregisterInput( s->fd );
+ s->fd = -1;
+ }
+ if (s->addr) {
+ free( s->addr );
+ s->addr = NULL;
+ }
+ for (g = s->mcastgroups; g; g = n) {
+ n = g->next;
+ if (g->addr)
+ free( g->addr );
+ free( g );
+ }
+ s->mcastgroups = NULL;
+}
+
+static struct socklist*
+FindInList( struct socklist *list, ARRAY8Ptr addr )
+{
+ struct socklist *s;
+
+ for (s = list; s; s = s->next) {
+ if (s->addrlen == addr->length) {
+ char *addrdata;
+
+ switch (s->addr->sa_family) {
+ case AF_INET:
+ addrdata = (char *)
+ &(((struct sockaddr_in *)s->addr)->sin_addr.s_addr);
+ break;
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ addrdata = (char *)
+ &(((struct sockaddr_in6 *)s->addr)->sin6_addr.s6_addr);
+ break;
+#endif
+ default:
+ /* Unrecognized address family */
+ continue;
+ }
+ if (!memcmp( addrdata, addr->data, addr->length ))
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static struct socklist *
+CreateSocklistEntry( ARRAY8Ptr addr )
+{
+ struct socklist *s;
+
+ if (!(s = Calloc( 1, sizeof(struct socklist) )))
+ return NULL;
+
+ if (addr->length == 4) { /* IPv4 */
+ struct sockaddr_in *sin4;
+ sin4 = Calloc( 1, sizeof(struct sockaddr_in) );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin4->sin_len = sizeof(struct sockaddr_in);
+#endif
+ s->addr = (struct sockaddr *)sin4;
+ s->salen = sizeof(struct sockaddr_in);
+ s->addrlen = sizeof(struct in_addr);
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = htons( (short)request_port );
+ memcpy( &sin4->sin_addr, addr->data, addr->length );
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ else if (addr->length == 16) { /* IPv6 */
+ struct sockaddr_in6 *sin6;
+ sin6 = Calloc( 1, sizeof(struct sockaddr_in6) );
+#ifdef SIN6_LEN
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ s->addr = (struct sockaddr *)sin6;
+ s->salen = sizeof(struct sockaddr_in6);
+ s->addrlen = sizeof(struct in6_addr);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons( (short)request_port );
+ memcpy( &sin6->sin6_addr, addr->data, addr->length );
+ }
+#endif
+ else {
+ /* Unknown address type */
+ free( s );
+ s = NULL;
+ }
+ return s;
+}
+
+static void
+UpdateListener( ARRAY8Ptr addr, void **closure )
+{
+ struct socklist *s;
+
+ *closure = NULL;
+
+ if (addr == NULL) {
+ ARRAY8 tmpaddr;
+ struct in_addr in;
+#if defined(IPv6) && defined(AF_INET6)
+ struct in6_addr in6 = in6addr_any;
+ tmpaddr.length = sizeof(in6);
+ tmpaddr.data = (CARD8Ptr) &in6;
+ UpdateListener( &tmpaddr, closure );
+ if (*closure)
+ return;
+#endif
+ in.s_addr = htonl( INADDR_ANY );
+ tmpaddr.length = sizeof(in);
+ tmpaddr.data = (CARD8Ptr) &in;
+ UpdateListener( &tmpaddr, closure );
+ return;
+ }
+
+ if (c_request_port == request_port &&
+ (s = FindInList( listensocks, addr )))
+ {
+ *closure = (void *)s;
+ s->ref = 1;
+ return;
+ }
+
+ if (!(s = CreateSocklistEntry( addr )))
+ return;
+
+ if ((s->fd = CreateListeningSocket( s->addr, s->salen )) < 0) {
+ free( s->addr );
+ free( s );
+ return;
+ }
+ s->ref = 1;
+ s->next = listensocks;
+ listensocks = s;
+ *closure = (void *)s;
+}
+
+#define JOIN_MCAST_GROUP 0
+#define LEAVE_MCAST_GROUP 1
+
+static void
+ChangeMcastMembership( struct socklist *s, struct socklist *g, int op )
+{
+ int sockopt;
+
+ switch (s->addr->sa_family)
+ {
+ case AF_INET:
+ {
+ struct ip_mreq mreq;
+ memcpy( &mreq.imr_multiaddr,
+ &((struct sockaddr_in *)g->addr)->sin_addr,
+ sizeof(struct in_addr) );
+ memcpy( &mreq.imr_interface,
+ &((struct sockaddr_in *)s->addr)->sin_addr,
+ sizeof(struct in_addr) );
+ if (op == JOIN_MCAST_GROUP)
+ sockopt = IP_ADD_MEMBERSHIP;
+ else
+ sockopt = IP_DROP_MEMBERSHIP;
+ if (setsockopt( s->fd, IPPROTO_IP, sockopt,
+ &mreq, sizeof(mreq) ) < 0) {
+ LogError( "XDMCP socket multicast %s to %s failed, errno %d\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop",
+ inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ),
+ errno );
+ } else if (debugLevel & DEBUG_CORE) {
+ Debug( "XDMCP socket multicast %s to %s succeeded\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop",
+ inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ) );
+ }
+ return;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+# ifndef IPV6_JOIN_GROUP
+# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
+# endif
+# ifndef IPV6_LEAVE_GROUP
+# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
+# endif
+ case AF_INET6:
+ {
+ struct ipv6_mreq mreq6;
+ memcpy( &mreq6.ipv6mr_multiaddr,
+ &((struct sockaddr_in6 *)g->addr)->sin6_addr,
+ sizeof(struct in6_addr) );
+ mreq6.ipv6mr_interface = 0; /* TODO: fix this */
+ if (op == JOIN_MCAST_GROUP)
+ sockopt = IPV6_JOIN_GROUP;
+ else
+ sockopt = IPV6_LEAVE_GROUP;
+ if (setsockopt( s->fd, IPPROTO_IPV6, sockopt,
+ &mreq6, sizeof(mreq6) ) < 0)
+ {
+ int saveerr = errno;
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop( s->addr->sa_family,
+ &((struct sockaddr_in6 *)g->addr)->sin6_addr,
+ addrbuf, sizeof(addrbuf) );
+
+ LogError( "XDMCP socket multicast %s to %s failed, errno %d\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf,
+ saveerr );
+ } else if (debugLevel & DEBUG_CORE) {
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop( s->addr->sa_family,
+ &((struct sockaddr_in6 *)g->addr)->sin6_addr,
+ addrbuf, sizeof(addrbuf) );
+
+ Debug( "XDMCP socket multicast %s to %s succeeded\n",
+ (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf );
+ }
+ return;
+ }
+#endif
+ }
+}
+
+static void
+UpdateMcastGroup( ARRAY8Ptr addr, void **closure )
+{
+ struct socklist *s = (struct socklist *)*closure;
+ struct socklist *g;
+
+ if (!s)
+ return;
+
+ /* Already in the group, mark & continue */
+ if ((g = FindInList( s->mcastgroups, addr ))) {
+ g->ref = 1;
+ return;
+ }
+
+ /* Need to join the group */
+ if (!(g = CreateSocklistEntry( addr )))
+ return;
+
+ ChangeMcastMembership( s, g, JOIN_MCAST_GROUP );
+ free( g );
+}
+
+/* Open or close listening sockets to match the current settings read in
+ from the access database. */
+void
+UpdateListenSockets( void )
+{
+ struct socklist *s, *g, **ls, **lg;
+ void *tmpPtr = NULL;
+
+ /* Clear Ref bits - any not marked by UpdateCallback will be closed */
+ for (s = listensocks; s; s = s->next) {
+ s->ref = 0;
+ for (g = s->mcastgroups; g; g = g->next)
+ g->ref = 0;
+ }
+ ForEachListenAddr( UpdateListener, UpdateMcastGroup, &tmpPtr );
+ c_request_port = request_port;
+ for (ls = &listensocks; (s = *ls); )
+ if (!s->ref) {
+ DestroyListeningSocket( s );
+ *ls = s->next;
+ free( s );
+ } else {
+ ls = &s->next;
+ for (lg = &s->mcastgroups; (g = *lg); )
+ if (!g->ref) {
+ ChangeMcastMembership( s, g, LEAVE_MCAST_GROUP );
+ *lg = g->next;
+ free( g );
+ } else
+ lg = &g->next;
+ }
+}
+
+int
+AnyListenSockets( void )
+{
+ return listensocks != NULL;
+}
+
+int
+ProcessListenSockets( FD_TYPE *reads )
+{
+ struct socklist *s;
+ int ret = 0;
+
+ for (s = listensocks; s; s = s->next)
+ if (FD_ISSET( s->fd, reads )) {
+ ProcessRequestSocket( s->fd );
+ ret = 1;
+ }
+ return ret;
+}
+
+#endif /* !STREAMSCONN && XDMCP */
diff --git a/tdm/backend/streams.c b/tdm/backend/streams.c
new file mode 100644
index 000000000..f60fb955e
--- /dev/null
+++ b/tdm/backend/streams.c
@@ -0,0 +1,127 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002,2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * streams.c - Support for STREAMS
+ */
+
+#include "dm.h"
+
+#if defined(XDMCP) && defined(STREAMSCONN)
+
+#include "dm_error.h"
+
+#include <fcntl.h>
+#include <tiuser.h>
+#include <netconfig.h>
+#include <netdir.h>
+
+static int xdmcpFd = -1, c_request_port;
+
+void
+UpdateListenSockets( void )
+{
+ struct t_bind bind_addr;
+ struct netconfig *nconf;
+ struct nd_hostserv service;
+ struct nd_addrlist *servaddrs;
+ char bindbuf[15];
+ int it;
+
+ if (c_request_port == request_port)
+ return;
+ c_request_port = request_port;
+
+ if (xdmcpFd != -1) {
+ CloseNClearCloseOnFork( xdmcpFd );
+ UnregisterInput( xdmcpFd );
+ xdmcpFd = -1;
+ }
+
+ if (!request_port)
+ return;
+
+ Debug( "creating UDP stream %d\n", request_port );
+
+ nconf = getnetconfigent( "udp" );
+ if (!nconf) {
+ t_error( "getnetconfigent udp" );
+ return;
+ }
+
+ xdmcpFd = t_open( nconf->nc_device, O_RDWR, NULL );
+ if (xdmcpFd == -1) {
+ LogError( "XDMCP stream creation failed\n" );
+ t_error( "CreateWellKnownSockets(xdmcpFd): t_open failed" );
+ return;
+ }
+
+ service.h_host = HOST_SELF;
+ sprintf( bindbuf, "%d", request_port );
+ service.h_serv = bindbuf;
+ netdir_getbyname( nconf, &service, &servaddrs );
+ freenetconfigent( nconf );
+
+ bind_addr.qlen = 5;
+ bind_addr.addr.buf = servaddrs->n_addrs[0].buf;
+ bind_addr.addr.len = servaddrs->n_addrs[0].len;
+ bind_addr.addr.maxlen = servaddrs->n_addrs[0].len;
+ it = t_bind( xdmcpFd, &bind_addr, &bind_addr );
+ netdir_free( (char *)servaddrs, ND_ADDRLIST );
+ if (it < 0) {
+ LogError( "Error binding UDP port %d\n", request_port );
+ t_error( "CreateWellKnownSockets(xdmcpFd): t_bind failed" );
+ t_close( xdmcpFd );
+ xdmcpFd = -1;
+ return;
+ }
+ RegisterCloseOnFork( xdmcpFd );
+ RegisterInput( xdmcpFd );
+}
+
+int
+AnyListenSockets( void )
+{
+ return xdmcpFd != -1;
+}
+
+int
+ProcessRequestSockets( FD_TYPE *reads )
+{
+ if (xdmcpFd >= 0 && FD_ISSET( xdmcpFd, reads )) {
+ ProcessRequestSocket( xdmcpFd );
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* STREAMSCONN && XDMCP */
diff --git a/tdm/backend/util.c b/tdm/backend/util.c
new file mode 100644
index 000000000..7dd58f031
--- /dev/null
+++ b/tdm/backend/util.c
@@ -0,0 +1,637 @@
+/*
+
+Copyright 1989, 1998 The Open Group
+Copyright 2000-2005 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * various utility routines
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if 0 /*def USG; this was hpux once upon a time */
+# define NEED_UTSNAME
+#endif
+
+#ifdef NEED_UTSNAME
+# include <sys/utsname.h>
+#endif
+
+void *
+Calloc( size_t nmemb, size_t size )
+{
+ void *ret;
+
+ if (!(ret = calloc( nmemb, size )))
+ LogOutOfMem();
+ return ret;
+}
+
+void *
+Malloc( size_t size )
+{
+ void *ret;
+
+ if (!(ret = malloc( size )))
+ LogOutOfMem();
+ return ret;
+}
+
+void *
+Realloc( void *ptr, size_t size )
+{
+ void *ret;
+
+ if (!(ret = realloc( ptr, size )) && size)
+ LogOutOfMem();
+ return ret;
+}
+
+int
+StrCmp( const char *s1, const char *s2 )
+{
+ if (s1 == s2)
+ return 0;
+ if (!s1)
+ return -1;
+ if (!s2)
+ return 1;
+ return strcmp( s1, s2 );
+}
+
+void
+WipeStr( char *str )
+{
+ if (str) {
+ bzero( str, strlen( str ) );
+ free( str );
+ }
+}
+
+#ifndef HAVE_STRNLEN
+int
+StrNLen( const char *s, int max )
+{
+ unsigned l;
+
+ for (l = 0; l < (unsigned)max && s[l]; l++);
+ return l;
+}
+#endif
+
+/* duplicate src; wipe & free old dst string */
+int
+ReStrN( char **dst, const char *src, int len )
+{
+ char *ndst = 0;
+
+ if (src) {
+ if (len < 0)
+ len = strlen( src );
+ if (*dst && !memcmp( *dst, src, len ) && !(*dst)[len])
+ return 1;
+ if (!(ndst = Malloc( len + 1 ))) {
+ WipeStr( *dst );
+ *dst = 0;
+ return 0;
+ }
+ memcpy( ndst, src, len );
+ ndst[len] = 0;
+ }
+ WipeStr( *dst ); /* make an option, if we should become heavily used */
+ *dst = ndst;
+ return 2;
+}
+
+int
+ReStr( char **dst, const char *src )
+{
+ return ReStrN( dst, src, -1 );
+}
+
+/* duplicate src */
+int
+StrNDup( char **dst, const char *src, int len )
+{
+ if (src) {
+ if (len < 0)
+ len = strlen( src );
+ if (!(*dst = Malloc( len + 1 )))
+ return 0;
+ memcpy( *dst, src, len );
+ (*dst)[len] = 0;
+ } else
+ *dst = 0;
+ return 1;
+}
+
+int
+StrDup( char **dst, const char *src )
+{
+ return StrNDup( dst, src, -1 );
+}
+
+/* append any number of strings to dst */
+int
+StrApp( char **dst, ... )
+{
+ int len;
+ char *bk, *pt, *dp;
+ va_list va;
+
+ len = 1;
+ if (*dst)
+ len += strlen( *dst );
+ va_start( va, dst );
+ for (;;) {
+ pt = va_arg( va, char * );
+ if (!pt)
+ break;
+ len += strlen( pt );
+ }
+ va_end( va );
+ if (!(bk = Malloc( len ))) {
+ if (*dst) {
+ free( *dst );
+ *dst = 0;
+ }
+ return 0;
+ }
+ dp = bk;
+ if (*dst) {
+ len = strlen( *dst );
+ memcpy( dp, *dst, len );
+ dp += len;
+ free( *dst );
+ }
+ va_start( va, dst );
+ for (;;) {
+ pt = va_arg( va, char * );
+ if (!pt)
+ break;
+ len = strlen( pt );
+ memcpy( dp, pt, len );
+ dp += len;
+ }
+ va_end( va );
+ *dp = '\0';
+ *dst = bk;
+ return 1;
+}
+
+
+char **
+initStrArr( char **arr )
+{
+ if (!arr && (arr = Malloc( sizeof(char *) )))
+ arr[0] = 0;
+ return arr;
+}
+
+int
+arrLen( char **arr )
+{
+ int nu = 0;
+ if (arr)
+ for (; arr[nu]; nu++);
+ return nu;
+}
+
+static char **
+extStrArr( char **arr, char ***strp )
+{
+ char **rarr;
+ int nu;
+
+ nu = arrLen( arr );
+ if ((rarr = Realloc( arr, sizeof(char *) * (nu + 2) ))) {
+ rarr[nu + 1] = 0;
+ *strp = rarr + nu;
+ return rarr;
+ }
+ freeStrArr( arr );
+ return 0;
+}
+
+char **
+addStrArr( char **arr, const char *str, int len )
+{
+ char **strp;
+
+ if ((arr = extStrArr( arr, &strp ))) {
+ if (StrNDup( strp, str, len ))
+ return arr;
+ freeStrArr( arr );
+ }
+ return 0;
+}
+
+char **
+xCopyStrArr( int rn, char **arr )
+{
+ char **rarr;
+ int nu;
+
+ nu = arrLen( arr );
+ if ((rarr = Calloc( sizeof(char *), nu + rn + 1 )))
+ memcpy( rarr + rn, arr, sizeof(char *) * nu );
+ return rarr;
+}
+
+void
+freeStrArr( char **arr )
+{
+ char **a;
+
+ if (arr) {
+ for (a = arr; *a; a++)
+ free( *a );
+ free( arr );
+ }
+}
+
+
+char **
+parseArgs( char **argv, const char *string )
+{
+ const char *word;
+ char **strp, *str;
+ int wlen;
+
+ if (!(argv = initStrArr( argv )))
+ return 0;
+ while (*string) {
+ if (isspace( *string )) {
+ string++;
+ continue;
+ }
+ word = string;
+ wlen = 0;
+ do {
+ if (*string == '\\') {
+ if (!*++string)
+ string--;
+ wlen++;
+ } else if (*string == '\'') {
+ while (*++string != '\'' && *string)
+ wlen++;
+ } else if (*string == '"') {
+ while (*++string != '"' && *string) {
+ if (*string == '\\') {
+ if (!*++string)
+ string--;
+ }
+ wlen++;
+ }
+ } else
+ wlen++;
+ } while (*++string && !isspace( *string ));
+ if (!(argv = extStrArr( argv, &strp )))
+ return 0;
+ if (!(*strp = str = Malloc( wlen + 1 ))) {
+ freeStrArr( argv );
+ return 0;
+ }
+ do {
+ if (*word == '\\') {
+ if (!*++word)
+ word--;
+ *str++ = *word;
+ } else if (*word == '\'') {
+ while (*++word != '\'' && *word)
+ *str++ = *word;
+ } else if (*word == '"') {
+ while (*++word != '"' && *word) {
+ if (*word == '\\') {
+ if (!*++word)
+ word--;
+ }
+ *str++ = *word;
+ }
+ } else
+ *str++ = *word;
+ } while (*++word && !isspace( *word ));
+ *str = 0;
+ }
+ return argv;
+}
+
+
+const char *
+getEnv( char **e, const char *name )
+{
+ if (e) {
+ int l = strlen( name );
+ for (; *e; e++)
+ if (!memcmp( *e, name, l ) && (*e)[l] == '=')
+ return (*e) + l + 1;
+ }
+ return 0;
+}
+
+char **
+setEnv( char **e, const char *name, const char *value )
+{
+ char **new, **old;
+ char *newe;
+ int envsize;
+ int l;
+
+#ifdef _AIX
+ /* setpenv() depends on "SYSENVIRON:", not "SYSENVIRON:=" */
+ if (!value) {
+ if (!StrDup( &newe, name ))
+ return e;
+ } else
+#endif
+ {
+ newe = 0;
+ if (!StrApp( &newe, name, "=", value, (char *)0 ))
+ return e;
+ }
+ envsize = 0;
+ if (e) {
+ l = strlen( name );
+ for (old = e; *old; old++)
+ if (!memcmp( *old, name, l ) && ((*old)[l] == '=' || !(*old)[l]))
+ {
+ free( *old );
+ *old = newe;
+ return e;
+ }
+ envsize = old - e;
+ }
+ if (!(new = (char **)
+ Realloc( (char *)e, (unsigned)((envsize + 2) * sizeof(char *)) )))
+ {
+ free( newe );
+ return e;
+ }
+ new[envsize] = newe;
+ new[envsize + 1] = 0;
+ return new;
+}
+
+char **
+putEnv( const char *string, char **env )
+{
+ char *n;
+ char *b;
+
+ if (!(b = strchr( string, '=' )))
+ return NULL;
+ if (!StrNDup( &n, string, b - string ))
+ return NULL;
+ env = setEnv( env, n, b + 1 );
+ free( n );
+ return env;
+}
+
+static int
+GetHostname( char *buf, int maxlen )
+{
+ int len;
+
+#ifdef NEED_UTSNAME
+ /*
+ * same host name crock as in server and xinit.
+ */
+ struct utsname name;
+
+ uname( &name );
+ len = strlen( name.nodename );
+ if (len >= maxlen) len = maxlen - 1;
+ memcpy( buf, name.nodename, len );
+ buf[len] = '\0';
+#else
+ buf[0] = '\0';
+ (void)gethostname( buf, maxlen );
+ buf[maxlen - 1] = '\0';
+ len = strlen( buf );
+#endif /* NEED_UTSNAME */
+ return len;
+}
+
+static char localHostbuf[256];
+static int gotLocalHostname;
+
+const char *
+localHostname( void )
+{
+ if (!gotLocalHostname)
+ {
+ GetHostname( localHostbuf, sizeof(localHostbuf) - 1 );
+ gotLocalHostname = 1;
+ }
+ return localHostbuf;
+}
+
+static int
+AtomicIO( ssize_t (*f)( int, void *, size_t ), int fd, void *buf, int count )
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = f( fd, (void *)((char *)buf + rlen), count - rlen );
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+int
+Reader( int fd, void *buf, int count )
+{
+ return AtomicIO( read, fd, buf, count );
+}
+
+int
+Writer( int fd, const void *buf, int count )
+{
+ return AtomicIO( (ssize_t(*)( int, void *, size_t ))write,
+ fd, (void *)buf, count );
+}
+
+int
+fGets( char *buf, int max, FILE *f )
+{
+ int len;
+
+ if (!fgets( buf, max, f ))
+ return -1;
+ len = strlen( buf );
+ if (len && buf[len - 1] == '\n')
+ buf[--len] = 0;
+ return len;
+}
+
+time_t
+mTime( const char *fn )
+{
+ struct stat st;
+
+ if (stat( fn, &st ))
+ return -1;
+ else
+ return st.st_mtime;
+}
+
+void
+randomStr( char *s )
+{
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ unsigned i, rn = secureRandom();
+
+ for (i = 0; i < 6; i++) {
+ *s++ = letters[rn % 62];
+ rn /= 62;
+ }
+ *s = 0;
+}
+
+static int
+StrNChrCnt( const char *s, int slen, char c )
+{
+ int i, cnt;
+
+ for (i = cnt = 0; i < slen && s[i]; i++)
+ if (s[i] == c)
+ cnt++;
+ return cnt;
+}
+
+/* X -from ip6-addr does not work here, so i don't know whether this is needed.
+#define IP6_MAGIC
+*/
+
+void
+ListSessions( int flags, struct display *d, void *ctx,
+ void (*emitXSess)( struct display *, struct display *, void * ),
+ void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) )
+{
+ struct display *di;
+#ifdef IP6_MAGIC
+ int le, dot;
+#endif
+#ifdef BSD_UTMP
+ int fd;
+ struct utmp ut[1];
+#else
+ STRUCTUTMP *ut;
+#endif
+
+ for (di = displays; di; di = di->next)
+ if (((flags & lstRemote) || (di->displayType & d_location) == dLocal) &&
+ (di->status == remoteLogin ||
+ ((flags & lstPassive) ? di->status == running : di->userSess >= 0)))
+ emitXSess( di, d, ctx );
+
+ if (!(flags & lstTTY))
+ return;
+
+#ifdef BSD_UTMP
+ if ((fd = open( UTMP_FILE, O_RDONLY )) < 0)
+ return;
+ while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0])) {
+ if (*ut->ut_user) { /* no idea how to list passive TTYs on BSD */
+#else
+ SETUTENT();
+ while ((ut = GETUTENT())) {
+ if (ut->ut_type == USER_PROCESS
+# if 0 /* list passive TTYs at all? not too sensible, i think. */
+ || ((flags & lstPassive) && ut->ut_type == LOGIN_PROCESS)
+# endif
+ )
+ {
+#endif
+ if (*ut->ut_host) { /* from remote or x */
+ if (!(flags & lstRemote))
+ continue;
+ } else {
+ /* hack around broken konsole which does not set ut_host. */
+ /* this check is probably linux-specific. */
+ /* alternatively we could open the device and try VT_OPENQRY. */
+ if (memcmp( ut->ut_line, "tty", 3 ) ||
+ !isdigit( ut->ut_line[3] ))
+ continue;
+ }
+ if (StrNChrCnt( ut->ut_line, sizeof(ut->ut_line), ':' ))
+ continue; /* x login */
+ switch (StrNChrCnt( ut->ut_host, sizeof(ut->ut_host), ':' )) {
+ case 1: /* x terminal */
+ continue;
+ default:
+#ifdef IP6_MAGIC
+ /* unknown - IPv6 makes things complicated */
+ le = StrNLen( ut->ut_host, sizeof(ut->ut_host) );
+ /* cut off screen number */
+ for (dot = le; ut->ut_host[--dot] != ':'; )
+ if (ut->ut_host[dot] == '.') {
+ le = dot;
+ break;
+ }
+ for (di = displays; di; di = di->next)
+ if (!memcmp( di->name, ut->ut_host, le ) && !di->name[le])
+ goto cont; /* x terminal */
+ break;
+ cont:
+ continue;
+ case 0: /* no x terminal */
+#endif
+ break;
+ }
+ emitTTYSess( ut, d, ctx );
+ }
+ }
+#ifdef BSD_UTMP
+ close( fd );
+#else
+ ENDUTENT();
+#endif
+}
+
diff --git a/tdm/backend/xdmauth.c b/tdm/backend/xdmauth.c
new file mode 100644
index 000000000..86257c651
--- /dev/null
+++ b/tdm/backend/xdmauth.c
@@ -0,0 +1,267 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2001,2003 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * generate authorization data for XDM-AUTHORIZATION-1 as per XDMCP spec
+ */
+
+#include <config.h>
+
+#ifdef HASXDMAUTH
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+static char auth_name[256];
+static int auth_name_len;
+
+void
+XdmInitAuth( unsigned short name_len, const char *name )
+{
+ if (name_len > 256)
+ name_len = 256;
+ auth_name_len = name_len;
+ memmove( auth_name, name, name_len );
+}
+
+/*
+ * Generate authorization for XDM-AUTHORIZATION-1
+ *
+ * When being used with XDMCP, 8 bytes are generated for the session key
+ * (sigma), as the random number (rho) is already shared between xdm and
+ * the server. Otherwise, we'll prepend a random number to pass in the file
+ * between xdm and the server (16 bytes total)
+ */
+
+static Xauth *
+XdmGetAuthHelper( unsigned short namelen, const char *name, int includeRho )
+{
+ Xauth *new;
+
+ if (!(new = (Xauth *)Malloc( sizeof(Xauth) )))
+ return (Xauth *)0;
+ new->family = FamilyWild;
+ new->address_length = 0;
+ new->address = 0;
+ new->number_length = 0;
+ new->number = 0;
+ if (includeRho)
+ new->data_length = 16;
+ else
+ new->data_length = 8;
+
+ new->data = (char *)Malloc( new->data_length );
+ if (!new->data) {
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ new->name = (char *)Malloc( namelen );
+ if (!new->name) {
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ memmove( (char *)new->name, name, namelen );
+ new->name_length = namelen;
+ if (!GenerateAuthData( (char *)new->data, new->data_length )) {
+ free( (char *)new->name );
+ free( (char *)new->data );
+ free( (char *)new );
+ return (Xauth *)0;
+ }
+ /*
+ * set the first byte of the session key to zero as it
+ * is a DES key and only uses 56 bits
+ */
+ ((char *)new->data)[new->data_length - 8] = '\0';
+ Debug( "local server auth %02[*hhx\n", new->data_length, new->data );
+ return new;
+}
+
+Xauth *
+XdmGetAuth( unsigned short namelen, const char *name )
+{
+ return XdmGetAuthHelper( namelen, name, TRUE );
+}
+
+#ifdef XDMCP
+
+void
+XdmGetXdmcpAuth( struct protoDisplay *pdpy,
+ unsigned short authorizationNameLen,
+ const char *authorizationName )
+{
+ Xauth *fileauth, *xdmcpauth;
+
+ if (pdpy->fileAuthorization && pdpy->xdmcpAuthorization)
+ return;
+ xdmcpauth = XdmGetAuthHelper( authorizationNameLen, authorizationName,
+ FALSE );
+ if (!xdmcpauth)
+ return;
+ fileauth = (Xauth *)Malloc( sizeof(Xauth) );
+ if (!fileauth) {
+ XauDisposeAuth( xdmcpauth );
+ return;
+ }
+ /* build the file auth from the XDMCP auth */
+ *fileauth = *xdmcpauth;
+ fileauth->name = Malloc( xdmcpauth->name_length );
+ fileauth->data = Malloc( 16 );
+ fileauth->data_length = 16;
+ if (!fileauth->name || !fileauth->data) {
+ XauDisposeAuth( xdmcpauth );
+ if (fileauth->name)
+ free( (char *)fileauth->name );
+ if (fileauth->data)
+ free( (char *)fileauth->data );
+ free( (char *)fileauth );
+ return;
+ }
+ /*
+ * for the file authorization, prepend the random number (rho)
+ * which is simply the number we've been passing back and
+ * forth via XDMCP
+ */
+ memmove( fileauth->name, xdmcpauth->name, xdmcpauth->name_length );
+ memmove( fileauth->data, pdpy->authenticationData.data, 8 );
+ memmove( fileauth->data + 8, xdmcpauth->data, 8 );
+ Debug( "accept packet auth %02[*hhx\nauth file auth %02[*hhx\n",
+ xdmcpauth->data_length, xdmcpauth->data,
+ fileauth->data_length, fileauth->data );
+ /* encrypt the session key for its trip back to the server */
+ XdmcpWrap( (unsigned char *)xdmcpauth->data, (unsigned char *)&pdpy->key,
+ (unsigned char *)xdmcpauth->data, 8 );
+ pdpy->fileAuthorization = fileauth;
+ pdpy->xdmcpAuthorization = xdmcpauth;
+}
+
+#define atox(c) ('0' <= c && c <= '9' ? c - '0' : \
+ 'a' <= c && c <= 'f' ? c - 'a' + 10 : \
+ 'A' <= c && c <= 'F' ? c - 'A' + 10 : -1)
+
+static int
+HexToBinary( char *key )
+{
+ char *out, *in;
+ int top, bottom;
+
+ in = key + 2;
+ out= key;
+ while (in[0] && in[1]) {
+ top = atox( in[0] );
+ if (top == -1)
+ return 0;
+ bottom = atox( in[1] );
+ if (bottom == -1)
+ return 0;
+ *out++ = (top << 4) | bottom;
+ in += 2;
+ }
+ if (in[0])
+ return 0;
+ *out++ = '\0';
+ return 1;
+}
+
+/*
+ * Search the Keys file for the entry matching this display. This
+ * routine accepts either plain ascii strings for keys, or hex-encoded numbers
+ */
+
+static int
+XdmGetKey( struct protoDisplay *pdpy, ARRAY8Ptr displayID )
+{
+ FILE *keys;
+ char line[1024], id[1024], key[1024];
+ int keylen;
+
+ Debug( "lookup key for %.*s\n", displayID->length, displayID->data );
+ keys = fopen( keyFile, "r" );
+ if (!keys)
+ return FALSE;
+ while (fgets( line, sizeof(line), keys )) {
+ if (line[0] == '#' || sscanf( line, "%s %s", id, key ) != 2)
+ continue;
+ bzero( line, sizeof(line) );
+ Debug( "key entry for %\"s %d bytes\n", id, strlen( key ) );
+ if (strlen( id ) == displayID->length &&
+ !strncmp( id, (char *)displayID->data, displayID->length ))
+ {
+ if (!strncmp( key, "0x", 2 ) || !strncmp( key, "0X", 2 ))
+ if (!HexToBinary( key ))
+ break;
+ keylen = strlen( key );
+ while (keylen < 7)
+ key[keylen++] = '\0';
+ pdpy->key.data[0] = '\0';
+ memmove( pdpy->key.data + 1, key, 7 );
+ bzero( key, sizeof(key) );
+ fclose( keys );
+ return TRUE;
+ }
+ }
+ bzero( line, sizeof(line) );
+ bzero( key, sizeof(key) );
+ fclose( keys );
+ return FALSE;
+}
+
+/*ARGSUSED*/
+int
+XdmCheckAuthentication( struct protoDisplay *pdpy,
+ ARRAY8Ptr displayID,
+ ARRAY8Ptr authenticationName ATTR_UNUSED,
+ ARRAY8Ptr authenticationData )
+{
+ XdmAuthKeyPtr incoming;
+
+ if (!XdmGetKey( pdpy, displayID ))
+ return FALSE;
+ if (authenticationData->length != 8)
+ return FALSE;
+ XdmcpUnwrap( authenticationData->data, (unsigned char *)&pdpy->key,
+ authenticationData->data, 8 );
+ Debug( "request packet auth %02[*hhx\n",
+ authenticationData->length, authenticationData->data );
+ if (!XdmcpCopyARRAY8( authenticationData, &pdpy->authenticationData ))
+ return FALSE;
+ incoming = (XdmAuthKeyPtr)authenticationData->data;
+ XdmcpIncrementKey( incoming );
+ XdmcpWrap( authenticationData->data, (unsigned char *)&pdpy->key,
+ authenticationData->data, 8 );
+ return TRUE;
+}
+
+#endif /* XDMCP */
+#endif /* HASXDMAUTH (covering the entire file) */
diff --git a/tdm/backend/xdmcp.c b/tdm/backend/xdmcp.c
new file mode 100644
index 000000000..6abaf5fc8
--- /dev/null
+++ b/tdm/backend/xdmcp.c
@@ -0,0 +1,1165 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+Copyright 2001-2004 Oswald Buddenhagen <[email protected]>
+
+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
+ *
+ * xdmcp.c - Support for XDMCP
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_auth.h"
+#include "dm_socket.h"
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#include <netdb.h>
+#if defined(IPv6) && defined(AF_INET6)
+# include <arpa/inet.h>
+#endif
+
+/*
+ * Forward reference
+ */
+static void broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void forward_respond (struct sockaddr *from, int fromlen, int length, int fd);
+static void manage( struct sockaddr *from, int fromlen, int length, int fd );
+static void query_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void request_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, int fd );
+static void send_alive( struct sockaddr *from, int fromlen, int length, int fd );
+static void send_decline( struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status, int fd );
+static void send_failed( struct sockaddr *from, int fromlen, const char *name, CARD32 sessionID, const char *reason, int fd );
+static void send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd );
+static void send_unwilling( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
+static void send_willing( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
+
+
+static XdmcpBuffer buffer;
+
+static void
+sendForward( CARD16 connectionType, ARRAY8Ptr address, char *closure )
+{
+#ifdef AF_INET
+ struct sockaddr_in in_addr;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_in6 in6_addr;
+#endif
+#ifdef AF_DECnet
+#endif
+ struct sockaddr *addr;
+ int addrlen;
+
+ switch (connectionType) {
+#ifdef AF_INET
+ case FamilyInternet:
+ addr = (struct sockaddr *)&in_addr;
+ bzero( (char *)&in_addr, sizeof(in_addr) );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons( (short)XDM_UDP_PORT );
+ if (address->length != 4)
+ return;
+ memmove( (char *)&in_addr.sin_addr, address->data, address->length );
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+ addr = (struct sockaddr *)&in6_addr;
+ bzero( (char *)&in6_addr, sizeof(in6_addr) );
+# ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+# endif
+ in6_addr.sin6_family = AF_INET6;
+ in6_addr.sin6_port = htons( (short)XDM_UDP_PORT );
+ if (address->length != 16)
+ return;
+ memmove( (char *)&in6_addr.sin6_addr, address->data, address->length );
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+#ifdef AF_DECnet
+ case FamilyDECnet:
+#endif
+ default:
+ return;
+ }
+ XdmcpFlush( (int)closure, &buffer, (XdmcpNetaddr)addr, addrlen );
+ return;
+}
+
+static void
+ClientAddress( struct sockaddr *from,
+ ARRAY8Ptr addr, /* return */
+ ARRAY8Ptr port, /* return */
+ CARD16 *type ) /* return */
+{
+ int length, family;
+ char *data;
+
+ data = NetaddrPort( (XdmcpNetaddr)from, &length );
+ XdmcpAllocARRAY8( port, length );
+ memmove( port->data, data, length );
+ port->length = length;
+
+ family = ConvertAddr( (XdmcpNetaddr)from, &length, &data );
+ XdmcpAllocARRAY8( addr, length );
+ memmove( addr->data, data, length );
+ addr->length = length;
+
+ *type = family;
+}
+
+static void
+all_query_respond( struct sockaddr *from, int fromlen,
+ ARRAYofARRAY8Ptr authenticationNames,
+ xdmOpCode type, int fd )
+{
+ ARRAY8Ptr authenticationName;
+ ARRAY8 status;
+ ARRAY8 addr;
+ CARD16 connectionType;
+ int family;
+ int length;
+
+ family = ConvertAddr( (XdmcpNetaddr)from, &length, &(addr.data) );
+ addr.length = length; /* convert int to short */
+ Debug( "all_query_respond: conntype=%d, addr=%02[*:hhx\n",
+ family, addr.length, addr.data );
+ if (family < 0)
+ return;
+ connectionType = family;
+
+ if (type == INDIRECT_QUERY)
+ RememberIndirectClient( &addr, connectionType );
+ else
+ ForgetIndirectClient( &addr, connectionType );
+
+ authenticationName = ChooseAuthentication( authenticationNames );
+ if (Willing( &addr, connectionType, authenticationName, &status, type ))
+ send_willing( from, fromlen, authenticationName, &status, fd );
+ else
+ if (type == QUERY)
+ send_unwilling( from, fromlen, authenticationName, &status, fd );
+ XdmcpDisposeARRAY8( &status );
+}
+
+static void
+indirect_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ ARRAYofARRAY8 queryAuthenticationNames;
+ ARRAY8 clientAddress;
+ ARRAY8 clientPort;
+ CARD16 connectionType;
+ int expectedLen;
+ int i;
+ XdmcpHeader header;
+ int localHostAsWell;
+
+ Debug( "<indirect> respond %d\n", length );
+ if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
+ return;
+ expectedLen = 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ expectedLen += 2 + queryAuthenticationNames.data[i].length;
+ if (length == expectedLen) {
+ ClientAddress( from, &clientAddress, &clientPort, &connectionType );
+ /*
+ * set up the forward query packet
+ */
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)FORWARD_QUERY;
+ header.length = 0;
+ header.length += 2 + clientAddress.length;
+ header.length += 2 + clientPort.length;
+ header.length += 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ header.length += 2 + queryAuthenticationNames.data[i].length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, &clientAddress );
+ XdmcpWriteARRAY8( &buffer, &clientPort );
+ XdmcpWriteARRAYofARRAY8( &buffer, &queryAuthenticationNames );
+
+ localHostAsWell =
+ ForEachMatchingIndirectHost( &clientAddress, connectionType,
+ sendForward, (char *)fd );
+
+ XdmcpDisposeARRAY8( &clientAddress );
+ XdmcpDisposeARRAY8( &clientPort );
+ if (localHostAsWell)
+ all_query_respond( from, fromlen, &queryAuthenticationNames,
+ INDIRECT_QUERY, fd );
+ } else
+ Debug( "<indirect> length error got %d expect %d\n",
+ length, expectedLen );
+ XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
+}
+
+void
+ProcessRequestSocket( int fd )
+{
+ XdmcpHeader header;
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_storage addr;
+#else
+ struct sockaddr addr;
+#endif
+ int addrlen = sizeof(addr);
+
+ Debug( "ProcessRequestSocket\n" );
+ bzero( (char *)&addr, sizeof(addr) );
+ if (!XdmcpFill( fd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) {
+ Debug( "XdmcpFill failed\n" );
+ return;
+ }
+ if (!XdmcpReadHeader( &buffer, &header )) {
+ Debug( "XdmcpReadHeader failed\n" );
+ return;
+ }
+ if (header.version != XDM_PROTOCOL_VERSION) {
+ Debug( "XDMCP header version read was %d, expected %d\n",
+ header.version, XDM_PROTOCOL_VERSION );
+ return;
+ }
+ Debug( "header: %d %d %d\n", header.version, header.opcode, header.length );
+ switch (header.opcode) {
+ case BROADCAST_QUERY:
+ broadcast_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case QUERY:
+ query_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case INDIRECT_QUERY:
+ indirect_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case FORWARD_QUERY:
+ forward_respond ((struct sockaddr *)&addr, addrlen, header.length, fd);
+ break;
+ case REQUEST:
+ request_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case MANAGE:
+ manage( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case KEEPALIVE:
+ send_alive( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ }
+}
+
+/*
+ * respond to a request on the UDP socket.
+ */
+
+static void
+direct_query_respond( struct sockaddr *from, int fromlen,
+ int length, xdmOpCode type, int fd )
+{
+ ARRAYofARRAY8 queryAuthenticationNames;
+ int expectedLen;
+ int i;
+
+ if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
+ return;
+ expectedLen = 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ expectedLen += 2 + queryAuthenticationNames.data[i].length;
+ if (length == expectedLen)
+ all_query_respond( from, fromlen, &queryAuthenticationNames, type, fd );
+ XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
+}
+
+static void
+query_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ Debug( "<query> respond %d\n", length );
+ direct_query_respond( from, fromlen, length, QUERY, fd );
+}
+
+static void
+broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ direct_query_respond( from, fromlen, length, BROADCAST_QUERY, fd );
+}
+
+/* computes an X display name */
+
+static char *
+NetworkAddressToName( CARD16 connectionType, ARRAY8Ptr connectionAddress,
+ struct sockaddr *originalAddress, CARD16 displayNumber )
+{
+ switch (connectionType) {
+ case FamilyInternet:
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+#endif
+ {
+ CARD8 *data;
+ struct hostent *hostent;
+ char *hostname = NULL;
+ char *name;
+ const char *localhost;
+ int multiHomed = 0;
+ int type;
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai = NULL, *nai, hints;
+ char dotted[INET6_ADDRSTRLEN];
+
+ if (connectionType == FamilyInternet6)
+ type = AF_INET6;
+ else
+#endif
+ type = AF_INET;
+
+ data = connectionAddress->data;
+ hostent = gethostbyaddr( (char *)data,
+ connectionAddress->length, type );
+ if (hostent) {
+ if (sourceAddress) {
+#if defined(IPv6) && defined(AF_INET6)
+ bzero( &hints, sizeof(hints) );
+ hints.ai_flags = AI_CANONNAME;
+ if (!getaddrinfo( hostent->h_name, NULL, &hints, &ai )) {
+ hostname = ai->ai_canonname;
+ for (nai = ai->ai_next; nai; nai = nai->ai_next)
+ if (ai->ai_protocol == nai->ai_protocol &&
+ memcmp( ai->ai_addr, nai->ai_addr,
+ ai->ai_addrlen ))
+ multiHomed = 1;
+ }
+#else
+ hostent = gethostbyname( hostent->h_name );
+ if (hostent && hostent->h_addrtype == AF_INET) {
+ multiHomed = hostent->h_addr_list[1] != NULL;
+ hostname = hostent->h_name;
+ }
+#endif
+ } else
+ hostname = hostent->h_name;
+ }
+
+ localhost = localHostname();
+
+ /*
+ * protect against bogus host names
+ */
+ if (hostname && *hostname && *hostname != '.' && !multiHomed) {
+ if (!strcmp( localhost, hostname ))
+ ASPrintf( &name, "localhost:%d", displayNumber );
+ else {
+ if (removeDomainname) {
+ char *remoteDot;
+ char *localDot;
+
+ /* check for a common domain name. This
+ * could reduce names by recognising common
+ * super-domain names as well, but I don't think
+ * this is as useful, and will confuse more
+ * people
+ */
+ if ((localDot = strchr( localhost, '.' )) &&
+ (remoteDot = strchr( hostname, '.' )))
+ {
+ /* smash the name in place; it won't
+ * be needed later.
+ */
+ if (!strcmp( localDot+1, remoteDot+1 ))
+ *remoteDot = '\0';
+ }
+ }
+
+ ASPrintf( &name, "%s:%d", hostname, displayNumber );
+ }
+ } else {
+#if defined(IPv6) && defined(AF_INET6)
+ if (multiHomed) {
+ if (connectionType == FamilyInternet) {
+ data = (CARD8 *)
+ &((struct sockaddr_in *)originalAddress)->sin_addr;
+ } else {
+ data = (CARD8 *)
+ &((struct sockaddr_in6 *)originalAddress)->sin6_addr;
+ }
+ }
+ inet_ntop( type, data, dotted, sizeof(dotted) );
+ ASPrintf( &name, "%s:%d", dotted, displayNumber );
+#else
+ if (multiHomed)
+ data = (CARD8 *)
+ &((struct sockaddr_in *)originalAddress)->sin_addr;
+ ASPrintf( &name, "%[4|'.'hhu:%d", data, displayNumber );
+#endif
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ if (ai)
+ freeaddrinfo( ai );
+#endif
+ return name;
+ }
+#ifdef DNET
+ case FamilyDECnet:
+ return NULL;
+#endif /* DNET */
+ default:
+ return NULL;
+ }
+}
+
+/*ARGSUSED*/
+static void
+forward_respond ( struct sockaddr *from, int fromlen ATTR_UNUSED,
+ int length, int fd)
+{
+ ARRAY8 clientAddress;
+ ARRAY8 clientPort;
+ ARRAYofARRAY8 authenticationNames;
+ struct sockaddr *client;
+ int clientlen;
+ int expectedLen;
+ int i;
+
+ Debug( "<forward> respond %d\n", length );
+ clientAddress.length = 0;
+ clientAddress.data = 0;
+ clientPort.length = 0;
+ clientPort.data = 0;
+ authenticationNames.length = 0;
+ authenticationNames.data = 0;
+ if (XdmcpReadARRAY8( &buffer, &clientAddress ) &&
+ XdmcpReadARRAY8( &buffer, &clientPort ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &authenticationNames ))
+ {
+ expectedLen = 0;
+ expectedLen += 2 + clientAddress.length;
+ expectedLen += 2 + clientPort.length;
+ expectedLen += 1; /* authenticationNames */
+ for (i = 0; i < (int)authenticationNames.length; i++)
+ expectedLen += 2 + authenticationNames.data[i].length;
+ if (length == expectedLen) {
+ int j;
+
+ j = 0;
+ for (i = 0; i < (int)clientPort.length; i++)
+ j = j * 256 + clientPort.data[i];
+ Debug( "<forward> client address (port %d) %[*hhu\n", j,
+ clientAddress.length, clientAddress.data );
+ switch (from->sa_family) {
+#ifdef AF_INET
+ case AF_INET:
+ {
+ struct sockaddr_in in_addr;
+
+ if (clientAddress.length != 4 || clientPort.length != 2)
+ goto badAddress;
+ bzero( (char *)&in_addr, sizeof(in_addr) );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+#endif
+ in_addr.sin_family = AF_INET;
+ memmove( &in_addr.sin_addr, clientAddress.data, 4 );
+ memmove( (char *)&in_addr.sin_port, clientPort.data, 2 );
+ client = (struct sockaddr *)&in_addr;
+ clientlen = sizeof(in_addr);
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct sockaddr_in6 in6_addr;
+
+ if (clientAddress.length != 16 || clientPort.length != 2)
+ goto badAddress;
+ bzero( (char *)&in6_addr, sizeof(in6_addr) );
+#ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+#endif
+ in6_addr.sin6_family = AF_INET6;
+ memmove( &in6_addr,clientAddress.data,clientAddress.length );
+ memmove( (char *)&in6_addr.sin6_port, clientPort.data, 2 );
+ client = (struct sockaddr *)&in6_addr;
+ clientlen = sizeof(in6_addr);
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#ifdef AF_UNIX
+ case AF_UNIX:
+ {
+ struct sockaddr_un un_addr;
+
+ if (clientAddress.length >= sizeof(un_addr.sun_path))
+ goto badAddress;
+ bzero( (char *)&un_addr, sizeof(un_addr) );
+ un_addr.sun_family = AF_UNIX;
+ memmove( un_addr.sun_path, clientAddress.data, clientAddress.length );
+ un_addr.sun_path[clientAddress.length] = '\0';
+ client = (struct sockaddr *)&un_addr;
+#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) && !defined(__Lynx__) && defined(UNIXCONN)
+ un_addr.sun_len = strlen( un_addr.sun_path );
+ clientlen = SUN_LEN( &un_addr );
+#else
+ clientlen = sizeof(un_addr);
+#endif
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+ goto badAddress;
+#endif
+#ifdef AF_DECnet
+ case AF_DECnet:
+ goto badAddress;
+#endif
+ }
+ } else
+ Debug( "<forward> length error got %d expect %d\n", length, expectedLen );
+ }
+ badAddress:
+ XdmcpDisposeARRAY8( &clientAddress );
+ XdmcpDisposeARRAY8( &clientPort );
+ XdmcpDisposeARRAYofARRAY8( &authenticationNames );
+}
+
+static ARRAY8 Hostname;
+
+static void
+send_willing( struct sockaddr *from, int fromlen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <willing> %.*s %.*s\n", authenticationName->length,
+ authenticationName->data,
+ status->length,
+ status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)WILLING;
+ header.length =
+ 6 + authenticationName->length + Hostname.length + status->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, &Hostname );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_unwilling( struct sockaddr *from, int fromlen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <unwilling> %.*s %.*s\n", authenticationName->length,
+ authenticationName->data,
+ status->length,
+ status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)UNWILLING;
+ header.length = 4 + Hostname.length + status->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, &Hostname );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static unsigned long globalSessionID;
+
+#define NextSessionID() (++globalSessionID)
+
+void init_session_id( void )
+{
+ /* Set randomly so we are unlikely to reuse id's from a previous
+ * incarnation so we don't say "Alive" to those displays.
+ * Start with low digits 0 to make debugging easier.
+ */
+ globalSessionID = (time( (Time_t *)0 ) & 0x7fff) * 16000;
+
+ Hostname.data = (char *)localHostname();
+ Hostname.length = strlen( Hostname.data );
+}
+
+static ARRAY8 outOfMemory = { (CARD16)13, (CARD8Ptr)"Out of memory" };
+static ARRAY8 noValidAddr = { (CARD16)16, (CARD8Ptr)"No valid address" };
+static ARRAY8 noValidAuth = { (CARD16)22, (CARD8Ptr)"No valid authorization" };
+static ARRAY8 noAuthentic = { (CARD16)29, (CARD8Ptr)"XDM has no authentication key" };
+
+static void
+request_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD16 displayNumber;
+ ARRAY16 connectionTypes;
+ ARRAYofARRAY8 connectionAddresses;
+ ARRAY8 authenticationName;
+ ARRAY8 authenticationData;
+ ARRAYofARRAY8 authorizationNames;
+ ARRAY8 manufacturerDisplayID;
+ ARRAY8Ptr reason = 0;
+ int expectlen;
+ int i, j;
+ struct protoDisplay *pdpy;
+ ARRAY8 authorizationName, authorizationData;
+ ARRAY8Ptr connectionAddress;
+
+ Debug( "<request> respond %d\n", length );
+ connectionTypes.data = 0;
+ connectionAddresses.data = 0;
+ authenticationName.data = 0;
+ authenticationData.data = 0;
+ authorizationNames.data = 0;
+ authorizationName.length = 0;
+ authorizationData.length = 0;
+ manufacturerDisplayID.data = 0;
+ if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadARRAY16( &buffer, &connectionTypes ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &connectionAddresses ) &&
+ XdmcpReadARRAY8( &buffer, &authenticationName ) &&
+ XdmcpReadARRAY8( &buffer, &authenticationData ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &authorizationNames ) &&
+ XdmcpReadARRAY8( &buffer, &manufacturerDisplayID ))
+ {
+ expectlen = 0;
+ expectlen += 2; /* displayNumber */
+ expectlen += 1 + 2 * connectionTypes.length; /* connectionTypes */
+ expectlen += 1; /* connectionAddresses */
+ for (i = 0; i < (int)connectionAddresses.length; i++)
+ expectlen += 2 + connectionAddresses.data[i].length;
+ expectlen += 2 + authenticationName.length; /* authenticationName */
+ expectlen += 2 + authenticationData.length; /* authenticationData */
+ expectlen += 1; /* authoriationNames */
+ for (i = 0; i < (int)authorizationNames.length; i++)
+ expectlen += 2 + authorizationNames.data[i].length;
+ expectlen += 2 + manufacturerDisplayID.length; /* displayID */
+ if (expectlen != length) {
+ Debug( "<request> length error got %d expect %d\n",
+ length, expectlen );
+ goto abort;
+ }
+ if (connectionTypes.length == 0 ||
+ connectionAddresses.length != connectionTypes.length)
+ {
+ reason = &noValidAddr;
+ pdpy = 0;
+ goto decline;
+ }
+ pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
+ if (!pdpy) {
+
+ /* Check this Display against the Manager's policy */
+ reason = Accept( from, fromlen, displayNumber );
+ if (reason)
+ goto decline;
+
+ /* Check the Display's stream services against Manager's policy */
+ i = SelectConnectionTypeIndex( &connectionTypes,
+ &connectionAddresses );
+ if (i < 0) {
+ reason = &noValidAddr;
+ goto decline;
+ }
+
+ /* The Manager considers this a new session */
+ connectionAddress = &connectionAddresses.data[i];
+ pdpy = NewProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber,
+ connectionTypes.data[i], connectionAddress,
+ NextSessionID() );
+ Debug( "NewProtoDisplay %p\n", pdpy );
+ if (!pdpy) {
+ reason = &outOfMemory;
+ goto decline;
+ }
+ }
+ if (authorizationNames.length == 0)
+ j = 0;
+ else
+ j = SelectAuthorizationTypeIndex( &authenticationName,
+ &authorizationNames );
+ if (j < 0) {
+ reason = &noValidAuth;
+ goto decline;
+ }
+ if (!CheckAuthentication( pdpy,
+ &manufacturerDisplayID,
+ &authenticationName,
+ &authenticationData ))
+ {
+ reason = &noAuthentic;
+ goto decline;
+ }
+ if (j < (int)authorizationNames.length) {
+ Xauth *auth;
+ SetProtoDisplayAuthorization( pdpy,
+ (unsigned short)authorizationNames.data[j].length,
+ (char *)authorizationNames.data[j].data );
+ auth = pdpy->xdmcpAuthorization;
+ if (!auth)
+ auth = pdpy->fileAuthorization;
+ if (auth) {
+ authorizationName.length = auth->name_length;
+ authorizationName.data = (CARD8Ptr) auth->name;
+ authorizationData.length = auth->data_length;
+ authorizationData.data = (CARD8Ptr) auth->data;
+ }
+ }
+ if (pdpy) {
+ send_accept( from, fromlen, pdpy->sessionID,
+ &authenticationName,
+ &authenticationData,
+ &authorizationName,
+ &authorizationData, fd );
+ } else {
+ decline:
+ send_decline( from, fromlen, &authenticationName,
+ &authenticationData,
+ reason, fd );
+ if (pdpy)
+ DisposeProtoDisplay( pdpy );
+ }
+ }
+ abort:
+ XdmcpDisposeARRAY16( &connectionTypes );
+ XdmcpDisposeARRAYofARRAY8( &connectionAddresses );
+ XdmcpDisposeARRAY8( &authenticationName );
+ XdmcpDisposeARRAY8( &authenticationData );
+ XdmcpDisposeARRAYofARRAY8( &authorizationNames );
+ XdmcpDisposeARRAY8( &manufacturerDisplayID );
+}
+
+static void
+send_accept( struct sockaddr *to, int tolen, CARD32 sessionID,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
+ ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData,
+ int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "<accept> session ID %ld\n", (long)sessionID );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)ACCEPT;
+ header.length = 4; /* session ID */
+ header.length += 2 + authenticationName->length;
+ header.length += 2 + authenticationData->length;
+ header.length += 2 + authorizationName->length;
+ header.length += 2 + authorizationData->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, authenticationData );
+ XdmcpWriteARRAY8( &buffer, authorizationName );
+ XdmcpWriteARRAY8( &buffer, authorizationData );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
+}
+
+static void
+send_decline( struct sockaddr *to, int tolen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
+ ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "<decline> %.*s\n", status->length, status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)DECLINE;
+ header.length = 0;
+ header.length += 2 + status->length;
+ header.length += 2 + authenticationName->length;
+ header.length += 2 + authenticationData->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, authenticationData );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
+}
+
+static void
+manage( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD32 sessionID;
+ CARD16 displayNumber;
+ ARRAY8 displayClass;
+ int expectlen;
+ struct protoDisplay *pdpy;
+ struct display *d;
+ char *name = NULL;
+ char *class2 = NULL;
+ XdmcpNetaddr from_save;
+ ARRAY8 clientAddress, clientPort;
+ CARD16 connectionType;
+
+ Debug( "<manage> %d\n", length );
+ displayClass.data = 0;
+ displayClass.length = 0;
+ if (XdmcpReadCARD32( &buffer, &sessionID ) &&
+ XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadARRAY8( &buffer, &displayClass ))
+ {
+ expectlen = 4 + /* session ID */
+ 2 + /* displayNumber */
+ 2 + displayClass.length; /* displayClass */
+ if (expectlen != length) {
+ Debug( "<manage> length error got %d expect %d\n", length, expectlen );
+ goto abort;
+ }
+ pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
+ Debug( "<manage> session ID %ld, pdpy %p\n", (long)sessionID, pdpy );
+ if (!pdpy || pdpy->sessionID != sessionID) {
+ /*
+ * We may have already started a session for this display
+ * but it hasn't seen the response in the form of an
+ * XOpenDisplay() yet. So check if it is in the list of active
+ * displays, and if so check that the session id's match.
+ * If all this is true, then we have a duplicate request that
+ * can be ignored.
+ */
+ if (!pdpy &&
+ (d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
+ displayNumber )) &&
+ d->sessionID == sessionID)
+ {
+ Debug( "manage: got duplicate pkt, ignoring\n" );
+ goto abort;
+ }
+ Debug( "session ID %ld refused\n", (long)sessionID );
+ if (pdpy)
+ Debug( "existing session ID %ld\n", (long)pdpy->sessionID );
+ send_refuse( from, fromlen, sessionID, fd );
+ } else {
+ name = NetworkAddressToName( pdpy->connectionType,
+ &pdpy->connectionAddress,
+ from,
+ pdpy->displayNumber );
+ if (!name) {
+ Debug( "could not compute display name\n" );
+ send_failed( from, fromlen, "(no name)", sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ Debug( "computed display name: %s\n", name );
+ if ((d = FindDisplayByName( name ))) {
+ Debug( "terminating active session for %s\n", d->name );
+ StopDisplay( d );
+ }
+ if (displayClass.length) {
+ if (!StrNDup( &class2, (char *)displayClass.data,
+ displayClass.length ))
+ {
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ }
+ if (!(from_save = (XdmcpNetaddr)Malloc( fromlen ))) {
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ memmove( from_save, from, fromlen );
+ if (!(d = NewDisplay( name ))) {
+ free( (char *)from_save );
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ d->class2 = class2;
+ class2 = 0;
+ d->displayType = dForeign | dTransient | dFromXDMCP;
+ d->sessionID = pdpy->sessionID;
+ d->from.data = (unsigned char *)from_save;
+ d->from.length = fromlen;
+ d->displayNumber = pdpy->displayNumber;
+ ClientAddress( from, &clientAddress, &clientPort,
+ &connectionType );
+ d->useChooser = 0;
+ d->xdmcpFd = fd;
+ if (IsIndirectClient( &clientAddress, connectionType )) {
+ Debug( "IsIndirectClient\n" );
+ ForgetIndirectClient( &clientAddress, connectionType );
+ if (UseChooser( &clientAddress, connectionType )) {
+ d->useChooser = 1;
+ Debug( "use chooser for %s\n", d->name );
+ }
+ }
+ d->clientAddr = clientAddress;
+ d->connectionType = connectionType;
+ d->remoteHost = NetworkAddressToHostname (pdpy->connectionType,
+ &pdpy->connectionAddress);
+
+ XdmcpDisposeARRAY8( &clientPort );
+ if (pdpy->fileAuthorization) {
+ d->authorizations = (Xauth **)Malloc( sizeof(Xauth *) );
+ if (!d->authorizations) {
+ free( (char *)from_save );
+ free( (char *)d );
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ d->authorizations[0] = pdpy->fileAuthorization;
+ d->authNum = 1;
+ pdpy->fileAuthorization = 0;
+ }
+ DisposeProtoDisplay( pdpy );
+ Debug( "starting display %s,%s\n", d->name, d->class2 );
+ if (LoadDisplayResources( d ) < 0) {
+ LogError( "Unable to read configuration for display %s; "
+ "stopping it.\n", d->name );
+ StopDisplay( d );
+ } else
+ StartDisplay( d );
+ CloseGetter();
+ }
+ }
+abort:
+ XdmcpDisposeARRAY8( &displayClass );
+ if (name)
+ free( (char *)name );
+ if (class2)
+ free( (char *)class2 );
+}
+
+void
+SendFailed( struct display *d, const char *reason )
+{
+ Debug( "display start failed, sending <failed>\n" );
+ send_failed( (struct sockaddr *)(d->from.data), d->from.length, d->name,
+ d->sessionID, reason, d->xdmcpFd );
+}
+
+static void
+send_failed( struct sockaddr *from, int fromlen,
+ const char *name, CARD32 sessionID, const char *reason, int fd )
+{
+ char buf[360];
+ XdmcpHeader header;
+ ARRAY8 status;
+
+ sprintf( buf, "Session %ld failed for display %.260s: %s",
+ (long)sessionID, name, reason );
+ Debug( "send_failed(%\"s)\n", buf );
+ status.length = strlen( buf );
+ status.data = (CARD8Ptr) buf;
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)FAILED;
+ header.length = 6 + status.length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpWriteARRAY8( &buffer, &status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <refuse> %ld\n", (long)sessionID );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)REFUSE;
+ header.length = 4;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_alive( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD32 sessionID;
+ CARD16 displayNumber;
+ struct display *d;
+ XdmcpHeader header;
+ CARD8 sendRunning;
+ CARD32 sendSessionID;
+
+ Debug( "send <alive>\n" );
+ if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadCARD32( &buffer, &sessionID ))
+ {
+ if (length == 6) {
+ if (!(d = FindDisplayBySessionID( sessionID )))
+ d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
+ displayNumber );
+ sendRunning = 0;
+ sendSessionID = 0;
+ if (d && d->status == running) {
+ if (d->sessionID == sessionID)
+ sendRunning = 1;
+ sendSessionID = d->sessionID;
+ }
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)ALIVE;
+ header.length = 5;
+ Debug( "<alive>: %d %ld\n", sendRunning, (long)sendSessionID );
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD8( &buffer, sendRunning );
+ XdmcpWriteCARD32( &buffer, sendSessionID );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+ }
+ }
+}
+
+char *
+NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress )
+{
+ switch (connectionType) {
+ case FamilyInternet:
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+#endif
+ {
+ struct hostent *he;
+ char *name, *lname;
+ char *myDot;
+ int af_type;
+#if defined(IPv6) && defined(AF_INET6)
+ char dotted[INET6_ADDRSTRLEN];
+
+ if (connectionType == FamilyInternet6)
+ af_type = AF_INET6;
+ else
+#endif
+ af_type = AF_INET;
+
+ he = gethostbyaddr( (char *)connectionAddress->data,
+ connectionAddress->length, af_type );
+ if (he) {
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai, *nai;
+ if (!getaddrinfo( he->h_name, NULL, NULL, &ai )) {
+ for (nai = ai; nai; nai = nai->ai_next) {
+ if (af_type == nai->ai_family &&
+ !memcmp( nai->ai_family == AF_INET ?
+ (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
+ (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
+ connectionAddress->data,
+ connectionAddress->length ))
+ {
+ freeaddrinfo( ai );
+ goto oki;
+ }
+ }
+ freeaddrinfo( ai );
+#else
+ if ((he = gethostbyname( he->h_name )) &&
+ he->h_addrtype == AF_INET)
+ {
+ int i;
+ for (i = 0; he->h_addr_list[i]; i++)
+ if (!memcmp( he->h_addr_list[i],
+ connectionAddress->data, 4 ))
+ goto oki;
+#endif
+ LogError( "DNS spoof attempt or misconfigured resolver.\n" );
+ }
+ goto gotnone;
+ oki:
+ if (StrDup( &name, he->h_name ) &&
+ !strchr( name, '.' ) &&
+ (myDot = strchr( localHostname(), '.' )))
+ {
+ if (ASPrintf( &lname, "%s%s", name, myDot )) {
+#if defined(IPv6) && defined(AF_INET6)
+ if (!getaddrinfo( lname, NULL, NULL, &ai )) {
+ for (nai = ai; nai; nai = nai->ai_next) {
+ if (af_type == nai->ai_family &&
+ !memcmp( nai->ai_family == AF_INET ?
+ (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
+ (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
+ connectionAddress->data,
+ connectionAddress->length ))
+ {
+ freeaddrinfo( ai );
+ free( name );
+ return lname;
+ }
+ }
+ freeaddrinfo( ai );
+ }
+#else
+ if ((he = gethostbyname( lname )) &&
+ he->h_addrtype == AF_INET)
+ {
+ int i;
+ for (i = 0; he->h_addr_list[i]; i++)
+ if (!memcmp( he->h_addr_list[i],
+ connectionAddress->data, 4 ))
+ {
+ free( name );
+ return lname;
+ }
+ }
+#endif
+ free( lname );
+ }
+ }
+ } else {
+ gotnone:
+ /* can't get name, so use emergency fallback */
+#if defined(IPv6) && defined(AF_INET6)
+ inet_ntop( af_type, connectionAddress->data,
+ dotted, sizeof(dotted) );
+ StrDup( &name, dotted );
+#else
+ ASPrintf( &name, "%[4|'.'hhu", connectionAddress->data );
+#endif
+ LogWarn( "Cannot convert Internet address %s to host name\n",
+ name );
+ }
+ return name;
+ }
+#ifdef DNET
+ case FamilyDECnet:
+ break;
+#endif /* DNET */
+ default:
+ break;
+ }
+ return 0;
+}
+
+#endif /* XDMCP */
+