You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
798 lines
16 KiB
798 lines
16 KiB
/*
|
|
|
|
TDE Greeter module for xdm
|
|
|
|
Copyright (C) 2001-2003 Oswald Buddenhagen <ossi@kde.org>
|
|
|
|
This file contains code from the old xdm core,
|
|
Copyright 1988, 1998 Keith Packard, MIT X Consortium/The Open Group
|
|
|
|
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 General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "tdm_greet.h"
|
|
#include "tdmconfig.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <setjmp.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#ifdef _POSIX_PRIORITY_SCHEDULING
|
|
# include <sched.h>
|
|
#endif
|
|
|
|
# include <X11/Xlib.h>
|
|
#if defined(HAVE_XTEST) || defined(HAVE_XKB)
|
|
# include <X11/keysym.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_XTEST
|
|
# include <X11/extensions/XTest.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_XKB
|
|
# include <X11/XKBlib.h>
|
|
#endif
|
|
|
|
extern void LogOutOfMem( void );
|
|
|
|
static void *
|
|
Realloc( void *ptr, size_t size )
|
|
{
|
|
void *ret;
|
|
|
|
if (!(ret = realloc( ptr, size )) && size)
|
|
LogOutOfMem();
|
|
return ret;
|
|
}
|
|
|
|
#define PRINT_QUOTES
|
|
#define PRINT_ARRAYS
|
|
#define LOG_NAME "tdm_greet"
|
|
#define LOG_DEBUG_MASK DEBUG_GREET
|
|
#define LOG_PANIC_EXIT 1
|
|
#define STATIC
|
|
#include <printf.c>
|
|
|
|
static void
|
|
GDebug( const char *fmt, ... )
|
|
{
|
|
va_list args;
|
|
|
|
if (debugLevel & DEBUG_HLPCON) {
|
|
va_start( args, fmt );
|
|
Logger( DM_DEBUG, fmt, args );
|
|
va_end( args );
|
|
}
|
|
}
|
|
|
|
|
|
char *dname;
|
|
|
|
int rfd;
|
|
static int wfd, mrfd, mwfd, srfd, swfd;
|
|
static const char *who;
|
|
|
|
void
|
|
GSet( int master )
|
|
{
|
|
if (master)
|
|
rfd = mrfd, wfd = mwfd, who = "core (master)";
|
|
else
|
|
rfd = srfd, wfd = swfd, who = "core";
|
|
|
|
}
|
|
|
|
static int
|
|
Reader( void *buf, int count )
|
|
{
|
|
int ret, rlen;
|
|
|
|
for (rlen = 0; rlen < count; ) {
|
|
dord:
|
|
ret = read( rfd, (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;
|
|
}
|
|
|
|
static void
|
|
GRead( void *buf, int count )
|
|
{
|
|
if (Reader( buf, count ) != count)
|
|
LogPanic( "Can't read from %s\n", who );
|
|
}
|
|
|
|
static void
|
|
GWrite( const void *buf, int count )
|
|
{
|
|
if (write( wfd, buf, count ) != count)
|
|
LogPanic( "Can't write to %s\n", who );
|
|
#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, who );
|
|
GWrite( &val, sizeof(val) );
|
|
}
|
|
|
|
void
|
|
GSendStr( const char *buf )
|
|
{
|
|
int len = buf ? strlen( buf ) + 1 : 0;
|
|
GDebug( "Sending string %'s to %s\n", buf, who );
|
|
GWrite( &len, sizeof(len) );
|
|
GWrite( buf, len );
|
|
}
|
|
|
|
/*
|
|
static void
|
|
GSendNStr( const char *buf, int len )
|
|
{
|
|
int tlen = len + 1;
|
|
GDebug( "Sending string %'.*s to %s\n", len, buf, who );
|
|
GWrite( &tlen, sizeof(tlen) );
|
|
GWrite( buf, len );
|
|
GWrite( "", 1 );
|
|
}
|
|
*/
|
|
|
|
void
|
|
GSendArr( int len, const char *buf )
|
|
{
|
|
GDebug( "Sending array %02[:*hhx to %s\n", len, buf, who );
|
|
GWrite( &len, sizeof(len) );
|
|
GWrite( buf, len );
|
|
}
|
|
|
|
int
|
|
GRecvInt()
|
|
{
|
|
int val;
|
|
|
|
GDebug( "Receiving int from %s ...\n", who );
|
|
GRead( &val, sizeof(val) );
|
|
GDebug( " -> %d (%#x)\n", val, val );
|
|
return val;
|
|
}
|
|
|
|
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 )))
|
|
LogPanic( "No memory for read buffer\n" );
|
|
GRead( buf, len );
|
|
return buf;
|
|
}
|
|
|
|
char *
|
|
GRecvStr()
|
|
{
|
|
int len;
|
|
char *buf;
|
|
|
|
GDebug( "Receiving string from %s ...\n", who );
|
|
buf = iGRecvArr( &len );
|
|
GDebug( " -> %'.*s\n", len, buf );
|
|
return buf;
|
|
}
|
|
|
|
char **
|
|
GRecvStrArr( int *rnum )
|
|
{
|
|
int num;
|
|
char **argv, **cargv;
|
|
|
|
GDebug( "Receiving string array from %s ...\n", who );
|
|
GRead( &num, sizeof(num) );
|
|
GDebug( " -> %d strings\n", num );
|
|
if (rnum)
|
|
*rnum = num;
|
|
if (!num)
|
|
return (char **)0;
|
|
if (!(argv = malloc( num * sizeof(char *))))
|
|
LogPanic( "No memory for read buffer\n" );
|
|
for (cargv = argv; --num >= 0; cargv++)
|
|
*cargv = GRecvStr();
|
|
return argv;
|
|
}
|
|
|
|
char *
|
|
GRecvArr( int *num )
|
|
{
|
|
char *arr;
|
|
|
|
GDebug( "Receiving array from %s ...\n", who );
|
|
GRead( num, sizeof(*num) );
|
|
GDebug( " -> %d bytes\n", *num );
|
|
if (!*num)
|
|
return (char *)0;
|
|
if (!(arr = malloc( *num )))
|
|
LogPanic( "No memory for read buffer\n" );
|
|
GRead( arr, *num );
|
|
GDebug( " -> %02[*hhx\n", *num, arr );
|
|
return arr;
|
|
}
|
|
|
|
static void
|
|
ReqCfg( int id )
|
|
{
|
|
GSendInt( G_GetCfg );
|
|
GSendInt( id );
|
|
switch (GRecvInt()) {
|
|
case GE_NoEnt:
|
|
LogPanic( "Config value %#x not available\n", id );
|
|
case GE_BadType:
|
|
LogPanic( "Core does not know type of config value %#x\n", id );
|
|
}
|
|
}
|
|
|
|
int
|
|
GetCfgInt( int id )
|
|
{
|
|
ReqCfg( id );
|
|
return GRecvInt();
|
|
}
|
|
|
|
char *
|
|
GetCfgStr( int id )
|
|
{
|
|
ReqCfg( id );
|
|
return GRecvStr();
|
|
}
|
|
|
|
char **
|
|
GetCfgStrArr( int id, int *len )
|
|
{
|
|
ReqCfg( id );
|
|
return GRecvStrArr( len );
|
|
}
|
|
|
|
static void
|
|
disposeSession( dpySpec *sess )
|
|
{
|
|
free( sess->display );
|
|
free( sess->from );
|
|
if (sess->user)
|
|
free( sess->user );
|
|
if (sess->session)
|
|
free( sess->session );
|
|
}
|
|
|
|
dpySpec *
|
|
fetchSessions( int flags )
|
|
{
|
|
dpySpec *sess, *sessions = 0, tsess;
|
|
|
|
GSet( 1 );
|
|
GSendInt( G_List );
|
|
GSendInt( flags );
|
|
next:
|
|
while ((tsess.display = GRecvStr())) {
|
|
tsess.from = GRecvStr();
|
|
#ifdef HAVE_VTS
|
|
tsess.vt = GRecvInt();
|
|
#endif
|
|
tsess.user = GRecvStr();
|
|
tsess.session = GRecvStr();
|
|
tsess.flags = GRecvInt();
|
|
if ((tsess.flags & isTTY) && *tsess.from)
|
|
for (sess = sessions; sess; sess = sess->next)
|
|
if (sess->user && !strcmp( sess->user, tsess.user ) &&
|
|
!strcmp( sess->from, tsess.from ))
|
|
{
|
|
sess->count++;
|
|
disposeSession( &tsess );
|
|
goto next;
|
|
}
|
|
if (!(sess = malloc( sizeof(*sess) )))
|
|
LogPanic( "Out of memory\n" );
|
|
tsess.count = 1;
|
|
tsess.next = sessions;
|
|
*sess = tsess;
|
|
sessions = sess;
|
|
}
|
|
GSet( 0 );
|
|
return sessions;
|
|
}
|
|
|
|
void
|
|
disposeSessions( dpySpec *sess )
|
|
{
|
|
while (sess) {
|
|
dpySpec *nsess = sess->next;
|
|
disposeSession( sess );
|
|
free( sess );
|
|
sess = nsess;
|
|
}
|
|
}
|
|
|
|
void
|
|
freeStrArr( char **arr )
|
|
{
|
|
char **tarr;
|
|
|
|
if (arr) {
|
|
for (tarr = arr; *tarr; tarr++)
|
|
free( *tarr );
|
|
free( arr );
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
ignoreErrors( Display *dpy ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED )
|
|
{
|
|
Debug( "ignoring X 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( Display *dpy, Window window )
|
|
{
|
|
Window root, parent, *children;
|
|
unsigned child, nchildren = 0;
|
|
|
|
while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren )
|
|
&& nchildren > 0)
|
|
{
|
|
for (child = 0; child < nchildren; child++) {
|
|
Debug( "XKillClient 0x%lx\n", (unsigned long)children[child] );
|
|
XKillClient( dpy, children[child] );
|
|
}
|
|
XFree( (char *)children );
|
|
}
|
|
}
|
|
|
|
static jmp_buf resetJmp;
|
|
|
|
static void
|
|
abortReset( int n ATTR_UNUSED )
|
|
{
|
|
longjmp (resetJmp, 1);
|
|
}
|
|
|
|
/*
|
|
* this display connection better not have any windows...
|
|
*/
|
|
|
|
static void
|
|
pseudoReset( Display *dpy )
|
|
{
|
|
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( dpy, RootWindow( dpy, screen ) );
|
|
}
|
|
Debug( "before XSync\n" );
|
|
XSync( dpy, False );
|
|
(void)alarm( 0 );
|
|
}
|
|
signal( SIGALRM, SIG_DFL );
|
|
XSetErrorHandler( (XErrorHandler)0 );
|
|
Debug( "pseudoReset done\n" );
|
|
}
|
|
|
|
|
|
static jmp_buf syncJump;
|
|
|
|
static void
|
|
syncTimeout( int n ATTR_UNUSED )
|
|
{
|
|
longjmp( syncJump, 1 );
|
|
}
|
|
|
|
void
|
|
SecureDisplay( Display *dpy )
|
|
{
|
|
Debug( "SecureDisplay %s\n", dname );
|
|
(void)signal( SIGALRM, syncTimeout );
|
|
if (setjmp( syncJump )) {
|
|
LogError( "Display %s could not be secured\n", dname );
|
|
exit( EX_RESERVER_DPY );
|
|
}
|
|
(void)alarm( (unsigned)_grabTimeout );
|
|
Debug( "Before XGrabServer %s\n", dname );
|
|
XGrabServer( dpy );
|
|
Debug( "XGrabServer succeeded %s\n", dname );
|
|
if (XGrabKeyboard( dpy, DefaultRootWindow( dpy ), True, GrabModeAsync,
|
|
GrabModeAsync, CurrentTime ) != GrabSuccess)
|
|
{
|
|
(void)alarm( 0 );
|
|
(void)signal( SIGALRM, SIG_DFL );
|
|
LogError( "Keyboard on display %s could not be secured\n", dname );
|
|
sleep( 10 );
|
|
exit( EX_RESERVER_DPY );
|
|
}
|
|
(void)alarm( 0 );
|
|
(void)signal( SIGALRM, SIG_DFL );
|
|
pseudoReset( dpy );
|
|
if (!_grabServer)
|
|
{
|
|
XUngrabServer( dpy );
|
|
XSync( dpy, 0 );
|
|
}
|
|
Debug( "done secure %s\n", dname );
|
|
#ifdef HAVE_XKBSETPERCLIENTCONTROLS
|
|
/*
|
|
* Activate the correct mapping for modifiers in XKB extension as
|
|
* grabbed keyboard has its own mapping by default
|
|
*/
|
|
{
|
|
int opcode, evbase, errbase, majret, minret;
|
|
unsigned int value = XkbPCF_GrabsUseXKBStateMask;
|
|
if (XkbQueryExtension( dpy, &opcode, &evbase,
|
|
&errbase, &majret, &minret ))
|
|
XkbSetPerClientControls( dpy, value, &value );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
UnsecureDisplay( Display *dpy )
|
|
{
|
|
Debug( "Unsecure display %s\n", dname );
|
|
if (_grabServer) {
|
|
XUngrabServer( dpy );
|
|
XSync( dpy, 0 );
|
|
}
|
|
}
|
|
|
|
static jmp_buf pingTime;
|
|
|
|
static int
|
|
PingLostIOErr( Display *dpy ATTR_UNUSED )
|
|
{
|
|
longjmp( pingTime, 1 );
|
|
}
|
|
|
|
static void
|
|
PingLostSig( int n ATTR_UNUSED )
|
|
{
|
|
longjmp( pingTime, 1 );
|
|
}
|
|
|
|
int
|
|
PingServer( Display *dpy )
|
|
{
|
|
int (*oldError)( Display * );
|
|
void (*oldSig)( int );
|
|
int oldAlarm;
|
|
|
|
oldError = XSetIOErrorHandler( PingLostIOErr );
|
|
oldAlarm = alarm( 0 );
|
|
oldSig = signal( SIGALRM, PingLostSig );
|
|
(void)alarm( _pingTimeout * 60 );
|
|
if (!setjmp( pingTime )) {
|
|
Debug( "Ping server\n" );
|
|
XSync( dpy, 0 );
|
|
} else {
|
|
Debug( "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( "Server alive\n" );
|
|
XSetIOErrorHandler( oldError );
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Modifier changing code based on tdebase/kxkb/kcmmisc.cpp
|
|
*
|
|
* XTest part: Copyright (C) 2000-2001 Lubos Lunak <l.lunak@kde.org>
|
|
* XKB part: Copyright (C) 2001-2002 Oswald Buddenhagen <ossi@kde.org>
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_XKB
|
|
static int
|
|
xkb_init( Display *dpy )
|
|
{
|
|
int xkb_opcode, xkb_event, xkb_error;
|
|
int xkb_lmaj = XkbMajorVersion;
|
|
int xkb_lmin = XkbMinorVersion;
|
|
return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) &&
|
|
XkbQueryExtension( dpy, &xkb_opcode, &xkb_event,
|
|
&xkb_error, &xkb_lmaj, &xkb_lmin );
|
|
}
|
|
|
|
static unsigned int
|
|
xkb_modifier_mask_work( XkbDescPtr xkb, const char *name )
|
|
{
|
|
int i;
|
|
|
|
if (!xkb->names)
|
|
return 0;
|
|
for (i = 0; i < XkbNumVirtualMods; i++) {
|
|
char *modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] );
|
|
if( modStr == NULL ) {
|
|
continue;
|
|
}
|
|
if( strcmp( name, modStr ) == 0 ) {
|
|
unsigned int mask;
|
|
XkbVirtualModsToReal( xkb, 1 << i, &mask );
|
|
XFree(modStr);
|
|
return mask;
|
|
}
|
|
XFree(modStr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int
|
|
xkb_modifier_mask( Display *dpy, const char *name )
|
|
{
|
|
XkbDescPtr xkb;
|
|
|
|
if ((xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd ))) {
|
|
unsigned int mask = xkb_modifier_mask_work( xkb, name );
|
|
XkbFreeKeyboard( xkb, 0, True );
|
|
return mask;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xkb_get_modifier_state( Display *dpy, const char *name )
|
|
{
|
|
unsigned int mask;
|
|
XkbStateRec state;
|
|
|
|
if (!(mask = xkb_modifier_mask( dpy, name )))
|
|
return 0;
|
|
XkbGetState( dpy, XkbUseCoreKbd, &state );
|
|
return (mask & state.locked_mods) != 0;
|
|
}
|
|
|
|
static int
|
|
xkb_set_modifier( Display *dpy, const char *name, int sts )
|
|
{
|
|
unsigned int mask;
|
|
|
|
if (!(mask = xkb_modifier_mask( dpy, name )))
|
|
return 0;
|
|
XkbLockModifiers( dpy, XkbUseCoreKbd, mask, sts ? mask : 0 );
|
|
return 1;
|
|
}
|
|
#endif /* HAVE_XKB */
|
|
|
|
#ifdef HAVE_XTEST
|
|
static int
|
|
xtest_get_modifier_state( Display *dpy, int key )
|
|
{
|
|
XModifierKeymap *map;
|
|
KeyCode modifier_keycode;
|
|
unsigned int i, mask;
|
|
Window dummy1, dummy2;
|
|
int dummy3, dummy4, dummy5, dummy6;
|
|
|
|
if ((modifier_keycode = XKeysymToKeycode( dpy, key )) == NoSymbol)
|
|
return 0;
|
|
map = XGetModifierMapping( dpy );
|
|
for (i = 0; i < 8; ++i)
|
|
if (map->modifiermap[map->max_keypermod * i] == modifier_keycode) {
|
|
XFreeModifiermap( map );
|
|
XQueryPointer( dpy, DefaultRootWindow( dpy ),
|
|
&dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
|
|
&mask );
|
|
return (mask & (1 << i)) != 0;
|
|
}
|
|
XFreeModifiermap( map );
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
xtest_fake_keypress( Display *dpy, int key )
|
|
{
|
|
XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), True, CurrentTime );
|
|
XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), False, CurrentTime );
|
|
}
|
|
#endif /* HAVE_XTEST */
|
|
|
|
#ifdef HAVE_XKB
|
|
static int havexkb;
|
|
#endif
|
|
static int nummodified, oldnumstate, newnumstate;
|
|
static Display *dpy;
|
|
|
|
void
|
|
setup_modifiers( Display *mdpy, int numlock )
|
|
{
|
|
if (numlock == 2)
|
|
return;
|
|
newnumstate = numlock;
|
|
nummodified = 1;
|
|
dpy = mdpy;
|
|
#ifdef HAVE_XKB
|
|
if (xkb_init( mdpy )) {
|
|
havexkb = 1;
|
|
oldnumstate = xkb_get_modifier_state( mdpy, "NumLock" );
|
|
xkb_set_modifier( mdpy, "NumLock", numlock );
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_XTEST
|
|
oldnumstate = xtest_get_modifier_state( mdpy, XK_Num_Lock );
|
|
if (oldnumstate != numlock)
|
|
xtest_fake_keypress( mdpy, XK_Num_Lock );
|
|
#endif
|
|
}
|
|
|
|
void
|
|
restore_modifiers( void )
|
|
{
|
|
#ifdef HAVE_XTEST
|
|
int numstat;
|
|
#endif
|
|
|
|
if (!nummodified)
|
|
return;
|
|
#ifdef HAVE_XKB
|
|
if (havexkb) {
|
|
if (xkb_get_modifier_state( dpy, "NumLock" ) == newnumstate)
|
|
xkb_set_modifier( dpy, "NumLock", oldnumstate );
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_XTEST
|
|
numstat = xtest_get_modifier_state( dpy, XK_Num_Lock );
|
|
if (numstat == newnumstate && newnumstate != oldnumstate)
|
|
xtest_fake_keypress( dpy, XK_Num_Lock );
|
|
#endif
|
|
}
|
|
|
|
void
|
|
setCursor( Display *mdpy, int window, int shape )
|
|
{
|
|
Cursor xcursor;
|
|
|
|
if ((xcursor = XCreateFontCursor( mdpy, shape ))) {
|
|
XDefineCursor( mdpy, window, xcursor );
|
|
XFreeCursor( mdpy, xcursor );
|
|
XFlush( mdpy );
|
|
}
|
|
}
|
|
|
|
static void
|
|
sigterm( int n ATTR_UNUSED )
|
|
{
|
|
exit( EX_NORMAL );
|
|
}
|
|
|
|
static char *savhome;
|
|
|
|
static void
|
|
cleanup( void )
|
|
{
|
|
char buf[128];
|
|
|
|
if (strcmp( savhome, getenv( "HOME" ) ) || memcmp( savhome, "/tmp/", 5 ))
|
|
LogError( "Internal error: memory corruption detected\n" ); /* no panic: recursion */
|
|
else {
|
|
sprintf( buf, "rm -rf %s", savhome );
|
|
system( buf );
|
|
}
|
|
}
|
|
|
|
extern void kg_main( const char *argv0 );
|
|
|
|
int
|
|
main( int argc ATTR_UNUSED, char **argv )
|
|
{
|
|
char *ci;
|
|
int i;
|
|
char qtrc[40];
|
|
|
|
if (!(ci = getenv( "CONINFO" ))) {
|
|
fprintf( stderr, "This program is part of tdm and should not be run manually.\n" );
|
|
return 1;
|
|
}
|
|
if (sscanf( ci, "%d %d %d %d", &srfd, &swfd, &mrfd, &mwfd ) != 4)
|
|
return 1;
|
|
fcntl( srfd, F_SETFD, FD_CLOEXEC );
|
|
fcntl( swfd, F_SETFD, FD_CLOEXEC );
|
|
fcntl( mrfd, F_SETFD, FD_CLOEXEC );
|
|
fcntl( mwfd, F_SETFD, FD_CLOEXEC );
|
|
GSet( 0 );
|
|
|
|
InitLog();
|
|
|
|
if ((debugLevel = GRecvInt()) & DEBUG_WGREET)
|
|
sleep( 100 );
|
|
|
|
signal( SIGTERM, sigterm );
|
|
|
|
dname = getenv( "DISPLAY" );
|
|
|
|
init_config();
|
|
|
|
/* for TQSettings */
|
|
srand( time( 0 ) );
|
|
for (i = 0; i < 10000; i++) {
|
|
sprintf( qtrc, "/tmp/%010d", rand() );
|
|
if (!mkdir( qtrc, 0700 ))
|
|
goto okay;
|
|
}
|
|
LogPanic( "Cannot create $HOME\n" );
|
|
okay:
|
|
if (setenv( "HOME", qtrc, 1 ))
|
|
LogPanic( "Cannot set $HOME\n" );
|
|
if (!(savhome = strdup( qtrc )))
|
|
LogPanic( "Cannot save $HOME\n" );
|
|
atexit( cleanup );
|
|
|
|
if ( getenv( "LANG" ) == NULL ) {
|
|
setenv( "LC_ALL", _language, 1 );
|
|
}
|
|
else {
|
|
setenv( "LC_ALL", getenv( "LANG" ), 1 );
|
|
}
|
|
|
|
kg_main( argv[0] );
|
|
|
|
return EX_NORMAL;
|
|
}
|