Commit | Line | Data |
---|---|---|
9a6eb889 NW |
1 | /* |
2 | * Copyright (c) 1993 Christoph M. Robitschko | |
800ffe89 NW |
3 | * All rights reserved. |
4 | * | |
800ffe89 NW |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
9a6eb889 NW |
15 | * This product includes software developed by Christoph M. Robitschko |
16 | * 4. The name of the author may not be used to endorse or promote products | |
17 | * derived from this software withough specific prior written permission | |
800ffe89 | 18 | * |
9a6eb889 NW |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
800ffe89 NW |
29 | */ |
30 | ||
31 | /* | |
32 | * configure.c | |
33 | * the main part of the configuration subsystem. | |
34 | */ | |
35 | ||
36 | #ifdef CONFIGURE | |
37 | ||
38 | #include <stdlib.h> | |
39 | #include <unistd.h> | |
40 | #include <string.h> | |
41 | #include <ttyent.h> | |
42 | #include <sys/types.h> | |
43 | #include <sys/signal.h> | |
44 | #include <sys/time.h> | |
45 | #include <sys/resource.h> | |
46 | #include <sys/file.h> | |
47 | #include <syslog.h> | |
48 | #include <stdio.h> | |
49 | ||
50 | #include "init.h" | |
51 | #include "prototypes.h" | |
52 | #include "cf_defs.h" | |
53 | ||
54 | ||
55 | ||
56 | /* Prototypes for local functions: */ | |
57 | static int evaluate_line(char *, int, int, char **, int); | |
58 | static int parseline(char *, int, int, char **); | |
59 | ||
60 | ||
61 | char *fgetline(FILE *, size_t *); /* XXX */ | |
62 | ||
63 | ||
64 | extern const struct Command Commands[]; | |
65 | extern const int NCommands; | |
66 | ||
67 | ||
68 | ||
69 | /* | |
70 | * CONFIGURE | |
71 | * read a configuration file | |
72 | */ | |
73 | void | |
74 | configure(filename) | |
75 | char *filename; | |
76 | { | |
77 | static int ncalled = 0; | |
78 | FILE *cf; | |
79 | char *line, *s, *errmsg; | |
80 | int lineno; | |
81 | char **cline; | |
82 | int argc; | |
83 | ||
84 | ||
85 | Debug (1, "Configuring from file %s", filename); | |
86 | if (++ncalled > 50) { | |
87 | syslog(LOG_ERR, "include loop in configuration files !"); | |
88 | ncalled --; | |
89 | return; | |
90 | } | |
91 | ||
92 | cf = fopen(filename, "r"); | |
93 | if (!cf) { | |
94 | Debug(1, "Could not open config file %s: %m", filename); | |
95 | ncalled --; | |
96 | return; | |
97 | } | |
98 | Debug(1, "Config file %s opened.", filename); | |
99 | ||
100 | lineno = 0; | |
101 | while ((line = fgetline(cf, (size_t *)0))) { | |
102 | lineno ++; | |
103 | if (*line == '#') | |
104 | continue; /* Skip comment line */ | |
105 | for (s = line; *s; s++) | |
106 | if ((*s != ' ') && (*s != '\t')) | |
107 | break; | |
108 | if (!*s) | |
109 | continue; /* Skip empty line */ | |
110 | cline = string_to_argv(line, &argc, &errmsg); | |
111 | if (errmsg) | |
112 | syslog(LOG_ERR, "%s line %d: %s", filename, lineno, errmsg); | |
113 | if (cline && argc > 0) | |
114 | (void)parseline(filename, lineno, argc, cline); | |
115 | ||
116 | } | |
117 | fclose(cf); | |
118 | ncalled --; | |
119 | } | |
120 | ||
121 | ||
122 | /* | |
123 | * Local support functions | |
124 | */ | |
125 | ||
126 | /* PARSELINE -- the obvious */ | |
127 | static int | |
128 | parseline(configfile, linenumber, argc, argv) | |
129 | char *configfile; | |
130 | int linenumber; | |
131 | int argc; | |
132 | char **argv; | |
133 | { | |
134 | int f, level; | |
135 | int nmatch, wmatch; | |
136 | char comline[1024]; /* XXX should test for overflow */ | |
137 | ||
138 | ||
139 | comline[0] = '\0'; | |
140 | /* Search through the Commands list; | |
141 | * Each argv[level] is matched against a list of Commands of the same level, | |
142 | * which must be subcommands of argv[level-1] if level > 0. | |
143 | ||
144 | * There are two types of Matches: T_EX 'Exact match' means that argv[level] | |
145 | * is an abbreviation of Commands[f].name, case ignored. | |
146 | * T_STR, T_NUM etc match always (the validity of argv[level] is checked | |
147 | * when it is evaluated, later), unless there has already been an exact | |
148 | * match for the same argv[level]. | |
149 | ||
150 | * Note that the search is not aborted after a match is found, but | |
151 | * continues up to the end of the list or the end of the scope of the | |
152 | * previous (sub)command (i.e. if Commands[f].level < level). | |
153 | * At that point, exactly one match must have been found. The search | |
154 | * is then restarted for the next level, beginning at the previous match. | |
155 | */ | |
156 | ||
157 | for (f=nmatch=wmatch=level=0; f<NCommands; f++) { | |
158 | if (((Commands[f].level & LEVEL) == level) && | |
159 | (((Commands[f].type != T_EX) && (!nmatch)) || /* Values match always, unless there was an exact match previously */ | |
160 | (!strCcmp(argv[level], Commands[f].name)))) /* Exact match */ | |
161 | nmatch++, wmatch = f; | |
162 | ||
163 | if ((f == NCommands -1) || ((Commands[f].level & LEVEL) < level)) | |
164 | switch (nmatch) { | |
165 | case 0: syslog(LOG_ERR, "%s line %d: unknown word \"%s\"", configfile, linenumber, argv[level]); | |
166 | return(-1); | |
167 | default:syslog(LOG_ERR, "%s line %d: \"%s\" is ambiguous", configfile, linenumber, argv[level]); | |
168 | return(-1); | |
169 | case 1: if (level) strcat (comline, " "); | |
170 | strcat (comline, Commands[wmatch].name); | |
171 | if (Commands[wmatch].level & SUB) { | |
172 | if (++level >= argc) { | |
173 | syslog(LOG_ERR, "%s line %d: required parameter missing to command \"%s\"", configfile, linenumber, comline); | |
174 | return (-1); | |
175 | } | |
176 | f = wmatch; | |
177 | nmatch = 0; | |
178 | } else { /* XXX OPT not yet implemented */ | |
179 | if (level +1 != argc) { | |
180 | syslog(LOG_ERR, "%s line %d: too many arguments to command \"%s\"", configfile, linenumber, comline); | |
181 | return(-1); | |
182 | } | |
183 | Debug(3, "%s line %d: OK \"%s\"", configfile, linenumber, comline); | |
184 | return(evaluate_line(configfile, linenumber, argc, argv, wmatch)); | |
185 | } | |
186 | break; | |
187 | } | |
188 | /* NOTREACHED */ | |
189 | } | |
190 | return(-1); /* to stop gcc from complaining */ | |
191 | } | |
192 | ||
193 | ||
194 | ||
195 | /* | |
196 | * Evaluate_line | |
197 | * Execute the action (set a variable, call a function) specified in the | |
198 | * Commands entry of the *last* argument on the config line. | |
199 | */ | |
200 | /* XXX no support for string values; only last argument evaluated */ | |
201 | static int | |
202 | evaluate_line(cfgfile, lineno, argc, argv, wmatch) | |
203 | char *cfgfile; | |
204 | int lineno, argc, wmatch; | |
205 | char **argv; | |
206 | { | |
207 | char *s, *arg; | |
208 | long tmp, tmp2; | |
209 | ||
210 | ||
211 | arg = argv[argc-1]; | |
212 | if (Commands[wmatch].flags & CFUNC) { /* Call the function */ | |
213 | int (* func)() = Commands[wmatch].var; | |
214 | return(func(cfgfile, lineno, argc, argv, Commands[wmatch].val)); | |
215 | } | |
216 | ||
217 | switch (Commands[wmatch].type) { | |
218 | case T_STR: /* set var to String */ | |
219 | s = newstring(arg); | |
220 | if (s) /* XXX ohne Geld ka Musi */ | |
221 | *(char **)Commands[wmatch].var = s; | |
222 | return(0); /* XXX Flags handling */ | |
223 | case T_EX: /* Copy val to *var */ | |
224 | tmp = Commands[wmatch].val.intval; | |
225 | break; | |
226 | case T_INT: /* Arg is an integer */ | |
227 | tmp = strtol(arg, &s, 0); | |
228 | if ((!*arg) || (*s)) { | |
229 | syslog(LOG_ERR, "%s line %d: \"%s\" is not an integer", cfgfile, lineno, arg); | |
230 | return(-1); | |
231 | } | |
232 | break; | |
233 | case T_BYTE: /* Arg is a byte limit */ | |
234 | tmp = strtol(arg, &s, 0); | |
235 | if ((tmp < 0) || (*arg > '9') || (*arg < '0')) | |
236 | goto notbyte; | |
237 | switch (*s) { | |
238 | case '\0': | |
239 | tmp *= 1024; | |
240 | break; | |
241 | case 'm': case 'M': | |
242 | tmp *= 1024; | |
243 | /* no break */ | |
244 | case 'k': case 'K': | |
245 | tmp *= 1024; | |
246 | /* no break */ | |
247 | case 'b': case 'B': | |
248 | if (!*++s) | |
249 | break; | |
250 | default: | |
251 | notbyte: | |
252 | syslog(LOG_ERR, "%s line %d: \"%s\" is not a valid bytecount", cfgfile, lineno, arg); | |
253 | return(-1); | |
254 | } | |
255 | break; | |
256 | case T_TIME: /* Arg is a time limit */ | |
257 | tmp = strtol(arg, &s, 0); | |
258 | if ((tmp < 0) || (*arg > '9') || (*arg < '0')) | |
259 | goto nottime; | |
260 | switch (*s) { | |
261 | case '\0': | |
262 | break; | |
263 | case 'h': case 'H': | |
264 | tmp *= 60; | |
265 | /* no break */ | |
266 | case 'm': case 'M': | |
267 | tmp *= 60; | |
268 | /* no break */ | |
269 | case 's': case 'S': | |
270 | if (!*++s) | |
271 | break; | |
272 | case ':': | |
273 | if ((tmp < 0) || (tmp > 59)) goto nottime; | |
274 | tmp *= 60; | |
275 | if ((*++s < '0') || (*s > '9')) goto nottime; | |
276 | tmp2 = strtol(s, &s, 0); | |
277 | if ((tmp2 < 0) || (tmp2 > 59)) goto nottime; | |
278 | tmp += tmp2; | |
279 | if (!*s) break; | |
280 | if (*s != ':') goto nottime; | |
281 | tmp *= 60; | |
282 | if ((*++s < '0') || (*s > '9')) goto nottime; | |
283 | tmp2 = strtol(s, &s, 0); | |
284 | if ((tmp2 < 0) || (tmp2 > 59)) goto nottime; | |
285 | tmp += tmp2; | |
286 | if (!*s) break; | |
287 | /* no break */ | |
288 | default: | |
289 | nottime: | |
290 | syslog(LOG_ERR, "%s line %d: \"%s\" is not a valid timespan", cfgfile, lineno, arg); | |
291 | return(-1); | |
292 | } | |
293 | break; | |
294 | default: | |
295 | syslog(LOG_ERR, "internal error: unknown argument type.", cfgfile, lineno); | |
296 | return(-1); | |
297 | } | |
298 | ||
299 | if (Commands[wmatch].flags & MAXVAL) | |
300 | if (tmp > Commands[wmatch].val.intval) { | |
301 | syslog(LOG_ERR, "%s line %d: \"%s\" (%d) is bigger than the allowed maximum (%d)", cfgfile, lineno, arg, tmp, Commands[wmatch].val.intval); | |
302 | return(-1); | |
303 | } | |
304 | if (Commands[wmatch].flags & NRAISE) | |
305 | if (tmp > *(int *)(Commands[wmatch].var)) { | |
306 | syslog(LOG_ERR, "%s line %d: Unable to raise limit beyond the current maximum (%d)", cfgfile, lineno, *(int *)Commands[wmatch].var); | |
307 | return(-1); | |
308 | } | |
309 | ||
310 | if (Commands[wmatch].var) | |
311 | *(int *)Commands[wmatch].var = tmp; | |
312 | return(0); | |
313 | } | |
314 | ||
315 | ||
316 | #endif /* CONFIGURE */ |