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., but we have to #include it so that the C
7 * objects from the lexer are available.
9 * Copyright (C)1996-1997 Ian Jackson
11 * This is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with userv; if not, write to the Free Software
23 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 static int parse_file(const char *string, int *didexist);
27 static int parser(int allowce);
28 int parse_string(const char *string, const char *descrip, int isinternal);
31 * Error-handling routines
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));
44 eh.file= 0; eh.filename= 0; eh.filekeep= 0;
47 static void senderrmsg(const char *errmsg, int useehandling) {
48 char suberrmsg[MAX_ERRMSG_LEN];
53 switch (useehandling) {
54 case tokv_word_errorstostderr:
55 senderrmsgstderr(errmsg);
57 case tokv_word_errorstosyslog:
58 ensurelogopen(eh.logfacility);
59 syslog(eh.loglevel,"%s",errmsg);
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,
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);
83 static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) {
84 static const char suffix[]= "references ...";
85 char errmsg[MAX_ERRMSG_LEN];
88 strnycpy(bufput,"<initialisation>: ",bufputlen);
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;
97 snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,
98 tstate->filename,tstate->reportlineno);
101 void parseerrprint(const char *fmt, ...) {
103 char errmsg[MAX_ERRMSG_LEN];
106 errwhere(cstate,errmsg,sizeof(errmsg)>>1);
107 vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
108 senderrmsg(errmsg,eh.handling);
112 static int unexpected(int found, int wanted, const char *wantedstr) {
113 /* pass wanted==-1 if you know it's not what you wanted;
114 * otherwise this function will check it for you.
116 if (found == wanted) return 0;
117 if (found == tokv_error) return found;
118 parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
122 static int stringoverflow(const char *where) {
123 parseerrprint("string buffer became far too large building %s",where);
128 * General assistance functions
131 static void freecharparray(char **array) {
135 for (pp=array; *pp; pp++) free(*pp);
139 static int dequote(char *inplace) {
140 char *p, *q, buf[4], *bep;
145 while (*p && *p != '"') {
146 if (*p != '\\') { *q++= *p++; continue; }
148 case 'n': *q++= '\n'; continue;
149 case 'r': *q++= '\r'; continue;
150 case 't': *q++= '\t'; continue;
152 assert(buf[0]= *++p); assert(buf[1]= *++p); buf[2]= 0;
153 v= strtoul(buf,&bep,16); assert(bep == buf+2);
154 assert(!(v & ~0xff)); *q++= v; p++; continue;
157 parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
159 } else if (isdigit(*p)) {
160 assert(buf[0]= *++p); assert(buf[1]= *++p); assert(buf[2]= *++p);
161 buf[3]= 0; v= strtoul(buf,&bep,8);
162 if (bep != buf+3 || (v & ~0xff)); {
163 parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
166 *q++= v; p++; continue;
167 } else if (ispunct(*p)) {
168 *q++= *p++; continue;
170 while (*p==' ' || *p=='\t') p++;
175 assert(*p); assert(!*++p);
176 return tokv_quotedstring;
179 const char *printtoken(int token) {
180 /* Returns pointer to static buffer, overwritten by next call. */
181 static const char keywordfmt[]= "keyword `%s'";
182 static const char operatorfmt[]= "operator `%s'";
184 static char buf[250];
190 if ((token & tokm_repres) == tokr_word) {
191 assert(strlen(yytext)+sizeof(keywordfmt)<sizeof(buf));
192 snyprintf(buf,sizeof(buf),keywordfmt,yytext);
194 } else if (token & tokt_number) {
195 snyprintf(buf,sizeof(buf),"number %d",lr_min); return buf;
196 } else if (token & tokt_fdrange && lr_max==-1) {
197 snyprintf(buf,sizeof(buf),"fdrange %d-",lr_min); return buf;
198 } else if (token & tokt_fdrange) {
199 snyprintf(buf,sizeof(buf),"fdrange %d-%d",lr_min,lr_max); return buf;
200 } else if ((token & tokm_repres) == tokr_punct) {
201 assert(strlen(yytext)+sizeof(operatorfmt)<sizeof(buf));
202 snyprintf(buf,sizeof(buf),operatorfmt,yytext);
204 } else if (token & tokt_string) {
206 case tokv_barestring: strcpy(buf,"unquoted string (bare word)"); break;
207 case tokv_quotedstring: strcpy(buf,"quoted string"); break;
211 l= strlen(buf); i= sizeof(buf)-l-2; p= yytext; q= buf+l;
213 if (i-- <= 0) { q--; strcpy(q-3,"..."); break; }
214 if (isspace(c)) c= ' ';
215 else if (!isprint(c) || iscntrl(c)) c= '?';
222 case tokv_lwsp: return "linear whitespace";
223 case tokv_newline: return "newline (or comment followed by newline)";
224 case tokv_eof: return "end of input file";
225 case tokv_error: return "syntax error token";
227 sprintf(buf,"token#0%o",token); return buf;
232 static const char *string2path(const char *in) {
233 /* Returned pointers become invalid on next call.
234 * May return 0, having printed an error message.
239 if (strncmp(in,"~/",2)) return in;
240 if (makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1)) {
241 stringoverflow("pathname");
244 snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2);
249 * Parser component functions for parsing parameters to directives
251 * Functions pa_... parse just one parameter,
252 * and return tokv_error or 0, having scanned exactly what they were expecting
253 * Functions paa_... parse complete parameter lists, including the leading lwsp,
254 * and return tokv_error or 0, having scanned up to and including the newline
256 * For any function which takes `const char **rv' the
257 * value returned in *rv is overwritten by repeated calls to
258 * any of those functions (whether the same or another).
261 static int pa_mnl(void) {
262 return unexpected(yylex(),tokv_newline,"newline");
264 static int pa_mwsp(void) {
265 return unexpected(yylex(),tokv_lwsp,"linear whitespace");
268 static int pa_string(const char **rv) {
274 r= pa_mwsp(); if (r) return r;
275 r= yylex(); if (r == tokv_error) return r;
276 if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string");
278 if (makeroom(&p,&pl,l)) return stringoverflow("string argument");
279 strcpy(p,yytext); *rv= p;
284 static int pa_numberdollar(int *value_r) {
285 /* Also parses the whitespace beforehand. */
288 r= pa_mwsp(); if (r) return r;
289 r= yylex(); if (r == tokv_error || r == tokv_dollar) return r;
290 if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error;
295 static int paa_1string(const char **rv) {
298 r= pa_string(rv); if (r) return r;
302 static int paa_1path(const char **rv) {
306 r= paa_1string(&cp); if (r) return r;
307 *rv= string2path(cp); if (!*rv) return tokv_error;
311 static int paa_pathargs(const char **path_r, char ***newargs_r) {
312 /* Repeated calls do _not_ overwrite newargs_r; caller must free.
313 * Repeated calls _do_ overwrite string returned in path_r.
314 * Any call to this function invalidates previous returns in *rv.
320 r= pa_string(&string); if (r == tokv_error) return r;
321 *path_r= string2path(string); if (!*path_r) return tokv_error;
324 newargs= xmalloc(sizeof(char*)*(size+1));
327 r= yylex(); if (r == tokv_error) goto error;
328 if (r==tokv_newline) break;
329 if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
330 r= tokv_error; goto error;
332 r= yylex(); if (r==tokv_error) goto error;
333 if ((r & tokm_repres) == tokr_nonstring) {
334 r= unexpected(r,-1,"string for command argument");
338 if (used >= MAX_ARGSDEFVAR) {
339 parseerrprint("far too many arguments to service program");
340 r= tokv_error; goto error;
343 newargs= xrealloc(newargs,sizeof(char*)*(size+1));
345 newargs[used++]= xstrsave(yytext);
353 freecharparray(newargs);
357 static int paa_message(const char **message_r) {
358 /* Returned value is invalidated by repeated calls. */
359 static char *buildbuf;
360 static int buildbuflen;
364 r= pa_mwsp(); if (r) return r;
366 if (makeroom(&buildbuf,&buildbuflen,50)) return stringoverflow("start of message");
369 r= yylex(); if (r == tokv_error) return r;
371 parseerrprint("unexpected end of file in message text");
374 if (r == tokv_newline) break;
376 if (makeroom(&buildbuf,&buildbuflen,tl)) return stringoverflow("message");
377 strcat(buildbuf,yytext);
379 *message_r= buildbuf;
384 * Skipping routines (used by e.g. the `if' code).
387 static int skiptoeol(void) {
388 /* Returns 0 if OK, having just parsed the newline
389 * or tokv_error if an error occurs, leaving the position at the error
390 * (which may be EOF or a syntax error token).
394 do { token= yylex(); }
395 while (token != tokv_newline && !(token & tokt_exception));
396 if (token == tokv_newline) return 0;
397 if (token == tokv_error) return token;
398 assert(token == tokv_eof);
399 parseerrprint("unexpected end of file while looking for end of line");
403 static int skip(int allowce) {
404 /* Scans a piece of config without executing it. Returns
405 * tokv_error (leaving the parsing state at the error),
406 * or the tokt_controlend token with type allowce if one
407 * was found (in which case rest of line, including any whitespace
408 * after tokt_controlend, has not been parsed), or tokv_eof.
412 for (;;) { /* loop over lines */
413 cstate->reportlineno= cstate->lineno;
414 do { token= yylex(); } while (token == tokv_lwsp);
415 if (token & tokt_exception) {
417 } else if (token & tokt_controlend) {
418 if (allowce == lr_controlend) return token;
419 else return unexpected(token,-1,"control structure end of a different kind");
420 } else if (token & tokt_controlstart) {
422 while (r & tokt_controlstart) {
423 r= skiptoeol(); if (r) return r;
424 cstate->reportlineno= cstate->lineno;
425 r= skip(token); if (r & tokt_exception) return r;
427 } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
428 parseerrprint("not a directive (or conditional operator) "
429 "while looking for control structure end");
432 r= skiptoeol(); if (r) return r;
437 * Routines for parsing and getting the values of parameters
440 static void parm_1string(char ***rvalues, const char *tocopy) {
442 a= xmalloc(sizeof(char*)*2);
443 a[0]= xstrsave(tocopy);
448 static char *parm_ulong(unsigned long ul) {
452 l= CHAR_BIT*sizeof(unsigned long)/3+4;
454 snyprintf(p,l,"%lu",ul);
458 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
461 a= xmalloc(sizeof(char*)*3);
462 a[0]= xstrsave(name);
463 a[1]= parm_ulong(id);
469 static int parm_groups(char ***rvalues, int size,
470 gid_t *gids, const char *const *groups) {
474 if (size >= 2 && gids[0] == gids[1]) { size--; gids++; groups++; }
475 a= xmalloc(sizeof(char*)*(size+1)*2);
476 for (i=0; i<size; i++) {
477 a[i]= xstrsave(groups[i]);
478 a[size+i]= parm_ulong(gids[i]);
485 static int pf_service(int ptoken, char ***rvalues) {
486 parm_1string(rvalues,service); return 0;
489 static int pf_callinguser(int ptoken, char ***rvalues) {
490 return parm_usernameuid(rvalues,logname,request_mbuf.callinguid);
493 static int pf_serviceuser(int ptoken, char ***rvalues) {
494 return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
497 static int pf_callinggroup(int ptoken, char ***rvalues) {
498 return parm_groups(rvalues,request_mbuf.ngids,calling_gids,calling_groups);
501 static int pf_servicegroup(int ptoken, char ***rvalues) {
502 return parm_groups(rvalues,service_ngids,service_gids,service_groups);
505 static int pf_callingusershell(int ptoken, char ***rvalues) {
506 parm_1string(rvalues,callinguser_shell); return 0;
509 static int pf_serviceusershell(int ptoken, char ***rvalues) {
510 parm_1string(rvalues,serviceuser_shell); return 0;
513 static int pa_parameter(char ***rvalues, char **rname) {
514 /* Scans a single parameter token and calls the appropriate parameter
515 * function, returning tokv_error (leaving the parser state wherever),
516 * or 0 on success (having just parsed the parameter name).
517 * If rname is non-null then the name of the parameter (malloc'd) will
520 * Caller must free *rvalues using freecharparray.
525 token= yylex(); if (token == tokv_error) return token;
526 name= xstrsave(yytext);
527 if ((token & tokm_repres) != tokr_nonstring &&
528 !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
530 i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i].key);
532 if (i>=request_mbuf.nvars) {
533 *rvalues= xmalloc(sizeof(char*));
536 parm_1string(rvalues,defvararray[i].value);
538 } else if (token & tokt_parameter) {
539 r= (lr_parameter)(token,rvalues);
540 if (r) { free(name); return r; }
543 return unexpected(token,-1,"parameter name");
545 debug_dumpparameter(name,*rvalues);
546 if (rname) *rname= name;
552 * Routines for parsing conditions, including
553 * parameter-based conditional functions (parmcondition's).
556 int pcf_glob(int ctoken, char *const *pv, int *rtrue) {
557 int token, actrue, r;
561 r= pa_mwsp(); if (r) return r;
564 if ((token & tokm_repres) == tokr_nonstring)
565 return unexpected(token,-1,"glob pattern");
566 for (pp= pv; !actrue && *pp; pp++) if (!fnmatch(yytext,*pp,0)) actrue= 1;
568 if (token == tokv_newline) break;
569 if (unexpected(token,tokv_lwsp,"newline after or whitespace between glob patterns"))
576 int pcf_range(int ctoken, char *const *pv, int *rtrue) {
577 int mintoken, min, maxtoken, max, r;
582 r= pa_mwsp(); if (r) return r;
583 mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
584 r= pa_mwsp(); if (r) return r;
585 maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
586 r= pa_mnl(); if (r) return r;
587 for (pp= pv; *pp; pp++) {
588 v= strtoul(*pp,&ep,10); if (*ep) continue;
589 if (mintoken != tokv_dollar && v < min) continue;
590 if (maxtoken != tokv_dollar && v > max) continue;
596 int pcf_grep(int ctoken, char *const *pv, int *rtrue) {
601 int r, maxlen, l, c, actrue, posstrue;
603 r= paa_1path(&cp); if (r) return r;
606 parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
610 for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
611 buf= xmalloc(maxlen+2); actrue= 0; c= 0;
612 while (!actrue && c!=EOF) {
613 c= getc(file); if (c==EOF) break;
614 if (isspace(c)) continue;
616 while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); }
617 if (c=='\n' || c==EOF || isspace(c)) {
618 while (p>buf && isspace(p[-1])) --p;
620 for (pp= pv; !posstrue && *pp; pp++)
621 if (!strcmp(*pp,buf)) posstrue= 1;
625 if (c!='\n' && c!=EOF) {
628 if (c==EOF || c=='\n') break;
629 if (!isspace(c)) posstrue= 0;
632 if (posstrue) actrue= 1;
635 parseerrprint("error while reading `%s' for grep: %s",cp,strerror(errno));
636 fclose(file); free(buf); return tokv_error;
638 assert(actrue || feof(file));
639 fclose(file); free(buf); *rtrue= actrue;
643 static int pa_condition(int *rtrue) {
644 /* Scans up to and including the newline following the condition;
645 * may scan more than one line if the condition is a multi-line
646 * one. Returns 0 (setting *rtrue, and parsing up to and including
647 * the last newline) or tokv_error.
648 * Expects to scan the whitespace before its condition.
650 int r, token, andor, ctrue, actrue;
653 r= pa_mwsp(); if (r) return r;
655 if (token == tokv_error) {
657 } else if (token == tokv_not) {
658 r= pa_condition(&ctrue); if (r) return r;
659 *rtrue= !ctrue; return 0;
660 } else if (token == tokv_openparen) {
661 andor= 0; actrue= -1;
663 cstate->reportlineno= cstate->lineno;
664 r= pa_condition(&ctrue); if (r) return r;
666 case 0: assert(actrue==-1); actrue= ctrue; break;
667 case tokv_and: assert(actrue>=0); actrue= actrue && ctrue; break;
668 case tokv_or: assert(actrue>=0); actrue= actrue || ctrue; break;
671 do { token= yylex(); } while (token == tokv_lwsp);
672 if (token == tokv_error) return token;
673 if (token == tokv_closeparen) break;
675 r= unexpected(token,andor,"same conjunction as before"); if (r) return r;
677 if (token != tokv_and && token != tokv_or)
678 return unexpected(token,-1,"first conjunction inside connective");
682 r= pa_mnl(); if (r) return r;
683 *rtrue= actrue; return 0;
684 } else if (token & tokt_parmcondition) {
685 r= pa_mwsp(); if (r) return r;
686 r= pa_parameter(&parmvalues,0); if (r) return r;
687 r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
690 return unexpected(token,-1,"condition");
695 * Directive functions and associated `common code' functions
698 /* Directives specifying the service program (execute-... and reset) */
700 static void execreset(void) {
703 free(execpath); execpath= 0;
704 freecharparray(execargs); execargs= 0;
707 int df_reject(int dtoken) {
710 r= pa_mnl(); if (r) return r;
712 execute= tokv_word_reject;
716 int df_executefrompath(int dtoken) {
719 r= pa_mnl(); if (r) return r;
721 execute= tokv_word_executefrompath;
725 int df_execute(int dtoken) {
730 r= paa_pathargs(&rv,&newargs); if (r) return r;
732 execute= tokv_word_execute;
734 execpath= xstrsave(rv);
738 int df_executefromdirectory(int dtoken) {
739 const char *p, *q, *rv;
744 r= paa_pathargs(&rv,&newargs); if (r) return r;
745 p= strrchr(service,'/'); if (p) p++; else p= service;
746 if (!*p || !isalnum(*p)) {
747 parseerrprint("execute-from-directory requires initial char of service "
748 "portion to be alphanumeric (service portion was `%s')",
750 freecharparray(newargs);
753 for (q=p+1; *q; q++) {
754 if (!isalnum(*q) && *q != '-') {
755 parseerrprint("execute-from-directory requires service portion to "
756 "contain only alphanumerics and hyphens (was `%s')",
758 freecharparray(newargs);
762 l= strlen(rv)+1+strlen(p)+1;
764 snyprintf(fn,l,"%s/%s",rv,p);
765 if (stat(fn,&stab)) {
766 if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
767 parseerrprint("failed to stat `%s' for execute-from-directory: %s",
769 free(fn); freecharparray(newargs); return tokv_error;
771 if (!S_ISREG(stab.st_mode)) {
772 parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
773 " or link to one (mode=0%lo)",fn,(unsigned long)stab.st_mode);
774 free(fn); freecharparray(newargs); return tokv_error;
777 execute= tokv_word_executefromdirectory;
783 /* Parsing builtin service requests (execute-builtin) */
785 static int bispa_none(char ***rnewargs) {
789 static int bispa_parameter(char ***rnewargs) {
791 char **parmvalues, *name, **newargs;
793 r= pa_mwsp(); if (r) return r;
794 r= pa_parameter(&parmvalues,&name); if (r) return r;
795 for (i=0; parmvalues[i]; i++);
796 newargs= xmalloc(sizeof(char*)*(i+2));
798 memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1));
800 r= pa_mnl(); if (r) { free(newargs); return r; }
805 int df_executebuiltin(int dtoken) {
807 builtinserviceexec_fnt *bisexec;
808 char *newpath, **newargs;
810 r= pa_mwsp(); if (r) return r;
811 r= yylex(); if (r == tokv_error) return r;
812 if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name");
814 newpath= xstrsave(yytext);
816 r= lr_bispa(&newargs); if (r) { free(newpath); return r; }
819 execute= tokv_word_executebuiltin;
820 execbuiltin= bisexec;
826 /* Directives for changing other execution parameters */
828 int dfg_setflag(int dtoken) {
831 r= pa_mnl(); if (r) return r;
832 *lr_flag= lr_flagval;
836 int df_reset(int dtoken) {
839 r= pa_mnl(); if (r) return r;
840 r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>",1);
845 int dfg_fdwant(int dtoken) {
846 int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
848 needreadwrite= lr_fdwant_readwrite;
849 r= pa_mwsp(); if (r) return r;
850 r= yylex(); if (r == tokv_error) return r;
851 if (!(r & tokt_fdrange)) return unexpected(r,-1,"file descriptor range");
852 fdmin= lr_min; fdmax= lr_max;
853 if (fdmin<0 || fdmin>MAX_ALLOW_FD ||
854 (fdmax != -1 && fdmax<0) || fdmax>MAX_ALLOW_FD) {
855 parseerrprint("file descriptor in range is negative or far too large");
858 r= yylex(); if (r == tokv_error) return r;
859 if (r == tokv_newline) {
860 if (needreadwrite > 0) {
861 parseerrprint("read or write is required");
865 } else if (r == tokv_lwsp) {
866 if (needreadwrite < 0) {
867 parseerrprint("read or write not allowed"); return tokv_error;
869 r= yylex(); if (r == tokv_error) return r;
870 if (!(r & tokt_readwrite))
871 return unexpected(r,-1,"read or write (or perhaps newline)");
873 r= pa_mnl(); if (r) return r;
875 return unexpected(r,-1,"whitespace before read or write or newline");
877 ensurefdarray(fdmin);
879 if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
880 parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd");
881 fdmax= fdarrayused-1;
882 restfdwantstate= dtoken;
883 restfdwantrw= havereadwrite;
885 ensurefdarray(fdmax);
886 for (fd=fdmin; fd<=fdmax; fd++) {
887 fdarray[fd].wantstate= dtoken;
888 fdarray[fd].wantrw= havereadwrite;
893 /* Directives for changing error handling */
895 int df_errorstostderr(int dtoken) {
898 r= pa_mnl(); if (r) return r;
899 closeerrorfile(); eh.handling= dtoken;
903 int df_errorstosyslog(int dtoken) {
904 int token, level, facility;
906 facility= DEFUSERLOGFACILITY;
907 level= DEFUSERLOGLEVEL;
909 if (token == tokv_lwsp) {
910 token= yylex(); if (token == tokv_error) return token;
911 if (!(token & tokt_logfacility))
912 return unexpected(token,-1,"syslog facility (or end of line)");
913 facility= lr_logfacility;
916 if (token == tokv_lwsp) {
917 token= yylex(); if (token == tokv_error) return token;
918 if (!(token & tokt_loglevel))
919 return unexpected(token,-1,"syslog level (or end of line) after facility");
923 if (unexpected(token,tokv_newline,"end of line after errors-to-syslog"))
925 closeerrorfile(); eh.handling= tokv_word_errorstosyslog;
926 eh.logfacility= facility; eh.loglevel= level;
930 int df_errorstofile(int dtoken) {
935 r= paa_1path(&cp); if (r) return r;
938 parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
941 if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
942 parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
943 fclose(file); return tokv_error;
945 closeerrorfile(); eh.handling= tokv_word_errorstofile;
946 eh.file= file; eh.filename= xstrsave(cp);
950 /* Directives for including other files or configuration data */
952 int dfi_includeuserrcfile(int dtoken) {
955 r= pa_mnl(); if (r) return r;
957 return parse_file(userrcfile,0);
960 int dfi_includeclientconfig(int dtoken) {
963 r= pa_mnl(); if (r) return r;
964 assert(overridedata);
965 return parse_string(overridedata,"<configuration override data>",0);
968 int df_include(int dtoken) {
972 r= paa_1path(&cp); if (r) return r;
973 r= parse_file(cp,&found); if (r) return r;
974 if (found || dtoken == tokv_word_includeifexist) return 0;
975 parseerrprint(dtoken == tokv_word_includesysconfig ?
976 "system configuration file `%s' does not exist" :
977 "included file `%s' does not exist",
982 int df_includedirectory(int dtoken) {
983 static char *buildbuf=0;
984 static int buildbuflen=0;
986 int r, cpl, tel, c, found;
991 r= paa_1path(&cp); if (r) return r;
994 parseerrprint("unable to open directory `%s': %s",cp,strerror(errno));
998 while ((de= readdir(d))) {
999 tel= strlen(de->d_name);
1002 if (!*p || !isalnum(*p)) continue;
1003 while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
1005 if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1))
1006 return stringoverflow("pathname in directory");
1007 snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name);
1008 r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
1010 parseerrprint("unable to open file `%s' in included directory `%s': %s",
1011 de->d_name,cp,strerror(errno));
1017 parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1023 int df_includelookup(int dtoken) {
1024 static char *buildbuf=0;
1027 char **parmvalues, **pp, *p, *q;
1030 int r, done, thisdone, cpl, c;
1032 r= pa_mwsp(); if (r) return r;
1033 r= pa_parameter(&parmvalues,0); if (r) return r;
1034 r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
1035 if (stat(cp,&stab)) {
1036 parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
1037 freecharparray(parmvalues); return tokv_error;
1039 if (!S_ISDIR(stab.st_mode)) {
1040 parseerrprint("object `%s' is not a directory or link to one",cp);
1041 freecharparray(parmvalues); return tokv_error;
1045 if (!parmvalues[0]) {
1046 if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP)))
1047 return stringoverflow("pathname in directory for lookup of undefined parameter");
1048 snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp);
1049 r= parse_file(buildbuf,&thisdone);
1050 if (r) { freecharparray(parmvalues); return r; }
1051 if (thisdone) done= 1;
1054 *pp && (!done || dtoken == tokv_word_includelookupall);
1056 if (makeroom(&buildbuf,&buildbuflen,
1057 cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1))
1058 return stringoverflow("pathname in directory for lookup");
1059 strcpy(buildbuf,cp);
1060 p= *pp; q= buildbuf+cpl;
1063 strcpy(q,EMPTYINCLUDELOOKUP);
1065 if (*p=='.') *q++= ':';
1067 if (c=='/') { *q++= ':'; c='-'; }
1068 else if (c==':') { *q++= ':'; }
1073 r= parse_file(buildbuf,&thisdone);
1074 if (r) { freecharparray(parmvalues); return r; }
1075 if (thisdone) done= 1;
1078 freecharparray(parmvalues);
1080 if (makeroom(&buildbuf,&buildbuflen,
1081 cpl+1+sizeof(DEFAULTINCLUDELOOKUP)))
1082 return stringoverflow("pathname in directory for lookup of default");
1083 snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp);
1084 r= parse_file(buildbuf,0); if (r) return r;
1089 /* Control constructs */
1091 int df_catchquit(int dtoken) {
1094 r= pa_mnl(); if (r) return r;
1095 r= parser(tokv_word_catchquit);
1096 if (r == tokv_quit || r == tokv_error) {
1097 if (r == tokv_error) {
1098 r= parse_string(RESET_CONFIGURATION,
1099 "<builtin reset configuration (caught error)>",1);
1102 r= skip(tokv_word_catchquit);
1104 if (r & tokt_controlend) {
1105 assert(r == tokv_word_hctac);
1111 int df_if(int dtoken) {
1116 r= pa_condition(&true); if (r) return r;
1117 if (!done && true) { r= parser(tokv_word_if); done= 1; }
1118 else { r= skip(tokv_word_if); }
1119 if (!(r & tokt_controlend)) return r;
1120 } while (r == tokv_word_elif);
1121 if (r == tokv_word_else) {
1122 r= pa_mnl(); if (r) return r;
1123 cstate->reportlineno= cstate->lineno;
1124 if (done) r= skip(tokv_word_if);
1125 else r= parser(tokv_word_if);
1126 if (!(r & tokt_controlend)) return r;
1128 if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1132 int df_errorspush(int dt) {
1133 struct error_handling save;
1136 r= pa_mnl(); if (r) return r;
1141 r= parser(tokv_word_errorspush);
1146 if (r & tokt_controlend) {
1147 assert(r == tokv_word_srorre);
1153 /* Miscelleanous directives */
1155 int df_cd(int dtoken) {
1159 r= paa_1path(&cp); if (r) return r;
1160 if (!chdir(cp)) return 0;
1161 parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
1165 int df_userrcfile(int dtoken) {
1169 r= paa_1path(&cp); if (r) return r;
1170 free(userrcfile); userrcfile= xstrsave(cp);
1174 int df_message(int dtoken) {
1178 r= paa_message(&mp); if (r) return r;
1179 parseerrprint("`message' directive: %s",mp);
1183 int df_error(int dtoken) {
1187 r= paa_message(&mp); if (r) return r;
1188 parseerrprint("`error' directive: %s",mp);
1192 int df_eof(int dtoken) {
1195 r= pa_mnl(); if (r) return r;
1199 int df_quit(int dtoken) {
1202 r= pa_mnl(); if (r) return r;
1207 * Main parser routines
1210 static void parser_push(struct parser_state *usestate,
1211 const char *newfile,
1212 const struct stat *newfilestab,
1213 YY_BUFFER_STATE ybuf,
1215 usestate->lineno= 1;
1216 usestate->reportlineno= 1;
1217 usestate->filename= newfile;
1218 usestate->filestab= *newfilestab;
1219 usestate->notedreferer= 0;
1220 usestate->isinternal= isinternal;
1221 usestate->ybuf= ybuf;
1222 usestate->upstate= cstate;
1225 yy_switch_to_buffer(ybuf);
1228 static void parser_pop(void) {
1229 struct parser_state *oldstate;
1232 cstate= cstate->upstate;
1233 if (cstate) yy_switch_to_buffer(cstate->ybuf);
1234 yy_delete_buffer(oldstate->ybuf);
1237 int parse_string(const char *string, const char *descrip, int isinternal) {
1238 /* Returns the same things as parser, except that tokv_eof
1241 static const struct stat blankstab;
1243 struct parser_state usestate;
1244 YY_BUFFER_STATE ybuf;
1247 ybuf= yy_scan_string(string);
1248 if (!ybuf) syscallerror("unable to create flex buffer for internal string");
1249 parser_push(&usestate,descrip,&blankstab,ybuf,isinternal);
1254 if (r == tokv_eof) r= 0;
1258 static int parse_file(const char *string, int *didexist) {
1259 /* Returns the same things as parser, except that tokv_eof
1260 * is turned into 0. If *didexist is 0 then errno will
1263 static int fileparselevel= 0;
1265 struct parser_state usestate, *checkrecurse;
1266 YY_BUFFER_STATE ybuf;
1269 struct stat newstab;
1271 if (fileparselevel >= MAX_INCLUDE_NEST) {
1272 parseerrprint("too many nested levels of included files");
1275 file= fopen(string,"r");
1277 if (errno == ENOENT) {
1278 if (didexist) *didexist= 0;
1281 parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
1284 r= fstat(fileno(file),&newstab); if (r) syscallerror("unable to fstat new file");
1285 for (checkrecurse= cstate; checkrecurse; checkrecurse= checkrecurse->upstate) {
1286 if (!checkrecurse->filestab.st_mode) continue;
1287 if (newstab.st_dev==checkrecurse->filestab.st_dev &&
1288 newstab.st_ino==checkrecurse->filestab.st_ino) {
1289 parseerrprint("recursion detected - config file `%s' calls itself",string);
1295 if (didexist) *didexist= 1;
1297 ybuf= yy_create_buffer(file,YY_BUF_SIZE);
1298 if (!ybuf) syscallerror("unable to create flex buffer for file");
1299 parser_push(&usestate,string,&newstab,ybuf,0);
1304 parseerrprint("error reading configuration file `%s'",string);
1311 if (r == tokv_eof) r= 0;
1315 static int parser(int allowce) {
1317 * an exception (error, eof or quit)
1318 * then rest of `file' is uninteresting
1320 * token if allowce was !0 and equal to token's controlend
1321 * then rest of `file' (including rest of line with the
1322 * controlend - even the whitespace) not scanned yet
1326 for (;;) { /* loop over lines */
1327 cstate->reportlineno= cstate->lineno;
1328 do { token= yylex(); } while (token == tokv_lwsp);
1329 if (token & tokt_exception) {
1331 } else if (token & tokt_controlend) {
1332 if (lr_controlend == allowce) return token;
1333 else return unexpected(token,-1,"directive (not this kind of"
1334 " control structure end)");
1335 } else if (token & tokt_directive) {
1336 if ((token & tokt_internal) && !cstate->isinternal)
1337 return unexpected(token,-1,"published directive, not internal-use-only one");
1338 r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; }
1339 } else if (token == tokv_newline) {
1340 /* ignore blank lines (and comment-only lines) */
1342 return unexpected(token,-1,"directive");