mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-24 19:02:51 +00:00
3320 lines
87 KiB
C
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;
|
|
}
|