chiark / gitweb /
23958373ddb563a8db860bd90a90702bf946ca42
[userv.git] / parser.c
1 /*
2  * userv - parser.c
3  * configuration file parser; this file is actually #included from
4  * lexer.c, which is generated using flex from lexer.l, in turn from
5  * lexer.l.m4.  It's in a separate file so that we don't have to worry
6  * about m4 quoting &c.
7  *
8  * Copyright (C)1996-1997 Ian Jackson
9  *
10  * This is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with userv; if not, write to the Free Software
22  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  */
24
25 struct parser_state {
26   int lineno, atnewline, notedreferer;
27   const char *filename;
28   YY_BUFFER_STATE ybuf;
29   struct parser_state *upstate;
30 };
31
32 static const char *currentfile= 0;
33 static struct parser_state *parser_topstate= 0;
34
35 static directive_fnt *lr_dir=0;
36 static parmcondition_fnt *lr_parmcond=0;
37 static parameter_fnt *lr_parameter=0;
38 static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
39 static int lr_flagval, lr_controlend;
40 static int lr_fdwant_readwrite;
41
42 static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ }
43
44 static void closeerrorfile(void) {
45   if (ehfile && !ehfilekeep) { fclose(ehfile); free(ehfilename); }
46   ehfile= 0; ehfilename= 0; ehfilekeep= 0;
47 }
48
49 static void senderrmsg(const char *errmsg, int useehandling) {
50   char suberrmsg[MAX_ERRMSG_LEN];
51   int e;
52   time_t now;
53   struct tm *lt;
54
55   switch (useehandling) {
56   case tokv_word_errorstostderr:
57     senderrmsgstderr(errmsg);
58     break;
59   case tokv_word_errorstosyslog:
60     ensurelogopen(ehlogfacility);
61     syslog(ehloglevel,"%s",errmsg);
62     break;
63   case tokv_word_errorstofile:
64     if (time(&now)==-1) syscallerror("get current time");
65     lt= localtime(&now); if (!lt) syscallerror("convert current time");
66     if (fprintf(ehfile,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n",
67                 lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday,
68                 lt->tm_hour, lt->tm_min, lt->tm_sec,
69                 errmsg) == EOF) {
70       e= errno;
71       closeerrorfile(); ehandling= tokv_word_errorstofile;
72       snyprintf(suberrmsg,sizeof(suberrmsg),
73                 "error writing to error log file `%.*s': %s;"
74                 " reverting to errors-to-stderr",
75                 (int)(sizeof(suberrmsg)>>1),ehfilename,strerror(e));
76       senderrmsg(suberrmsg,ehandling);
77       senderrmsg(errmsg,ehandling);
78     }
79     break;
80   default:
81     abort();
82   }
83 }
84
85 static void errwhere(int iflineno, const char *filename, int *ifnotedreferer,
86                      struct parser_state *upstate, char *bufput, int bufputlen) {
87   static const char suffix[]= "references ...";
88   char errmsg[MAX_ERRMSG_LEN];
89   
90   if (!upstate) {
91     filename= "<initialisation>";
92   } else if (!*ifnotedreferer && upstate->upstate && upstate->upstate->upstate) {
93     errwhere(upstate->lineno-upstate->atnewline,upstate->filename,
94              &upstate->notedreferer,upstate->upstate,
95              errmsg,sizeof(errmsg)-sizeof(suffix));
96     strcat(errmsg,suffix);
97     senderrmsg(errmsg,ehandling);
98     *ifnotedreferer= 1;
99   }
100   snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,filename,iflineno);
101 }
102
103 void parseerrprint(const char *fmt, ...) {
104   va_list al;
105   char errmsg[MAX_ERRMSG_LEN];
106
107   va_start(al,fmt);
108   errwhere(lineno-atnewline,currentfile,&notedreferer,parser_topstate,
109            errmsg,sizeof(errmsg)>>1);
110   vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
111   senderrmsg(errmsg,ehandling);
112   va_end(al);
113 }
114
115 static void freecharparray(char **array) {
116   char **pp;
117
118   if (!array) return;
119   for (pp=array; *pp; pp++) free(*pp);
120   free(array);
121 }
122
123 static int dequote(char *inplace) {
124   char *p, *q, buf[4], *bep;
125   int v;
126   
127   p=q=inplace;
128   assert(*p++ = '"');
129   while (*p && *p != '"') {
130     if (*p != '\\') { *q++= *p++; continue; }
131     switch (*++p) {
132     case 'n': *q++= '\n'; continue;
133     case 'r': *q++= '\r'; continue;
134     case 't': *q++= '\t'; continue;
135     case 'x':
136       assert(buf[0]= *++p); assert(buf[1]= *++p); buf[2]= 0;
137       v= strtoul(buf,&bep,16); assert(bep == buf+2);
138       assert(!(v & ~0xff)); *q++= v; p++; continue;
139     default:
140       if (isalpha(*p)) {
141         parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
142         return tokv_error;
143       } else if (isdigit(*p)) {
144         assert(buf[0]= *++p); assert(buf[1]= *++p); assert(buf[2]= *++p);
145         buf[3]= 0; v= strtoul(buf,&bep,8);
146         if (bep != buf+3 || (v & ~0xff)); {
147           parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
148           return tokv_error;
149         }
150         *q++= v; p++; continue;
151       } else if (ispunct(*p)) {
152         *q++= *p++; continue;
153       } else {
154         while (*p==' ' || *p=='\t') p++;
155         assert(*p=='\n');
156       }
157     }
158   }
159   assert(*p); assert(!*++p); return tokv_quotedstring;
160 }
161
162 const char *printtoken(int token) {
163   static char buf[250];
164   
165   char *q;
166   const char *p;
167   int i, c;
168   
169   if ((token & tokm_repres) == tokr_word) {
170     assert(strlen(yytext)+50<sizeof(buf));
171     sprintf(buf,"word `%s'",yytext);
172     return buf;
173   } else if (token & tokt_number) {
174     sprintf(buf,"number %d",lr_min); return buf;
175   } else if (token & tokt_fdrange) {
176     sprintf(buf,"fdrange %d..%d",lr_min,lr_max); return buf;
177   } else if ((token & tokm_repres) == tokr_punct) {
178     assert(strlen(yytext)+50<sizeof(buf));
179     sprintf(buf,"operator `%s'",yytext);
180     return buf;
181   } else if (token & tokt_string) {
182     switch (token) {
183     case tokv_barestring: strcpy(buf,"unquoted string (bare word)"); break;
184     case tokv_quotedstring: strcpy(buf,"quoted string"); break;
185     default: abort();
186     }
187     strcat(buf," `");
188     i= sizeof(buf)-50; p= yytext; q= buf+strlen(buf);
189     while (i-- >0 && (c= *p++)) {
190       if (isspace(c)) c= ' ';
191       else if (!isprint(c) || iscntrl(c)) c= '?';
192       *q++= c;
193     }
194     strcpy(q,"'");
195     return buf;
196   } else {
197     switch (token) {
198     case tokv_lwsp:    return "linear whitespace";
199     case tokv_newline: return "newline (or comment followed by newline)";
200     case tokv_eof:     return "end of input file";
201     case tokv_error:   return "syntax error token";
202     default:
203       sprintf(buf,"token#0%o",token); return buf;
204     }
205   }
206 }
207
208 static const char *string2path(const char *in) {
209   static char *p=0;
210   static int pl=0;
211   
212   int l;
213   
214   if (strncmp(in,USERDIRPREFIX,sizeof(USERDIRPREFIX)-1)) return in;
215   l= strlen(serviceuser_dir)+strlen(in)+1-(sizeof(USERDIRPREFIX)-1)+
216      sizeof(DIRSEP)-1;
217   if (l>pl) { p= realloc(p,l); pl=l; }
218   strcpy(p,serviceuser_dir); strcat(p,DIRSEP);
219   strcat(p,in+(sizeof(USERDIRPREFIX)-1));
220   return p;
221 }
222
223 static void parser_push(struct parser_state *saveinto,
224                         const char *newfile) {
225   saveinto->lineno= lineno;
226   saveinto->atnewline= atnewline;
227   saveinto->filename= currentfile;
228   saveinto->notedreferer= notedreferer;
229   saveinto->ybuf= YY_CURRENT_BUFFER;
230   saveinto->upstate= parser_topstate;
231
232   lineno= 1;
233   notedreferer= 0;
234   currentfile= newfile;
235   parser_topstate= saveinto;
236 }
237
238 static void parser_pop(void) {
239   YY_BUFFER_STATE ybuf;
240   ybuf= YY_CURRENT_BUFFER;
241   
242   lineno= parser_topstate->lineno;
243   atnewline= parser_topstate->atnewline;
244   currentfile= parser_topstate->filename;
245   notedreferer= parser_topstate->notedreferer;
246   if (parser_topstate->ybuf) yy_switch_to_buffer(parser_topstate->ybuf);
247   parser_topstate= parser_topstate->upstate;
248   
249   yy_delete_buffer(ybuf);
250 }
251
252 /* parser component functions pa_ return tokv_error or 0,
253  *  having scanned exactly what they were expecting
254  * complete argument list parsers paa_ return tokv_error or 0,
255  *  having scanned up to and including the newline
256  */
257
258 static int unexpected(int found, int wanted, const char *wantedstr) {
259   /* pass wanted==-1 if you know it's not what you wanted;
260    * otherwise this function will check it for you.
261    */
262   if (found == wanted) return 0;
263   if (found != tokv_error) {
264     parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
265   }
266   return tokv_error;
267 }
268
269 static int pa_mnl(void) {
270   return unexpected(yylex(),tokv_newline,"newline");
271 }
272 static int pa_mwsp(void) {
273   return unexpected(yylex(),tokv_lwsp,"linear whitespace");
274 }
275
276 static void parm_1string(char ***rvalues, const char *tocopy) {
277   char **a;
278   a= xmalloc(sizeof(char*)*2);
279   a[0]= xstrdup(tocopy);
280   a[1]= 0;
281   *rvalues= a;
282 }
283
284 static int pa_string(const char **rv) {
285   /* Value returned in *rv is overwritten by repeated calls */
286   static char *p= 0;
287   static int pl= 0;
288
289   int r, l;
290
291   r= pa_mwsp(); if (r) return r;
292   r= yylex(); if (r == tokv_error) return r;
293   if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string");
294   l= strlen(yytext)+1;
295   makeroom(&p,&pl,l);
296   strcpy(p,yytext); *rv= p;
297
298   return 0;
299 }  
300
301 static int paa_1string(const char **rv) {
302   /* Value returned in *rv is overwritten by repeated calls */
303   int r;
304
305   r= pa_string(rv); if (r) return r;
306   return pa_mnl();
307 }  
308
309 static int paa_1path(const char **rv) {
310   /* Value returned in *rv is overwritten by repeated calls */
311   const char *cp;
312   int r;
313   
314   r= paa_1string(&cp); if (r) return r;
315   *rv= string2path(cp); return 0;
316 }
317
318 static int pa_parameter(char ***rvalues) {
319   /* Scans a single parameter token and calls the appropriate parameter
320    * function, returning tokv_error or 0 just like the parameter function.
321    */
322   int token, r, i;
323
324   token= yylex(); if (token == tokv_error) return token;
325   if ((token & tokm_repres) != tokr_nonstring &&
326       !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
327     for (i=0;
328          i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i][0]);
329          i++);
330     if (i>=request_mbuf.nvars) {
331       *rvalues= xmalloc(sizeof(char*));
332       **rvalues= 0;
333     } else {
334       parm_1string(rvalues,defvararray[i][1]);
335     }
336   } else {
337     if (!(token & tokt_parameter)) return unexpected(token,-1,"parameter name");
338     r= (lr_parameter)(token,rvalues);
339     if (r) return r;
340   }
341   debug_dumpparameter(yytext,*rvalues);
342   return 0;
343 }
344
345 static int pa_condition(int *rtrue) {
346   /* Scans up to and including the newline following the condition;
347    * may scan more than one line if the condition is a multi-line
348    * one.  Returns 0 (setting *rtrue) or tokv_error.  Expects to
349    * scan the whitespace before its condition.
350    */
351   int r, token, andor, ctrue, actrue;
352   char **parmvalues;
353
354   r= pa_mwsp(); if (r) return r;
355   token= yylex();
356   if (token == tokv_error) {
357     return token;
358   } else if (token == tokv_not) {
359     r= pa_condition(&ctrue); if (r) return r;
360     *rtrue= !ctrue; return 0;
361   } else if (token == tokv_openparen) {
362     andor= 0; actrue= -1;
363     for (;;) {
364       r= pa_condition(&ctrue); if (r) return r;
365       switch (andor) {
366       case 0:         assert(actrue==-1); actrue= ctrue;           break;
367       case tokv_and:  assert(actrue>=0); actrue= actrue && ctrue;  break;
368       case tokv_or:   assert(actrue>=0); actrue= actrue || ctrue;  break;
369       default:        abort();
370       }
371       do { token= yylex(); } while (token == tokv_lwsp);
372       if (token == tokv_error) return token;
373       if (token == tokv_closeparen) break;
374       if (andor) {
375         r= unexpected(token,andor,"same conjunction as before"); if (r) return r;
376       } else {
377         if (token != tokv_and && token != tokv_or)
378           return unexpected(token,-1,"first conjunction inside connective");
379         andor= token;
380       }
381     }
382     r= pa_mnl(); if (r) return r;
383     *rtrue= actrue; return 0;
384   } else if (token & tokt_parmcondition) {
385     r= pa_mwsp(); if (r) return r;
386     r= pa_parameter(&parmvalues); if (r) return r;
387     r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
388     return r;
389   }
390   return unexpected(token,-1,"condition");
391 }
392
393 static int pa_numberdollar(int *rv) {
394   /* Also parses the whitespace beforehand. */
395   int r;
396
397   r= pa_mwsp(); if (r) return r;
398   r= yylex(); if (r == tokv_error || r == tokv_dollar) return r;
399   if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error;
400   *rv= lr_min; return r;
401 }
402
403 /* Main parser routines */
404
405 static int skiptoeol(void) {
406   int token;
407
408   do { token= yylex(); }
409   while (token != tokv_newline && !(token & tokt_exception));
410   if (token == tokv_newline) return 0;
411   if (token == tokv_error) return token;
412   assert(token == tokv_eof);
413   parseerrprint("unexpected end of file while looking for end of line");
414   return tokv_error;
415 }
416
417 static int skip(int allowce) {
418   /* Scans a piece of config without executing it.
419    * Returns tokv_error, or the tokt_controlend token
420    * with type allowce if one was found.
421    */
422   int token, r;
423
424   for (;;) { /* loop over lines */
425     do { token= yylex(); } while (token == tokv_lwsp);
426     if (token & tokt_exception) {
427       return token;
428     } else if (token & tokt_controlend) {
429       if (allowce == lr_controlend) return token;
430       return unexpected(token,-1,"control structure end of a different kind");
431     } else if (token & tokt_controlstart) {
432       r= token;
433       while (r & tokt_controlstart) {
434         r= skiptoeol(); if (r) return r;
435         r= skip(token); if (r & tokt_exception) return r;
436       }
437     } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
438       parseerrprint("not a directive (or conditional operator) "
439                     "while looking for control structure end");
440       return tokv_error;
441     }
442     r= skiptoeol(); if (r) return r;
443   }
444 }
445       
446 static int parser(int allowce) {
447   /* Returns:
448    *  an exception (error, eof or quit)
449    *   then rest of `file' is uninteresting
450    * or
451    *  token if allowce was !0 and equal to token's controlend
452    *   then rest of `file' not scanned yet
453    */
454   int token, r;
455
456   for (;;) { /* loop over lines */
457     do { token= yylex(); } while (token == tokv_lwsp);
458     if (token & tokt_exception) {
459       return token;
460     } else if (token & tokt_controlend) {
461       if (lr_controlend == allowce) return token;
462       return unexpected(token,-1,"directive (not this kind of control structure end)");
463     } else if (token & tokt_directive) {
464       r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; }
465     } else if (token == tokv_newline) {
466       /* ignore blank links (and comment-only lines) */
467     } else {
468       return unexpected(token,-1,"directive");
469     }
470   }
471 }
472
473 int parse_string(const char *string, const char *descrip) {
474   /* Returns the same things as parser, except that tokv_eof
475    * is turned into 0. */
476   struct parser_state save;
477   YY_BUFFER_STATE ybuf;
478   int r;
479   
480   parser_push(&save,descrip);
481   ybuf= yy_scan_string(string);
482   if (!ybuf) syscallerror("unable to create flex buffer for internal string");
483   yy_switch_to_buffer(ybuf);
484   
485   r= parser(0);
486
487   parser_pop();
488   if (r == tokv_eof) r= 0;
489   return r;
490 }
491
492 static int parse_file(const char *string, int *didexist) {
493   /* Returns the same things as parser, except that tokv_eof
494    * is turned into 0. */
495   static int fileparselevel= 0;
496   
497   struct parser_state save;
498   YY_BUFFER_STATE ybuf;
499   int r;
500   FILE *file;
501
502   if (fileparselevel >= MAX_INCLUDE_NEST) {
503     parseerrprint("too many nested levels of included files");
504     return tokv_error;
505   }
506   file= fopen(string,"r"); if (!file) {
507     if (errno == ENOENT) {
508       if (didexist) *didexist= 0;
509       return 0;
510     }
511     parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
512     return tokv_error;
513   }
514   if (didexist) *didexist= 1;
515
516   parser_push(&save,string);
517   ybuf= yy_create_buffer(file,YY_BUF_SIZE);
518   if (!ybuf) syscallerror("unable to create flex buffer for file");
519   yy_switch_to_buffer(ybuf);
520   fileparselevel++;
521   
522   r= parser(0);
523   if (ferror(file)) {
524     parseerrprint("error reading configuration file `%s'",string);
525     r= tokv_error;
526   }
527   
528   fileparselevel--;
529   parser_pop();
530   fclose(file);
531   if (r == tokv_eof) r= 0;
532   return r;
533 }
534
535 /* Parameter-based conditional functions (parmcondition's) */
536
537 int pcf_glob(int ctoken, char **pv, int *rtrue) {
538   int token, actrue, r;
539   char **pp;
540
541   actrue= 0;
542   r= pa_mwsp(); if (r) return r;
543   for (;;) {
544     token= yylex();
545     if ((token & tokm_repres) == tokr_nonstring)
546       return unexpected(token,-1,"glob pattern");
547     for (pp= pv; !actrue && *pp; pp++) if (!fnmatch(yytext,*pp,0)) actrue= 1;
548     token= yylex(); 
549     if (token == tokv_newline) break;
550     if (unexpected(token,tokv_lwsp,"newline after or whitespace between glob patterns"))
551       return tokv_error;
552   }
553   *rtrue= actrue;
554   return 0;
555 }
556
557 int pcf_range(int ctoken, char **pv, int *rtrue) {
558   int mintoken, min, maxtoken, max, r;
559   char **pp, *ep;
560   unsigned long v;
561
562   r= pa_mwsp(); if (r) return r;
563   mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
564   r= pa_mwsp(); if (r) return r;
565   maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
566   r= pa_mnl(); if (r) return r;
567   for (pp= pv; *pp; pp++) {
568     v= strtoul(*pp,&ep,10); if (*ep) continue;
569     if (mintoken != tokv_dollar && v < min) continue;
570     if (maxtoken != tokv_dollar && v > max) continue;
571     *rtrue= 1; return 0;
572   }
573   *rtrue= 0; return 0;
574 }
575
576 int pcf_grep(int ctoken, char **pv, int *rtrue) {
577   FILE *file;
578   const char *cp;
579   char **pp, *buf, *p;
580   int r, maxlen, l, c, actrue, posstrue;
581   
582   r= paa_1path(&cp); if (r) return r;
583   file= fopen(cp,"r"); if (!file) {
584     parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
585     return tokv_error;
586   }
587   maxlen= 0;
588   for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
589   buf= xmalloc(maxlen+2); actrue= 0; c= 0;
590   while (!actrue && c!=EOF) {
591     c= getc(file); if (c==EOF) break;
592     if (isspace(c)) continue;
593     l= maxlen+1; p= buf;
594     while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); } 
595     if (c=='\n' || c==EOF || isspace(c)) {
596       while (p>buf && isspace(p[-1])) --p;
597       *p= 0; posstrue= 0;
598       for (pp= pv; *pp; pp++) if (!strcmp(*pp,buf)) { posstrue= 1; break; }
599     } else {
600       posstrue= 0;
601     }
602     if (c!='\n' && c!=EOF) {
603       for (;;) {
604         c= getc(file);
605         if (c==EOF || c=='\n') break;
606         if (!isspace(c)) posstrue= 0;
607       }
608     }
609     if (posstrue) actrue= 1;
610   }
611   if (ferror(file)) {
612     parseerrprint("error while reading `%s' for grep: %s",cp,strerror(errno));
613     fclose(file); free(buf); return tokv_error;
614   }
615   assert(actrue || feof(file));
616   fclose(file); free(buf); *rtrue= actrue; return 0;
617
618
619 /* Parameter functions and associated `common code' functions */
620
621 int pf_service(int ptoken, char ***rvalues) {
622   parm_1string(rvalues,service); return 0;
623 }
624
625 static char *parm_ulong(unsigned long ul) {
626   char *p;
627   int l;
628
629   l= CHAR_BIT*sizeof(unsigned long)/3+4;
630   p= xmalloc(l);
631   snyprintf(p,l,"%lu",ul);
632   return p;
633 }
634
635 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
636   char **a;
637   a= xmalloc(sizeof(char*)*3);
638   a[0]= xstrdup(name);
639   a[1]= parm_ulong(id);
640   a[2]= 0;
641   *rvalues= a; return 0;
642 }
643
644 int pf_callinguser(int ptoken, char ***rvalues) {
645   return parm_usernameuid(rvalues,logname,request_mbuf.callinguid);
646 }
647
648 int pf_serviceuser(int ptoken, char ***rvalues) {
649   return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
650 }
651
652 static char *parm_gidname(gid_t id) {
653   static char ebuf[200];
654
655   struct group *ge;
656
657   ge= getgrgid(id);
658   if (!ge) {
659     sprintf(ebuf,"look up group with id %lu",(unsigned long)id);
660     syscallerror(ebuf);
661   }
662   return xstrdup(ge->gr_name);
663 }
664
665 static int parm_gids(char ***rvalues, int size, gid_t *list) {
666   char **a;
667   int i;
668   
669   if (size >= 2 && list[0] == list[1]) { size--; list++; }
670   a= xmalloc(sizeof(char*)*(size+1)*2);
671   for (i=0; i<size; i++) {
672     a[i]= parm_gidname(list[i]);
673     a[size+i]= parm_ulong(list[i]);
674   }
675   a[size*2]= 0;
676   *rvalues= a; return 0;
677 }  
678
679 int pf_callinggroup(int ptoken, char ***rvalues) {
680   return parm_gids(rvalues,request_mbuf.ngids,gidarray);
681 }
682
683 int pf_servicegroup(int ptoken, char ***rvalues) {
684   static int size=-1;
685   static gid_t *list;
686
687   if (size == -1) {
688     size= getgroups(0,0); if (size == -1) syscallerror("getgroups(0,0)");
689     list= xmalloc(sizeof(gid_t)*(size+1));
690     if (getgroups(size,list+1) != size) syscallerror("getgroups(size,list)");
691     list[0]= serviceuser_gid;
692   }
693   return parm_gids(rvalues,size,list);
694 }
695
696 int pf_callingusershell(int ptoken, char ***rvalues) {
697   parm_1string(rvalues,callinguser_shell); return 0;
698 }
699
700 int pf_serviceusershell(int ptoken, char ***rvalues) {
701   parm_1string(rvalues,serviceuser_shell); return 0;
702 }
703
704 /* Directive functions and associated `common code' functions */
705
706 int df_reject(int dtoken) {
707   int r;
708   
709   r= pa_mnl(); if (r) return r;
710   execute= tokv_word_reject;
711   free(execpath); execpath= 0;
712   freecharparray(execargs); execargs= 0;
713   return 0;
714 }
715
716 int df_executefrompath(int dtoken) {
717   int r;
718   
719   r= pa_mnl(); if (r) return r;
720   execute= tokv_word_executefrompath;
721   free(execpath); execpath= 0;
722   freecharparray(execargs); execargs= 0;
723   return 0;
724 }
725
726 static int paa_pathargs(const char **rv, char ***newargs_r) {
727   /* Repeated calls do _not_ overwrite newargs_r; caller must free.
728    * Repeated calls _do_ overwrite string returned in rv.
729    */
730   char **newargs;
731   int used, size, r;
732
733   used=0; size=0;
734   newargs= xmalloc(sizeof(char*)*(size+1));
735   r= pa_string(rv); if (r == tokv_error) return r;
736   for (;;) {
737     r= yylex(); if (r == tokv_error) goto error;
738     if (r==tokv_newline) break;
739     if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
740       r= tokv_error;
741       goto error;
742     }
743     r= yylex(); if (r==tokv_error) goto error;
744     if ((r & tokm_repres) == tokr_nonstring) {
745       r= unexpected(r,-1,"string for command argument");
746       goto error;
747     }
748     if (used>=size) {
749       size= (used+5)<<2;
750       newargs= xrealloc(newargs,sizeof(char*)*(size+1));
751     }
752     newargs[used++]= xmstrsave(yytext);
753   }
754   newargs[used]= 0;
755   *newargs_r= newargs;
756   return 0;
757   
758 error:
759   newargs[used]=0;
760   freecharparray(newargs);
761   return r;
762 }
763
764 int df_execute(int dtoken) {
765   const char *rv;
766   char **newargs;
767   int r;
768
769   r= paa_pathargs(&rv,&newargs); if (r) return r;
770   execute= tokv_word_execute;
771   freecharparray(execargs); execargs= newargs;
772   free(execpath); execpath= xmstrsave(rv);
773   return 0;
774 }
775
776 int df_executefromdirectory(int dtoken) {
777   const char *p, *q, *rv;
778   struct stat stab;
779   char *fn, **newargs;
780   int l, r;
781
782   r= paa_pathargs(&rv,&newargs); if (r) return r;
783   p= strrchr(service,'/'); if (p) p++; else p= service;
784   if (!*p || !isalnum(*p)) {
785     parseerrprint("execute-from-directory requires initial char of service "
786                   "portion to be alphanumeric (service portion was `%s')",
787                   p);
788     freecharparray(newargs);
789     return tokv_error;
790   }
791   for (q=p+1; *q; q++) {
792     if (!isalnum(*q) && *q != '-') {
793       parseerrprint("execute-from-directory requires service portion to "
794                     "contain only alphanumerics and hyphens (was `%s')",
795                     p);
796       freecharparray(newargs);
797       return tokv_error;
798     }
799   }
800   l= strlen(rv)+sizeof(DIRSEP)+strlen(p);
801   fn= xmalloc(l); strcpy(fn,rv); strcat(fn,DIRSEP); strcat(fn,p);
802   if (stat(fn,&stab)) {
803     if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
804     parseerrprint("failed to stat `%s' for execute-from-directory: %s",
805                   fn,strerror(errno));
806     free(fn); freecharparray(newargs); return tokv_error;
807   }
808   if (!S_ISREG(stab.st_mode)) {
809     parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
810                   " or link to one (mode=0%o)",fn,stab.st_mode);
811     free(fn); freecharparray(newargs); return tokv_error;
812   }
813   execute= tokv_word_executefromdirectory;
814   freecharparray(execargs); execargs= newargs;
815   free(execpath); execpath= fn;
816   return 0;
817 }
818
819 int df_errorstostderr(int dtoken) {
820   int r;
821   
822   r= pa_mnl(); if (r) return r;
823   closeerrorfile(); ehandling= dtoken; return 0;
824 }
825
826 int df_errorstosyslog(int dtoken) {
827   int token, level, facility;
828
829   facility= DEFUSERLOGFACILITY;
830   level= DEFUSERLOGLEVEL;
831   token= yylex();
832   if (token == tokv_lwsp) {
833     token= yylex(); if (token == tokv_error) return token;
834     if (!(token & tokt_logfacility))
835       return unexpected(token,-1,"syslog facility (or end of line)");
836     facility= lr_logfacility;
837     token= yylex();
838   }    
839   if (token == tokv_lwsp) {
840     token= yylex(); if (token == tokv_error) return token;
841     if (!(token & tokt_loglevel))
842       return unexpected(token,-1,"syslog level (or end of line) after facility");
843     level= lr_loglevel;
844     token= yylex();
845   }
846   if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog"))
847     return tokv_error;
848   closeerrorfile(); ehandling= tokv_word_errorstosyslog;
849   ehlogfacility= facility; ehloglevel= level; return 0;
850 }
851
852 int df_errorstofile(int dtoken) {
853   const char *cp;
854   FILE *file;
855   int r;
856   
857   r= paa_1path(&cp); if (r) return r;
858   file= fopen(cp,"a");
859   if (!file) {
860     parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
861     return tokv_error;
862   }
863   if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
864     parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
865     fclose(file); return tokv_error;
866   }
867   closeerrorfile(); ehandling= tokv_word_errorstofile;
868   ehfile= file; ehfilename= xmstrsave(cp); ehfilekeep= 0; return 0;
869 }
870
871 int dfg_setflag(int dtoken) {
872   int r;
873
874   r= pa_mnl(); if (r) return r;
875   *lr_flag= lr_flagval; return 0;
876 }
877
878 int df_reset(int dtoken) {
879   int r;
880
881   r= pa_mnl(); if (r) return r;
882   r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>");
883   assert(!r); return 0;  
884 }
885
886 int df_cd(int dtoken) {
887   const char *cp;
888   int r;
889
890   r= paa_1path(&cp); if (r) return r;
891   if (!chdir(cp)) return 0;
892   parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
893   return tokv_error;
894 }
895
896 int df_userrcfile(int dtoken) {
897   const char *cp;
898   int r;
899
900   r= paa_1path(&cp); if (r) return r;
901   free(userrcfile); userrcfile= xstrdup(cp);
902   return 0;
903 }
904
905 int dfi_includeuserrcfile(int dtoken) {
906   int r;
907
908   r= pa_mnl(); if (r) return r;
909   if (userrcfile) return parse_file(userrcfile,0);
910   parseerrprint("_include-user-rcfile (for-internal-use directive) "
911                 "found but user-rcfile not set"); return tokv_error;
912 }
913
914 int dfi_includeclientconfig(int dtoken) {
915   int r;
916
917   r= pa_mnl(); if (r) return r;
918   if (!overridedata) {
919     parseerrprint("_include-client-config (for-internal-use directive) "
920                   "found but configuration not overridden");
921     return tokv_error;
922   }
923   return parse_string(overridedata,"<configuration override data>");
924 }
925
926 int df_include(int dtoken) {
927   const char *cp;
928   int r, found;
929
930   r= paa_1path(&cp); if (r) return r;
931   r= parse_file(cp,&found); if (r) return r;
932   if (found || dtoken == tokv_word_includeifexist) return 0;
933   parseerrprint(dtoken == tokv_word_includesysconfig ?
934                 "system configuration file `%s' does not exist" :
935                 "included file `%s' does not exist",
936                 cp);
937   return tokv_error;
938 }
939
940 static int paa_message(const char **message_r) {
941   static char *buildbuf;
942   static int buildbuflen;
943
944   int r, tl;
945
946   r= pa_mwsp(); if (r) return r;
947   tl= 1;
948   makeroom(&buildbuf,&buildbuflen,10);
949   buildbuf[0]= 0;
950   for (;;) {
951     r= yylex(); if (r == tokv_error) return r;
952     if (r == tokv_eof) {
953       parseerrprint("unexpected end of file in message text"); return tokv_error;
954     }
955     if (r == tokv_newline) break;
956     tl+= strlen(yytext);
957     makeroom(&buildbuf,&buildbuflen,tl);
958     strcat(buildbuf,yytext);
959   }
960   *message_r= buildbuf;
961   return 0;
962 }
963
964 int df_message(int dtoken) {
965   const char *mp;
966   int r;
967
968   r= paa_message(&mp); if (r) return r;
969   parseerrprint("`message' directive: %s",mp);
970   return 0;
971 }
972
973 int df_error(int dtoken) {
974   const char *mp;
975   int r;
976
977   r= paa_message(&mp); if (r) return r;
978   parseerrprint("`error' directive: %s",mp); return tokv_error;
979 }
980
981 int df_includedirectory(int dtoken) {
982   static char *buildbuf=0;
983   static int buildbuflen=0;
984   
985   int r, cpl, tel, c, found;
986   DIR *d;
987   struct dirent *de;
988   const char *p, *cp;
989   
990   r= paa_1path(&cp); if (r) return r;
991   d= opendir(cp);
992   if (!d) {
993     parseerrprint("unable to open directory `%s': %s",cp,strerror(errno));
994     return tokv_error;
995   }
996   cpl= strlen(cp);
997   while ((de= readdir(d))) {
998     tel= strlen(de->d_name);
999     if (!tel) continue;
1000     p= de->d_name;
1001     if (!isalnum(*p)) continue;
1002     while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
1003     if (c) continue;
1004     makeroom(&buildbuf,&buildbuflen,cpl+tel+sizeof(DIRSEP));
1005     strcpy(buildbuf,cp);
1006     strcat(buildbuf,DIRSEP);
1007     strcat(buildbuf,de->d_name);
1008     r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
1009     if (!found) {
1010       parseerrprint("unable to open file `%s' in included directory `%s': %s",
1011                     de->d_name,cp,strerror(errno));
1012       closedir(d); return tokv_error;
1013     }
1014   }
1015   if (closedir(d)) {
1016     parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1017     return tokv_error;
1018   }
1019   return 0;
1020 }
1021
1022 int df_includelookup(int dtoken) {
1023   static char *buildbuf=0;
1024   int buildbuflen=0;
1025   
1026   char **parmvalues, **pp, *p, *q;
1027   const char *cp;
1028   struct stat stab;
1029   int r, done, thisdone, cpl, c;
1030
1031   r= pa_mwsp(); if (r) return r;
1032   r= pa_parameter(&parmvalues); if (r) return r;
1033   r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
1034   if (stat(cp,&stab)) {
1035     parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
1036     freecharparray(parmvalues); return tokv_error;
1037   }
1038   if (!S_ISDIR(stab.st_mode)) {
1039     parseerrprint("object `%s' is not a directory or link to one",cp);
1040     freecharparray(parmvalues); return tokv_error;
1041   }
1042   done= 0;
1043   cpl= strlen(cp);
1044   if (!parmvalues[0]) {
1045     makeroom(&buildbuf,&buildbuflen,cpl+sizeof(DIRSEP NONEINCLUDELOOKUP));
1046     strcpy(buildbuf,cp);
1047     strcat(buildbuf,DIRSEP NONEINCLUDELOOKUP);
1048     r= parse_file(buildbuf,&thisdone);
1049     if (r) { freecharparray(parmvalues); return r; }
1050     if (thisdone) done= 1;
1051   } else {
1052     for (pp=parmvalues;
1053          *pp && (!done || dtoken == tokv_word_includelookupall);
1054          pp++) {
1055       makeroom(&buildbuf,&buildbuflen,
1056                cpl+sizeof(DIRSEP)+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP));
1057       strcpy(buildbuf,cp);
1058       strcat(buildbuf,DIRSEP);
1059       p= *pp; q= buildbuf+cpl+sizeof(DIRSEP)-1;
1060       if (*p=='.') *q++= ':';
1061       while ((c= *p++)) {
1062         if (c=='/') { *q++= ':'; c='-'; }
1063         else if (c==':') { *q++= ':'; }
1064         *q++= c;
1065       }
1066       *q++= 0;
1067       r= parse_file(buildbuf,&thisdone);
1068       if (r) { freecharparray(parmvalues); return r; }
1069       if (thisdone) done= 1;
1070     }
1071   }
1072   freecharparray(parmvalues);
1073   if (!done) {
1074     makeroom(&buildbuf,&buildbuflen,
1075              cpl+sizeof(DIRSEP)+sizeof(DEFAULTINCLUDELOOKUP));
1076     strcpy(buildbuf,cp);
1077     strcat(buildbuf,DIRSEP DEFAULTINCLUDELOOKUP);
1078     r= parse_file(buildbuf,0); if (r) return r;
1079   }
1080   return 0;
1081 }
1082
1083 int df_catchquit(int dtoken) {
1084   int r;
1085
1086   r= pa_mnl(); if (r) return r;
1087   r= parser(tokv_word_catchquit);
1088   if (r & tokt_controlend) {
1089     assert(r == tokv_word_hctac);
1090     r= pa_mnl();
1091   } else if (r == tokv_quit || r == tokv_error) {
1092     if (r == tokv_error) {
1093       r= parse_string(RESET_CONFIGURATION,
1094                       "<builtin reset configuration (caught error)>");
1095       assert(!r);
1096       while (!atnewline) {
1097         r= yylex(); if (r == tokv_error) return r;
1098       }
1099     }
1100     r= skip(tokv_word_catchquit);
1101     if (r & tokt_controlend) {
1102       assert(r == tokv_word_hctac);
1103       r= pa_mnl();
1104     }
1105   }
1106   return r;
1107 }
1108
1109 int df_if(int dtoken) {
1110   int r, true, done;
1111   
1112   done= 0;
1113   do {
1114     r= pa_condition(&true); if (r) return r;
1115     if (!done && true) { r= parser(tokv_word_if); done= 1; }
1116     else { r= skip(tokv_word_if); }
1117     if (!(r & tokv_word_if)) return r;
1118   } while (r == tokv_word_elif);
1119   if (r == tokv_word_else) {
1120     r= pa_mnl(); if (r) return r;
1121     if (done) r= skip(tokv_word_if);
1122     else r= parser(tokv_word_if);
1123     if (!(r & tokv_word_if)) return r;
1124   }
1125   if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1126   return pa_mnl();
1127 }
1128
1129 int dfg_fdwant(int dtoken) {
1130   int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
1131
1132   needreadwrite= lr_fdwant_readwrite;
1133   r= pa_mwsp(); if (r) return r;
1134   r= yylex(); if (r == tokv_error) return r;
1135   if (!(r & tokt_fdrange)) return r;
1136   fdmin= lr_min; fdmax= lr_max;
1137   r= yylex(); if (r == tokv_error) return r;
1138   if (r == tokv_newline) {
1139     if (needreadwrite > 0) {
1140       parseerrprint("read or write is required"); return tokv_error;
1141     }
1142     havereadwrite= 0;
1143   } else if (r == tokv_lwsp) {
1144     if (needreadwrite < 0) {
1145       parseerrprint("read or write not allowed"); return tokv_error;
1146     }
1147     r= yylex(); if (r == tokv_error) return r;
1148     if (!(r & tokt_readwrite))
1149       return unexpected(r,-1,"read or write (or perhaps newline)");
1150     havereadwrite= r;
1151     r= pa_mnl(); if (r) return r;
1152   } else {
1153     return unexpected(r,-1,"whitespace before read or write or newline");
1154   }
1155   ensurefdarray(fdmin);
1156   if (fdmax == -1) {
1157     if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
1158       parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd");
1159     fdmax= fdarrayused-1;
1160     restfdwantstate= dtoken;
1161     restfdwantrw= havereadwrite;
1162   }    
1163   for (fd=fdmin; fd<=fdmax; fd++) {
1164     fdarray[fd].wantstate= dtoken;
1165     fdarray[fd].wantrw= havereadwrite;
1166   }
1167   return 0;
1168 }
1169
1170 int df_quit(int dtoken) {
1171   int r;
1172
1173   r= pa_mnl(); if (r) return r;
1174   return tokv_quit;
1175 }
1176
1177 int df_eof(int dtoken) {
1178   int r;
1179
1180   r= pa_mnl(); if (r) return r;
1181   return tokv_eof;
1182 }
1183
1184 int df_errorspush(int dt) {
1185   int saveehandling, saveehlogfacility, saveehloglevel, saveehfilekeep;
1186   FILE *saveehfile;
1187   char *saveehfilename;
1188   int r;
1189
1190   r= pa_mnl(); if (r) return r;
1191
1192   saveehandling= ehandling;
1193   saveehlogfacility= ehlogfacility;
1194   saveehloglevel= ehloglevel;
1195   saveehfile= ehfile;
1196   saveehfilekeep= ehfilekeep;
1197   saveehfilename= ehfilename;
1198   if (ehandling == tokv_word_errorstofile) ehfilekeep= 1;
1199
1200   r= parser(tokv_word_errorspush);
1201   
1202   ehandling= saveehandling;
1203   ehlogfacility= saveehlogfacility;
1204   ehloglevel= saveehloglevel;
1205   ehfile= saveehfile;
1206   ehfilekeep= saveehfilekeep;
1207   ehfilename= saveehfilename;
1208
1209   if (r & tokt_controlend) {
1210     assert(r == tokv_word_srorre);
1211     r= pa_mnl();
1212   }
1213   return r;
1214 }