add more spacing
[personal-kdebase.git] / workspace / kdm / kfrontend / kdm_config.c
blob8e5487057b25cc3972e6a82971840092887cdd39
1 /*
3 Read options from kdmrc
5 Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <config-workspace.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <grp.h>
37 #ifdef _POSIX_PRIORITY_SCHEDULING
38 # include <sched.h>
39 #endif
41 #include <X11/X.h>
42 #ifdef FamilyInternet6
43 # define IPv6
44 #endif
46 #include <greet.h>
48 #define WANT_CONF_READ
49 #include <config.ci>
52 * Section/Entry definition structs
55 typedef struct Ent {
56 const char *name;
57 int id;
58 void *ptr;
59 const char *def;
60 } Ent;
62 typedef struct Sect {
63 const char *name;
64 Ent *ents;
65 int numents;
66 } Sect;
69 * Parsed ini file structs
72 typedef struct Entry {
73 struct Entry *next;
74 const char *val;
75 Ent *ent;
76 int vallen;
77 int line;
78 } Entry;
80 typedef struct Section {
81 struct Section *next;
82 Entry *entries;
83 Sect *sect;
84 const char *name, *dname, *dhost, *dnum, *dclass;
85 int nlen, dlen, dhostl, dnuml, dclassl;
86 } Section;
90 * Split up display-name/-class for fast comparison
92 typedef struct DSpec {
93 const char *dhost, *dnum, *dclass;
94 int dhostl, dnuml, dclassl;
95 } DSpec;
99 * Config value storage structures
102 typedef union Value {
103 struct {
104 const char *ptr;
105 int len; /* including 0-terminator */
106 } str;
107 struct {
108 union Value *ptr;
109 int totlen; /* summed up length of all contained strings */
110 } argv;
111 int num;
112 } Value;
114 typedef struct Val {
115 Value val;
116 int id;
117 } Val;
119 typedef struct ValArr {
120 Val *ents;
121 int nents, esiz, nchars, nptrs;
122 } ValArr;
125 static void *Malloc( size_t size );
126 static void *Realloc( void *ptr, size_t size );
128 #define PRINT_QUOTES
129 #define LOG_NAME "kdm_config"
130 #define LOG_DEBUG_MASK DEBUG_CONFIG
131 #define LOG_PANIC_EXIT 1
132 #define STATIC static
133 #include <printf.c>
136 static void *
137 Malloc( size_t size )
139 void *ret;
141 if (!(ret = malloc( size )))
142 logOutOfMem();
143 return ret;
146 static void *
147 Realloc( void *ptr, size_t size )
149 void *ret;
151 if (!(ret = realloc( ptr, size )) && size)
152 logOutOfMem();
153 return ret;
157 static void
158 mkDSpec( DSpec *spec, const char *dname, const char *dclass )
160 spec->dhost = dname;
161 for (spec->dhostl = 0; dname[spec->dhostl] != ':'; spec->dhostl++);
162 spec->dnum = dname + spec->dhostl + 1;
163 spec->dnuml = strlen( spec->dnum );
164 spec->dclass = dclass;
165 spec->dclassl = strlen( dclass );
169 static int rfd, wfd;
171 static int
172 reader( void *buf, int count )
174 int ret, rlen;
176 for (rlen = 0; rlen < count; ) {
177 dord:
178 ret = read( rfd, (void *)((char *)buf + rlen), count - rlen );
179 if (ret < 0) {
180 if (errno == EINTR)
181 goto dord;
182 if (errno == EAGAIN)
183 break;
184 return -1;
186 if (!ret)
187 break;
188 rlen += ret;
190 return rlen;
193 static void
194 gRead( void *buf, int count )
196 if (reader( buf, count ) != count)
197 logPanic( "Cannot read from core\n" );
200 static void
201 gWrite( const void *buf, int count )
203 if (write( wfd, buf, count ) != count)
204 logPanic( "Cannot write to core\n" );
205 #ifdef _POSIX_PRIORITY_SCHEDULING
206 if ((debugLevel & DEBUG_HLPCON))
207 sched_yield();
208 #endif
211 static void
212 gSendInt( int val )
214 gWrite( &val, sizeof(val) );
217 static void
218 gSendStr( const char *buf )
220 if (buf) {
221 int len = strlen( buf ) + 1;
222 gWrite( &len, sizeof(len) );
223 gWrite( buf, len );
224 } else
225 gWrite( &buf, sizeof(int) );
228 static void
229 gSendNStr( const char *buf, int len )
231 int tlen = len + 1;
232 gWrite( &tlen, sizeof(tlen) );
233 gWrite( buf, len );
234 gWrite( "", 1 );
237 #ifdef XDMCP
238 static void
239 gSendArr( int len, const char *data )
241 gWrite( &len, sizeof(len) );
242 gWrite( data, len );
244 #endif
246 static int
247 gRecvCmd( int *val )
249 if (reader( val, sizeof(*val) ) != sizeof(*val))
250 return False;
251 return True;
254 static int
255 gRecvInt()
257 int val;
259 gRead( &val, sizeof(val) );
260 return val;
263 static char *
264 gRecvStr()
266 int len;
267 char *buf;
269 len = gRecvInt();
270 if (!len)
271 return 0;
272 if (!(buf = malloc( len )))
273 logPanic( "No memory for read buffer" );
274 gRead( buf, len );
275 return buf;
279 /* #define WANT_CLOSE 1 */
281 typedef struct File {
282 char *buf, *eof, *cur;
283 #if defined(HAVE_MMAP) && defined(WANT_CLOSE)
284 int ismapped;
285 #endif
286 } File;
288 static int
289 readFile( File *file, const char *fn, const char *what )
291 int fd;
292 off_t flen;
294 if ((fd = open( fn, O_RDONLY )) < 0) {
295 logInfo( "Cannot open %s file %s\n", what, fn );
296 return False;
299 flen = lseek( fd, 0, SEEK_END );
300 #ifdef HAVE_MMAP
301 # ifdef WANT_CLOSE
302 file->ismapped = False;
303 # endif
304 file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
305 # ifdef WANT_CLOSE
306 if (file->buf)
307 file->ismapped = True;
308 else
309 # else
310 if (!file->buf)
311 # endif
312 #endif
314 if (!(file->buf = Malloc( flen + 1 ))) {
315 close( fd );
316 return False;
318 lseek( fd, 0, SEEK_SET );
319 if (read( fd, file->buf, flen ) != flen) {
320 free( file->buf );
321 logError( "Cannot read %s file %s\n", what, fn );
322 close( fd );
323 return False;
326 file->eof = (file->cur = file->buf) + flen;
327 close( fd );
328 return True;
331 #ifdef WANT_CLOSE
332 static void
333 freeBuf( File *file )
335 # ifdef HAVE_MMAP
336 if (file->ismapped)
337 munmap( file->buf, file->eof - file->buf + 1 );
338 else
339 # endif
340 free( file->buf );
342 #endif
344 CONF_READ_VARS
346 #define C_MTYPE_MASK 0x30000000
347 # define C_PATH 0x10000000 /* C_TYPE_STR is a path spec */
348 # define C_BOOL 0x10000000 /* C_TYPE_INT is a boolean */
349 # define C_ENUM 0x20000000 /* C_TYPE_INT is an enum (option) */
350 # define C_GRP 0x30000000 /* C_TYPE_INT is a group spec */
351 #define C_INTERNAL 0x40000000 /* don't expose to core */
352 #define C_CONFIG 0x80000000 /* process only for finding deps */
354 #ifdef XDMCP
355 static int
356 PrequestPort( Value *retval )
358 if (!VxdmcpEnable.num) {
359 retval->num = 0;
360 return True;
362 return False;
364 #endif
366 static Value
367 emptyStr = { { "", 1 } },
368 nullValue = { { 0, 0 } },
369 emptyArgv = { { (char *)&nullValue, 0 } };
371 static int
372 PnoPassUsers( Value *retval )
374 if (!VnoPassEnable.num) {
375 *retval = emptyArgv;
376 return True;
378 return False;
381 static int
382 PautoLoginX( Value *retval )
384 if (!VautoLoginEnable.num) {
385 *retval = emptyStr;
386 return True;
388 return False;
391 CONF_READ_ENTRIES
393 static const char *kdmrc = KDMCONF "/kdmrc";
395 static Section *rootsec;
397 static void
398 readConfig()
400 const char *nstr, *dstr, *cstr, *dhost, *dnum, *dclass;
401 char *s, *e, *st, *en, *ek, *sl, *pt;
402 Section *cursec;
403 Entry *curent;
404 Ent *ce;
405 int nlen, dlen, clen, dhostl, dnuml, dclassl;
406 int i, line, sectmoan, restl;
407 File file;
408 static int confread;
410 if (confread)
411 return;
412 confread = True;
414 debug( "reading config %s ...\n", kdmrc );
415 if (!readFile( &file, kdmrc, "master configuration" ))
416 return;
418 for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) {
419 line++;
421 while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
422 s++;
424 if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) {
425 sktoeol:
426 while ((s < file.eof) && (*s != '\n'))
427 s++;
428 continue;
430 sl = s;
432 if (*s == '[') {
433 sectmoan = False;
434 while ((s < file.eof) && (*s != '\n'))
435 s++;
436 e = s - 1;
437 while ((e > sl) && isspace( *e ))
438 e--;
439 if (*e != ']') {
440 cursec = 0;
441 logError( "Invalid section header at %s:%d\n", kdmrc, line );
442 continue;
444 nstr = sl + 1;
445 nlen = e - nstr;
446 for (cursec = rootsec; cursec; cursec = cursec->next)
447 if (nlen == cursec->nlen &&
448 !memcmp( nstr, cursec->name, nlen ))
450 logInfo( "Multiple occurrences of section [%.*s] in %s. "
451 "Consider merging them.\n", nlen, nstr, kdmrc );
452 goto secfnd;
454 if (nstr[0] == 'X' && nstr[1] == '-') {
455 cstr = nstr + nlen;
456 clen = 0;
457 while (++clen, *--cstr != '-');
458 if (cstr == nstr + 1)
459 goto illsec;
460 dstr = nstr + 2;
461 dlen = nlen - clen - 2;
462 dhost = dstr;
463 dhostl = 0;
464 for (restl = dlen; restl; restl--) {
465 if (dhost[dhostl] == ':') {
466 dnum = dhost + dhostl + 1;
467 dnuml = 0;
468 for (restl--; restl; restl--) {
469 if (dnum[dnuml] == '_') {
470 dclass = dnum + dnuml + 1;
471 dclassl = restl;
472 goto gotall;
474 dnuml++;
476 goto gotnum;
478 dhostl++;
480 dnum = "*";
481 dnuml = 1;
482 gotnum:
483 dclass = "*";
484 dclassl = 1;
485 gotall: ;
486 } else {
487 if (nstr[0] == '-')
488 goto illsec;
489 dstr = 0;
490 dlen = 0;
491 dhost = 0;
492 dhostl = 0;
493 dnum = 0;
494 dnuml = 0;
495 dclass = 0;
496 dclassl = 0;
497 cstr = nstr;
498 clen = nlen;
500 for (i = 0; i < as(allSects); i++)
501 if ((int)strlen( allSects[i]->name ) == clen &&
502 !memcmp( allSects[i]->name, cstr, clen ))
503 goto newsec;
504 illsec:
505 cursec = 0;
506 logError( "Unrecognized section name [%.*s] at %s:%d\n",
507 nlen, nstr, kdmrc, line );
508 continue;
509 newsec:
510 if (!(cursec = Malloc( sizeof(*cursec) )))
511 return;
512 cursec->name = nstr;
513 cursec->nlen = nlen;
514 cursec->dname = dstr;
515 cursec->dlen = dlen;
516 cursec->dhost = dhost;
517 cursec->dhostl = dhostl;
518 cursec->dnum = dnum;
519 cursec->dnuml = dnuml;
520 cursec->dclass = dclass;
521 cursec->dclassl = dclassl;
522 cursec->sect = allSects[i];
523 cursec->entries = 0;
524 cursec->next = rootsec;
525 rootsec = cursec;
526 /*debug( "now in section [%.*s], dpy '%.*s', core '%.*s'\n",
527 nlen, nstr, dlen, dstr, clen, cstr );*/
528 secfnd:
529 continue;
532 if (!cursec) {
533 if (sectmoan) {
534 sectmoan = False;
535 logError( "Entry outside any section at %s:%d", kdmrc, line );
537 goto sktoeol;
540 for (; (s < file.eof) && (*s != '\n'); s++)
541 if (*s == '=')
542 goto haveeq;
543 logError( "Invalid entry (missing '=') at %s:%d\n", kdmrc, line );
544 continue;
546 haveeq:
547 for (ek = s - 1; ; ek--) {
548 if (ek < sl) {
549 logError( "Invalid entry (empty key) at %s:%d\n", kdmrc, line );
550 goto sktoeol;
552 if (!isspace( *ek ))
553 break;
556 s++;
557 while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
558 s++;
559 for (pt = st = en = s; s < file.eof && *s != '\n'; s++) {
560 if (*s == '\\') {
561 s++;
562 if (s >= file.eof || *s == '\n') {
563 logError( "Trailing backslash at %s:%d\n", kdmrc, line );
564 break;
566 switch (*s) {
567 case 's': *pt++ = ' '; break;
568 case 't': *pt++ = '\t'; break;
569 case 'n': *pt++ = '\n'; break;
570 case 'r': *pt++ = '\r'; break;
571 case '\\': *pt++ = '\\'; break;
572 default: *pt++ = '\\'; *pt++ = *s; break;
574 en = pt;
575 } else {
576 *pt++ = *s;
577 if (*s != ' ' && *s != '\t')
578 en = pt;
582 nstr = sl;
583 nlen = ek - sl + 1;
584 /*debug( "read entry '%.*s'='%.*s'\n", nlen, nstr, en - st, st );*/
585 for (i = 0; i < cursec->sect->numents; i++) {
586 ce = cursec->sect->ents + i;
587 if ((int)strlen( ce->name ) == nlen &&
588 !memcmp( ce->name, nstr, nlen ))
589 goto keyok;
591 logError( "Unrecognized key '%.*s' in section [%.*s] at %s:%d\n",
592 nlen, nstr, cursec->nlen, cursec->name, kdmrc, line );
593 continue;
594 keyok:
595 for (curent = cursec->entries; curent; curent = curent->next)
596 if (ce == curent->ent) {
597 logError( "Multiple occurrences of key '%s' in section [%.*s]"
598 " of %s\n",
599 ce->name, cursec->nlen, cursec->name, kdmrc );
600 goto keyfnd;
602 if (!(curent = Malloc( sizeof(*curent) )))
603 return;
604 curent->ent = ce;
605 curent->line = line;
606 curent->val = st;
607 curent->vallen = en - st;
608 curent->next = cursec->entries;
609 cursec->entries = curent;
610 keyfnd:
611 continue;
615 static Entry *
616 findGEnt( int id )
618 Section *cursec;
619 Entry *curent;
621 for (cursec = rootsec; cursec; cursec = cursec->next)
622 if (!cursec->dname)
623 for (curent = cursec->entries; curent; curent = curent->next)
624 if (curent->ent->id == id) {
625 debug( "line %d: %s = %'.*s\n",
626 curent->line, curent->ent->name,
627 curent->vallen, curent->val );
628 return curent;
630 return 0;
633 /* Display name match scoring:
634 * - class (any/exact) -> 0/1
635 * - number (any/exact) -> 0/2
636 * - host (any/nonempty/trail/exact) -> 0/4/8/12
638 static Entry *
639 findDEnt( int id, DSpec *dspec )
641 Section *cursec, *bestsec = 0;
642 Entry *curent, *bestent;
643 int score, bestscore;
645 bestscore = -1, bestent = 0;
646 for (cursec = rootsec; cursec; cursec = cursec->next)
647 if (cursec->dname) {
648 score = 0;
649 if (cursec->dclassl != 1 || cursec->dclass[0] != '*') {
650 if (cursec->dclassl == dspec->dclassl &&
651 !memcmp( cursec->dclass, dspec->dclass, dspec->dclassl ))
652 score = 1;
653 else
654 continue;
656 if (cursec->dnuml != 1 || cursec->dnum[0] != '*') {
657 if (cursec->dnuml == dspec->dnuml &&
658 !memcmp( cursec->dnum, dspec->dnum, dspec->dnuml ))
659 score += 2;
660 else
661 continue;
663 if (cursec->dhostl != 1 || cursec->dhost[0] != '*') {
664 if (cursec->dhostl == 1 && cursec->dhost[0] == '+') {
665 if (dspec->dhostl)
666 score += 4;
667 else
668 continue;
669 } else if (cursec->dhost[0] == '.') {
670 if (cursec->dhostl < dspec->dhostl &&
671 !memcmp( cursec->dhost,
672 dspec->dhost + dspec->dhostl - cursec->dhostl,
673 cursec->dhostl ))
674 score += 8;
675 else
676 continue;
677 } else {
678 if (cursec->dhostl == dspec->dhostl &&
679 !memcmp( cursec->dhost, dspec->dhost, dspec->dhostl ))
680 score += 12;
681 else
682 continue;
685 if (score > bestscore) {
686 for (curent = cursec->entries; curent; curent = curent->next)
687 if (curent->ent->id == id) {
688 bestent = curent;
689 bestsec = cursec;
690 bestscore = score;
691 break;
695 if (bestent)
696 debug( "line %d: %.*s:%.*s_%.*s/%s = %'.*s\n", bestent->line,
697 bestsec->dhostl, bestsec->dhost,
698 bestsec->dnuml, bestsec->dnum,
699 bestsec->dclassl, bestsec->dclass,
700 bestent->ent->name, bestent->vallen, bestent->val );
701 return bestent;
704 static const char *
705 convertValue( Ent *et, Value *retval, int vallen, const char *val, char **eopts )
707 Value *ents;
708 int i, b, e, tlen, nents, esiz;
709 char buf[80];
711 switch (et->id & C_TYPE_MASK) {
712 case C_TYPE_INT:
713 for (i = 0; i < vallen && i < (int)sizeof(buf) - 1; i++)
714 buf[i] = tolower( val[i] );
715 buf[i] = 0;
716 if ((et->id & C_MTYPE_MASK) == C_BOOL) {
717 if (!strcmp( buf, "true" ) ||
718 !strcmp( buf, "on" ) ||
719 !strcmp( buf, "yes" ) ||
720 !strcmp( buf, "1" ))
721 retval->num = 1;
722 else if (!strcmp( buf, "false" ) ||
723 !strcmp( buf, "off" ) ||
724 !strcmp( buf, "no" ) ||
725 !strcmp( buf, "0" ))
726 retval->num = 0;
727 else
728 return "boolean";
729 return 0;
730 } else if ((et->id & C_MTYPE_MASK) == C_ENUM) {
731 for (i = 0; eopts[i]; i++)
732 if (!memcmp( eopts[i], val, vallen ) && !eopts[i][vallen]) {
733 retval->num = i;
734 return 0;
736 return "option";
737 } else if ((et->id & C_MTYPE_MASK) == C_GRP) {
738 struct group *ge;
739 if ((ge = getgrnam( buf ))) {
740 retval->num = ge->gr_gid;
741 return 0;
744 if (sscanf( buf, "%i", &retval->num ) != 1)
745 return "integer";
746 return 0;
747 case C_TYPE_STR:
748 retval->str.ptr = val;
749 retval->str.len = vallen + 1;
750 if ((et->id & C_MTYPE_MASK) == C_PATH)
751 if (vallen && val[vallen-1] == '/')
752 retval->str.len--;
753 return 0;
754 case C_TYPE_ARGV:
755 if (!(ents = Malloc( sizeof(Value) * (esiz = 10) )))
756 return 0;
757 for (nents = 0, tlen = 0, i = 0; ; i++) {
758 for (; i < vallen && isspace( val[i] ); i++);
759 for (b = i; i < vallen && val[i] != ','; i++);
760 if (b == i)
761 break;
762 for (e = i; e > b && isspace( val[e - 1] ); e--);
763 if (esiz < nents + 2) {
764 Value *entsn = Realloc( ents,
765 sizeof(Value) * (esiz = esiz * 2 + 1) );
766 if (!nents)
767 break;
768 ents = entsn;
770 ents[nents].str.ptr = val + b;
771 ents[nents].str.len = e - b;
772 nents++;
773 tlen += e - b + 1;
775 ents[nents].str.ptr = 0;
776 retval->argv.ptr = ents;
777 retval->argv.totlen = tlen;
778 return 0;
779 default:
780 logError( "Internal error: unknown value type in id %#x\n", et->id );
781 return 0;
785 static void
786 getValue( Ent *et, DSpec *dspec, Value *retval, char **eopts )
788 Entry *ent;
789 const char *errs;
791 /* debug( "Getting value %#x\n", et->id );*/
792 if (dspec)
793 ent = findDEnt( et->id, dspec );
794 else
795 ent = findGEnt( et->id );
796 if (ent) {
797 if (!(errs = convertValue( et, retval, ent->vallen, ent->val, eopts )))
798 return;
799 logError( "Invalid %s value '%.*s' at %s:%d\n",
800 errs, ent->vallen, ent->val, kdmrc, ent->line );
802 debug( "default: %s = %'s\n", et->name, et->def );
803 if ((errs = convertValue( et, retval, strlen( et->def ), et->def, eopts )))
804 logError( "Internal error: invalid default %s value '%s' for key %s\n",
805 errs, et->def, et->name );
808 static int
809 addValue( ValArr *va, int id, Value *val )
811 int nu;
813 /* debug( "Addig value %#x\n", id );*/
814 if (va->nents == va->esiz) {
815 va->ents = Realloc( va->ents, sizeof(Val) * (va->esiz += 50) );
816 if (!va->ents)
817 return False;
819 va->ents[va->nents].id = id;
820 va->ents[va->nents].val = *val;
821 va->nents++;
822 switch (id & C_TYPE_MASK) {
823 case C_TYPE_INT:
824 break;
825 case C_TYPE_STR:
826 va->nchars += val->str.len;
827 break;
828 case C_TYPE_ARGV:
829 va->nchars += val->argv.totlen;
830 for (nu = 0; val->argv.ptr[nu++].str.ptr; );
831 va->nptrs += nu;
832 break;
834 return True;
837 static void
838 copyValues( ValArr *va, Sect *sec, DSpec *dspec, int isconfig )
840 Value val;
841 int i;
843 debug( "getting values for section class [%s]\n", sec->name );
844 for (i = 0; i < sec->numents; i++) {
845 /*debug ("value %#x\n", sec->ents[i].id);*/
846 if ((sec->ents[i].id & (int)C_CONFIG) != isconfig)
848 else if (sec->ents[i].id & C_INTERNAL) {
849 getValue( sec->ents + i, dspec, ((Value *)sec->ents[i].ptr), 0 );
850 } else {
851 if (((sec->ents[i].id & C_MTYPE_MASK) == C_ENUM) ||
852 !sec->ents[i].ptr ||
853 !((int (*)( Value * ))sec->ents[i].ptr)(&val)) {
854 getValue( sec->ents + i, dspec, &val,
855 (char **)sec->ents[i].ptr );
857 if (!addValue( va, sec->ents[i].id, &val ))
858 break;
861 return;
864 static void
865 sendValues( ValArr *va )
867 Value *cst;
868 int i, nu;
870 gSendInt( va->nents );
871 gSendInt( va->nptrs );
872 gSendInt( 0/*va->nints*/ );
873 gSendInt( va->nchars );
874 for (i = 0; i < va->nents; i++) {
875 gSendInt( va->ents[i].id & ~C_PRIVATE );
876 switch (va->ents[i].id & C_TYPE_MASK) {
877 case C_TYPE_INT:
878 gSendInt( va->ents[i].val.num );
879 break;
880 case C_TYPE_STR:
881 gSendNStr( va->ents[i].val.str.ptr, va->ents[i].val.str.len - 1 );
882 break;
883 case C_TYPE_ARGV:
884 cst = va->ents[i].val.argv.ptr;
885 for (nu = 0; cst[nu].str.ptr; nu++);
886 gSendInt( nu );
887 for (; cst->str.ptr; cst++)
888 gSendNStr( cst->str.ptr, cst->str.len );
889 break;
895 #ifdef XDMCP
896 static char *
897 readWord( File *file, int *len, int EOFatEOL )
899 char *wordp, *wordBuffer;
900 int quoted;
901 char c;
903 rest:
904 wordp = wordBuffer = file->cur;
905 mloop:
906 quoted = False;
907 qloop:
908 if (file->cur == file->eof) {
909 doeow:
910 if (wordp == wordBuffer)
911 return 0;
912 retw:
913 *wordp = '\0';
914 *len = wordp - wordBuffer;
915 return wordBuffer;
917 c = *file->cur++;
918 switch (c) {
919 case '#':
920 if (quoted)
921 break;
922 do {
923 if (file->cur == file->eof)
924 goto doeow;
925 c = *file->cur++;
926 } while (c != '\n');
927 case '\0':
928 case '\n':
929 if (EOFatEOL && !quoted) {
930 file->cur--;
931 goto doeow;
933 if (wordp != wordBuffer) {
934 file->cur--;
935 goto retw;
937 goto rest;
938 case ' ':
939 case '\t':
940 if (wordp != wordBuffer)
941 goto retw;
942 goto rest;
943 case '\\':
944 if (!quoted) {
945 quoted = True;
946 goto qloop;
948 break;
950 *wordp++ = c;
951 goto mloop;
954 #define ALIAS_CHARACTER '%'
955 #define EQUAL_CHARACTER '='
956 #define NEGATE_CHARACTER '!'
957 #define CHOOSER_STRING "CHOOSER"
958 #define BROADCAST_STRING "BROADCAST"
959 #define NOBROADCAST_STRING "NOBROADCAST"
960 #define LISTEN_STRING "LISTEN"
961 #define WILDCARD_STRING "*"
963 typedef struct _HostEntry {
964 struct _HostEntry *next;
965 int type;
966 union _hostOrAlias {
967 char *aliasPattern;
968 char *hostPattern;
969 struct _display {
970 int connectionType;
971 int hostAddrLen;
972 char *hostAddress;
973 } displayAddress;
974 } entry;
975 } HostEntry;
977 typedef struct _ListenEntry {
978 struct _ListenEntry *next;
979 int iface;
980 int mcasts;
981 int nmcasts;
982 } ListenEntry;
984 typedef struct _AliasEntry {
985 struct _AliasEntry *next;
986 char *name;
987 HostEntry **pHosts;
988 int hosts;
989 int nhosts;
990 int hasBad;
991 } AliasEntry;
993 typedef struct _AclEntry {
994 struct _AclEntry *next;
995 HostEntry **pEntries;
996 int entries;
997 int nentries;
998 HostEntry **pHosts;
999 int hosts;
1000 int nhosts;
1001 int flags;
1002 } AclEntry;
1005 static int
1006 hasGlobCharacters( char *s )
1008 for (;;)
1009 switch (*s++) {
1010 case '?':
1011 case '*':
1012 return True;
1013 case '\0':
1014 return False;
1018 #define PARSE_ALL 0
1019 #define PARSE_NO_BCAST 1
1020 #define PARSE_NO_PAT 2
1021 #define PARSE_NO_ALIAS 4
1023 static int
1024 parseHost( int *nHosts, HostEntry ***hostPtr, int *nChars,
1025 char *hostOrAlias, int len, int parse )
1027 #if defined(IPv6) && defined(AF_INET6)
1028 struct addrinfo *ai;
1029 #else
1030 struct hostent *hostent;
1031 #endif
1032 void *addr;
1033 int addr_type, addr_len;
1035 if (!(**hostPtr = (HostEntry *)Malloc( sizeof(HostEntry) )))
1036 return False;
1037 if (!(parse & PARSE_NO_BCAST) && !strcmp( hostOrAlias, BROADCAST_STRING ))
1039 (**hostPtr)->type = HOST_BROADCAST;
1041 else if (!(parse & PARSE_NO_ALIAS) && *hostOrAlias == ALIAS_CHARACTER)
1043 (**hostPtr)->type = HOST_ALIAS;
1044 (**hostPtr)->entry.aliasPattern = hostOrAlias + 1;
1045 *nChars += len;
1047 else if (!(parse & PARSE_NO_PAT) && hasGlobCharacters( hostOrAlias ))
1049 (**hostPtr)->type = HOST_PATTERN;
1050 (**hostPtr)->entry.hostPattern = hostOrAlias;
1051 *nChars += len + 1;
1053 else
1055 (**hostPtr)->type = HOST_ADDRESS;
1056 #if defined(IPv6) && defined(AF_INET6)
1057 if (getaddrinfo( hostOrAlias, NULL, NULL, &ai ))
1058 #else
1059 if (!(hostent = gethostbyname( hostOrAlias )))
1060 #endif
1062 logWarn( "XDMCP ACL: unresolved host %'s\n", hostOrAlias );
1063 free( (char *)(**hostPtr) );
1064 return False;
1066 #if defined(IPv6) && defined(AF_INET6)
1067 addr_type = ai->ai_addr->sa_family;
1068 if (ai->ai_family == AF_INET) {
1069 addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
1070 addr_len = sizeof(struct in_addr);
1071 } else /*if (ai->ai_addr->sa_family == AF_INET6)*/ {
1072 addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
1073 addr_len = sizeof(struct in6_addr);
1075 #else
1076 addr_type = hostent->h_addrtype;
1077 addr = hostent->h_addr;
1078 addr_len = hostent->h_length;
1079 #endif
1080 if (!((**hostPtr)->entry.displayAddress.hostAddress =
1081 Malloc( addr_len )))
1083 #if defined(IPv6) && defined(AF_INET6)
1084 freeaddrinfo( ai );
1085 #endif
1086 free( (char *)(**hostPtr) );
1087 return False;
1089 memcpy( (**hostPtr)->entry.displayAddress.hostAddress, addr, addr_len );
1090 *nChars += addr_len;
1091 (**hostPtr)->entry.displayAddress.hostAddrLen = addr_len;
1092 (**hostPtr)->entry.displayAddress.connectionType = addr_type;
1093 #if defined(IPv6) && defined(AF_INET6)
1094 freeaddrinfo( ai );
1095 #endif
1097 *hostPtr = &(**hostPtr)->next;
1098 (*nHosts)++;
1099 return True;
1102 /* Returns True if string is matched by pattern. Does case folding. */
1103 static int
1104 patternMatch( const char *string, const char *pattern )
1106 int p, s;
1108 if (!string)
1109 string = "";
1111 for (;;) {
1112 s = *string++;
1113 switch (p = *pattern++) {
1114 case '*':
1115 if (!*pattern)
1116 return True;
1117 for (string--; *string; string++)
1118 if (patternMatch( string, pattern ))
1119 return True;
1120 return False;
1121 case '?':
1122 if (s == '\0')
1123 return False;
1124 break;
1125 case '\0':
1126 return s == '\0';
1127 case '\\':
1128 p = *pattern++;
1129 /* fall through */
1130 default:
1131 if (tolower( p ) != tolower( s ))
1132 return False;
1137 #define MAX_DEPTH 32
1139 #define CHECK_NOT 1
1140 #define CHECK_NO_PAT 2
1142 static int
1143 checkHostlist( HostEntry **hosts, int nh, AliasEntry *aliases, int na,
1144 int depth, int flags )
1146 HostEntry *h;
1147 AliasEntry *a;
1148 int hn, an, am;
1150 for (h = *hosts, hn = 0; hn < nh; hn++, h = h->next)
1151 if (h->type == HOST_ALIAS) {
1152 if (depth == MAX_DEPTH) {
1153 logError( "XDMCP ACL: alias recursion involving %%%s\n",
1154 h->entry.aliasPattern );
1155 return True;
1157 for (a = aliases, an = 0, am = False; an < na; an++, a = a->next)
1158 if (patternMatch( a->name, h->entry.aliasPattern )) {
1159 am = True;
1160 if ((flags & CHECK_NOT) && a->hasBad) {
1161 logError( "XDMCP ACL: alias %%%s with unresolved hosts "
1162 "in denying rule\n", a->name );
1163 return True;
1165 if (checkHostlist( a->pHosts, a->nhosts, aliases, na,
1166 depth + 1, flags ))
1167 return True;
1169 if (!am) {
1170 if (flags & CHECK_NOT) {
1171 logError( "XDMCP ACL: unresolved alias pattern %%%s "
1172 "in denying rule\n", h->entry.aliasPattern );
1173 return True;
1174 } else
1175 logWarn( "XDMCP ACL: unresolved alias pattern %%%s\n",
1176 h->entry.aliasPattern );
1178 } else if (h->type == HOST_PATTERN && (flags & CHECK_NO_PAT))
1179 logWarn( "XDMCP ACL: wildcarded pattern %'s in host-only context\n",
1180 h->entry.hostPattern );
1181 return False;
1184 static void
1185 readAccessFile( const char *fname )
1187 HostEntry *hostList, **hostPtr = &hostList;
1188 AliasEntry *aliasList, **aliasPtr = &aliasList;
1189 AclEntry *acList, **acPtr = &acList, *acl;
1190 ListenEntry *listenList, **listenPtr = &listenList;
1191 char *displayOrAlias, *hostOrAlias;
1192 File file;
1193 int nHosts, nAliases, nAcls, nListens, nChars, error, bad;
1194 int i, len;
1196 nHosts = nAliases = nAcls = nListens = nChars = 0;
1197 error = False;
1198 if (!readFile( &file, fname, "XDMCP access control" ))
1199 goto sendacl;
1200 while ((displayOrAlias = readWord( &file, &len, False ))) {
1201 if (*displayOrAlias == ALIAS_CHARACTER)
1203 if (!(*aliasPtr = (AliasEntry *)Malloc( sizeof(AliasEntry) ))) {
1204 error = True;
1205 break;
1207 (*aliasPtr)->name = displayOrAlias + 1;
1208 nChars += len;
1209 (*aliasPtr)->hosts = nHosts;
1210 (*aliasPtr)->pHosts = hostPtr;
1211 (*aliasPtr)->nhosts = 0;
1212 (*aliasPtr)->hasBad = False;
1213 while ((hostOrAlias = readWord( &file, &len, True ))) {
1214 if (parseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
1215 PARSE_NO_BCAST ))
1216 (*aliasPtr)->nhosts++;
1217 else
1218 (*aliasPtr)->hasBad = True;
1220 aliasPtr = &(*aliasPtr)->next;
1221 nAliases++;
1223 else if (!strcmp( displayOrAlias, LISTEN_STRING ))
1225 if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry) ))) {
1226 error = True;
1227 break;
1229 (*listenPtr)->iface = nHosts;
1230 if (!(hostOrAlias = readWord( &file, &len, True )) ||
1231 !strcmp( hostOrAlias, WILDCARD_STRING ) ||
1232 !parseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
1233 PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS ))
1235 (*listenPtr)->iface = -1;
1237 (*listenPtr)->mcasts = nHosts;
1238 (*listenPtr)->nmcasts = 0;
1239 while ((hostOrAlias = readWord( &file, &len, True ))) {
1240 if (parseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
1241 PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS ))
1242 (*listenPtr)->nmcasts++;
1244 listenPtr = &(*listenPtr)->next;
1245 nListens++;
1247 else
1249 if (!(*acPtr = (AclEntry *)Malloc( sizeof(AclEntry) ))) {
1250 error = True;
1251 break;
1253 (*acPtr)->flags = 0;
1254 if (*displayOrAlias == NEGATE_CHARACTER) {
1255 (*acPtr)->flags |= a_notAllowed;
1256 displayOrAlias++;
1257 } else if (*displayOrAlias == EQUAL_CHARACTER)
1258 displayOrAlias++;
1259 (*acPtr)->entries = nHosts;
1260 (*acPtr)->pEntries = hostPtr;
1261 (*acPtr)->nentries = 1;
1262 if (!parseHost( &nHosts, &hostPtr, &nChars, displayOrAlias, len,
1263 PARSE_NO_BCAST ))
1265 bad = True;
1266 if ((*acPtr)->flags & a_notAllowed) {
1267 logError( "XDMCP ACL: unresolved host in denying rule\n" );
1268 error = True;
1270 } else
1271 bad = False;
1272 (*acPtr)->hosts = nHosts;
1273 (*acPtr)->pHosts = hostPtr;
1274 (*acPtr)->nhosts = 0;
1275 while ((hostOrAlias = readWord( &file, &len, True ))) {
1276 if (!strcmp( hostOrAlias, CHOOSER_STRING ))
1277 (*acPtr)->flags |= a_useChooser;
1278 else if (!strcmp( hostOrAlias, NOBROADCAST_STRING ))
1279 (*acPtr)->flags |= a_notBroadcast;
1280 else {
1281 if (parseHost( &nHosts, &hostPtr, &nChars,
1282 hostOrAlias, len, PARSE_NO_PAT ))
1283 (*acPtr)->nhosts++;
1286 if (!bad) {
1287 acPtr = &(*acPtr)->next;
1288 nAcls++;
1293 if (!nListens) {
1294 if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry) )))
1295 error = True;
1296 else {
1297 (*listenPtr)->iface = -1;
1298 (*listenPtr)->mcasts = nHosts;
1299 (*listenPtr)->nmcasts = 0;
1300 #if defined(IPv6) && defined(AF_INET6) && defined(XDM_DEFAULT_MCAST_ADDR6)
1301 if (parseHost( &nHosts, &hostPtr, &nChars,
1302 XDM_DEFAULT_MCAST_ADDR6,
1303 sizeof(XDM_DEFAULT_MCAST_ADDR6)-1,
1304 PARSE_ALL ))
1305 (*listenPtr)->nmcasts++;
1306 #endif
1307 nListens++;
1311 for (acl = acList, i = 0; i < nAcls; i++, acl = acl->next)
1312 if (checkHostlist( acl->pEntries, acl->nentries, aliasList, nAliases,
1313 0, (acl->flags & a_notAllowed) ? CHECK_NOT : 0 ) ||
1314 checkHostlist( acl->pHosts, acl->nhosts, aliasList, nAliases,
1315 0, CHECK_NO_PAT ))
1316 error = True;
1318 if (error) {
1319 nHosts = nAliases = nAcls = nListens = nChars = 0;
1320 sendacl:
1321 logError( "No XDMCP requests will be granted\n" );
1323 gSendInt( nHosts );
1324 gSendInt( nListens );
1325 gSendInt( nAliases );
1326 gSendInt( nAcls );
1327 gSendInt( nChars );
1328 for (i = 0; i < nHosts; i++, hostList = hostList->next) {
1329 gSendInt( hostList->type );
1330 switch (hostList->type) {
1331 case HOST_ALIAS:
1332 gSendStr( hostList->entry.aliasPattern );
1333 break;
1334 case HOST_PATTERN:
1335 gSendStr( hostList->entry.hostPattern );
1336 break;
1337 case HOST_ADDRESS:
1338 gSendArr( hostList->entry.displayAddress.hostAddrLen,
1339 hostList->entry.displayAddress.hostAddress );
1340 gSendInt( hostList->entry.displayAddress.connectionType );
1341 break;
1344 for (i = 0; i < nListens; i++, listenList = listenList->next) {
1345 gSendInt( listenList->iface );
1346 gSendInt( listenList->mcasts );
1347 gSendInt( listenList->nmcasts );
1349 for (i = 0; i < nAliases; i++, aliasList = aliasList->next) {
1350 gSendStr( aliasList->name );
1351 gSendInt( aliasList->hosts );
1352 gSendInt( aliasList->nhosts );
1354 for (i = 0; i < nAcls; i++, acList = acList->next) {
1355 gSendInt( acList->entries );
1356 gSendInt( acList->nentries );
1357 gSendInt( acList->hosts );
1358 gSendInt( acList->nhosts );
1359 gSendInt( acList->flags );
1362 #endif
1365 int main( int argc ATTR_UNUSED, char **argv )
1367 DSpec dspec;
1368 ValArr va;
1369 char *ci, *disp, *dcls, *cfgfile;
1370 int what;
1372 if (!(ci = getenv( "CONINFO" ))) {
1373 fprintf( stderr, "This program is part of kdm and should not be run manually.\n" );
1374 return 1;
1376 if (sscanf( ci, "%d %d", &rfd, &wfd ) != 2)
1377 return 1;
1379 InitLog();
1381 if ((debugLevel = gRecvInt()) & DEBUG_WCONFIG)
1382 sleep( 100 );
1384 /* debug ("parsing command line\n");*/
1385 if (**++argv)
1386 kdmrc = *argv;
1388 while (*++argv) {
1392 for (;;) {
1393 /* debug ("Awaiting command ...\n");*/
1394 if (!gRecvCmd( &what ))
1395 break;
1396 switch (what) {
1397 case GC_Files:
1398 /* debug ("GC_Files\n");*/
1399 readConfig();
1400 copyValues( 0, &secGeneral, 0, C_CONFIG );
1401 #ifdef XDMCP
1402 copyValues( 0, &secXdmcp, 0, C_CONFIG );
1403 gSendInt( 2 );
1404 #else
1405 gSendInt( 1 );
1406 #endif
1407 gSendStr( kdmrc );
1408 gSendInt( -1 );
1409 #ifdef XDMCP
1410 gSendNStr( VXaccess.str.ptr, VXaccess.str.len - 1 );
1411 gSendInt( 0 );
1412 #endif
1413 for (; (what = gRecvInt()) != -1; )
1414 switch (what) {
1415 case GC_gGlobal:
1416 case GC_gDisplay:
1417 gSendInt( 0 );
1418 break;
1419 #ifdef XDMCP
1420 case GC_gXaccess:
1421 gSendInt( 1 );
1422 break;
1423 #endif
1424 default:
1425 gSendInt( -1 );
1426 break;
1428 break;
1429 case GC_GetConf:
1430 /* debug( "GC_GetConf\n" );*/
1431 memset( &va, 0, sizeof(va) );
1432 what = gRecvInt();
1433 cfgfile = gRecvStr();
1434 switch (what) {
1435 case GC_gGlobal:
1436 /* debug( "GC_gGlobal\n" );*/
1437 debug( "getting global config\n" );
1438 readConfig();
1439 copyValues( &va, &secGeneral, 0, 0 );
1440 #ifdef XDMCP
1441 copyValues( &va, &secXdmcp, 0, 0 );
1442 #endif
1443 copyValues( &va, &secShutdown, 0, 0 );
1444 sendValues( &va );
1445 break;
1446 case GC_gDisplay:
1447 /* debug( "GC_gDisplay\n" );*/
1448 disp = gRecvStr();
1449 /* debug( " Display %s\n", disp );*/
1450 dcls = gRecvStr();
1451 /* debug( " Class %s\n", dcls );*/
1452 debug( "getting config for display %s, class %s\n", disp, dcls );
1453 mkDSpec( &dspec, disp, dcls ? dcls : "" );
1454 readConfig();
1455 copyValues( &va, &sec_Core, &dspec, 0 );
1456 copyValues( &va, &sec_Greeter, &dspec, 0 );
1457 free( disp );
1458 if (dcls)
1459 free( dcls );
1460 sendValues( &va );
1461 break;
1462 #ifdef XDMCP
1463 case GC_gXaccess:
1464 readAccessFile( cfgfile );
1465 break;
1466 #endif
1467 default:
1468 debug( "Unsupported config category %#x\n", what );
1470 free( cfgfile );
1471 break;
1472 default:
1473 debug( "Unknown config command %#x\n", what );
1477 /* debug( "Config reader exiting ..." );*/
1478 return EX_NORMAL;