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