/* Create a suitable configuration for tdm taking old xdm/tdm installations into account Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org> 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 <X11/Xlib.h> #include <X11/Xresource.h> #include <config.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <stdarg.h> #include <sys/stat.h> #include <fcntl.h> #include <utime.h> #include <dirent.h> #include <errno.h> #include <pwd.h> #include <time.h> #include <limits.h> #include <sys/stat.h> #include <sys/param.h> #ifdef BSD # include <utmp.h> #endif #include "config.ci" #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) # define ATTR_UNUSED __attribute__((unused)) #else # define ATTR_UNUSED #endif #if defined(__sun) && !defined(__sun__) # define __sun__ #endif #define as(ar) ((int)(sizeof(ar)/sizeof(ar[0]))) #define __stringify(x) #x #define stringify(x) __stringify(x) #define RCVERSTR stringify(RCVERMAJOR) "." stringify(RCVERMINOR) static int old_scripts, no_old_scripts, old_confs, no_old, no_backup, no_in_notice, use_destdir, mixed_scripts; static const char *newdir = TDMCONF, *facesrc = TDMDATA "/pics/users", *oldxdm, *oldkde; static int oldver; typedef struct StrList { struct StrList *next; const char *str; } StrList; static void * mmalloc( size_t sz ) { void *ptr; if (!(ptr = malloc( sz ))) { fprintf( stderr, "Out of memory\n" ); exit( 1 ); } return ptr; } static void * mcalloc( size_t sz ) { void *ptr; if (!(ptr = calloc( 1, sz ))) { fprintf( stderr, "Out of memory\n" ); exit( 1 ); } return ptr; } static void * mrealloc( void *optr, size_t sz ) { void *ptr; if (!(ptr = realloc( optr, sz ))) { fprintf( stderr, "Out of memory\n" ); exit( 1 ); } return ptr; } static char * mstrdup( const char *optr ) { char *ptr; if (!optr) return 0; if (!(ptr = strdup( optr ))) { fprintf( stderr, "Out of memory\n" ); exit( 1 ); } return ptr; } #define NO_LOGGER #define STATIC static #include <printf.c> typedef struct { char *buf; int clen, blen, tlen; } OCABuf; static void OutCh_OCA( void *bp, char c ) { OCABuf *ocabp = (OCABuf *)bp; ocabp->tlen++; if (ocabp->clen >= ocabp->blen) { ocabp->blen = ocabp->blen * 3 / 2 + 100; ocabp->buf = mrealloc( ocabp->buf, ocabp->blen ); } 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; } static void StrCat( char **strp, const char *fmt, ... ) { char *str, *tstr; va_list args; int el; va_start( args, fmt ); el = VASPrintf( &str, fmt, args ); va_end( args ); if (*strp) { int ol = strlen( *strp ); tstr = mmalloc( el + ol + 1 ); memcpy( tstr, *strp, ol ); memcpy( tstr + ol, str, el + 1 ); free( *strp ); free( str ); *strp = tstr; } else *strp = str; } #define WANT_CLOSE 1 typedef struct File { char *buf, *eof, *cur; #if defined(HAVE_MMAP) && defined(WANT_CLOSE) int ismapped; #endif } File; static int readFile( File *file, const char *fn ) { off_t flen; int fd; if ((fd = open( fn, O_RDONLY )) < 0) return 0; flen = lseek( fd, 0, SEEK_END ); #ifdef HAVE_MMAP # ifdef WANT_CLOSE file->ismapped = 0; # endif file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 ); # ifdef WANT_CLOSE if (file->buf) file->ismapped = 1; else # else if (!file->buf) # endif #endif { file->buf = mmalloc( flen + 1 ); lseek( fd, 0, SEEK_SET ); if (read( fd, file->buf, flen ) != flen) { free( file->buf ); close( fd ); fprintf( stderr, "Cannot read file\n" ); return 0; /* maybe better abort? */ } } file->eof = file->buf + flen; close( fd ); return 1; } #ifdef WANT_CLOSE static void freeBuf( File *file ) { # ifdef HAVE_MMAP if (file->ismapped) munmap( file->buf, file->eof - file->buf ); else # endif free( file->buf ); } #endif static int isTrue( const char *val ) { return !strcmp( val, "true" ) || !strcmp( val, "yes" ) || !strcmp( val, "on" ) || atoi( val ); } static int mkdirp( const char *name, int mode, const char *what, int existok ) { char *mfname = mstrdup( name ); int i; struct stat st; for (i = 1; mfname[i]; i++) if (mfname[i] == '/') { mfname[i] = 0; if (stat( mfname, &st )) { if (mkdir( mfname, 0755 )) { fprintf( stderr, "Cannot create parent %s of %s directory %s: %s\n", mfname, what, name, strerror( errno ) ); free( mfname ); return 0; } chmod( mfname, 0755 ); } mfname[i] = '/'; } free( mfname ); if (stat( name, &st )) { if (mkdir( name, mode )) { fprintf( stderr, "Cannot create %s directory %s: %s\n", what, name, strerror( errno ) ); return 0; } chmod( name, mode ); return 1; } return existok; } static void displace( const char *fn ) { if (!no_backup) { char bn[PATH_MAX + 4]; sprintf( bn, "%s.bak", fn ); /* won't overflow if only existing paths are passed */ rename( fn, bn ); } else unlink( fn ); } static char * locate( const char *exe ) { int len; char *path, *name, *thenam, nambuf[PATH_MAX+1]; char *pathe; if (!(path = getenv( "PATH" ))) 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 )) return mstrdup( thenam ); } } path = pathe; } while (*path++ != '\0'); return 0; } /* * target data to be written to tdmrc */ typedef struct Entry { struct Entry *next; struct Ent *spec; const char *value; int active:1; int written:1; } Entry; typedef struct Section { struct Section *next; struct Sect *spec; const char *name; const char *comment; Entry *ents; } Section; static Section *config; /* the tdmrc data to be written */ /* * Specification of the (currently possible) tdmrc entries */ typedef struct Ent { const char *key; int prio; void (*func)( Entry *ce, Section *cs ); const char *comment; } Ent; typedef struct Sect { const char *name; Ent *ents; int nents; } Sect; static Sect *findSect( const char *name ); static Ent *findEnt( Sect *sect, const char *key ); /* * Functions to manipulate the current tdmrc data */ static const char * getfqval( const char *sect, const char *key, const char *defval ) { Section *cs; Entry *ce; for (cs = config; cs; cs = cs->next) if (!strcmp( cs->name, sect )) { for (ce = cs->ents; ce; ce = ce->next) if (!strcmp( ce->spec->key, key )) { if (ce->active && ce->written) return ce->value; break; } break; } return defval; } static void putfqval( const char *sect, const char *key, const char *value ) { Section *cs, **csp; Entry *ce, **cep; if (!value) return; for (csp = &config; (cs = *csp); csp = &(cs->next)) if (!strcmp( sect, cs->name )) goto havesec; cs = mcalloc( sizeof(*cs) ); ASPrintf( (char **)&cs->name, "%s", sect ); cs->spec = findSect( sect ); *csp = cs; havesec: for (cep = &(cs->ents); (ce = *cep); cep = &(ce->next)) if (!strcmp( key, ce->spec->key )) goto haveent; ce = mcalloc( sizeof(*ce) ); ce->spec = findEnt( cs->spec, key ); *cep = ce; haveent: ASPrintf( (char **)&ce->value, "%s", value ); ce->written = ce->active = 1; } static const char *csect; #define setsect(se) csect = se static void putval( const char *key, const char *value ) { putfqval( csect, key, value ); } static void wrconf( FILE *f ) { Section *cs; Entry *ce; StrList *sl = 0, *sp; const char *cmt; putfqval( "General", "ConfigVersion", RCVERSTR ); for (cs = config; cs; cs = cs->next) { fprintf( f, "%s[%s]\n", cs->comment ? cs->comment : "\n", cs->name ); for (ce = cs->ents; ce; ce = ce->next) { if (ce->spec->comment) { cmt = ce->spec->comment; for (sp = sl; sp; sp = sp->next) if (sp->str == cmt) { cmt = "# See above\n"; goto havit; } if (!(sp = malloc( sizeof(*sp) ))) fprintf( stderr, "Warning: Out of memory\n" ); else { sp->str = cmt; sp->next = sl; sl = sp; } } else cmt = ""; havit: fprintf( f, "%s%s%s=%s\n", cmt, ce->active ? "" : "#", ce->spec->key, ce->value ); } } } /* * defaults */ #ifdef XDMCP static const char def_xaccess[] = "# Xaccess - Access control file for XDMCP connections\n" "#\n" "# To control Direct and Broadcast access:\n" "#\n" "# pattern\n" "#\n" "# To control Indirect queries:\n" "#\n" "# pattern list of hostnames and/or macros ...\n" "#\n" "# To use the chooser:\n" "#\n" "# pattern CHOOSER BROADCAST\n" "#\n" "# or\n" "#\n" "# pattern CHOOSER list of hostnames and/or macros ...\n" "#\n" "# To define macros:\n" "#\n" "# %name list of hosts ...\n" "#\n" "# The first form tells xdm which displays to respond to itself.\n" "# The second form tells xdm to forward indirect queries from hosts matching\n" "# the specified pattern to the indicated list of hosts.\n" "# The third form tells xdm to handle indirect queries using the chooser;\n" "# the chooser is directed to send its own queries out via the broadcast\n" "# address and display the results on the terminal.\n" "# The fourth form is similar to the third, except instead of using the\n" "# broadcast address, it sends DirectQuerys to each of the hosts in the list\n" "#\n" "# In all cases, xdm uses the first entry which matches the terminal;\n" "# for IndirectQuery messages only entries with right hand sides can\n" "# match, for Direct and Broadcast Query messages, only entries without\n" "# right hand sides can match.\n" "#\n" "\n" "#* #any host can get a login window\n" "\n" "#\n" "# To hardwire a specific terminal to a specific host, you can\n" "# leave the terminal sending indirect queries to this host, and\n" "# use an entry of the form:\n" "#\n" "\n" "#terminal-a host-a\n" "\n" "\n" "#\n" "# The nicest way to run the chooser is to just ask it to broadcast\n" "# requests to the network - that way new hosts show up automatically.\n" "# Sometimes, however, the chooser can't figure out how to broadcast,\n" "# so this may not work in all environments.\n" "#\n" "\n" "#* CHOOSER BROADCAST #any indirect host can get a chooser\n" "\n" "#\n" "# If you'd prefer to configure the set of hosts each terminal sees,\n" "# then just uncomment these lines (and comment the CHOOSER line above)\n" "# and edit the %hostlist line as appropriate\n" "#\n" "\n" "#%hostlist host-a host-b\n" "\n" "#* CHOOSER %hostlist #\n"; #endif #ifdef XDMCP static const char def_willing[] = "#! /bin/sh\n" "# The output of this script is displayed in the chooser window\n" "# (instead of \"Willing to manage\").\n" "\n" "load=`uptime|sed -e 's/^.*load[^0-9]*//'`\n" "nrusers=`who|cut -c 1-8|sort -u|wc -l|sed 's/^[ \t]*//'`\n" "s=\"\"; [ \"$nrusers\" != 1 ] && s=s\n" "\n" "echo \"${nrusers} user${s}, load: ${load}\"\n"; #endif static const char def_setup[] = "#! /bin/sh\n" "# Xsetup - run as root before the login dialog appears\n" "\n" "#xconsole -geometry 480x130-0-0 -notify -verbose -fn fixed -exitOnFail -file /dev/xconsole &\n"; static const char def_startup[] = "#! /bin/sh\n" "# Xstartup - run as root before session starts\n" "\n" "\n" "\n" "if [ -e /etc/nologin ]; then\n" " # always display the nologin message, if possible\n" " if [ -s /etc/nologin ] && which xmessage > /dev/null 2>&1; then\n" " xmessage -file /etc/nologin -geometry 640x480\n" " fi\n" " if [ \"$(id -u)\" != \"0\" ] && \\\n" " ! grep -qs '^ignore-nologin' /etc/trinity/tdm/tdm.options; then\n" " exit 1\n" " fi\n" "fi\n" "\n" "if grep -qs '^use-sessreg' /etc/trinity/tdm/tdm.options && \\\n" " which sessreg > /dev/null 2>&1; then\n" " exec sessreg -a -l \"$DISPLAY\" -u /var/run/utmp \\\n" " -h \"`echo $DISPLAY | cut -d: -f1`\" \"$USER\"\n" " # NOTREACHED\n" "fi\n"; static const char def_reset[] = "#! /bin/sh\n" "# Xreset - run as root after session exits\n" "\n" "# Reassign ownership of the console to root, this should disallow\n" "# assignment of console output to any random users's xterm. See Xstartup.\n" "#\n" "#chown root /dev/console\n" "#chmod 622 /dev/console\n" "\n" #ifdef _AIX "#devname=`echo $DISPLAY | cut -c1-8`\n" "#exec sessreg -d -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\"" #else "if grep -qs '^use-sessreg' /etc/trinity/tdm/tdm.options && \\\n" " which sessreg > /dev/null 2>&1; then\n" " exec sessreg -d -l \"$DISPLAY\" -u /var/run/utmp \\\n" " -h \"`echo $DISPLAY | cut -d: -f1`\" \"$USER\"\n" " # NOTREACHED\n" "fi\n"; #endif /* _AIX */ static const char def_session1[] = "#! /bin/sh\n" "# Xsession - run as user\n" "\n" "session=$1\n" "\n" "# Note that the respective logout scripts are not sourced.\n" "case $SHELL in\n" " */bash)\n" " [ -z \"$BASH\" ] && exec $SHELL $0 \"$@\"\n" " set +o posix\n" " [ -f /etc/profile ] && . /etc/profile\n" " if [ -f $HOME/.bash_profile ]; then\n" " . $HOME/.bash_profile\n" " elif [ -f $HOME/.bash_login ]; then\n" " . $HOME/.bash_login\n" " elif [ -f $HOME/.profile ]; then\n" " . $HOME/.profile\n" " fi\n" " ;;\n" " */zsh)\n" " [ -z \"$ZSH_NAME\" ] && exec $SHELL $0 \"$@\"\n" " emulate -R zsh\n" " [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc\n" " zhome=${ZDOTDIR:-$HOME}\n" " # zshenv is always sourced automatically.\n" " [ -f $zdir/zprofile ] && . $zdir/zprofile\n" " [ -f $zhome/.zprofile ] && . $zhome/.zprofile\n" " [ -f $zdir/zlogin ] && . $zdir/zlogin\n" " [ -f $zhome/.zlogin ] && . $zhome/.zlogin\n" " setopt shwordsplit noextendedglob\n" " ;;\n" " */csh|*/tcsh)\n" " # [t]cshrc is always sourced automatically.\n" " # Note that sourcing csh.login after .cshrc is non-standard.\n" " xsess_tmp="; static const char def_session2[] = "\n" " $SHELL -c \"if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c export -p >! $xsess_tmp\"\n" " . $xsess_tmp\n" " rm -f $xsess_tmp\n" " ;;\n" " *) # Plain sh, ksh, and anything we don't know.\n" " [ -f /etc/profile ] && . /etc/profile\n" " [ -f $HOME/.profile ] && . $HOME/.profile\n" " ;;\n" "esac\n" "# invoke global X session script\n" ". /etc/X11/Xsession\n"; static const char def_background[] = "[Desktop0]\n" "BackgroundMode=Flat\n" "BlendBalance=100\n" "BlendMode=NoBlending\n" "ChangeInterval=60\n" "Color1=0,0,200\n" "Color2=192,192,192\n" "CurrentWallpaper=0\n" "LastChange=0\n" "MinOptimizationDepth=1\n" "MultiWallpaperMode=NoMulti\n" "Pattern=fish\n" "Program=\n" "ReverseBlending=false\n" "UseSHM=false\n" "Wallpaper=isadora.png\n" "WallpaperList=\n" "WallpaperMode=Scaled\n"; static char * prepName( const char *fn ) { const char *tname; char *nname; tname = strrchr( fn, '/' ); ASPrintf( &nname, "%s/%s", newdir, tname ? tname + 1 : fn ); displace( nname ); return nname; } static FILE * Create( const char *fn, int mode ) { char *nname; FILE *f; nname = prepName( fn ); if (!(f = fopen( nname, "w" ))) { fprintf( stderr, "Cannot create %s\n", nname ); exit( 1 ); } chmod( nname, mode ); free( nname ); return f; } static void WriteOut( const char *fn, int mode, time_t stamp, const char *buf, size_t len ) { char *nname; int fd; struct utimbuf utim; nname = prepName( fn ); if ((fd = creat( nname, mode )) < 0) { fprintf( stderr, "Cannot create %s\n", nname ); exit( 1 ); } write( fd, buf, len ); close( fd ); if (stamp) { utim.actime = utim.modtime = stamp; utime( nname, &utim ); } free( nname ); } /* returns static array! */ static const char * resect( const char *sec, const char *name ) { static char sname[64]; char *p; if ((p = strrchr( sec, '-' ))) { sprintf( sname, "%.*s-%s", p - sec, sec, name ); return sname; } else return name; } static int inNewDir( const char *name ) { return !memcmp( name, TDMCONF "/", sizeof(TDMCONF) ); } static int inList( StrList *sp, const char *s ) { for (; sp; sp = sp->next) if (!strcmp( sp->str, s )) return 1; return 0; } static void addStr( StrList **sp, const char *s ) { for (; *sp; sp = &(*sp)->next) if (!strcmp( (*sp)->str, s )) return; *sp = mcalloc( sizeof(**sp) ); ASPrintf( (char **)&(*sp)->str, "%s", s ); } StrList *aflist, *uflist, *eflist, *cflist, *lflist; /* file is part of new config */ static void addedFile( const char *fn ) { addStr( &aflist, fn ); } /* file from old config was parsed */ static void usedFile( const char *fn ) { addStr( &uflist, fn ); } /* file from old config was copied with slight modifications */ static void editedFile( const char *fn ) { addStr( &eflist, fn ); } /* file from old config was copied verbatim */ static void copiedFile( const char *fn ) { addStr( &cflist, fn ); } /* file from old config is still being used */ static void linkedFile( const char *fn ) { addStr( &lflist, fn ); } /* * XXX this stuff is highly borked. it does not handle collisions at all. */ static int copyfile( Entry *ce, const char *tname, int mode, int (*proc)( File * ) ) { const char *tptr; char *nname; File file; int rt; if (!*ce->value) return 1; tptr = strrchr( tname, '/' ); ASPrintf( &nname, TDMCONF "/%s", tptr ? tptr + 1 : tname ); if (inList( cflist, ce->value ) || inList( eflist, ce->value ) || inList( lflist, ce->value )) { rt = 1; goto doret; } if (!readFile( &file, ce->value )) { fprintf( stderr, "Warning: cannot copy file %s\n", ce->value ); rt = 0; } else { if (!proc || !proc( &file )) { if (!use_destdir && !strcmp( ce->value, nname )) linkedFile( nname ); else { struct stat st; stat( ce->value, &st ); WriteOut( nname, mode, st.st_mtime, file.buf, file.eof - file.buf ); copiedFile( ce->value ); } } else { WriteOut( nname, mode, 0, file.buf, file.eof - file.buf ); editedFile( ce->value ); } if (strcmp( ce->value, nname ) && inNewDir( ce->value ) && !use_destdir) displace( ce->value ); addedFile( nname ); rt = 1; } doret: ce->value = nname; return rt; } static void dlinkfile( const char *name ) { File file; if (!readFile( &file, name )) { fprintf( stderr, "Warning: cannot read file %s\n", name ); return; } if (inNewDir( name ) && use_destdir) { struct stat st; stat( name, &st ); WriteOut( name, st.st_mode, st.st_mtime, file.buf, file.eof - file.buf ); copiedFile( name ); } else linkedFile( name ); addedFile( name ); } static void linkfile( Entry *ce ) { if (ce->written && *ce->value) dlinkfile( ce->value ); } static void writefile( const char *tname, int mode, const char *cont ) { WriteOut( tname, mode, 0, cont, strlen( cont ) ); addedFile( tname ); } char *background; static void handBgCfg( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) /* can be only the X-*-Greeter one */ writefile( def_BackgroundCfg, 0644, background ? background : def_background ); #if 0 /* risk of kcontrol clobbering the original file */ else if (old_confs) linkfile( ce ); #endif else { if (!copyfile( ce, ce->value, 0644, 0 )) { if (!strcmp( cs->name, "X-*-Greeter" )) writefile( def_BackgroundCfg, 0644, def_background ); ce->active = 0; } } } #ifdef HAVE_VTS static char * mem_mem( char *mem, int lmem, const char *smem, int lsmem ) { for (; lmem >= lsmem; mem++, lmem--) if (!memcmp( mem, smem, lsmem )) return mem + lsmem; return 0; } static int maxTTY, TTYmask; static void getInitTab( void ) { if (maxTTY) return; if (!maxTTY) { maxTTY = 6; TTYmask = 0x3f; } } #endif /* TODO: handle solaris' local_uid specs */ static char * ReadWord( File *file, int EOFatEOL ) { char *wordp, *wordBuffer; int quoted; char c; rest: wordp = wordBuffer = file->cur; mloop: quoted = 0; qloop: if (file->cur == file->eof) { doeow: if (wordp == wordBuffer) return 0; retw: *wordp = '\0'; return wordBuffer; } c = *file->cur++; switch (c) { case '#': if (quoted) break; do { if (file->cur == file->eof) goto doeow; c = *file->cur++; } while (c != '\n'); case '\0': case '\n': if (EOFatEOL && !quoted) { file->cur--; goto doeow; } if (wordp != wordBuffer) { file->cur--; goto retw; } goto rest; case ' ': case '\t': if (wordp != wordBuffer) goto retw; goto rest; case '\\': if (!quoted) { quoted = 1; goto qloop; } break; } *wordp++ = c; goto mloop; } /* backslashes are double-escaped - for TDEConfig and for parseArgs */ static const char * joinArgs( StrList *argv ) { StrList *av; const char *s, *rs; char *str; int slen; if (!argv) return ""; for (slen = 0, av = argv; slen++, av; av = av->next) { int nq = 0; for (s = av->str; *s; s++, slen++) if (isspace( *s ) || *s == '\'') nq = 2; else if (*s == '"') slen += 2; else if (*s == '\\') slen += 3; slen += nq; } rs = str = mmalloc( slen ); for (av = argv; av; av = av->next) { int nq = 0; for (s = av->str; *s; s++) if (isspace( *s ) || *s == '\'') nq = 2; if (av != argv) *str++ = ' '; if (nq) *str++ = '"'; for (s = av->str; *s; s++) { if (*s == '\\') *str++ = '\\'; if (*s == '"' || *s == '\\') { *str++ = '\\'; *str++ = '\\'; } *str++ = *s; } if (nq) *str++ = '"'; } *str = 0; return rs; } # define dLocation 1 # define dLocal 0 # define dForeign 1 static struct displayMatch { const char *name; int len, type; } displayTypes[] = { { "local", 5, dLocal }, { "foreign", 7, dForeign }, }; static int parseDisplayType( const char *string, const char **atPos ) { struct displayMatch *d; *atPos = 0; for (d = displayTypes; d < displayTypes + as(displayTypes); d++) { if (!memcmp( d->name, string, d->len ) && (!string[d->len] || string[d->len] == '@')) { if (string[d->len] == '@' && string[d->len + 1]) *atPos = string + d->len + 1; return d->type; } } return -1; } typedef struct serverEntry { struct serverEntry *next; const char *name, *class2, *console, *argvs, *arglvs; StrList *argv, *arglv; int type, reserve, vt; } ServerEntry; static void absorb_xservers( const char *sect ATTR_UNUSED, char **value ) { ServerEntry *se, *se1, *serverList, **serverPtr; const char *word, *word2; char *sdpys, *rdpys; StrList **argp, **arglp, *ap, *ap2; File file; int nldpys = 0, nrdpys = 0, dpymask = 0; int cpcmd, cpcmdl; #ifdef HAVE_VTS int dn, cpvt, mtty; #endif if (**value == '/') { if (!readFile( &file, *value )) return; usedFile( *value ); } else { file.buf = *value; file.eof = *value + strlen( *value ); } file.cur = file.buf; serverPtr = &serverList; #ifdef HAVE_VTS bustd: #endif while ((word = ReadWord( &file, 0 ))) { se = mcalloc( sizeof(*se) ); se->name = word; if (!(word = ReadWord( &file, 1 ))) continue; se->type = parseDisplayType( word, &se->console ); if (se->type < 0) { se->class2 = word; if (!(word = ReadWord( &file, 1 ))) continue; se->type = parseDisplayType( word, &se->console ); if (se->type < 0) { while (ReadWord( &file, 1 )); continue; } } word = ReadWord( &file, 1 ); if (word && !strcmp( word, "reserve" )) { se->reserve = 1; word = ReadWord( &file, 1 ); } if (((se->type & dLocation) == dLocal) != (word != 0)) continue; argp = &se->argv; arglp = &se->arglv; while (word) { #ifdef HAVE_VTS if (word[0] == 'v' && word[1] == 't') se->vt = atoi( word + 2 ); else if (!strcmp( word, "-crt" )) { /* SCO style */ if (!(word = ReadWord( &file, 1 )) || memcmp( word, "/dev/tty", 8 )) goto bustd; se->vt = atoi( word + 8 ); } else #endif if (strcmp( word, se->name )) { ap = mmalloc( sizeof(*ap) ); ap->str = word; if (!strcmp( word, "-nolisten" )) { if (!(word2 = ReadWord( &file, 1 ))) break; ap2 = mmalloc( sizeof(*ap2) ); ap2->str = word2; ap->next = ap2; if (!strcmp( word2, "unix" )) { *argp = ap; argp = &ap2->next; } else { *arglp = ap; arglp = &ap2->next; } } else { *argp = ap; argp = &ap->next; } } word = ReadWord( &file, 1 ); } *argp = *arglp = 0; if ((se->type & dLocation) == dLocal) { nldpys++; dpymask |= 1 << atoi( se->name + 1 ); if (se->reserve) nrdpys++; } *serverPtr = se; serverPtr = &se->next; } *serverPtr = 0; #ifdef HAVE_VTS /* don't copy only if all local displays are ordered and have a vt */ cpvt = 0; getInitTab(); for (se = serverList, mtty = maxTTY; se; se = se->next) if ((se->type & dLocation) == dLocal) { mtty++; if (se->vt != mtty) { cpvt = 1; break; } } #endif for (se = serverList; se; se = se->next) { se->argvs = joinArgs( se->argv ); se->arglvs = joinArgs( se->arglv ); } se1 = 0, cpcmd = cpcmdl = 0; for (se = serverList; se; se = se->next) if ((se->type & dLocation) == dLocal) { if (!se1) se1 = se; else { if (strcmp( se1->argvs, se->argvs )) cpcmd = 1; if (strcmp( se1->arglvs, se->arglvs )) cpcmdl = 1; } } if (se1) { putfqval( "X-:*-Core", "ServerCmd", se1->argvs ); putfqval( "X-:*-Core", "ServerArgsLocal", se1->arglvs ); for (se = serverList; se; se = se->next) if ((se->type & dLocation) == dLocal) { char sec[32]; sprintf( sec, "X-%s-Core", se->name ); if (cpcmd) putfqval( sec, "ServerCmd", se->argvs ); if (cpcmdl) putfqval( sec, "ServerArgsLocal", se->arglvs ); #ifdef HAVE_VTS if (cpvt && se->vt) { char vt[8]; sprintf( vt, "%d", se->vt ); putfqval( sec, "ServerVT", vt ); } #else if (se->console) putfqval( sec, "ServerTTY", se->console ); #endif } } sdpys = rdpys = 0; for (se = serverList; se; se = se->next) StrCat( se->reserve ? &rdpys : &sdpys, se->class2 ? ",%s_%s" : ",%s", se->name, se->class2 ); #ifdef HAVE_VTS /* add reserve dpys */ if (nldpys < 4 && nldpys && !nrdpys) for (; nldpys < 4; nldpys++) { for (dn = 0; dpymask & (1 << dn); dn++); dpymask |= (1 << dn); StrCat( &rdpys, ",:%d", dn ); } #endif putfqval( "General", "StaticServers", sdpys ? sdpys + 1 : "" ); putfqval( "General", "ReserveServers", rdpys ? rdpys + 1 : "" ); if (**value == '/' && inNewDir( *value ) && !use_destdir) displace( *value ); } #ifdef HAVE_VTS static void upd_servervts( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) { /* there is only the Global one */ #ifdef __linux__ /* XXX actually, sysvinit */ getInitTab(); ASPrintf( (char **)&ce->value, "-%d", maxTTY + 1 ); ce->active = ce->written = 1; #endif } } static void upd_consolettys( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) { /* there is only the Global one */ #ifdef __linux__ /* XXX actually, sysvinit */ char *buf; int i; getInitTab(); for (i = 0, buf = 0; i < 16; i++) if (TTYmask & (1 << i)) StrCat( &buf, ",tty%d", i + 1 ); if (buf) { ce->value = buf + 1; ce->active = ce->written = 1; } #endif } } #endif #ifdef XDMCP static void cp_keyfile( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) /* there is only the Global one */ return; if (old_confs) linkfile( ce ); else if (!copyfile( ce, "tdmkeys", 0600, 0 )) ce->active = 0; } static void mk_xaccess( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) /* there is only the Global one */ writefile( def_Xaccess, 0644, def_xaccess ); else if (old_confs) linkfile( ce ); else copyfile( ce, "Xaccess", 0644, 0 ); /* don't handle error, it will disable Xdmcp automatically */ } static void mk_willing( Entry *ce, Section *cs ATTR_UNUSED ) { char *fname; if (!ce->active) /* there is only the Global one */ goto dflt; else { if (!(fname = strchr( ce->value, '/' ))) return; /* obviously in-line (or empty) */ if (old_scripts || inNewDir( fname )) dlinkfile( fname ); else { dflt: ce->value = TDMCONF "/Xwilling"; ce->active = ce->written = 1; writefile( ce->value, 0755, def_willing ); } } } #endif /* static int edit_resources( File *file ) { // XXX remove any login*, chooser*, ... resources return 0; } */ static void cp_resources( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) /* the X-*-Greeter one */ return; if (old_confs) linkfile( ce ); else if (!copyfile( ce, ce->value, 0644, 0/*edit_resources*/ )) ce->active = 0; } static int delstr( File *fil, const char *pat ) { char *p, *pp, *bpp; const char *pap, *paap; *fil->eof = 0; for (p = fil->buf; *p; p++) { for (pp = p, pap = pat; ; ) { if (!*pap) { *p = '\n'; memcpy( p + 1, pp, fil->eof - pp + 1 ); fil->eof -= pp - p - 1; return 1; } else if (!memcmp( pap, "*/", 2 )) { paap = pap += 2; while (!isspace( *pap )) pap++; if (*pp != '/') break; for (;;) for (bpp = ++pp; *pp != '/'; pp++) if (!*pp || isspace( *pp )) goto wbrk; wbrk: if ((pp - bpp != pap - paap) || memcmp( bpp, paap, pap - paap )) break; } else if (*pap == '\t') { pap++; while (*pp == ' ' || *pp == '\t') pp++; } else if (*pap == '[') { pap++; for (;;) { if (!*pap) { fprintf( stderr, "Internal error: unterminated char set\n" ); exit( 1 ); } if (*pap == *pp) { while (*++pap != ']') if (!*pap) { fprintf( stderr, "Internal error: unterminated char set\n" ); exit( 1 ); } pap++; pp++; break; } if (*++pap == ']') goto no; } } else { if (*pap == '\n') while (*pp == ' ' || *pp == '\t') pp++; if (*pap != *pp) break; pap++; pp++; } } no: ; } return 0; } /* XXX the UseBackground voodoo will horribly fail, if multiple sections link to the same Xsetup file */ static int mod_usebg; static int edit_setup( File *file ) { int chg = delstr( file, "\n" "(\n" " PIDFILE=/var/run/tdmdesktop-$DISPLAY.pid\n" " */tdmdesktop\t&\n" " echo $! >$PIDFILE\n" " wait $!\n" " rm $PIDFILE\n" ")\t&\n" ) | delstr( file, "\n" "*/tdmdesktop\t&\n" ) | delstr( file, "\n" "tdmdesktop\t&\n" ) | delstr( file, "\n" "tdmdesktop\n" ); putval( "UseBackground", chg ? "true" : "false" ); return chg; } static void mk_setup( Entry *ce, Section *cs ) { setsect( resect( cs->name, "Greeter" ) ); if (old_scripts || mixed_scripts) { if (mod_usebg && *ce->value) putval( "UseBackground", "false" ); linkfile( ce ); } else { if (ce->active && inNewDir( ce->value )) { if (mod_usebg) copyfile( ce, ce->value, 0755, edit_setup ); else linkfile( ce ); } else { ce->value = TDMCONF "/Xsetup"; ce->active = ce->written = 1; writefile( ce->value, 0755, def_setup ); } } } static int edit_startup( File *file ) { int chg1 = 0, chg2 = 0; if (mod_usebg && (delstr( file, "\n" "PIDFILE=/var/run/tdmdesktop-$DISPLAY.pid\n" "if [[] -f $PIDFILE ] ; then\n" " kill `cat $PIDFILE`\n" "fi\n" ) || delstr( file, "\n" "PIDFILE=/var/run/tdmdesktop-$DISPLAY.pid\n" "test -f $PIDFILE && kill `cat $PIDFILE`\n" ))) chg1 = 1; if (oldver < 0x0203) { chg2 = #ifdef _AIX delstr( file, "\n" "# We create a pseudodevice for finger. (host:0 becomes [kx]dm/host_0)\n" ); "# Without it, finger errors out with \"Can't stat /dev/host:0\".\n" "#\n" "if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n" " devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n" " hostname=`echo $DISPLAY | /usr/bin/cut -d':' -f1`\n" "\n" " if [[] -z \"$devname\" ]; then\n" " devname=\"unknown\"\n" " fi\n" " if [[] ! -d /dev/[kx]dm ]; then\n" " /usr/bin/mkdir /dev/[kx]dm\n" " /usr/bin/chmod 755 /dev/[kx]dm\n" " fi\n" " /usr/bin/touch /dev/[kx]dm/$devname\n" " /usr/bin/chmod 644 /dev/[kx]dm/$devname\n" "\n" " if [[] -z \"$hostname\" ]; then\n" " exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname $USER\n" " else\n" " exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname -h $hostname $USER\n" " fi\n" "fi\n") | #else # ifdef BSD delstr( file, "\n" "exec sessreg -a -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) | # endif #endif /* _AIX */ delstr( file, "\n" "exec sessreg -a -l $DISPLAY" #ifdef BSD " -x */Xservers" #endif " $USER\n" ) | delstr( file, "\n" "exec sessreg -a -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" ); putval( "UseSessReg", chg2 ? "true" : "false"); } return chg1 | chg2; } static void mk_startup( Entry *ce, Section *cs ) { setsect( cs->name ); if (old_scripts || mixed_scripts) linkfile( ce ); else { if (ce->active && inNewDir( ce->value )) { if (mod_usebg || oldver < 0x0203) copyfile( ce, ce->value, 0755, edit_startup ); else linkfile( ce ); } else { ce->value = TDMCONF "/Xstartup"; ce->active = ce->written = 1; writefile( ce->value, 0755, def_startup ); } } } static int edit_reset( File *file ) { return #ifdef _AIX delstr( file, "\n" "if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n" " devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n" " exec /usr/lib/X11/xdm/sessreg -d -l [kx]dm/$devname $USER\n" "fi\n" ) | #else # ifdef BSD delstr( file, "\n" "exec sessreg -d -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) | # endif #endif /* _AIX */ delstr( file, "\n" "exec sessreg -d -l $DISPLAY" # ifdef BSD " -x */Xservers" # endif " $USER\n" ) | delstr( file, "\n" "exec sessreg -d -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" ); } static void mk_reset( Entry *ce, Section *cs ATTR_UNUSED ) { if (old_scripts || mixed_scripts) linkfile( ce ); else { if (ce->active && inNewDir( ce->value )) { if (oldver < 0x0203) copyfile( ce, ce->value, 0755, edit_reset ); else linkfile( ce ); } else { ce->value = TDMCONF "/Xreset"; ce->active = ce->written = 1; writefile( ce->value, 0755, def_reset ); } } } static void mk_session( Entry *ce, Section *cs ATTR_UNUSED ) { char *def_session; const char *tmpf; if ((old_scripts || (ce->active && inNewDir( ce->value ))) && oldver >= 0x202) linkfile( ce ); else { tmpf = locate( "mktemp" ) ? "`mktemp /tmp/xsess-env-XXXXXX`" : locate( "tempfile" ) ? "`tempfile`" : "$HOME/.xsession-env-$DISPLAY"; ASPrintf( &def_session, "%s%s%s", def_session1, tmpf, def_session2 ); ce->value = TDMCONF "/Xsession"; ce->active = ce->written = 1; writefile( ce->value, 0755, def_session ); } } static void upd_language( Entry *ce, Section *cs ATTR_UNUSED ) { if (!strcmp( ce->value, "C" )) ce->value = (char *)"en_US"; } static void upd_guistyle( Entry *ce, Section *cs ATTR_UNUSED ) { if (!strcmp( ce->value, "Motif+" )) ce->value = (char *)"MotifPlus"; else if (!strcmp( ce->value, "KDE" )) ce->value = (char *)"Default"; } static void upd_showusers( Entry *ce, Section *cs ) { if (!strcmp( ce->value, "All" )) ce->value = (char *)"NotHidden"; else if (!strcmp( ce->value, "None" )) { if (ce->active) putfqval( cs->name, "UserList", "false" ); ce->value = (char *)"Selected"; ce->active = 0; ce->written = 1; } } static const char *defminuid, *defmaxuid; static void upd_minshowuid( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) { ce->value = defminuid; ce->active = ce->written = 1; } } static void upd_maxshowuid( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) { ce->value = defmaxuid; ce->active = ce->written = 1; } } static void upd_hiddenusers( Entry *ce, Section *cs ATTR_UNUSED ) { char *nv; const char *msu, *pt, *et; struct passwd *pw; unsigned minuid, maxuid; char nbuf[128]; if (!ce->active) return; msu = getfqval( cs->name, "MinShowUID", "0" ); sscanf( msu, "%u", &minuid ); msu = getfqval( cs->name, "MaxShowUID", "65535" ); sscanf( msu, "%u", &maxuid ); nv = 0; pt = ce->value; for (;;) { et = strpbrk( pt, ";," ); if (et) { memcpy( nbuf, pt, et - pt ); nbuf[et - pt] = 0; } else strcpy( nbuf, pt ); if ((pw = getpwnam( nbuf ))) { if (!pw->pw_uid || (pw->pw_uid >= minuid && pw->pw_uid <= maxuid)) { if (nv) StrCat( &nv, ",%s", nbuf ); else nv = mstrdup( nbuf ); } } if (!et) break; pt = et + 1; } ce->value = nv ? nv : ""; } static void upd_forgingseed( Entry *ce, Section *cs ATTR_UNUSED ) { if (!ce->active) { ASPrintf( (char **)&ce->value, "%d", time( 0 ) ); ce->active = ce->written = 1; } } static void upd_fifodir( Entry *ce, Section *cs ATTR_UNUSED ) { const char *dir; struct stat st; if (use_destdir) return; dir = ce->active ? ce->value : def_FifoDir; stat( dir, &st ); chmod( dir, st.st_mode | 0755 ); } static void upd_datadir( Entry *ce, Section *cs ATTR_UNUSED ) { char *oldsts, *newsts; const char *dir; if (use_destdir) return; dir = ce->active ? ce->value : def_DataDir; if (mkdirp( dir, 0755, "data", 0 ) && oldkde) { ASPrintf( &oldsts, "%s/tdm/tdmsts", oldkde ); ASPrintf( &newsts, "%s/tdmsts", dir ); rename( oldsts, newsts ); } } static void CopyFile( const char *from, const char *to ) { File file; int fd; if (readFile( &file, from )) { if ((fd = creat( to, 0644 )) >= 0) { write( fd, file.buf, file.eof - file.buf ); close( fd ); } freeBuf( &file ); } } static void upd_facedir( Entry *ce, Section *cs ATTR_UNUSED ) { char *oldpic, *newpic, *defpic, *rootpic; const char *dir; struct passwd *pw; if (use_destdir) return; dir = ce->active ? ce->value : def_FaceDir; if (mkdirp( dir, 0755, "user face", 0 )) { ASPrintf( &defpic, "%s/.default.face.icon", dir ); ASPrintf( &rootpic, "%s/root.face.icon", dir ); if (oldkde) { setpwent(); while ((pw = getpwent())) if (strcmp( pw->pw_name, "root" )) { ASPrintf( &oldpic, "%s/../apps/tdm/pics/users/%s.png", oldkde, pw->pw_name ); ASPrintf( &newpic, "%s/%s.face.icon", dir, pw->pw_name ); rename( oldpic, newpic ); free( newpic ); free( oldpic ); } endpwent(); ASPrintf( &oldpic, "%s/../apps/tdm/pics/users/default.png", oldkde ); if (!rename( oldpic, defpic )) defpic = 0; ASPrintf( &oldpic, "%s/../apps/tdm/pics/users/root.png", oldkde ); if (!rename( oldpic, rootpic )) rootpic = 0; } if (defpic) { ASPrintf( &oldpic, "%s/default1.png", facesrc ); CopyFile( oldpic, defpic ); } if (rootpic) { ASPrintf( &oldpic, "%s/root1.png", facesrc ); CopyFile( oldpic, rootpic ); } } } CONF_GEN_ENTRIES static Sect * findSect( const char *name ) { const char *p; int i; p = strrchr( name, '-' ); if (!p) p = name; for (i = 0; i < as(allSects); i++) if (!strcmp( allSects[i]->name, p )) return allSects[i]; fprintf( stderr, "Internal error: unknown section %s\n", name ); exit( 1 ); } static Ent * findEnt( Sect *sect, const char *key ) { int i; for (i = 0; i < sect->nents; i++) if (!strcmp( sect->ents[i].key, key )) return sect->ents + i; fprintf( stderr, "Internal error: unknown key %s in section %s\n", key, sect->name ); exit( 1 ); } /* * defaults */ typedef struct DEnt { const char *key; const char *value; int active; } DEnt; typedef struct DSect { const char *name; DEnt *ents; int nents; const char *comment; } DSect; CONF_GEN_EXAMPLE static void mkdefconf( void ) { Section *cs, **csp; Entry *ce, **cep; int sc, ec; for (csp = &config, sc = 0; sc < as(dAllSects); csp = &(cs->next), sc++) { cs = mcalloc( sizeof(*cs) ); *csp = cs; cs->spec = findSect( dAllSects[sc].name ); cs->name = dAllSects[sc].name; cs->comment = dAllSects[sc].comment; for (cep = &(cs->ents), ec = 0; ec < dAllSects[sc].nents; cep = &(ce->next), ec++) { ce = mcalloc( sizeof(*ce) ); *cep = ce; ce->spec = findEnt( cs->spec, dAllSects[sc].ents[ec].key ); ce->value = dAllSects[sc].ents[ec].value; ce->active = dAllSects[sc].ents[ec].active; } } } /* * read rc file structure */ typedef struct REntry { struct REntry *next; const char *key; char *value; } REntry; typedef struct RSection { struct RSection *next; const char *name; REntry *ents; } RSection; static RSection * ReadConf( const char *fname ) { char *nstr; char *s, *e, *st, *en, *ek, *sl; RSection *rootsec = 0, *cursec; REntry *curent; int nlen; int line, sectmoan; File file; if (!readFile( &file, fname )) return 0; usedFile( fname ); for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) { line++; while ((s < file.eof) && isspace( *s ) && (*s != '\n')) s++; if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) { sktoeol: while ((s < file.eof) && (*s != '\n')) s++; continue; } sl = s; if (*s == '[') { while ((s < file.eof) && (*s != '\n')) s++; e = s - 1; while ((e > sl) && isspace( *e )) e--; if (*e != ']') { fprintf( stderr, "Invalid section header at %s:%d\n", fname, line ); continue; } sectmoan = 0; nstr = sl + 1; nlen = e - nstr; for (cursec = rootsec; cursec; cursec = cursec->next) if (!memcmp( nstr, cursec->name, nlen ) && !cursec->name[nlen]) { #if 0 /* not our business ... */ fprintf( stderr, "Warning: Multiple occurrences of section " "[%.*s] in %s. Consider merging them.\n", nlen, nstr, fname ); #endif goto secfnd; } cursec = mmalloc( sizeof(*cursec) ); ASPrintf( (char **)&cursec->name, "%.*s", nlen, nstr ); cursec->ents = 0; cursec->next = rootsec; rootsec = cursec; secfnd: continue; } if (!cursec) { if (sectmoan) { sectmoan = 0; fprintf( stderr, "Entry outside any section at %s:%d", fname, line ); } goto sktoeol; } for (; (s < file.eof) && (*s != '\n'); s++) if (*s == '=') goto haveeq; fprintf( stderr, "Invalid entry (missing '=') at %s:%d\n", fname, line ); continue; haveeq: for (ek = s - 1;; ek--) { if (ek < sl) { fprintf( stderr, "Invalid entry (empty key) at %s:%d\n", fname, line ); goto sktoeol; } if (!isspace( *ek )) break; } s++; while ((s < file.eof) && isspace( *s ) && (*s != '\n')) s++; st = s; while ((s < file.eof) && (*s != '\n')) s++; for (en = s - 1; en >= st && isspace( *en ); en--); nstr = sl; nlen = ek - sl + 1; for (curent = cursec->ents; curent; curent = curent->next) if (!memcmp( nstr, curent->key, nlen ) && !curent->key[nlen]) { fprintf( stderr, "Multiple occurrences of key '%s' in section " "[%s] of %s.\n", curent->key, cursec->name, fname ); goto keyfnd; } curent = mmalloc( sizeof(*curent) ); ASPrintf( (char **)&curent->key, "%.*s", nlen, nstr ); ASPrintf( (char **)&curent->value, "%.*s", en - st + 1, st ); curent->next = cursec->ents; cursec->ents = curent; keyfnd: continue; } return rootsec; } static int mergeKdmRcOld( const char *path ) { char *p; struct stat st; ASPrintf( &p, "%s/tdmrc", path ); if (stat( p, &st )) { free( p ); return 0; } printf( "Information: ignoring old tdmrc %s from kde < 2.2\n", p ); free( p ); return 1; } typedef struct { const char *sect, *key, *def; int (*cond)( void ); } FDefs; static void applydefs( FDefs *chgdef, int ndefs, const char *path ) { char *p; int i; for (i = 0; i < ndefs; i++) if (!getfqval( chgdef[i].sect, chgdef[i].key, 0 ) && (!chgdef[i].cond || chgdef[i].cond())) { ASPrintf( &p, chgdef[i].def, path ); putfqval( chgdef[i].sect, chgdef[i].key, p ); free( p ); } } #ifdef XDMCP static FDefs tdmdefs_all[] = { { "Xdmcp", "Xaccess", "%s/tdm/Xaccess", 0 }, { "Xdmcp", "Willing", "", 0 }, }; #endif static FDefs tdmdefs_eq_22[] = { { "General", "PidFile", "/var/run/xdm.pid", 0 }, { "X-*-Core", "Setup", "%s/tdm/Xsetup", 0 }, { "X-*-Core", "Startup", "%s/tdm/Xstartup", 0 }, { "X-*-Core", "Reset", "%s/tdm/Xreset", 0 }, { "X-*-Core", "Session", "%s/tdm/Xsession", 0 }, }; #ifdef XDMCP static int if_xdmcp (void) { return isTrue( getfqval( "Xdmcp", "Enable", "true" ) ); } static FDefs tdmdefs_le_30[] = { { "Xdmcp", "KeyFile", "%s/tdm/tdmkeys", if_xdmcp }, }; #endif /* HACK: misused by is22conf() below */ static FDefs tdmdefs_ge_30[] = { { "X-*-Core", "Setup", "", 0 }, { "X-*-Core", "Startup", "", 0 }, { "X-*-Core", "Reset", "", 0 }, { "X-*-Core", "Session", XBINDIR "/xterm -ls -T", 0 }, }; static int if_usebg (void) { return isTrue( getfqval( "X-*-Greeter", "UseBackground", "true" ) ); } static FDefs tdmdefs_ge_31[] = { { "X-*-Greeter","BackgroundCfg","%s/tdm/backgroundrc", if_usebg }, }; static int is22conf( const char *path ) { char *p; const char *val; int i, sl; sl = ASPrintf( &p, "%s/tdm/", path ); /* safe bet, i guess ... */ for (i = 0; i < 4; i++) { val = getfqval( "X-*-Core", tdmdefs_ge_30[i].key, 0 ); if (val && !memcmp( val, p, sl )) { free( p ); return 0; } } free( p ); return 1; } typedef struct KUpdEnt { const char *okey, *nsec, *nkey; void (*func)( const char *sect, char **value ); } KUpdEnt; typedef struct KUpdSec { const char *osec; KUpdEnt *ents; int nents; } KUpdSec; #ifdef XDMCP static void P_EnableChooser( const char *sect ATTR_UNUSED, char **value ) { *value = (char *)(isTrue( *value ) ? "DefaultLocal" : "LocalOnly"); } #endif static void P_UseLilo( const char *sect ATTR_UNUSED, char **value ) { *value = (char *)(isTrue( *value ) ? "Lilo" : "None"); } CONF_GEN_KMERGE static int mergeKdmRcNewer( const char *path ) { char *p; const char *cp, *sec, *key; RSection *rootsect, *cs; REntry *ce; int i, j; static char sname[64]; ASPrintf( &p, "%s/tdm/tdmrc", path ); if (!(rootsect = ReadConf( p ))) { free( p ); return 0; } printf( "Information: reading current tdmrc %s (from kde >= 2.2.x)\n", p ); free( p ); for (cs = rootsect; cs; cs = cs->next) { if (!strcmp( cs->name, "Desktop0" )) { background = mstrdup( "[Desktop0]\n" ); for (ce = cs->ents; ce; ce = ce->next) StrCat( &background, "%s=%s\n", ce->key, ce->value ); } else { cp = strrchr( cs->name, '-' ); if (!cp) cp = cs->name; else if (cs->name[0] != 'X' || cs->name[1] != '-') goto dropsec; for (i = 0; i < as(kupsects); i++) if (!strcmp( cp, kupsects[i].osec )) { for (ce = cs->ents; ce; ce = ce->next) { for (j = 0; j < kupsects[i].nents; j++) if (!strcmp( ce->key, kupsects[i].ents[j].okey )) { if (kupsects[i].ents[j].nsec == (char *)-1) { kupsects[i].ents[j].func( 0, &ce->value ); goto gotkey; } if (!kupsects[i].ents[j].nsec) sec = cs->name; else { sec = sname; sprintf( sname, "%.*s-%s", cp - cs->name, cs->name, kupsects[i].ents[j].nsec ); } if (!kupsects[i].ents[j].nkey) key = ce->key; else key = kupsects[i].ents[j].nkey; if (kupsects[i].ents[j].func) kupsects[i].ents[j].func( sec, &ce->value ); putfqval( sec, key, ce->value ); goto gotkey; } printf( "Information: dropping key %s from section [%s]\n", ce->key, cs->name ); gotkey: ; } goto gotsec; } dropsec: printf( "Information: dropping section [%s]\n", cs->name ); gotsec: ; } } #ifdef XDMCP applydefs( tdmdefs_all, as(tdmdefs_all), path ); #endif if (!*(cp = getfqval( "General", "ConfigVersion", "" ))) { /* < 3.1 */ mod_usebg = 1; if (is22conf( path )) { /* work around 2.2.x defaults borkedness */ applydefs( tdmdefs_eq_22, as(tdmdefs_eq_22), path ); printf( "Information: current tdmrc is from kde 2.2\n" ); } else { applydefs( tdmdefs_ge_30, as(tdmdefs_ge_30), path ); printf( "Information: current tdmrc is from kde 3.0\n" ); } #ifdef XDMCP /* work around minor <= 3.0.x defaults borkedness */ applydefs( tdmdefs_le_30, as(tdmdefs_le_30), path ); #endif } else { int ma, mi; sscanf( cp, "%d.%d", &ma, &mi ); oldver = (ma << 8) | mi; printf( "Information: current tdmrc is from kde >= 3.1 (config version %d.%d)\n", ma, mi ); applydefs( tdmdefs_ge_30, as(tdmdefs_ge_30), path ); applydefs( tdmdefs_ge_31, as(tdmdefs_ge_31), path ); } return 1; } typedef struct XResEnt { const char *xname; const char *ksec, *kname; void (*func)( const char *sect, char **value ); } XResEnt; static void handleXdmVal( const char *dpy, const char *key, char *value, const XResEnt *ents, int nents ) { const char *kname; int i; char knameb[80], sname[80]; for (i = 0; i < nents; i++) if (!strcmp( key, ents[i].xname ) || (key[0] == toupper( ents[i].xname[0] ) && !strcmp( key + 1, ents[i].xname + 1 ))) { if (ents[i].ksec == (char *)-1) { ents[i].func (0, &value); break; } sprintf( sname, ents[i].ksec, dpy ); if (ents[i].kname) kname = ents[i].kname; else { kname = knameb; sprintf( knameb, "%c%s", toupper( ents[i].xname[0] ), ents[i].xname + 1 ); } if (ents[i].func) ents[i].func( sname, &value ); putfqval( sname, kname, value ); break; } } static void P_List( const char *sect ATTR_UNUSED, char **value ) { int is, d, s; char *st; for (st = *value, is = d = s = 0; st[s]; s++) if (st[s] == ' ' || st[s] == '\t') { if (!is) st[d++] = ','; is = 1; } else { st[d++] = st[s]; is = 0; } st[d] = 0; } static void P_authDir( const char *sect ATTR_UNUSED, char **value ) { int l; l = strlen( *value ); if (l < 4) { *value = 0; return; } if ((*value)[l-1] == '/') (*value)[--l] = 0; if (!strncmp( *value, "/tmp/", 5 ) || !strncmp( *value, "/var/tmp/", 9 )) { printf( "Warning: Resetting inappropriate value %s for AuthDir to default\n", *value ); *value = 0; return; } if ((l >= 4 && !strcmp( *value + l - 4, "/tmp" )) || (l >= 6 && !strcmp( *value + l - 6, "/xauth" )) || (l >= 8 && !strcmp( *value + l - 8, "/authdir" )) || (l >= 10 && !strcmp( *value + l - 10, "/authfiles" ))) return; ASPrintf( value, "%s/authdir", *value ); } static void P_openDelay( const char *sect, char **value ) { putfqval( sect, "ServerTimeout", *value ); } static void P_noPassUsers( const char *sect, char **value ATTR_UNUSED ) { putfqval( sect, "NoPassEnable", "true" ); } static void P_autoUser( const char *sect, char **value ATTR_UNUSED ) { putfqval( sect, "AutoLoginEnable", "true" ); } #ifdef XDMCP static void P_requestPort( const char *sect, char **value ) { if (!strcmp( *value, "0" )) { *value = 0; putfqval( sect, "Enable", "false" ); } else putfqval( sect, "Enable", "true" ); } #endif static int tdmrcmode = 0644; static void P_autoPass( const char *sect ATTR_UNUSED, char **value ATTR_UNUSED ) { tdmrcmode = 0600; } CONF_GEN_XMERGE static XrmQuark XrmQString, empty = NULLQUARK; static Bool DumpEntry( XrmDatabase *db ATTR_UNUSED, XrmBindingList bindings, XrmQuarkList quarks, XrmRepresentation *type, XrmValuePtr value, XPointer data ATTR_UNUSED ) { const char *dpy, *key; int el, hasu; char dpybuf[80]; if (*type != XrmQString) return False; if (*bindings == XrmBindLoosely || strcmp( XrmQuarkToString (*quarks), "DisplayManager" )) return False; bindings++, quarks++; if (!*quarks) return False; if (*bindings != XrmBindLoosely && !quarks[1]) { /* DM.foo */ key = XrmQuarkToString (*quarks); handleXdmVal( 0, key, value->addr, globents, as(globents) ); return False; } else if (*bindings == XrmBindLoosely && !quarks[1]) { /* DM*bar */ dpy = "*"; key = XrmQuarkToString (*quarks); } else if (*bindings != XrmBindLoosely && quarks[1] && *bindings != XrmBindLoosely && !quarks[2]) { /* DM.foo.bar */ dpy = dpybuf + 4; strcpy( dpybuf + 4, XrmQuarkToString (*quarks) ); for (hasu = 0, el = 4; dpybuf[el]; el++) if (dpybuf[el] == '_') hasu = 1; if (!hasu/* && isupper (dpy[0])*/) { dpy = dpybuf; memcpy( dpybuf, "*:*_", 4 ); } else { for (; --el >= 0; ) if (dpybuf[el] == '_') { dpybuf[el] = ':'; for (; --el >= 4; ) if (dpybuf[el] == '_') dpybuf[el] = '.'; break; } } key = XrmQuarkToString (quarks[1]); } else return False; handleXdmVal( dpy, key, value->addr, dpyents, as(dpyents) ); return False; } static FDefs xdmdefs[] = { #ifdef XDMCP { "Xdmcp", "Xaccess", "%s/Xaccess", 0 }, { "Xdmcp", "Willing", "", 0 }, #endif { "X-*-Core", "Setup", "", 0 }, { "X-*-Core", "Startup", "", 0 }, { "X-*-Core", "Reset", "", 0 }, { "X-*-Core", "Session", "", 0 }, }; static int mergeXdmCfg( const char *path ) { char *p; XrmDatabase db; ASPrintf( &p, "%s/xdm-config", path ); if ((db = XrmGetFileDatabase( p ))) { printf( "Information: reading current xdm config file %s\n", p ); usedFile( p ); free( p ); XrmEnumerateDatabase( db, &empty, &empty, XrmEnumAllLevels, DumpEntry, (XPointer)0 ); applydefs( xdmdefs, as(xdmdefs), path ); mod_usebg = 1; return 1; } free( p ); return 0; } static void fwrapprintf( FILE *f, const char *msg, ... ) { char *txt, *ftxt, *line; va_list ap; int col, lword, fspace; va_start( ap, msg ); VASPrintf( &txt, msg, ap ); va_end( ap ); ftxt = 0; for (line = txt, col = 0, lword = fspace = -1; line[col]; ) { if (line[col] == '\n') { StrCat( &ftxt, "%.*s", ++col, line ); line += col; col = 0; lword = fspace = -1; continue; } else if (line[col] == ' ') { if (lword >= 0) { fspace = col; lword = -1; } } else { if (lword < 0) lword = col; if (col >= 78 && fspace >= 0) { StrCat( &ftxt, "%.*s\n", fspace, line ); line += lword; col -= lword; lword = 0; fspace = -1; } } col++; } free( txt ); fputs( ftxt, f ); free( ftxt ); } static const char *oldkdes[] = { KDE_CONFDIR, "/opt/trinity/share/config", "/usr/local/trinity/share/config", "/opt/kde/share/config", "/usr/local/kde/share/config", "/usr/local/share/config", "/usr/share/config", "/opt/kde2/share/config", "/usr/local/kde2/share/config", }; static const char *oldxdms[] = { "/etc/X11/xdm", XLIBDIR "/xdm", }; int main( int argc, char **argv ) { const char **where; char *newtdmrc; FILE *f; StrList *fp; Section *cs; Entry *ce, **cep; int i, ap, newer, locals, foreigns; int no_old_xdm = 0, no_old_kde = 0; struct stat st; char *nname; for (ap = 1; ap < argc; ap++) { if (!strcmp( argv[ap], "--help" )) { printf( "gentdmconf - generate configuration files for tdm\n" "\n" "If an older xdm/tdm configuration is found, its config files are \"absorbed\";\n" "if it lives in the new target directory, its scripts are reused (and possibly\n" "modified) as well, otherwise the scripts are ignored and default scripts are\n" "installed.\n" "\n" "options:\n" " --in /path/to/new/tdm-config-dir\n" " In which directory to put the new configuration. You can use this\n" " to support a $(DESTDIR), but not to change the final location of\n" " the installation - the paths inside the files are not affected.\n" " Default is " TDMCONF ".\n" " --old-xdm /path/to/old/xdm-dir\n" " Where to look for the config files of an xdm/older tdm.\n" " Default is to scan /etc/X11/tdm, $XLIBDIR/tdm, /etc/X11/xdm,\n" " $XLIBDIR/xdm; there in turn look for tdm-config and xdm-config.\n" " Note that you possibly need to use --no-old-kde to make this take effect.\n" " --old-kde /path/to/old/tde-config-dir\n" " Where to look for the tdmrc of an older tdm.\n" " Default is to scan " KDE_CONFDIR " and\n" " {/usr,/usr/local,{/opt,/usr/local}/{trinity,kde,kde2,kde1}}/share/config.\n" " --no-old\n" " Don't look at older xdm/tdm configurations, just create default config.\n" " --no-old-xdm\n" " Don't look at older xdm configurations.\n" " --no-old-kde\n" " Don't look at older tdm configurations.\n" " --old-scripts\n" " Directly use all scripts from the older xdm/tdm configuration.\n" " --no-old-scripts\n" " Don't use scripts from the older xdm/tdm configuration even if it lives\n" " in the new target directory.\n" " --old-confs\n" " Directly use all ancillary config files from the older xdm/tdm\n" " configuration. This is usually a bad idea.\n" " --no-backup\n" " Overwrite/delete old config files instead of backing them up.\n" " --no-in-notice\n" " Don't put the notice about --in being used into the generated README.\n" ); exit( 0 ); } if (!strcmp( argv[ap], "--no-old" )) { no_old = 1; continue; } if (!strcmp( argv[ap], "--old-scripts" )) { old_scripts = 1; continue; } if (!strcmp( argv[ap], "--no-old-scripts" )) { no_old_scripts = 1; continue; } if (!strcmp( argv[ap], "--old-confs" )) { old_confs = 1; continue; } if (!strcmp( argv[ap], "--no-old-xdm" )) { no_old_xdm = 1; continue; } if (!strcmp( argv[ap], "--no-old-kde" )) { no_old_kde = 1; continue; } if (!strcmp( argv[ap], "--no-backup" )) { no_backup = 1; continue; } if (!strcmp( argv[ap], "--no-in-notice" )) { no_in_notice = 1; continue; } where = 0; if (!strcmp( argv[ap], "--in" )) where = &newdir; else if (!strcmp( argv[ap], "--old-xdm" )) where = &oldxdm; else if (!strcmp( argv[ap], "--old-kde" )) where = &oldkde; else if (!strcmp( argv[ap], "--face-src" )) where = &facesrc; else { fprintf( stderr, "Unknown command line option '%s', try --help\n", argv[ap] ); exit( 1 ); } if (ap + 1 == argc || argv[ap + 1][0] == '-') { fprintf( stderr, "Missing argument to option '%s', try --help\n", argv[ap] ); exit( 1 ); } *where = argv[++ap]; } if (memcmp( newdir, TDMCONF, sizeof(TDMCONF) )) use_destdir = 1; if (!mkdirp( newdir, 0755, "target", 1 )) exit( 1 ); mkdefconf(); newer = 0; if (no_old) { DIR *dir; if ((dir = opendir( newdir ))) { struct dirent *ent; char bn[PATH_MAX]; while ((ent = readdir( dir ))) { int l; if (!strcmp( ent->d_name, "." ) || !strcmp( ent->d_name, ".." )) continue; l = sprintf( bn, "%s/%s", newdir, ent->d_name ); /* cannot overflow (kernel would not allow the creation of a longer path) */ if (!stat( bn, &st ) && !S_ISREG( st.st_mode )) continue; if (no_backup || !memcmp( bn + l - 4, ".bak", 5 )) unlink( bn ); else displace( bn ); } closedir( dir ); } } else { if (oldkde) { if (!(newer = mergeKdmRcNewer( oldkde )) && !mergeKdmRcOld( oldkde )) fprintf( stderr, "Cannot read old tdmrc at specified location\n" ); } else if (!no_old_kde) { for (i = 0; i < as(oldkdes); i++) { if ((newer = mergeKdmRcNewer( oldkdes[i] )) || mergeKdmRcOld( oldkdes[i] )) { oldkde = oldkdes[i]; break; } } } if (!newer && !no_old_xdm) { XrmInitialize(); XrmQString = XrmPermStringToQuark( "String" ); if (oldxdm) { if (!mergeXdmCfg( oldxdm )) fprintf( stderr, "Cannot read old tdm-config/xdm-config at specified location\n" ); } else for (i = 0; i < as(oldxdms); i++) if (mergeXdmCfg( oldxdms[i] )) { oldxdm = oldxdms[i]; break; } } else oldxdm = 0; } if (no_old_scripts) goto no_old_s; if (!old_scripts) { locals = foreigns = 0; for (cs = config; cs; cs = cs->next) if (!strcmp( cs->spec->name, "-Core" )) { for (ce = cs->ents; ce; ce = ce->next) if (ce->active && (!strcmp( ce->spec->key, "Setup" ) || !strcmp( ce->spec->key, "Startup" ) || !strcmp( ce->spec->key, "Reset" ))) { if (inNewDir( ce->value )) locals = 1; else foreigns = 1; } } if (foreigns) { if (locals) { fprintf( stderr, "Warning: both local and foreign scripts referenced. " "Won't touch any.\n" ); mixed_scripts = 1; } else { no_old_s: for (cs = config; cs; cs = cs->next) { if (!strcmp( cs->spec->name, "Xdmcp" )) { for (ce = cs->ents; ce; ce = ce->next) if (!strcmp( ce->spec->key, "Willing" )) ce->active = ce->written = 0; } else if (!strcmp( cs->spec->name, "-Core" )) { for (cep = &cs->ents; (ce = *cep); ) { if (ce->active && (!strcmp( ce->spec->key, "Setup" ) || !strcmp( ce->spec->key, "Startup" ) || !strcmp( ce->spec->key, "Reset" ) || !strcmp( ce->spec->key, "Session" ))) { if (!memcmp( cs->name, "X-*-", 4 )) ce->active = ce->written = 0; else { *cep = ce->next; free( ce ); continue; } } cep = &ce->next; } } } } } } #ifdef __linux__ if (!stat( "/etc/debian_version", &st )) { /* debian */ defminuid = "1000"; defmaxuid = "29999"; } else if (!stat( "/usr/portage", &st )) { /* gentoo */ defminuid = "1000"; defmaxuid = "65000"; } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */ defminuid = "500"; defmaxuid = "65000"; } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */ defminuid = "100"; defmaxuid = "65000"; } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */ defminuid = "500"; defmaxuid = "65000"; } #else defminuid = "1000"; defmaxuid = "65000"; #endif for (i = 0; i < CONF_MAX_PRIO; i++) for (cs = config; cs; cs = cs->next) for (ce = cs->ents; ce; ce = ce->next) if (ce->spec->func && i == ce->spec->prio) ce->spec->func( ce, cs ); ASPrintf( &newtdmrc, "%s/tdmrc", newdir ); f = Create( newtdmrc, tdmrcmode ); wrconf( f ); fclose( f ); ASPrintf( &nname, "%s/README", newdir ); f = Create( nname, 0644 ); fprintf( f, "This automatically generated configuration consists of the following files:\n" ); fprintf( f, "- " TDMCONF "/tdmrc\n" ); for (fp = aflist; fp; fp = fp->next) fprintf( f, "- %s\n", fp->str ); if (use_destdir && !no_in_notice) fwrapprintf( f, "All files destined for " TDMCONF " were actually saved in %s; " "this config won't be workable until moved in place.\n", newdir ); if (uflist || eflist || cflist || lflist) { fprintf( f, "\n" "This config was derived from existing files. As the used algorithms are\n" "pretty dumb, it may be broken.\n" ); if (uflist) { fprintf( f, "Information from these files was extracted:\n" ); for (fp = uflist; fp; fp = fp->next) fprintf( f, "- %s\n", fp->str ); } if (lflist) { fprintf( f, "These files were directly incorporated:\n" ); for (fp = lflist; fp; fp = fp->next) fprintf( f, "- %s\n", fp->str ); } if (cflist) { fprintf( f, "These files were copied verbatim:\n" ); for (fp = cflist; fp; fp = fp->next) fprintf( f, "- %s\n", fp->str ); } if (eflist) { fprintf( f, "These files were copied with modifications:\n" ); for (fp = eflist; fp; fp = fp->next) fprintf( f, "- %s\n", fp->str ); } if (!no_backup && !use_destdir) fprintf( f, "Old files that would have been overwritten were renamed to <oldname>.bak.\n" ); } fprintf( f, "\nTry 'gentdmconf --help' if you want to generate another configuration.\n" "\nYou may delete this README.\n" ); fclose( f ); return 0; }