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.
10 * Copyright 1996-2017 Ian Jackson <ian@davenant.greenend.org.uk>.
11 * Copyright 2000 Ben Harris <bjh21@cam.ac.uk>
12 * Copyright 2016-2017 Peter Benie <pjb1008@cam.ac.uk>
14 * This is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with userv; if not, see <http://www.gnu.org/licenses/>.
28 static int parse_file(const char *string, int *didexist);
29 static int parser(int allowce);
30 int parse_string(const char *string, const char *descrip, int isinternal);
33 * Error-handling routines
36 static void closeerrorfile(void) {
37 if (eh.file && !eh.filekeep) {
38 if (fclose(eh.file)) {
39 eh.handling= tokv_word_errorstostderr;
40 parseerrprint("error writing to error log file `%s': %s",
41 eh.filename,strerror(errno));
46 eh.file= 0; eh.filename= 0; eh.filekeep= 0;
49 static void senderrmsg(const char *errmsg, int useehandling) {
50 char suberrmsg[MAX_ERRMSG_LEN];
55 switch (useehandling) {
56 case tokv_word_errorstostderr:
57 senderrmsgstderr(errmsg);
59 case tokv_word_errorstosyslog:
60 ensurelogopen(eh.logfacility);
61 syslog(eh.loglevel,"%s",errmsg);
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(eh.file,"%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,
71 closeerrorfile(); eh.handling= 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),eh.filename,strerror(e));
76 senderrmsg(suberrmsg,eh.handling);
77 senderrmsg(errmsg,eh.handling);
85 static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) {
86 static const char suffix[]= "references ...";
87 char errmsg[MAX_ERRMSG_LEN];
90 strnycpy(bufput,"<initialisation>: ",bufputlen);
93 if (!tstate->notedreferer && tstate->upstate && !tstate->upstate->isinternal) {
94 errwhere(tstate->upstate,errmsg,sizeof(errmsg)-sizeof(suffix));
95 strcat(errmsg,suffix);
96 senderrmsg(errmsg,eh.handling);
97 tstate->notedreferer= 1;
99 snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,
100 tstate->filename,tstate->reportlineno);
103 int parseerrprint(const char *fmt, ...) {
105 char errmsg[MAX_ERRMSG_LEN];
108 errwhere(cstate,errmsg,sizeof(errmsg)>>1);
109 vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
110 senderrmsg(errmsg,eh.handling);
116 static int unexpected(int found, int wanted, const char *wantedstr) {
117 /* pass wanted==-1 if you know it's not what you wanted;
118 * otherwise this function will check it for you.
120 if (found == wanted) return 0;
121 if (found == tokv_error) return found;
122 return parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
125 static int stringoverflow(const char *where) {
126 return parseerrprint("string buffer became far too large building %s",where);
130 * General assistance functions
133 static void freecharparray(char **array) {
137 for (pp=array; *pp; pp++) free(*pp);
141 static void countnewlines(void) {
143 for (p=yytext; *p; p++)
148 static int dequote(char *inplace) {
149 char *p, *q, buf[4], *bep;
153 assert(*p=='"'); p++;
154 while (*p && *p != '"') {
155 if (*p != '\\') { *q++= *p++; continue; }
157 case 'n': *q++= '\n'; p++; continue;
158 case 'r': *q++= '\r'; p++; continue;
159 case 't': *q++= '\t'; p++; continue;
162 if (!((buf[0]= *p++) && (buf[1]= *p++)))
163 return parseerrprint("quoted string ends inside \\x<hex> sequence");
165 v= strtoul(buf,&bep,16);
167 return parseerrprint("invalid \\<hex> sequence \\x%s in quoted string",buf);
168 assert(!(v & ~0xff));
172 if (ISCHAR(isalpha,*p))
173 return parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
174 if (ISCHAR(isdigit,*p)) {
175 if (!((buf[0]= *p++) && (buf[1]= *p++) && (buf[2]= *p++))) abort();
176 buf[3]= 0; v= strtoul(buf,&bep,8);
177 if (bep != buf+3 || (v & ~0xff))
178 return parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
180 } else if (ISCHAR(ispunct,*p)) {
181 *q++= *p++; continue;
183 while (*p==' ' || *p=='\t') p++;
184 v= *p++; assert(v=='\n');
188 assert(*p); p++; assert(!*p);
190 return tokv_quotedstring;
193 const char *printtoken(int token) {
194 /* Returns pointer to static buffer, overwritten by next call. */
195 static const char keywordfmt[]= "keyword `%s'";
196 static const char operatorfmt[]= "operator `%s'";
198 static char buf[250];
204 if ((token & tokm_repres) == tokr_word) {
205 assert(strlen(yytext)+sizeof(keywordfmt)<sizeof(buf));
206 snyprintf(buf,sizeof(buf),keywordfmt,yytext);
208 } else if (token & tokt_number) {
209 snyprintf(buf,sizeof(buf),"number %d",lr_min); return buf;
210 } else if (token & tokt_fdrange && token & tokr_word) {
211 snyprintf(buf,sizeof(buf),"fd %s",buf); return buf;
212 } else if (token & tokt_fdrange && lr_max==-1) {
213 snyprintf(buf,sizeof(buf),"fdrange %d-",lr_min); return buf;
214 } else if (token & tokt_fdrange) {
215 snyprintf(buf,sizeof(buf),"fdrange %d-%d",lr_min,lr_max); return buf;
216 } else if ((token & tokm_repres) == tokr_punct) {
217 assert(strlen(yytext)+sizeof(operatorfmt)<sizeof(buf));
218 snyprintf(buf,sizeof(buf),operatorfmt,yytext);
220 } else if (token & tokt_string) {
222 case tokv_barestring: strcpy(buf,"unquoted string (bare word)"); break;
223 case tokv_quotedstring: strcpy(buf,"quoted string"); break;
227 l= strlen(buf); i= sizeof(buf)-l-2; p= yytext; q= buf+l;
229 if (i-- <= 0) { q--; strcpy(q-3,"..."); break; }
230 if (ISCHAR(isspace,c)) c= ' ';
231 else if (!ISCHAR(isprint,c) || ISCHAR(iscntrl,c)) c= '?';
238 case tokv_lwsp: return "linear whitespace";
239 case tokv_newline: return "newline (or comment followed by newline)";
240 case tokv_eof: return "end of input file";
241 case tokv_error: return "syntax error token";
243 sprintf(buf,"token#0%o",token); return buf;
248 static const char *string2path(const char *in) {
249 /* Returned pointers become invalid on next call.
250 * May return 0, having printed an error message.
255 if (strncmp(in,"~/",2)) return in;
256 if (makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1)) {
257 stringoverflow("pathname");
260 snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2);
265 * Parser component functions for parsing parameters to directives
267 * Functions pa_... parse just one parameter,
268 * and return tokv_error or 0, having scanned exactly what they were expecting
269 * Functions paa_... parse complete parameter lists, including the leading lwsp,
270 * and return tokv_error or 0, having scanned up to and including the newline
272 * For any function which takes `const char **rv' the
273 * value returned in *rv is overwritten by repeated calls to
274 * any of those functions (whether the same or another).
277 static int pa_mnl(void) {
278 return unexpected(yylex(),tokv_newline,"newline");
280 static int pa_mwsp(void) {
281 return unexpected(yylex(),tokv_lwsp,"linear whitespace");
284 static int pa_string(const char **rv) {
290 r= pa_mwsp(); if (r) return r;
291 r= yylex(); if (r == tokv_error) return r;
292 if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string");
294 if (makeroom(&p,&pl,l)) return stringoverflow("string argument");
295 strcpy(p,yytext); *rv= p;
300 static int pa_numberdollar(int *value_r) {
301 /* Also parses the whitespace beforehand. */
304 r= pa_mwsp(); if (r) return r;
305 r= yylex(); if (r == tokv_error || r == tokv_dollar) return r;
306 if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error;
311 static int paa_1string(const char **rv) {
314 r= pa_string(rv); if (r) return r;
318 static int paa_1path(const char **rv) {
322 r= paa_1string(&cp); if (r) return r;
323 *rv= string2path(cp); if (!*rv) return tokv_error;
327 static int paa_pathargs(const char **path_r, char ***newargs_r) {
328 /* Repeated calls do _not_ overwrite newargs_r; caller must free.
329 * Repeated calls _do_ overwrite string returned in path_r.
330 * Any call to this function invalidates previous returns in *rv.
336 r= pa_string(&string); if (r == tokv_error) return r;
337 *path_r= string2path(string); if (!*path_r) return tokv_error;
340 newargs= xmalloc(sizeof(char*)*(size+1));
343 r= yylex(); if (r == tokv_error) goto error;
344 if (r==tokv_newline) break;
345 if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
346 r= tokv_error; goto error;
348 r= yylex(); if (r==tokv_error) goto error;
349 if ((r & tokm_repres) == tokr_nonstring) {
350 r= unexpected(r,-1,"string for command argument");
354 if (used >= MAX_ARGSDEFVAR) {
355 r= parseerrprint("far too many arguments to service program");
359 newargs= xrealloc(newargs,sizeof(char*)*(size+1));
361 newargs[used++]= xstrsave(yytext);
369 freecharparray(newargs);
373 static int paa_message(const char **message_r) {
374 /* Returned value is invalidated by repeated calls. */
375 static char *buildbuf;
376 static int buildbuflen;
381 r= pa_mwsp(); if (r) return r;
383 if (makeroom(&buildbuf,&buildbuflen,50)) return stringoverflow("start of message");
386 r= yylex(); if (r == tokv_error) return r;
388 return parseerrprint("unexpected end of file in message text");
389 if (r == tokv_newline) break;
390 usetext= r == tokv_lwsp ? " " : yytext;
391 tl+= strlen(usetext);
392 if (makeroom(&buildbuf,&buildbuflen,tl)) return stringoverflow("message");
393 strcat(buildbuf,usetext);
395 *message_r= buildbuf;
400 * Skipping routines (used by e.g. the `if' code).
403 static int skiptoeol(void) {
404 /* Returns 0 if OK, having just parsed the newline
405 * or tokv_error if an error occurs, leaving the position at the error
406 * (which may be EOF or a syntax error token).
410 do { token= yylex(); }
411 while (token != tokv_newline && !(token & tokt_exception));
412 if (token == tokv_newline) return 0;
413 if (token == tokv_error) return token;
414 assert(token == tokv_eof);
415 return parseerrprint("unexpected end of file while looking for end of line");
418 static int skip(int allowce) {
419 /* Scans a piece of config without executing it. Returns
420 * tokv_error (leaving the parsing state at the error),
421 * or the tokt_controlend token with type allowce if one
422 * was found (in which case rest of line, including any whitespace
423 * after tokt_controlend, has not been parsed), or tokv_eof.
427 for (;;) { /* loop over lines */
428 cstate->reportlineno= cstate->lineno;
429 do { token= yylex(); } while (token == tokv_lwsp || token == tokv_newline);
430 if (token & tokt_exception) {
432 } else if (token & tokt_controlend) {
433 if (allowce == lr_controlend) return token;
434 else return unexpected(token,-1,"control structure end of a different kind");
435 } else if (token & tokt_controlstart) {
437 while (r & tokt_controlstart) {
438 r= skiptoeol(); if (r) return r;
439 cstate->reportlineno= cstate->lineno;
440 r= skip(token); if (r & tokt_exception) return r;
442 } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
443 return parseerrprint("not a directive (or conditional operator) "
444 "while looking for control structure end");
446 r= skiptoeol(); if (r) return r;
451 * Routines for parsing and getting the values of parameters
454 static void parm_1string(char ***rvalues, const char *tocopy) {
456 a= xmalloc(sizeof(char*)*2);
457 a[0]= xstrsave(tocopy);
462 static char *parm_ulong(unsigned long ul) {
466 l= CHAR_BIT*sizeof(unsigned long)/3+4;
468 snyprintf(p,l,"%lu",ul);
472 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
475 a= xmalloc(sizeof(char*)*3);
476 a[0]= xstrsave(name);
477 a[1]= parm_ulong(id);
483 static int parm_groups(char ***rvalues, int size,
484 gid_t *gids, const char *const *groups) {
488 if (size >= 2 && gids[0] == gids[1]) { size--; gids++; groups++; }
489 a= xmalloc(sizeof(char*)*(size+1)*2);
490 for (i=0; i<size; i++) {
491 a[i]= xstrsave(groups[i]);
492 a[size+i]= parm_ulong(gids[i]);
499 static int pf_service(int ptoken, char ***rvalues) {
500 parm_1string(rvalues,service); return 0;
503 static int pf_callinguser(int ptoken, char ***rvalues) {
504 return parm_usernameuid(rvalues,loginname,request_mbuf.callinguid);
507 static int pf_serviceuser(int ptoken, char ***rvalues) {
508 return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
511 static int pf_callinggroup(int ptoken, char ***rvalues) {
512 return parm_groups(rvalues,request_mbuf.ngids,calling_gids,calling_groups);
515 static int pf_servicegroup(int ptoken, char ***rvalues) {
516 return parm_groups(rvalues,service_ngids,service_gids,service_groups);
519 static int pf_callingusershell(int ptoken, char ***rvalues) {
520 parm_1string(rvalues,callinguser_shell); return 0;
523 static int pf_serviceusershell(int ptoken, char ***rvalues) {
524 parm_1string(rvalues,serviceuser_shell); return 0;
527 static int pa_parameter(char ***rvalues, char **rname) {
528 /* Scans a single parameter token and calls the appropriate parameter
529 * function, returning tokv_error (leaving the parser state wherever),
530 * or 0 on success (having just parsed the parameter name).
531 * If rname is non-null then the name of the parameter (malloc'd) will
534 * Caller must free *rvalues using freecharparray.
539 token= yylex(); if (token == tokv_error) return token;
540 name= xstrsave(yytext);
541 if ((token & tokm_repres) != tokr_nonstring &&
542 !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
544 i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i].key);
546 if (i>=request_mbuf.nvars) {
547 *rvalues= xmalloc(sizeof(char*));
550 parm_1string(rvalues,defvararray[i].value);
552 } else if (token & tokt_parameter) {
553 r= (lr_parameter)(token,rvalues);
554 if (r) { free(name); return r; }
557 return unexpected(token,-1,"parameter name");
559 debug_dumpparameter(name,*rvalues);
560 if (rname) *rname= name;
566 * Routines for parsing conditions, including
567 * parameter-based conditional functions (parmcondition's).
570 int pcf_glob(int ctoken, char *const *pv, int *rtrue) {
571 int token, actrue, r;
575 r= pa_mwsp(); if (r) return r;
578 if ((token & tokm_repres) == tokr_nonstring)
579 return unexpected(token,-1,"glob pattern");
580 for (pp= pv; !actrue && *pp; pp++) if (!fnmatch(yytext,*pp,0)) actrue= 1;
582 if (token == tokv_newline) break;
583 if (unexpected(token,tokv_lwsp,"newline after or whitespace between glob patterns"))
590 int pcf_range(int ctoken, char *const *pv, int *rtrue) {
591 int mintoken, min, maxtoken, max, r;
596 mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
597 maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
598 r= pa_mnl(); if (r) return r;
599 for (pp= pv; *pp; pp++) {
600 v= strtoul(*pp,&ep,10); if (*ep) continue;
601 if (mintoken != tokv_dollar && v < min) continue;
602 if (maxtoken != tokv_dollar && v > max) continue;
608 int pcf_grep(int ctoken, char *const *pv, int *rtrue) {
613 int r, maxlen, l, c, actrue, posstrue;
615 r= paa_1path(&cp); if (r) return r;
618 return parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
620 for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
621 buf= xmalloc(maxlen+2); actrue= 0; c= 0;
622 while (!actrue && c!=EOF) {
623 c= getc(file); if (c==EOF) break;
624 if (ISCHAR(isspace,c)) continue;
626 while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); }
627 if (c=='\n' || c==EOF || ISCHAR(isspace,c)) {
628 while (p>buf && ISCHAR(isspace,p[-1])) --p;
630 for (pp= pv; !posstrue && *pp; pp++)
631 if (!strcmp(*pp,buf)) posstrue= 1;
635 if (c!='\n' && c!=EOF) {
638 if (c==EOF || c=='\n') break;
639 if (!ISCHAR(isspace,c)) posstrue= 0;
642 if (posstrue) actrue= 1;
645 parseerrprint("error while reading `%s' for grep: %s",cp,strerror(errno));
646 fclose(file); free(buf); return tokv_error;
648 assert(actrue || feof(file));
649 fclose(file); free(buf); *rtrue= actrue;
653 static int pa_condition(int *rtrue) {
654 /* Scans up to and including the newline following the condition;
655 * may scan more than one line if the condition is a multi-line
656 * one. Returns 0 (setting *rtrue, and parsing up to and including
657 * the last newline) or tokv_error.
658 * Expects to scan the whitespace before its condition.
660 int r, token, andor, ctrue, actrue;
663 r= pa_mwsp(); if (r) return r;
665 if (token == tokv_error) {
667 } else if (token == tokv_not) {
668 r= pa_condition(&ctrue); if (r) return r;
669 *rtrue= !ctrue; return 0;
670 } else if (token == tokv_openparen) {
671 andor= 0; actrue= -1;
673 cstate->reportlineno= cstate->lineno;
674 r= pa_condition(&ctrue); if (r) return r;
676 case 0: assert(actrue==-1); actrue= ctrue; break;
677 case tokv_and: assert(actrue>=0); actrue= actrue && ctrue; break;
678 case tokv_or: assert(actrue>=0); actrue= actrue || ctrue; break;
681 do { token= yylex(); } while (token == tokv_lwsp);
682 if (token == tokv_error) return token;
683 if (token == tokv_closeparen) break;
685 r= unexpected(token,andor,"same conjunction as before"); if (r) return r;
687 if (token != tokv_and && token != tokv_or)
688 return unexpected(token,-1,"first conjunction inside connective");
692 r= pa_mnl(); if (r) return r;
693 *rtrue= actrue; return 0;
694 } else if (token & tokt_parmcondition) {
695 r= pa_mwsp(); if (r) return r;
696 r= pa_parameter(&parmvalues,0); if (r) return r;
697 r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
700 return unexpected(token,-1,"condition");
705 * Directive functions and associated `common code' functions
708 /* Directives specifying the service program (execute-... and reset) */
710 static void execreset(void) {
713 free(execpath); execpath= 0;
714 freecharparray(execargs); execargs= 0;
717 int df_reject(int dtoken) {
720 r= pa_mnl(); if (r) return r;
722 execute= tokv_word_reject;
726 int df_executefrompath(int dtoken) {
729 r= pa_mnl(); if (r) return r;
731 execute= tokv_word_executefrompath;
735 int df_execute(int dtoken) {
740 r= paa_pathargs(&rv,&newargs); if (r) return r;
742 execute= tokv_word_execute;
744 execpath= xstrsave(rv);
748 int df_executefromdirectory(int dtoken) {
749 const char *p, *q, *rv;
754 r= paa_pathargs(&rv,&newargs); if (r) return r;
755 p= strrchr(service,'/'); if (p) p++; else p= service;
756 if (!*p || !ISCHAR(isalnum,*p)) {
757 parseerrprint("execute-from-directory requires initial char of service "
758 "portion to be alphanumeric (service portion was `%s')",
760 freecharparray(newargs);
763 for (q=p+1; *q; q++) {
764 if (!ISCHAR(isalnum,*q) && *q != '-') {
765 parseerrprint("execute-from-directory requires service portion to "
766 "contain only alphanumerics and hyphens (was `%s')",
768 freecharparray(newargs);
772 l= strlen(rv)+1+strlen(p)+1;
774 snyprintf(fn,l,"%s/%s",rv,p);
775 if (stat(fn,&stab)) {
776 if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
777 parseerrprint("failed to stat `%s' for execute-from-directory: %s",
779 free(fn); freecharparray(newargs); return tokv_error;
781 if (!S_ISREG(stab.st_mode)) {
782 parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
783 " or link to one (mode=0%lo)",fn,(unsigned long)stab.st_mode);
784 free(fn); freecharparray(newargs); return tokv_error;
787 execute= tokv_word_executefromdirectory;
793 /* Parsing builtin service requests (execute-builtin) */
795 static int bispa_none(char ***rnewargs) {
799 static int bispa_parameter(char ***rnewargs) {
801 char **parmvalues, *name, **newargs;
803 r= pa_mwsp(); if (r) return r;
804 r= pa_parameter(&parmvalues,&name); if (r) return r;
805 for (i=0; parmvalues[i]; i++);
806 newargs= xmalloc(sizeof(char*)*(i+2));
808 memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1));
810 r= pa_mnl(); if (r) { free(newargs); return r; }
815 int df_executebuiltin(int dtoken) {
817 builtinserviceexec_fnt *bisexec;
818 char *newpath, **newargs;
820 r= pa_mwsp(); if (r) return r;
821 r= yylex(); if (r == tokv_error) return r;
822 if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name");
824 newpath= xstrsave(yytext);
826 r= lr_bispa(&newargs); if (r) { free(newpath); return r; }
829 execute= tokv_word_executebuiltin;
830 execbuiltin= bisexec;
836 /* Directives for changing other execution parameters */
838 int dfg_setflag(int dtoken) {
841 r= pa_mnl(); if (r) return r;
842 *lr_flag= lr_flagval;
846 int df_reset(int dtoken) {
849 r= pa_mnl(); if (r) return r;
850 r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>",1);
854 int dfg_fdwant(int dtoken) {
855 int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
857 needreadwrite= lr_fdwant_readwrite;
858 r= pa_mwsp(); if (r) return r;
859 r= yylex(); if (r == tokv_error) return r;
860 if (!(r & tokt_fdrange)) return unexpected(r,-1,"file descriptor range");
861 fdmin= lr_min; fdmax= lr_max;
862 if (fdmin<0 || fdmin>MAX_ALLOW_FD ||
863 (fdmax != -1 && fdmax<0) || fdmax>MAX_ALLOW_FD)
864 return parseerrprint("file descriptor in range is negative or far too large");
865 r= yylex(); if (r == tokv_error) return r;
866 if (r == tokv_newline) {
867 if (needreadwrite > 0)
868 return parseerrprint("read or write is required");
870 } else if (r == tokv_lwsp) {
871 if (needreadwrite < 0)
872 return parseerrprint("read or write not allowed");
873 r= yylex(); if (r == tokv_error) return r;
874 if (!(r & tokt_readwrite))
875 return unexpected(r,-1,"read or write (or perhaps newline)");
877 r= pa_mnl(); if (r) return r;
879 return unexpected(r,-1,"whitespace before read or write or newline");
881 ensurefdarray(fdmin);
883 if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
884 return parseerrprint("unspecified maximum only allowed"
885 " with reject-fd and ignore-fd");
886 fdmax= fdarrayused-1;
887 restfdwantstate= dtoken;
888 restfdwantrw= havereadwrite;
890 ensurefdarray(fdmax);
891 for (fd=fdmin; fd<=fdmax; fd++) {
892 fdarray[fd].wantstate= dtoken;
893 fdarray[fd].wantrw= havereadwrite;
898 /* Directives for changing error handling */
900 int df_errorstostderr(int dtoken) {
903 r= pa_mnl(); if (r) return r;
904 closeerrorfile(); eh.handling= dtoken;
908 int df_errorstosyslog(int dtoken) {
909 int token, level, facility;
911 facility= DEFUSERLOGFACILITY;
912 level= DEFUSERLOGLEVEL;
914 if (token == tokv_lwsp) {
915 token= yylex(); if (token == tokv_error) return token;
916 if (!(token & tokt_logfacility))
917 return unexpected(token,-1,"syslog facility (or end of line)");
918 facility= lr_logfacility;
921 if (token == tokv_lwsp) {
922 token= yylex(); if (token == tokv_error) return token;
923 if (!(token & tokt_loglevel))
924 return unexpected(token,-1,"syslog level (or end of line) after facility");
928 if (unexpected(token,tokv_newline,"end of line after errors-to-syslog"))
930 closeerrorfile(); eh.handling= tokv_word_errorstosyslog;
931 eh.logfacility= facility; eh.loglevel= level;
935 int df_errorstofile(int dtoken) {
940 r= paa_1path(&cp); if (r) return r;
943 return parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
944 if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
945 parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
946 fclose(file); return tokv_error;
948 closeerrorfile(); eh.handling= tokv_word_errorstofile;
949 eh.file= file; eh.filename= xstrsave(cp);
953 /* Directives for including other files or configuration data */
955 int dfi_includeuserrcfile(int dtoken) {
958 r= pa_mnl(); if (r) return r;
960 return parse_file(userrcfile,0);
963 int dfi_includeclientconfig(int dtoken) {
966 r= pa_mnl(); if (r) return r;
967 assert(overridedata);
968 return parse_string(overridedata,"<configuration override data>",0);
971 int df_include(int dtoken) {
975 r= paa_1path(&cp); if (r) return r;
976 r= parse_file(cp,&found); if (r) return r;
977 if (found || dtoken == tokv_word_includeifexist) return 0;
978 return parseerrprint(dtoken == tokv_word_includesysconfig ?
979 "system configuration file `%s' does not exist" :
980 "included file `%s' does not exist",
984 int df_includedirectory(int dtoken) {
985 static char *buildbuf=0;
986 static int buildbuflen=0;
988 int r, cpl, tel, c, found;
991 const char *p, *cpget;
994 r= paa_1path(&cpget); if (r) return r;
997 return parseerrprint("unable to open directory `%s': %s",cpget,strerror(errno));
1000 while ((errno=0, de= readdir(d))) {
1001 tel= strlen(de->d_name);
1004 if (!*p || !ISCHAR(isalnum,*p)) continue;
1005 while ((c= *++p)) if (!(ISCHAR(isalnum,c) || c=='-')) break;
1007 if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1)) {
1008 stringoverflow("pathname in directory");
1009 r= tokv_error; goto x_err;
1011 snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name);
1012 r= parse_file(buildbuf,&found); if (r) goto x_err;
1014 r= parseerrprint("unable to open file `%s' in included directory `%s': %s",
1015 de->d_name,cp,strerror(errno));
1020 parseerrprint("error reading directory `%s': %s",cp,strerror(errno));
1026 parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1039 static int oldquote = 0;
1041 int dfg_lookupquotemode(int dtoken) {
1043 r= pa_mnl(); if (r) return r;
1044 oldquote = dtoken == tokv_word_includelookupquoteold;
1048 int df_includelookup(int dtoken) {
1049 static char *buildbuf=0;
1052 char **parmvalues, **pp, *p, *q, *cp;
1055 int r, done, thisdone, cpl, c;
1057 r= pa_mwsp(); if (r) return r;
1058 r= pa_parameter(&parmvalues,0); if (r) return r;
1059 r= paa_1path(&cpget); if (r) { freecharparray(parmvalues); return r; }
1060 if (stat(cpget,&stab)) {
1061 parseerrprint("unable to access directory `%s': %s",cpget,strerror(errno));
1062 freecharparray(parmvalues); return tokv_error;
1064 if (!S_ISDIR(stab.st_mode)) {
1065 parseerrprint("object `%s' is not a directory or link to one",cpget);
1066 freecharparray(parmvalues); return tokv_error;
1069 cp= xstrsave(cpget);
1071 if (!parmvalues[0]) {
1072 if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP))) {
1073 stringoverflow("pathname in directory for lookup of undefined parameter");
1074 r= tokv_error; goto x_err;
1076 snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp);
1077 r= parse_file(buildbuf,&thisdone); if (r) goto x_err;
1078 if (thisdone) done= 1;
1081 *pp && (!done || dtoken == tokv_word_includelookupall);
1083 if (makeroom(&buildbuf,&buildbuflen,
1084 cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1)) {
1085 stringoverflow("pathname in directory for lookup");
1086 r= tokv_error; goto x_err;
1088 strcpy(buildbuf,cp);
1089 p= *pp; q= buildbuf+cpl;
1092 strcpy(q,EMPTYINCLUDELOOKUP);
1094 if (*p=='.') *q++= ':';
1099 } else if (oldquote ?
1100 !((c >= '0' && c <= '9') ||
1101 (c >= 'a' && c <= 'z') ||
1102 c == '-' || c == '_') :
1110 r= parse_file(buildbuf,&thisdone);
1112 if (thisdone) done= 1;
1116 if (makeroom(&buildbuf,&buildbuflen,
1117 cpl+1+sizeof(DEFAULTINCLUDELOOKUP))) {
1118 stringoverflow("pathname in directory for lookup of default");
1119 r= tokv_error; goto x_err;
1121 snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp);
1122 r= parse_file(buildbuf,0); if (r) goto x_err;
1127 freecharparray(parmvalues);
1132 /* Control constructs */
1134 int df_catchquit(int dtoken) {
1137 r= pa_mnl(); if (r) return r;
1138 r= parser(tokv_word_catchquit);
1139 if (r == tokv_quit || r == tokv_error) {
1140 if (r == tokv_error) {
1141 r= parse_string(RESET_CONFIGURATION,
1142 "<builtin reset configuration (caught error)>",1);
1145 r= skip(tokv_word_catchquit);
1147 if (r & tokt_controlend) {
1148 assert(r == tokv_word_hctac);
1154 int df_if(int dtoken) {
1159 r= pa_condition(&true); if (r) return r;
1160 if (!done && true) { r= parser(tokv_word_if); done= 1; }
1161 else { r= skip(tokv_word_if); }
1162 if (!(r & tokt_controlend)) return r;
1163 } while (r == tokv_word_elif);
1164 if (r == tokv_word_else) {
1165 r= pa_mnl(); if (r) return r;
1166 cstate->reportlineno= cstate->lineno;
1167 if (done) r= skip(tokv_word_if);
1168 else r= parser(tokv_word_if);
1169 if (!(r & tokt_controlend)) return r;
1171 if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1175 int df_errorspush(int dt) {
1176 struct error_handling save;
1179 r= pa_mnl(); if (r) return r;
1184 r= parser(tokv_word_errorspush);
1189 if (r & tokt_controlend) {
1190 assert(r == tokv_word_srorre);
1196 /* Miscelleanous directives */
1198 int df_cd(int dtoken) {
1202 r= paa_1path(&cp); if (r) return r;
1203 if (!chdir(cp)) return 0;
1204 return parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
1207 int df_userrcfile(int dtoken) {
1211 r= paa_1path(&cp); if (r) return r;
1212 free(userrcfile); userrcfile= xstrsave(cp);
1216 int df_message(int dtoken) {
1220 r= paa_message(&mp); if (r) return r;
1221 parseerrprint("`message' directive: %s",mp);
1225 int df_error(int dtoken) {
1229 r= paa_message(&mp); if (r) return r;
1230 return parseerrprint("`error' directive: %s",mp);
1233 int df_eof(int dtoken) {
1236 r= pa_mnl(); if (r) return r;
1240 int df_quit(int dtoken) {
1243 r= pa_mnl(); if (r) return r;
1248 * Main parser routines
1251 static void parser_push(struct parser_state *usestate,
1252 const char *newfile,
1253 const struct stat *newfilestab,
1254 YY_BUFFER_STATE ybuf,
1256 usestate->lineno= 1;
1257 usestate->reportlineno= 1;
1258 usestate->filename= newfile;
1259 usestate->filestab= *newfilestab;
1260 usestate->notedreferer= 0;
1261 usestate->isinternal= isinternal;
1262 usestate->ybuf= ybuf;
1263 usestate->upstate= cstate;
1266 yy_switch_to_buffer(ybuf);
1269 static void parser_pop(void) {
1270 struct parser_state *oldstate;
1273 cstate= cstate->upstate;
1274 if (cstate) yy_switch_to_buffer(cstate->ybuf);
1275 yy_delete_buffer(oldstate->ybuf);
1278 int parse_string(const char *string, const char *descrip, int isinternal) {
1279 /* Returns the same things as parser, except that tokv_eof is turned
1280 * into 0. *string must be statically allocated or copied, so that
1281 * it is not overwritten while the parsing takes place (unlike with
1284 static const struct stat blankstab;
1286 struct parser_state usestate;
1287 YY_BUFFER_STATE ybuf;
1290 ybuf= yy_scan_string(string);
1291 if (!ybuf) syscallerror("unable to create flex buffer for internal string");
1292 parser_push(&usestate,descrip,&blankstab,ybuf,isinternal);
1297 if (r == tokv_eof) r= 0;
1301 static int parse_file(const char *string, int *didexist) {
1302 /* Returns the same things as parser, except that tokv_eof is turned
1303 * into 0. If *didexist is 0 then errno will have been set.
1304 * *string will be copied by parse_file so it may be be overwritten
1305 * during the parsing (so, for example, yytext need not be copied).
1307 static int fileparselevel= 0;
1309 struct parser_state usestate, *checkrecurse;
1310 YY_BUFFER_STATE ybuf;
1314 struct stat newstab;
1316 if (fileparselevel >= MAX_INCLUDE_NEST)
1317 return parseerrprint("too many nested levels of included files");
1318 file= fopen(string,"r");
1320 if (errno == ENOENT) {
1321 if (didexist) *didexist= 0;
1324 return parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
1326 r= fstat(fileno(file),&newstab); if (r) syscallerror("unable to fstat new file");
1327 for (checkrecurse= cstate; checkrecurse; checkrecurse= checkrecurse->upstate) {
1328 if (!checkrecurse->filestab.st_mode) continue;
1329 if (newstab.st_dev==checkrecurse->filestab.st_dev &&
1330 newstab.st_ino==checkrecurse->filestab.st_ino) {
1332 return parseerrprint("recursion detected - config file `%s' calls itself",string);
1336 if (didexist) *didexist= 1;
1338 ybuf= yy_create_buffer(file,YY_BUF_SIZE);
1339 if (!ybuf) syscallerror("unable to create flex buffer for file");
1340 filename= xstrsave(string);
1341 parser_push(&usestate,filename,&newstab,ybuf,0);
1346 r= parseerrprint("error reading configuration file `%s'",string);
1352 if (r == tokv_eof) r= 0;
1356 static int parser(int allowce) {
1358 * an exception (error, eof or quit)
1359 * then rest of `file' is uninteresting
1361 * token if allowce was !0 and equal to token's controlend
1362 * then rest of `file' (including rest of line with the
1363 * controlend - even the whitespace) not scanned yet
1367 for (;;) { /* loop over lines */
1368 cstate->reportlineno= cstate->lineno;
1369 do { token= yylex(); } while (token == tokv_lwsp);
1370 if (token & tokt_exception) {
1372 } else if (token & tokt_controlend) {
1373 if (lr_controlend == allowce) return token;
1374 else return unexpected(token,-1,"directive (not this kind of"
1375 " control structure end)");
1376 } else if (token & tokt_directive) {
1377 if ((token & tokt_internal) && !cstate->isinternal)
1378 return unexpected(token,-1,"published directive, not internal-use-only one");
1379 r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; }
1380 } else if (token == tokv_newline) {
1381 /* ignore blank lines (and comment-only lines) */
1383 return unexpected(token,-1,"directive");