chiark / gitweb /
Makefile and minor updates.
[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   struct passwd *pw;
698
699   pw= getpwnam(logname); if (!pw) syscallerror("looking up calling user");
700   parm_1string(rvalues,pw->pw_shell); return 0;
701 }
702
703 int pf_serviceusershell(int ptoken, char ***rvalues) {
704   parm_1string(rvalues,serviceuser_shell); return 0;
705 }
706
707 /* Directive functions and associated `common code' functions */
708
709 int df_reject(int dtoken) {
710   int r;
711   
712   r= pa_mnl(); if (r) return r;
713   execute= tokv_word_reject;
714   free(execpath); execpath= 0;
715   freecharparray(execargs); execargs= 0;
716   return 0;
717 }
718
719 int df_executefrompath(int dtoken) {
720   int r;
721   
722   r= pa_mnl(); if (r) return r;
723   execute= tokv_word_executefrompath;
724   free(execpath); execpath= 0;
725   freecharparray(execargs); execargs= 0;
726   return 0;
727 }
728
729 static int paa_pathargs(const char **rv, char ***newargs_r) {
730   /* Repeated calls do _not_ overwrite newargs_r; caller must free.
731    * Repeated calls _do_ overwrite string returned in rv.
732    */
733   char **newargs;
734   int used, size, r;
735
736   used=0; size=0;
737   newargs= xmalloc(sizeof(char*)*(size+1));
738   r= pa_string(rv); if (r == tokv_error) return r;
739   for (;;) {
740     r= yylex(); if (r == tokv_error) goto error;
741     if (r==tokv_newline) break;
742     if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
743       r= tokv_error;
744       goto error;
745     }
746     r= yylex(); if (r==tokv_error) goto error;
747     if ((r & tokm_repres) == tokr_nonstring) {
748       r= unexpected(r,-1,"string for command argument");
749       goto error;
750     }
751     if (used>=size) {
752       size= (used+5)<<2;
753       newargs= xrealloc(newargs,sizeof(char*)*(size+1));
754     }
755     newargs[used++]= xmstrsave(yytext);
756   }
757   newargs[used]= 0;
758   *newargs_r= newargs;
759   return 0;
760   
761 error:
762   newargs[used]=0;
763   freecharparray(newargs);
764   return r;
765 }
766
767 int df_execute(int dtoken) {
768   const char *rv;
769   char **newargs;
770   int r;
771
772   r= paa_pathargs(&rv,&newargs); if (r) return r;
773   execute= tokv_word_execute;
774   freecharparray(execargs); execargs= newargs;
775   free(execpath); execpath= xmstrsave(rv);
776   return 0;
777 }
778
779 int df_executefromdirectory(int dtoken) {
780   const char *p, *q, *rv;
781   struct stat stab;
782   char *fn, **newargs;
783   int l, r;
784
785   r= paa_pathargs(&rv,&newargs); if (r) return r;
786   p= strrchr(service,'/'); if (p) p++; else p= service;
787   if (!*p || !isalnum(*p)) {
788     parseerrprint("execute-from-directory requires initial char of service "
789                   "portion to be alphanumeric (service portion was `%s')",
790                   p);
791     freecharparray(newargs);
792     return tokv_error;
793   }
794   for (q=p+1; *q; q++) {
795     if (!isalnum(*q) && *q != '-') {
796       parseerrprint("execute-from-directory requires service portion to "
797                     "contain only alphanumerics and hyphens (was `%s')",
798                     p);
799       freecharparray(newargs);
800       return tokv_error;
801     }
802   }
803   l= strlen(rv)+sizeof(DIRSEP)+strlen(p);
804   fn= xmalloc(l); strcpy(fn,rv); strcat(fn,DIRSEP); strcat(fn,p);
805   if (stat(fn,&stab)) {
806     if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
807     parseerrprint("failed to stat `%s' for execute-from-directory: %s",
808                   fn,strerror(errno));
809     free(fn); freecharparray(newargs); return tokv_error;
810   }
811   if (!S_ISREG(stab.st_mode)) {
812     parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
813                   " or link to one (mode=0%o)",fn,stab.st_mode);
814     free(fn); freecharparray(newargs); return tokv_error;
815   }
816   execute= tokv_word_executefromdirectory;
817   freecharparray(execargs); execargs= newargs;
818   free(execpath); execpath= fn;
819   return 0;
820 }
821
822 int df_errorstostderr(int dtoken) {
823   int r;
824   
825   r= pa_mnl(); if (r) return r;
826   closeerrorfile(); ehandling= dtoken; return 0;
827 }
828
829 int df_errorstosyslog(int dtoken) {
830   int token, level, facility;
831
832   facility= DEFUSERLOGFACILITY;
833   level= DEFUSERLOGLEVEL;
834   token= yylex();
835   if (token == tokv_lwsp) {
836     token= yylex(); if (token == tokv_error) return token;
837     if (!(token & tokt_logfacility))
838       return unexpected(token,-1,"syslog facility (or end of line)");
839     facility= lr_logfacility;
840     token= yylex();
841   }    
842   if (token == tokv_lwsp) {
843     token= yylex(); if (token == tokv_error) return token;
844     if (!(token & tokt_loglevel))
845       return unexpected(token,-1,"syslog level (or end of line) after facility");
846     level= lr_loglevel;
847     token= yylex();
848   }
849   if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog"))
850     return tokv_error;
851   closeerrorfile(); ehandling= tokv_word_errorstosyslog;
852   ehlogfacility= facility; ehloglevel= level; return 0;
853 }
854
855 int df_errorstofile(int dtoken) {
856   const char *cp;
857   FILE *file;
858   int r;
859   
860   r= paa_1path(&cp); if (r) return r;
861   file= fopen(cp,"a");
862   if (!file) {
863     parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
864     return tokv_error;
865   }
866   if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
867     parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
868     fclose(file); return tokv_error;
869   }
870   closeerrorfile(); ehandling= tokv_word_errorstofile;
871   ehfile= file; ehfilename= xmstrsave(cp); ehfilekeep= 0; return 0;
872 }
873
874 int dfg_setflag(int dtoken) {
875   int r;
876
877   r= pa_mnl(); if (r) return r;
878   *lr_flag= lr_flagval; return 0;
879 }
880
881 int df_reset(int dtoken) {
882   int r;
883
884   r= pa_mnl(); if (r) return r;
885   r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>");
886   assert(!r); return 0;  
887 }
888
889 int df_cd(int dtoken) {
890   const char *cp;
891   int r;
892
893   r= paa_1path(&cp); if (r) return r;
894   if (!chdir(cp)) return 0;
895   parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
896   return tokv_error;
897 }
898
899 int df_userrcfile(int dtoken) {
900   const char *cp;
901   int r;
902
903   r= paa_1path(&cp); if (r) return r;
904   free(userrcfile); userrcfile= xstrdup(cp);
905   return 0;
906 }
907
908 int dfi_includeuserrcfile(int dtoken) {
909   int r;
910
911   r= pa_mnl(); if (r) return r;
912   if (userrcfile) return parse_file(userrcfile,0);
913   parseerrprint("_include-user-rcfile (for-internal-use directive) "
914                 "found but user-rcfile not set"); return tokv_error;
915 }
916
917 int dfi_includeclientconfig(int dtoken) {
918   int r;
919
920   r= pa_mnl(); if (r) return r;
921   if (!overridedata) {
922     parseerrprint("_include-client-config (for-internal-use directive) "
923                   "found but configuration not overridden");
924     return tokv_error;
925   }
926   return parse_string(overridedata,"<configuration override data>");
927 }
928
929 int df_include(int dtoken) {
930   const char *cp;
931   int r, found;
932
933   r= paa_1path(&cp); if (r) return r;
934   r= parse_file(cp,&found); if (r) return r;
935   if (found || dtoken == tokv_word_includeifexist) return 0;
936   parseerrprint(dtoken == tokv_word_includesysconfig ?
937                 "system configuration file `%s' does not exist" :
938                 "included file `%s' does not exist",
939                 cp);
940   return tokv_error;
941 }
942
943 static int paa_message(const char **message_r) {
944   static char *buildbuf;
945   static int buildbuflen;
946
947   int r, tl;
948
949   r= pa_mwsp(); if (r) return r;
950   tl= 1;
951   makeroom(&buildbuf,&buildbuflen,10);
952   buildbuf[0]= 0;
953   for (;;) {
954     r= yylex(); if (r == tokv_error) return r;
955     if (r == tokv_eof) {
956       parseerrprint("unexpected end of file in message text"); return tokv_error;
957     }
958     if (r == tokv_newline) break;
959     tl+= strlen(yytext);
960     makeroom(&buildbuf,&buildbuflen,tl);
961     strcat(buildbuf,yytext);
962   }
963   *message_r= buildbuf;
964   return 0;
965 }
966
967 int df_message(int dtoken) {
968   const char *mp;
969   int r;
970
971   r= paa_message(&mp); if (r) return r;
972   parseerrprint("`message' directive: %s",mp);
973   return 0;
974 }
975
976 int df_error(int dtoken) {
977   const char *mp;
978   int r;
979
980   r= paa_message(&mp); if (r) return r;
981   parseerrprint("`error' directive: %s",mp); return tokv_error;
982 }
983
984 int df_includedirectory(int dtoken) {
985   static char *buildbuf=0;
986   static int buildbuflen=0;
987   
988   int r, cpl, tel, c, found;
989   DIR *d;
990   struct dirent *de;
991   const char *p, *cp;
992   
993   r= paa_1path(&cp); if (r) return r;
994   d= opendir(cp);
995   if (!d) {
996     parseerrprint("unable to open directory `%s': %s",cp,strerror(errno));
997     return tokv_error;
998   }
999   cpl= strlen(cp);
1000   while ((de= readdir(d))) {
1001     tel= strlen(de->d_name);
1002     if (!tel) continue;
1003     p= de->d_name;
1004     if (!isalnum(*p)) continue;
1005     while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
1006     if (c) continue;
1007     makeroom(&buildbuf,&buildbuflen,cpl+tel+sizeof(DIRSEP));
1008     strcpy(buildbuf,cp);
1009     strcat(buildbuf,DIRSEP);
1010     strcat(buildbuf,de->d_name);
1011     r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
1012     if (!found) {
1013       parseerrprint("unable to open file `%s' in included directory `%s': %s",
1014                     de->d_name,cp,strerror(errno));
1015       closedir(d); return tokv_error;
1016     }
1017   }
1018   if (closedir(d)) {
1019     parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1020     return tokv_error;
1021   }
1022   return 0;
1023 }
1024
1025 int df_includelookup(int dtoken) {
1026   static char *buildbuf=0;
1027   int buildbuflen=0;
1028   
1029   char **parmvalues, **pp, *p, *q;
1030   const char *cp;
1031   struct stat stab;
1032   int r, done, thisdone, cpl, c;
1033
1034   r= pa_mwsp(); if (r) return r;
1035   r= pa_parameter(&parmvalues); if (r) return r;
1036   r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
1037   if (stat(cp,&stab)) {
1038     parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
1039     freecharparray(parmvalues); return tokv_error;
1040   }
1041   if (!S_ISDIR(stab.st_mode)) {
1042     parseerrprint("object `%s' is not a directory or link to one",cp);
1043     freecharparray(parmvalues); return tokv_error;
1044   }
1045   done= 0;
1046   cpl= strlen(cp);
1047   if (!parmvalues[0]) {
1048     makeroom(&buildbuf,&buildbuflen,cpl+sizeof(DIRSEP NONEINCLUDELOOKUP));
1049     strcpy(buildbuf,cp);
1050     strcat(buildbuf,DIRSEP NONEINCLUDELOOKUP);
1051     r= parse_file(buildbuf,&thisdone);
1052     if (r) { freecharparray(parmvalues); return r; }
1053     if (thisdone) done= 1;
1054   } else {
1055     for (pp=parmvalues;
1056          *pp && (!done || dtoken == tokv_word_includelookupall);
1057          pp++) {
1058       makeroom(&buildbuf,&buildbuflen,
1059                cpl+sizeof(DIRSEP)+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP));
1060       strcpy(buildbuf,cp);
1061       strcat(buildbuf,DIRSEP);
1062       p= *pp; q= buildbuf+cpl+sizeof(DIRSEP)-1;
1063       if (*p=='.') *q++= ':';
1064       while ((c= *p++)) {
1065         if (c=='/') { *q++= ':'; c='-'; }
1066         else if (c==':') { *q++= ':'; }
1067         *q++= c;
1068       }
1069       *q++= 0;
1070       r= parse_file(buildbuf,&thisdone);
1071       if (r) { freecharparray(parmvalues); return r; }
1072       if (thisdone) done= 1;
1073     }
1074   }
1075   freecharparray(parmvalues);
1076   if (!done) {
1077     makeroom(&buildbuf,&buildbuflen,
1078              cpl+sizeof(DIRSEP)+sizeof(DEFAULTINCLUDELOOKUP));
1079     strcpy(buildbuf,cp);
1080     strcat(buildbuf,DIRSEP DEFAULTINCLUDELOOKUP);
1081     r= parse_file(buildbuf,0); if (r) return r;
1082   }
1083   return 0;
1084 }
1085
1086 int df_catchquit(int dtoken) {
1087   int r;
1088
1089   r= pa_mnl(); if (r) return r;
1090   r= parser(tokv_word_catchquit);
1091   if (r & tokt_controlend) {
1092     assert(r == tokv_word_hctac);
1093     r= pa_mnl();
1094   } else if (r == tokv_quit || r == tokv_error) {
1095     if (r == tokv_error) {
1096       r= parse_string(RESET_CONFIGURATION,
1097                       "<builtin reset configuration (caught error)>");
1098       assert(!r);
1099       while (!atnewline) {
1100         r= yylex(); if (r == tokv_error) return r;
1101       }
1102     }
1103     r= skip(tokv_word_catchquit);
1104     if (r & tokt_controlend) {
1105       assert(r == tokv_word_hctac);
1106       r= pa_mnl();
1107     }
1108   }
1109   return r;
1110 }
1111
1112 int df_if(int dtoken) {
1113   int r, true, done;
1114   
1115   done= 0;
1116   do {
1117     r= pa_condition(&true); if (r) return r;
1118     if (!done && true) { r= parser(tokv_word_if); done= 1; }
1119     else { r= skip(tokv_word_if); }
1120     if (!(r & tokv_word_if)) return r;
1121   } while (r == tokv_word_elif);
1122   if (r == tokv_word_else) {
1123     r= pa_mnl(); if (r) return r;
1124     if (done) r= skip(tokv_word_if);
1125     else r= parser(tokv_word_if);
1126     if (!(r & tokv_word_if)) return r;
1127   }
1128   if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1129   return pa_mnl();
1130 }
1131
1132 int dfg_fdwant(int dtoken) {
1133   int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
1134
1135   needreadwrite= lr_fdwant_readwrite;
1136   r= pa_mwsp(); if (r) return r;
1137   r= yylex(); if (r == tokv_error) return r;
1138   if (!(r & tokt_fdrange)) return r;
1139   fdmin= lr_min; fdmax= lr_max;
1140   r= yylex(); if (r == tokv_error) return r;
1141   if (r == tokv_newline) {
1142     if (needreadwrite > 0) {
1143       parseerrprint("read or write is required"); return tokv_error;
1144     }
1145     havereadwrite= 0;
1146   } else if (r == tokv_lwsp) {
1147     if (needreadwrite < 0) {
1148       parseerrprint("read or write not allowed"); return tokv_error;
1149     }
1150     r= yylex(); if (r == tokv_error) return r;
1151     if (!(r & tokt_readwrite))
1152       return unexpected(r,-1,"read or write (or perhaps newline)");
1153     havereadwrite= r;
1154     r= pa_mnl(); if (r) return r;
1155   } else {
1156     return unexpected(r,-1,"whitespace before read or write or newline");
1157   }
1158   ensurefdarray(fdmin);
1159   if (fdmax == -1) {
1160     if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
1161       parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd");
1162     fdmax= fdarrayused-1;
1163     restfdwantstate= dtoken;
1164     restfdwantrw= havereadwrite;
1165   }    
1166   for (fd=fdmin; fd<=fdmax; fd++) {
1167     fdarray[fd].wantstate= dtoken;
1168     fdarray[fd].wantrw= havereadwrite;
1169   }
1170   return 0;
1171 }
1172
1173 int df_quit(int dtoken) {
1174   int r;
1175
1176   r= pa_mnl(); if (r) return r;
1177   return tokv_quit;
1178 }
1179
1180 int df_eof(int dtoken) {
1181   int r;
1182
1183   r= pa_mnl(); if (r) return r;
1184   return tokv_eof;
1185 }
1186
1187 int df_errorspush(int dt) {
1188   int saveehandling, saveehlogfacility, saveehloglevel, saveehfilekeep;
1189   FILE *saveehfile;
1190   char *saveehfilename;
1191   int r;
1192
1193   r= pa_mnl(); if (r) return r;
1194
1195   saveehandling= ehandling;
1196   saveehlogfacility= ehlogfacility;
1197   saveehloglevel= ehloglevel;
1198   saveehfile= ehfile;
1199   saveehfilekeep= ehfilekeep;
1200   saveehfilename= ehfilename;
1201   if (ehandling == tokv_word_errorstofile) ehfilekeep= 1;
1202
1203   r= parser(tokv_word_errorspush);
1204   
1205   ehandling= saveehandling;
1206   ehlogfacility= saveehlogfacility;
1207   ehloglevel= saveehloglevel;
1208   ehfile= saveehfile;
1209   ehfilekeep= saveehfilekeep;
1210   ehfilename= saveehfilename;
1211
1212   if (r & tokt_controlend) {
1213     assert(r == tokv_word_srorre);
1214     r= pa_mnl();
1215   }
1216   return r;
1217 }