kde-workspace/kdm/kfrontend/genkdmconf.c
2014-11-13 19:30:51 +02:00

3320 lines
87 KiB
C

/*
Create a suitable configuration for kdm taking previous xdm/kdm
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 <greet.h>
#include <X11/Xlib.h>
#include <X11/Xresource.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/wait.h>
#include <sys/param.h>
#ifdef BSD
# ifdef BSD_UTMP
# include <utmp.h>
# endif
# ifndef _PATH_UTMP
# if defined(__FreeBSD_version) && __FreeBSD_version >= 900007
# define _PATH_UTMP "/var/run/utmp"
# else
# error "_PATH_UTMP must be defined"
# endif
# endif
#endif
#define WANT_CONF_GEN
#include <config.ci>
#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 = KDMCONF, *facesrc = KDMDATA "/pics/users",
*oldxdm, *oldkde, *oldkdepfx;
static int oldver;
typedef struct StrList {
struct StrList *next;
const char *str;
} StrList;
typedef struct StrMap {
struct StrMap *next;
const char *key, *value;
} StrMap;
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 };
doPrint(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 False;
flen = lseek(fd, 0, SEEK_END);
#ifdef HAVE_MMAP
# ifdef WANT_CLOSE
file->ismapped = False;
# endif
file->buf = mmap(0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
# ifdef WANT_CLOSE
if (file->buf)
file->ismapped = True;
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 False; /* maybe better abort? */
}
}
file->eof = file->buf + flen;
close(fd);
return True;
}
#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
mkpdirs(const char *name, const char *what)
{
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 False;
}
chmod(mfname, 0755);
}
mfname[i] = '/';
}
free(mfname);
return True;
}
static int
mkdirp(const char *name, int mode, const char *what, int existok)
{
struct stat st;
if (stat(name, &st)) {
mkpdirs(name, what);
if (mkdir(name, mode)) {
fprintf(stderr, "Cannot create %s directory %s: %s\n",
what, name, strerror(errno));
return False;
}
chmod(name, mode);
return True;
}
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 *curName;
/* Create a new file in KDMCONF */
static FILE *
createFile(const char *fn, int mode)
{
FILE *f;
free(curName);
ASPrintf(&curName, "%s/%s", newdir, fn);
displace(curName);
if (!(f = fopen(curName, "w"))) {
fprintf(stderr, "Cannot create %s\n", curName);
exit(1);
}
chmod(curName, mode);
return f;
}
static void
writeError()
{
fprintf(stderr, "Warning: cannot write %s (disk full?)\n", curName);
unlink(curName);
exit(1);
}
static void
fputs_(const char *str, FILE *f)
{
if (fputs(str, f) == EOF)
writeError();
}
static void
ATTR_PRINTFLIKE(2, 3)
fprintf_(FILE *f, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (vfprintf(f, fmt, args) < 0)
writeError();
va_end(args);
}
static void
fclose_(FILE *f)
{
if (fclose(f) == EOF)
writeError();
}
static char *
locate(const char *exe)
{
int len;
char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1];
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;
}
static int
runAndWait(char **args)
{
int pid, ret;
switch ((pid = fork())) {
case 0:
execv(args[0], args);
fprintf(stderr, "Cannot execute %s: %s\n",
args[0], strerror(errno));
_exit(127);
case -1:
fprintf(stderr, "Cannot fork to execute %s: %s\n",
args[0], strerror(errno));
return -1;
}
while (waitpid(pid, &ret, 0) < 0)
if (errno != EINTR)
return -1;
return ret;
}
/*
* target data to be written to kdmrc
*/
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 kdmrc data to be written */
/*
* Specification of the (currently possible) kdmrc 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 kdmrc 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 = True;
}
static const char *csect;
#define setSect(se) csect = se
static void
putVal(const char *key, const char *value)
{
putFqVal(csect, key, value);
}
static void
writeKdmrc(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 cannot 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 would 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"
"# By convention, both xconsole and xterm -C check that the\n"
"# console is owned by the invoking user and is readable before attaching\n"
"# the console output. This way a random user can invoke xterm -C without\n"
"# causing serious grief; still, it can cause havoc, so xconsole is started\n"
"# by Xsetup usually.\n"
"# This is not required if you use PAM with the pam_console module.\n"
"#\n"
"#chown $USER /dev/console\n"
"\n"
"# XDM configurations typically have sessreg here. KDM has it built-in.\n"
"\n"
"# NOTE: The session is aborted if the last command returns non-zero.\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"
"# XDM configurations typically have sessreg here. KDM has it built-in.\n";
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"
" ;;\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 do not know.\n"
" [ -f /etc/profile ] && . /etc/profile\n"
" [ -f $HOME/.profile ] && . $HOME/.profile\n"
" ;;\n"
"esac\n"
"\n"
"[ -f /etc/xprofile ] && . /etc/xprofile\n"
"[ -f $HOME/.xprofile ] && . $HOME/.xprofile\n"
"\n"
"if [ -d /etc/X11/Xresources ]; then\n"
" for i in /etc/X11/Xresources/*; do\n"
" [ -f $i ] && xrdb -merge $i\n"
" done\n"
"elif [ -f /etc/X11/Xresources ]; then\n"
" xrdb -merge /etc/X11/Xresources\n"
"fi\n"
"[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources\n"
"\n"
"case $session in\n"
" \"\")\n"
" exec xmessage -center -buttons OK:0 -default OK \"Sorry, $DESKTOP_SESSION is no valid session.\"\n"
" ;;\n"
" failsafe)\n"
" exec xterm -geometry 80x24-0-0\n"
" ;;\n"
" custom)\n"
" exec $HOME/.xsession\n"
" ;;\n"
" default)\n"
" exec " KDE_BINDIR "/startkde\n"
" ;;\n"
" *)\n"
" eval exec \"$session\"\n"
" ;;\n"
"esac\n"
"exec xmessage -center -buttons OK:0 -default OK \"Sorry, cannot execute $session. Check $DESKTOP_SESSION.desktop.\"\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=stripes.png\n"
"WallpaperList=\n"
"WallpaperMode=Scaled\n";
/* Create a copy of a file under KDMCONF and fill it */
static void
writeCopy(const char *fn, int mode, time_t stamp, const char *buf, size_t len)
{
char *nname;
int fd;
struct utimbuf utim;
ASPrintf(&nname, "%s/%s", newdir, fn);
displace(nname);
mkpdirs(nname, "target");
if ((fd = creat(nname, mode)) < 0) {
fprintf(stderr, "Cannot create %s\n", nname);
exit(1);
}
if (write(fd, buf, len) != (ssize_t)len || close(fd) < 0) {
fprintf(stderr, "Cannot write %s (disk full?)\n", nname);
unlink(nname);
exit(1);
}
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", (int)(p - sec), sec, name);
return sname;
} else {
return name;
}
}
static int
inNewDir(const char *name)
{
return !memcmp(name, KDMCONF "/", sizeof(KDMCONF));
}
static const char *
getMapping(StrMap *sm, const char *k)
{
for (; sm; sm = sm->next)
if (!strcmp(sm->key, k))
return sm->value;
return 0;
}
static void
addMapping(StrMap **sm, const char *k, const char *v)
{
for (; *sm; sm = &(*sm)->next)
if (!strcmp((*sm)->key, k))
return;
*sm = mcalloc(sizeof(**sm));
ASPrintf((char **)&(*sm)->key, "%s", k);
ASPrintf((char **)&(*sm)->value, "%s", v);
}
static int
inList(StrList *sp, const char *s)
{
for (; sp; sp = sp->next)
if (!strcmp(sp->str, s))
return True;
return False;
}
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);
}
static StrList *
splitList(const char *str)
{
StrList *sp, **spp = &sp;
const char *e;
if (!*str)
return 0;
for (;;) {
*spp = mcalloc(sizeof(**spp));
if (!(e = strchr(str, ',')))
break;
ASPrintf((char **)&(*spp)->str, "%.*s", (int)(e - str), str);
str = e + 1;
spp = &(*spp)->next;
}
(*spp)->str = mstrdup(str);
(*spp)->next = 0;
return sp;
}
static char *
joinList(StrList *sp)
{
char *s = 0;
if (!sp)
return mstrdup("");
s = mstrdup(sp->str);
for (;;) {
sp = sp->next;
if (!sp)
return s;
strCat(&s, ",%s", sp->str);
}
}
StrMap *cfmap;
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);
}
/*
* NOTE: This code will not correctly deal with default files colliding
* with pre-existing files. This should be OK, as for each class of files
* (scripts, configs) only one origin is used, and conflicts between classes
* are rather unlikely.
*/
/* Make a possibly modified copy of a file under KDMCONF */
static int
copyFile(Entry *ce, int mode, int (*proc)(File *))
{
const char *tptr;
char *nname;
File file;
int rt;
if (!*ce->value)
return True;
if ((nname = (char *)getMapping(cfmap, ce->value))) {
rt = inList(aflist, nname);
goto doret;
}
if (oldkde) {
int olen = strlen(oldkde);
if (!memcmp(ce->value, oldkde, olen)) {
if (!memcmp(ce->value + olen, "/kdm/", 5)) {
tptr = ce->value + olen + 4;
goto gotn;
}
if (ce->value[olen] == '/') {
tptr = ce->value + olen;
goto gotn;
}
}
}
if (oldxdm) {
int olen = strlen(oldxdm);
if (!memcmp(ce->value, oldxdm, olen) && ce->value[olen] == '/') {
tptr = ce->value + olen;
goto gotn;
}
}
if (!(tptr = strrchr(ce->value, '/'))) {
fprintf(stderr, "Warning: cannot cope with relative path %s\n", ce->value);
return False;
}
gotn:
ASPrintf(&nname, KDMCONF "%s", tptr);
if (inList(aflist, nname)) {
int cnt = 1;
do {
free(nname);
ASPrintf(&nname, KDMCONF "%s-%d", tptr , ++cnt);
} while (inList(aflist, nname));
}
addMapping(&cfmap, ce->value, nname);
if (!readFile(&file, ce->value)) {
fprintf(stderr, "Warning: cannot copy file %s\n", ce->value);
rt = False;
} else {
if (!proc || !proc(&file)) {
if (!use_destdir && !strcmp(ce->value, nname)) {
linkedFile(nname);
} else {
struct stat st;
stat(ce->value, &st);
writeCopy(nname + sizeof(KDMCONF), mode, st.st_mtime,
file.buf, file.eof - file.buf);
copiedFile(ce->value);
}
} else {
writeCopy(nname + sizeof(KDMCONF), 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 = True;
}
doret:
ce->value = nname;
return rt;
}
static void
doLinkFile(const char *name)
{
File file;
if (inList(aflist, name))
return;
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);
writeCopy(name + sizeof(KDMCONF), st.st_mode, st.st_mtime,
file.buf, file.eof - file.buf);
copiedFile(name);
} else {
linkedFile(name);
}
addedFile(name);
}
/* Incorporate an existing file */
static void
linkFile(Entry *ce)
{
if (ce->written && *ce->value)
doLinkFile(ce->value);
}
/* Create a new file in KDMCONF and fill it */
static void
writeFile(const char *tname, int mode, const char *cont)
{
FILE *f = createFile(tname + sizeof(KDMCONF), mode);
fputs_(cont, f);
fclose_(f);
addedFile(tname);
}
static void
handleBgCfg(Entry *ce, Section *cs)
{
if (!ce->active) { /* can be only the X-*-Greeter one */
writeFile(def_BackgroundCfg, 0644, def_background);
#if 0 /* risk of kcontrol clobbering the original file */
} else if (old_confs) {
linkFile(ce);
#endif
} else {
if (!copyFile(ce, 0644, 0)) {
if (!strcmp(cs->name, "X-*-Greeter"))
writeFile(def_BackgroundCfg, 0644, def_background);
ce->active = False;
}
}
}
#ifdef HAVE_VTS
static char *
memMem(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)
{
File it;
char *p, *eol, *ep;
int tty;
if (maxTTY)
return;
if (readFile(&it, "/etc/inittab")) {
usedFile("/etc/inittab");
for (p = it.buf; p < it.eof; p = eol + 1) {
for (eol = p; eol < it.eof && *eol != '\n'; eol++);
if (*p != '#') {
if ((ep = memMem(p, eol - p, " tty", 4)) &&
ep < eol && isdigit(*ep))
{
if (ep + 1 == eol || isspace(*(ep + 1)))
tty = *ep - '0';
else if (isdigit(*(ep + 1)) &&
(ep + 2 == eol || isspace(*(ep + 2))))
tty = (*ep - '0') * 10 + (*(ep + 1) - '0');
else
continue;
TTYmask |= 1 << (tty - 1);
if (tty > maxTTY)
maxTTY = tty;
}
}
}
freeBuf(&it);
}
if (!maxTTY) {
maxTTY = 6;
TTYmask = 0x3f;
}
}
#endif
static char *
readWord(File *file, int EOFatEOL)
{
char *wordp, *wordBuffer;
int quoted;
char c;
rest:
wordp = wordBuffer = file->cur;
mloop:
quoted = False;
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 = True;
goto qloop;
}
break;
}
*wordp++ = c;
goto mloop;
}
/* backslashes are double-escaped - first parseArgs, then KConfig */
static StrList *
splitArgs(const char *string)
{
const char *word;
char *str;
int wlen;
StrList *args, **argp = &args;
while (*string) {
if (isspace(*string)) {
string++;
continue;
}
word = string;
wlen = 0;
do {
if (*string == '\\') {
if (*++string != '\\')
string--;
if (*++string != '\\')
string--;
if (!*++string)
string--;
wlen++;
} else if (*string == '\'') {
while (*++string != '\'' && *string) {
if (*string == '\\' && *++string != '\\')
string--;
wlen++;
}
} else if (*string == '"') {
while (*++string != '"' && *string) {
if (*string == '\\') {
if (*++string != '\\')
string--;
if (*++string != '\\')
string--;
if (!*++string)
string--;
}
wlen++;
}
} else {
wlen++;
}
} while (*++string && !isspace(*string));
*argp = mmalloc(sizeof(**argp));
(*argp)->str = str = mmalloc(wlen + 1);
do {
if (*word == '\\') {
if (*++word != '\\')
word--;
if (*++word != '\\')
word--;
if (!*++word)
word--;
*str++ = *word;
} else if (*word == '\'') {
while (*++word != '\'' && *word) {
if (*word == '\\' && *++word != '\\')
word--;
*str++ = *word;
}
} else if (*word == '"') {
while (*++word != '"' && *word) {
if (*word == '\\') {
if (*++word != '\\')
word--;
if (*++word != '\\')
word--;
if (!*++word)
word--;
}
*str++ = *word;
}
} else {
*str++ = *word;
}
} while (*++word && !isspace(*word));
*str = 0;
argp = &(*argp)->next;
}
*argp = 0;
return args;
}
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;
}
typedef enum { InvalidDpy, LocalDpy, LocalUidDpy, ForeignDpy } DisplayMatchType;
static struct displayMatch {
const char *name;
int len;
DisplayMatchType type;
} displayTypes[] = {
{ "local", 5, LocalDpy },
{ "local_uid", 9, LocalUidDpy },
{ "foreign", 7, ForeignDpy },
};
static DisplayMatchType
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 InvalidDpy;
}
typedef struct serverEntry {
struct serverEntry *next;
const char *name, *class2, *console, *owner, *argvs, *arglvs;
StrList *argv, *arglv;
DisplayMatchType type;
int reserve, vt;
} ServerEntry;
static int
mstrcmp(const char *s1, const char *s2)
{
if (s1 == s2)
return 0;
if (!s1)
return -1;
if (!s2)
return 1;
return strcmp(s1, s2);
}
static void
absorbXservers(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 cpuid, 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 == InvalidDpy) {
se->class2 = word;
if (!(word = readWord(&file, 1)))
continue;
se->type = parseDisplayType(word, &se->console);
if (se->type == InvalidDpy) {
while (readWord(&file, 1));
continue;
}
}
if (se->type == LocalUidDpy)
if (!(se->owner = readWord(&file, 1)))
continue;
word = readWord(&file, 1);
if (word && !strcmp(word, "reserve")) {
se->reserve = True;
word = readWord(&file, 1);
}
if ((se->type != ForeignDpy) != (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 != ForeignDpy) {
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 = False;
getInitTab();
for (se = serverList, mtty = maxTTY; se; se = se->next)
if (se->type != ForeignDpy) {
mtty++;
if (se->vt != mtty) {
cpvt = True;
break;
}
}
#endif
for (se = serverList; se; se = se->next) {
se->argvs = joinArgs(se->argv);
se->arglvs = joinArgs(se->arglv);
}
se1 = 0, cpuid = cpcmd = cpcmdl = False;
for (se = serverList; se; se = se->next)
if (se->type != ForeignDpy) {
if (!se1) {
se1 = se;
} else {
if (strcmp(se1->argvs, se->argvs))
cpcmd = True;
if (strcmp(se1->arglvs, se->arglvs))
cpcmdl = True;
if (mstrcmp(se1->owner, se->owner))
cpuid = True;
}
}
if (se1) {
putFqVal("X-:*-Core", "ServerCmd", se1->argvs);
if (se1->owner)
putFqVal("X-:*-Core", "ServerUID", se1->owner);
putFqVal("X-:*-Core", "ServerArgsLocal", se1->arglvs);
for (se = serverList; se; se = se->next)
if (se->type != ForeignDpy) {
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
if (cpuid && se->owner)
putFqVal(sec, "ServerUID", se->owner);
}
}
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 = True;
#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 = True;
}
#endif
}
}
#endif
static void
upd_servercmd(Entry *ce, Section *cs ATTR_UNUSED)
{
StrList *sa;
FILE *fp;
char *svr;
char buf[20000];
if (!ce->active || oldver >= 0x0204)
return;
if (!(sa = splitArgs(ce->value)))
return;
ASPrintf(&svr, "%s -help 2>&1", sa->str);
if (!(fp = popen(svr, "r")))
return;
buf[fread(buf, 1, sizeof(buf) - 1, fp)] = 0;
pclose(fp);
if (strstr(buf, "\n-br "))
addStr(&sa, "-br");
if (strstr(buf, "\n-novtswitch "))
addStr(&sa, "-novtswitch");
if (strstr(buf, "\n-quiet "))
addStr(&sa, "-quiet");
ce->value = joinArgs(sa);
ce->written = True;
}
#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, 0600, 0))
ce->active = False;
}
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, 0644, 0); /* don't handle error, it will disable Xdmcp automatically */
}
static void
mk_willing(Entry *ce, Section *cs ATTR_UNUSED)
{
const 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)) {
doLinkFile(fname);
} else {
dflt:
ce->value = KDMCONF "/Xwilling";
ce->active = ce->written = True;
writeFile(ce->value, 0755, def_willing);
}
}
}
#endif
/*
static int
edit_resources(File *file)
{
// XXX remove any login*, chooser*, ... resources
return False;
}
*/
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, 0644, 0/*edit_resources*/))
ce->active = False;
}
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 True;
} 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 False;
}
/* 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/kdmdesktop-$DISPLAY.pid\n"
" */kdmdesktop\t&\n"
" echo $! >$PIDFILE\n"
" wait $!\n"
" rm $PIDFILE\n"
")\t&\n") |
delstr(file, "\n"
"*/kdmdesktop\t&\n") |
delstr(file, "\n"
"kdmdesktop\t&\n") |
delstr(file, "\n"
"kdmdesktop\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, 0755, edit_setup);
else
linkFile(ce);
} else {
ce->value = KDMCONF "/Xsetup";
ce->active = ce->written = True;
writeFile(ce->value, 0755, def_setup);
}
}
}
static int
edit_startup(File *file)
{
int chg1 = False, chg2 = False;
if (mod_usebg &&
(delstr(file, "\n"
"PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
"if [[] -f $PIDFILE ] ; then\n"
" kill `cat $PIDFILE`\n"
"fi\n") ||
delstr(file, "\n"
"PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
"test -f $PIDFILE && kill `cat $PIDFILE`\n")))
chg1 = True;
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 \"Cannot 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, 0755, edit_startup);
else
linkFile(ce);
} else {
ce->value = KDMCONF "/Xstartup";
ce->active = ce->written = True;
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, 0755, edit_reset);
else
linkFile(ce);
} else {
ce->value = KDMCONF "/Xreset";
ce->active = ce->written = True;
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 = KDMCONF "/Xsession";
ce->active = ce->written = True;
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 = False;
ce->written = True;
}
}
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 = True;
}
}
static void
upd_maxshowuid(Entry *ce, Section *cs ATTR_UNUSED)
{
if (!ce->active) {
ce->value = defmaxuid;
ce->active = ce->written = True;
}
}
static void
upd_hiddenusers(Entry *ce, Section *cs)
{
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 = True;
}
}
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 gid_t greeter_gid;
static uid_t greeter_uid;
static void
upd_greeteruid(Entry *ce, Section *cs ATTR_UNUSED)
{
struct passwd *pw;
char *ok, *useradd;
int uid;
if (use_destdir || !ce->active)
return;
if (!(pw = getpwnam(ce->value))) {
uid = strtol(ce->value, &ok, 10);
if (*ok || !(pw = getpwuid(uid))) {
if ((useradd = locate("useradd"))) {
const char *args[] = {
useradd, "--system", "--user-group", "-s", "/bin/false",
"--home-dir", "/var", "--no-create-home", "-c", "KDM Display Manager daemon",
ce->value, 0
};
if (runAndWait((char **)args)) {
fprintf(stderr, "Warning: Creation of missing GreeterUID"
" user %s failed\n", ce->value);
ce->active = False;
return;
}
} else {
fprintf(stderr, "Warning: Do not know how to create missing"
" GreeterUID user %s\n", ce->value);
ce->active = False;
return;
}
if (!(pw = getpwnam(ce->value))) {
fprintf(stderr, "Warning: Newly created GreeterUID user %s"
" still missing!?\n", ce->value);
ce->active = False;
return;
}
}
}
greeter_uid = pw->pw_uid;
greeter_gid = pw->pw_gid;
}
static void
upd_datadir(Entry *ce, Section *cs ATTR_UNUSED)
{
char *oldsts, *newsts;
const char *dir;
struct stat st;
if (use_destdir)
return;
dir = ce->active ? ce->value : def_DataDir;
ASPrintf(&newsts, "%s/kdmsts", dir);
if (mkdirp(dir, 0755, "data", 0) && oldkde) {
ASPrintf(&oldsts, "%s/kdm/kdmsts", oldkde);
rename(oldsts, newsts);
}
if (stat(dir, &st))
return;
if ((st.st_uid != greeter_uid || st.st_gid != greeter_gid) &&
chown(dir, greeter_uid, greeter_gid))
fprintf(stderr, "Warning: Cannot assign ownership of data directory"
" %s: %s\n", dir, strerror(errno));
if (stat(newsts, &st))
return;
if ((st.st_uid != greeter_uid || st.st_gid != greeter_gid) &&
chown(newsts, greeter_uid, greeter_gid))
fprintf(stderr, "Warning: Cannot assign ownership of status file"
" %s: %s\n", newsts, strerror(errno));
}
static void
upd_userlogfile(Entry *ce, Section *cs ATTR_UNUSED)
{
char *p;
if ((p = strstr(ce->value, "%s")))
ASPrintf((char **)&ce->value, "%.*s%%d%s", p - ce->value, ce->value, p + 2);
}
/*
* Copy single file.
* Do not overwrite existing target.
* Do not complain if source cannot be read.
*/
static void
copyPlainFile(const char *from, const char *to)
{
File file;
int fd;
if (readFile(&file, from)) {
if ((fd = open(to, O_WRONLY | O_CREAT | O_EXCL, 0644)) >= 0) {
size_t len = file.eof - file.buf;
if (write(fd, file.buf, len) != (ssize_t)len) {
fprintf(stderr, "Warning: cannot write %s (disk full?)\n", to);
unlink(to);
}
if (close(fd) < 0) {
fprintf(stderr, "Warning: cannot write %s (disk full?)\n", to);
unlink(to);
}
} else if (errno != EEXIST) {
fprintf(stderr, "Warning: cannot create %s\n", to);
}
freeBuf(&file);
}
}
static int
copyDir(const char *from, const char *to)
{
DIR *dir;
struct dirent *ent;
struct stat st;
char bn[PATH_MAX], bo[PATH_MAX];
if (!(dir = opendir(from)))
return False;
while ((ent = readdir(dir))) {
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
continue;
sprintf(bo, "%s/%s", from, ent->d_name);
if (stat(bo, &st) || !S_ISREG(st.st_mode))
continue;
sprintf(bn, "%s/%s", to, ent->d_name);
copyPlainFile(bo, bn);
}
closedir(dir);
return True;
}
static void
upd_facedir(Entry *ce, Section *cs ATTR_UNUSED)
{
char *oldpic, *newpic, *olddir;
struct passwd *pw;
if (use_destdir)
return;
if (oldkdepfx) { /* Do we have a previous install? */
/* This would be the prev install's default location */
ASPrintf(&olddir, "%s/share/apps/kdm/faces", oldkdepfx);
if (ce->active && strcmp(olddir, ce->value))
/* Not default location, so don't touch the setting. */
return;
/* Default location, so absorb it. */
ce->active = False;
/* Don't copy if old dir == new new. */
if (!strcmp(olddir, def_FaceDir))
olddir = 0;
} else {
olddir = 0;
}
if (mkdirp(def_FaceDir, 0755, "user face", True)) {
const char *defpic = def_FaceDir "/.default.face.icon";
const char *rootpic = def_FaceDir "/root.face.icon";
if (oldkde && (!olddir || !copyDir(olddir, def_FaceDir)) &&
oldver < 0x0201) /* This isn't exact - didn't inc version. */
{
setpwent();
while ((pw = getpwent()))
if (strcmp(pw->pw_name, "root")) {
ASPrintf(&oldpic, "%s/share/apps/kdm/pics/users/%s.png",
oldkdepfx, pw->pw_name);
ASPrintf(&newpic, def_FaceDir "/%s.face.icon", pw->pw_name);
rename(oldpic, newpic);
free(newpic);
free(oldpic);
}
endpwent();
ASPrintf(&oldpic, "%s/share/apps/kdm/pics/users/default.png", oldkdepfx);
if (!rename(oldpic, defpic))
defpic = 0;
ASPrintf(&oldpic, "%s/share/apps/kdm/pics/users/root.png", oldkdepfx);
if (!rename(oldpic, rootpic))
rootpic = 0;
}
if (defpic) {
ASPrintf(&oldpic, "%s/default1.png", facesrc);
copyPlainFile(oldpic, defpic);
}
if (rootpic) {
ASPrintf(&oldpic, "%s/root1.png", facesrc);
copyPlainFile(oldpic, rootpic);
}
}
}
static void
upd_sessionsdirs(Entry *ce, Section *cs ATTR_UNUSED)
{
StrList *sl, *sp;
int olen;
char olddir[PATH_MAX];
if (ce->written) {
sprintf(olddir, "%s/share/apps/kdm/sessions", oldkdepfx);
olen = strlen(oldkde);
sl = splitList(ce->value);
for (sp = sl; sp; sp = sp->next) {
if (!strcmp(sp->str, olddir)) {
sp->str = def_SessionsDirs;
} else if (!memcmp(sp->str, oldkde, olen) &&
!memcmp(sp->str + olen, "/kdm/", 5)) {
char nd[PATH_MAX];
sprintf(nd, "%s%s", newdir, sp->str + olen + 4);
mkdirp(nd, 0755, "sessions", False);
copyDir(sp->str, nd);
ASPrintf((char **)&sp->str, KDMCONF "%s", sp->str + olen + 4);
}
}
ce->value = joinList(sl);
} else {
char nd[PATH_MAX];
sprintf(nd, "%s/sessions", newdir);
mkdirp(nd, 0755, "sessions", False);
}
}
static void
upd_preloader(Entry *ce, Section *cs ATTR_UNUSED)
{
if (ce->written) { /* implies oldkde != 0 */
char *oldpl;
ASPrintf(&oldpl, "%s/bin/preloadkde", oldkdepfx);
if (!strcmp(ce->value, oldpl))
ce->value = (char *)KDE_BINDIR "/preloadkde";
free(oldpl);
}
}
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
makeDefaultConfig(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 *
readConfig(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 = False;
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 = False;
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/kdmrc", path);
if (stat(p, &st)) {
free(p);
return False;
}
printf("Information: ignoring pre-existing kdmrc %s from kde < 2.2\n", p);
free(p);
return True;
}
typedef struct {
const char *sect, *key, *def;
int (*cond)(void);
} FDefs;
/*
* The idea is to determine how exactly the pre-existing config would
* have been interpreted, so no default configs are created where builtin
* defaults were used so far.
*/
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);
}
}
static int
if_usebg (void)
{
return isTrue(getFqVal("X-*-Greeter", "UseBackground", "true"));
}
static FDefs kdmdefs_all[] = {
#ifdef XDMCP
{ "Xdmcp", "Xaccess", "%s/kdm/Xaccess", 0 },
{ "Xdmcp", "Willing", "", 0 },
#endif
{ "X-*-Core", "Setup", "", 0 },
{ "X-*-Core", "Startup", "", 0 },
{ "X-*-Core", "Reset", "", 0 },
{ "X-*-Core", "Session", XBINDIR "/xterm -ls -T", 0 },
{ "X-*-Greeter", "BackgroundCfg", "%s/kdm/backgroundrc", if_usebg },
};
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");
}
static void
P_EchoMode(const char *sect ATTR_UNUSED, char **value)
{
*value = (char *)(!strcmp(*value, "NoEcho") ? "false" : "true");
}
CONF_GEN_KMERGE
static int
mergeKdmRcNewer(const char *path, int obsRet)
{
char *p;
const char *cp, *sec, *key;
RSection *rootsect, *cs;
REntry *ce;
int i, j, ma, mi;
static char sname[64];
ASPrintf(&p, "%s/kdm/kdmrc", path);
if (!(rootsect = readConfig(p))) {
free(p);
return False;
}
for (cs = rootsect; cs; cs = cs->next)
if (!strcmp(cs->name, "General"))
for (ce = cs->ents; ce; ce = ce->next)
if (!strcmp(ce->key, "ConfigVersion"))
goto gotcfgv;
printf("Information: ignoring pre-existing kdmrc %s from kde < 3.1\n", p);
free(p);
return obsRet;
gotcfgv:
sscanf(ce->value, "%d.%d", &ma, &mi);
oldver = (ma << 8) | mi;
printf("Information: reading pre-existing kdmrc %s (config version %d.%d)\n",
p, ma, mi);
free(p);
for (cs = rootsect; cs; cs = cs->next) {
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",
(int)(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:
;
}
applyDefs(kdmdefs_all, as(kdmdefs_all), path);
return True;
}
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 = False, d = s = 0; st[s]; s++)
if (st[s] == ' ' || st[s] == '\t') {
if (!is)
st[d++] = ',';
is = True;
} else {
st[d++] = st[s];
is = False;
}
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 kdmrcmode = 0644;
static void
P_autoPass(const char *sect ATTR_UNUSED, char **value ATTR_UNUSED)
{
kdmrcmode = 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 = False, el = 4; dpybuf[el]; el++)
if (dpybuf[el] == '_')
hasu = True;
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 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 = True;
return True;
}
free(p);
return False;
}
static void
fprintfLineWrap(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);
if (ftxt) {
fputs_(ftxt, f);
free(ftxt);
}
}
static const char * const oldkdes[] = {
KDE_CONFDIR,
"/opt/kde4/share/config",
"/usr/local/kde4/share/config",
"/opt/kde/share/config",
"/usr/local/kde/share/config",
"/usr/local/share/config",
"/usr/share/config",
"/opt/kde3/share/config",
"/usr/local/kde3/share/config",
};
static const char * const oldxdms[] = {
"/etc/X11/xdm",
XLIBDIR "/xdm",
};
int main(int argc, char **argv)
{
const char **where;
FILE *f;
StrList *fp;
Section *cs;
Entry *ce, **cep;
int i, ap, locals, foreigns;
int no_old_xdm = 0, no_old_kde = 0;
struct stat st;
for (ap = 1; ap < argc; ap++) {
if (!strcmp(argv[ap], "--help")) {
printf(
"genkdmconf - generate configuration files for kdm\n"
"\n"
"If an older xdm/kdm 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/kdm-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 " KDMCONF ".\n"
" --old-xdm /path/to/old/xdm-dir\n"
" Where to look for the config files of an xdm.\n"
" Default is to scan /etc/X11/xdm & $XLIBDIR/xdm.\n"
" Note that you possibly need to use --no-old-kde to make this take effect.\n"
" --old-kde /path/to/old/kde-config-dir\n"
" Where to look for the kdmrc of a previously installed kdm.\n"
" Default is to scan " KDE_CONFDIR " and\n"
" {/usr,/usr/local,{/opt,/usr/local}/{kde4,kde,kde3}}/share/config.\n"
" --no-old\n"
" Do not look at older xdm/kdm configurations, just create default config.\n"
" --no-old-xdm\n"
" Do not look at older xdm configurations.\n"
" --no-old-kde\n"
" Do not look at older kdm configurations.\n"
" --old-scripts\n"
" Directly use all scripts from the older xdm/kdm configuration.\n"
" --no-old-scripts\n"
" Do not use scripts from the older xdm/kdm 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/kdm\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"
" Do not put the notice about --in being used into the generated README.\n"
);
exit(0);
}
if (!strcmp(argv[ap], "--no-old")) {
no_old = True;
continue;
}
if (!strcmp(argv[ap], "--old-scripts")) {
old_scripts = True;
continue;
}
if (!strcmp(argv[ap], "--no-old-scripts")) {
no_old_scripts = True;
continue;
}
if (!strcmp(argv[ap], "--old-confs")) {
old_confs = True;
continue;
}
if (!strcmp(argv[ap], "--no-old-xdm")) {
no_old_xdm = True;
continue;
}
if (!strcmp(argv[ap], "--no-old-kde")) {
no_old_kde = True;
continue;
}
if (!strcmp(argv[ap], "--no-backup")) {
no_backup = True;
continue;
}
if (!strcmp(argv[ap], "--no-in-notice")) {
no_in_notice = True;
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, KDMCONF, sizeof(KDMCONF)))
use_destdir = True;
if (!mkdirp(newdir, 0755, "target", True))
exit(1);
makeDefaultConfig();
if (no_old) {
DIR *dir;
StrList *bfl = 0;
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
addStr(&bfl, bn);
}
closedir(dir);
for (; bfl; bfl = bfl->next)
displace(bfl->str);
}
} else {
if (oldkde) {
if (!mergeKdmRcNewer(oldkde, True) && !mergeKdmRcOld(oldkde)) {
fprintf(stderr,
"Cannot read pre-existing kdmrc at specified location\n");
oldkde = 0;
}
} else if (!no_old_kde) {
for (i = 0; i < as(oldkdes); i++) {
if (i && !strcmp(oldkdes[0], oldkdes[i]))
continue;
if (mergeKdmRcNewer(oldkdes[i], 0)) {
oldkde = oldkdes[i];
break;
}
mergeKdmRcOld(oldkdes[i]); /* only prints a message */
}
}
if (oldkde) {
#define SHR_CONF "/share/config"
int olen = strlen(oldkde);
if (olen < (int)sizeof(SHR_CONF) ||
memcmp(oldkde + olen - sizeof(SHR_CONF) + 1,
SHR_CONF, sizeof(SHR_CONF)))
{
fprintf(stderr,
"Warning: --old-kde does not end with " SHR_CONF ". "
"Might wreak havoc.\n");
oldkdepfx = oldkde;
} else
ASPrintf((char **)&oldkdepfx,
"%.*s", olen - sizeof(SHR_CONF) + 1, oldkde);
oldxdm = 0;
} else if (!no_old_xdm) {
XrmInitialize();
XrmQString = XrmPermStringToQuark("String");
if (oldxdm) {
if (!mergeXdmCfg(oldxdm)) {
fprintf(stderr,
"Cannot read xdm-config at specified location\n");
oldxdm = 0;
}
} else
for (i = 0; i < as(oldxdms); i++)
if (mergeXdmCfg(oldxdms[i])) {
oldxdm = oldxdms[i];
break;
}
}
}
/*
* How to proceed with pre-existing scripts (which are named in the config):
* - old_scripts set or some scripts in new target already => keep 'em
* - no_old_scripts set or all scripts outside new target => pretend that
* the old config did not reference them in the first place
*/
if (no_old_scripts)
goto no_old_s;
if (!old_scripts) {
locals = foreigns = False;
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 = True;
else
foreigns = True;
}
}
if (foreigns) {
if (locals) {
fprintf(stderr,
"Warning: both local and foreign scripts referenced. "
"Will not touch any.\n");
mixed_scripts = True;
} 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 = False;
} 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 = False;
} 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);
f = createFile("kdmrc", kdmrcmode);
writeKdmrc(f);
fclose_(f);
f = createFile("README", 0644);
fprintf_(f,
"This automatically generated configuration consists of the following files:\n");
fprintf_(f, "- " KDMCONF "/kdmrc\n");
for (fp = aflist; fp; fp = fp->next)
fprintf_(f, "- %s\n", fp->str);
if (use_destdir && !no_in_notice)
fprintfLineWrap(f,
"All files destined for " KDMCONF " were actually saved in %s; "
"this config will not 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 'genkdmconf --help' if you want to generate another configuration.\n"
"\nYou may delete this README.\n");
fclose_(f);
return 0;
}