http://gimel.esc.cam.ac.uk/james/rpld/src/rpld-1.5.tar.gz
[rpld.git] / rpld_conf.y
1 /*************************************************
2 *     rpld - an IBM style RIPL server            *
3 *************************************************/
4
5 /* Copyright (c) 1999,2000, James McKenzie.
6  *                      All rights reserved
7  * Copyright (c) 1998,2000, Christopher Lightfoot.
8  *                      All rights reserved
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENCE file which can be found at the top level of
12  * the rpld distribution.
13  *
14  * IBM is a trademark of IBM corp.
15  *
16  */
17
18 /*
19  *      YACC grammar for RPLD conf file parser
20  *
21  * $Log: rpld_conf.y,v $
22  * Revision 1.7  2000/07/23 19:14:19  root
23  * #
24  *
25  * Revision 1.6  2000/07/17 11:59:45  root
26  * #
27  *
28  * Revision 1.5  2000/07/17 10:43:34  root
29  * #
30  *
31  * Revision 1.4  2000/07/16 14:05:28  root
32  * #
33  *
34  * Revision 1.3  2000/07/16 13:18:10  root
35  * #
36  *
37  * Revision 1.1  2000/07/16 13:16:33  root
38  * #
39  *
40  * Revision 1.2  1999/09/13 11:17:35  root
41  * \#
42  *
43  * Revision 1.1  1999/09/13 11:04:13  root
44  * \#
45  *
46  * Revision 1.10  1999/09/12 19:45:03  chris
47  * *** empty log message ***
48  *
49  * Revision 1.9  1999/09/12 19:14:07  chris
50  * Error messages now report name of last token scanned, instead of current yytext.
51  *
52  * Revision 1.8  1999/09/12 17:39:01  chris
53  * Configuration file now correctly builds structures; various minor problems fixed.
54  *
55  * Revision 1.7  1999/09/12 04:21:29  chris
56  * Wrote back-end to parser.
57  *
58  * Revision 1.6  1999/09/12 03:27:35  chris
59  * Added better error reporting.
60  *
61  * Revision 1.5  1999/09/12 01:05:00  chris
62  * Supports detecting start and end of blocks.
63  *
64  * Revision 1.4  1999/09/12 00:58:02  chris
65  * Added named block syntax.
66  *
67  * Revision 1.3  1999/09/11 19:00:51  chris
68  * Added support for nested blocks.
69  *
70  * Revision 1.2  1999/09/11 18:53:41  chris
71  * Added a comment to say what the file does.
72  *
73  *
74  */
75
76 %{
77
78 static char rcsid[]="$Id: rpld_conf.y,v 1.7 2000/07/23 19:14:19 root Exp $";
79
80 #include "project.h"
81
82 // state machine stuff
83
84 typedef enum {START, BLOCK_START, BLOCK_END, ASSERTION, ASSIGNMENT} THING ;
85 typedef enum {INIT, GLOBALBLOCK, HOSTBLOCK, FILEBLOCK} STATE ;
86
87 //void process_thing(THING thing, char *name, int type, YYSTYPE *pvalue);
88
89 %}
90
91 %token BLOCK_START BLOCK_END NAME TEXT NUMBER MACADDR MACADDR_PARTIAL
92
93 %union {
94                 long number;
95                 char *name;
96                 char *text;
97                 char mac_address[6];
98                 struct partial_mac {
99                         char mac_address[6];
100                         int mac_len;
101                 } pm;
102         }
103
104 %start block_list
105
106 %%
107
108 block_list:     block ';'
109         |       block_list block ';'
110         ;
111
112 block:          block_start statement_list BLOCK_END    { process_thing(BLOCK_END, "", 0, NULL); }
113         |       block_start BLOCK_END   { process_thing(BLOCK_END, "", 0, NULL); }
114
115 block_start:    NAME BLOCK_START        { process_thing(BLOCK_START, $1.name, 0, NULL); }
116         |       BLOCK_START             { process_thing(BLOCK_START, "", 0, NULL); }
117
118 statement_list: ';'
119         |       statement ';'
120         |       statement_list statement ';'
121
122
123 statement:      NAME                    { process_thing(ASSERTION, $1.name, 0, NULL); }
124         |       NAME '=' TEXT           { process_thing(ASSIGNMENT, $1.name, TEXT, &$3); }
125         |       NAME '=' NUMBER         { process_thing(ASSIGNMENT, $1.name, NUMBER, &$3); }
126         |       NAME '=' MACADDR        { process_thing(ASSIGNMENT, $1.name, MACADDR, &$3); }
127         |       NAME '=' MACADDR_PARTIAL        { process_thing(ASSIGNMENT, $1.name, MACADDR_PARTIAL, &$3); }
128         |       block
129
130 %%
131
132 //
133 // ERROR REPORTING
134 //
135
136 // the lineno variable from our parser
137 extern int lineno;
138
139 // the yytext variable from lex for error reporting
140 extern char* yytext;
141
142 void yyerror(char *s)
143 {
144         fprintf(stderr, "rpld: config line %d: %s near `%s'\n", lineno, s, yytext);
145 }
146
147 //
148 // CONFIGURATION PROCESSOR
149 //
150
151 // This is the bit that actually does the work
152
153 #define strsame(a, b)   (!strcmp((a), (b)))
154 /*
155 struct global_parameters
156 {
157
158 } g_params;
159 */
160
161 struct clientinfo
162 {
163         int have_mac;
164         int have_run_addr;
165         int have_files;
166         // optional
167         int have_framesize;
168         int have_blocksize;
169         int have_pacing;
170         int have_nospew;
171 };
172
173 struct clfileinfo
174 {
175         int have_path;
176         int have_load_addr;
177         // optional
178         int have_offset;
179         int have_length;
180 };
181
182 extern struct client *clients;
183
184 // hideous, we need to cope with a semicolon after this
185 //#define THROW_ERROR(a)        do { yyerror((a)); exit(1); } while(0)
186 #define THROW_ERROR(a)  do { fprintf(stderr, "rpld: config line %d: %s near `%s'\n", lineno, (a), name); exit(1); } while(0)
187
188 void process_thing(THING thing, char *name, int type, YYSTYPE *pvalue)
189 {
190         static STATE state;
191         static struct client *pc;
192         static struct clientinfo ci;
193         static struct clfile *pcf;
194         static struct clfileinfo cfi;
195
196         switch(thing)
197         {
198         // boot the state machine
199         case START:
200                 state = INIT;
201                 break;
202
203         //
204         // Blocks, which contain related options
205         //
206         
207         // start of a block
208         case BLOCK_START:
209                 // in initial state, move to GLOBALBLOCK or HOSTBLOCK
210                 if (state == INIT) {
211                         if (strsame(name, "GLOBAL")) {
212                                 state = GLOBALBLOCK;
213                                 break;
214                         } else if (strsame(name, "HOST") || strsame(name, "CLIENT")) {
215                                 // construct a new client entity
216                                 pc = (struct client*)malloc(sizeof(struct client));
217                                 bzero(pc, sizeof(struct client));
218                                 // reset info about what options have been set for this client
219                                 bzero(&ci, sizeof(ci));
220
221                                 pc->blocklen=MY_BLOCK_LEN;
222                                 pc->framelen=MY_FRAME_LEN;
223                                 pc->pacing=MY_PACING;
224                                 
225                                 state = HOSTBLOCK;
226                                 break;
227                         } else THROW_ERROR("Unknown top-level parameter block");
228                 }
229                 // in a HOST block, this must be a FILE
230                 else if (state == HOSTBLOCK) {
231                         if (strsame(name, "FILE")) {
232                                 // construct a new file entity
233                                 pcf = (struct clfile*)malloc(sizeof(struct clfile));
234                                 bzero(pcf, sizeof(struct clfile));
235                                 pcf->length=-1;
236                                 // reset info about options set for this file
237                                 bzero(&cfi, sizeof(cfi));
238                                 
239                                 state = FILEBLOCK;
240                                 break;
241                         } else THROW_ERROR("Only a FILE parameter block can be included in a HOST block");
242                 }
243                 // fuck knows
244                 else
245                 {
246                         yyerror("Unknown parameter block");
247                         exit(1);
248                 }
249                 break;
250
251         // end of a block, we should have a bunch of info now
252         case BLOCK_END:
253                 // end GLOBAL block
254                 if (state == GLOBALBLOCK) {
255                         // no more global params, at least for the moment
256                 }
257                 // end HOST block
258                 else if (state == HOSTBLOCK) {
259                         // should have a complete host specification
260                         if (!ci.have_mac) THROW_ERROR("Must specify an ethernet (MAC) address for host");
261                         else if (!ci.have_run_addr) THROW_ERROR("Must specify an initial execute address for host");
262                         else if (!ci.have_files) THROW_ERROR("Must specify at least one file to load for host");
263
264                         // OK, should have an entire host spec, so copy it in
265                         pc->next = clients;
266                         clients = pc;
267
268                         // finished this host spec
269                         state = INIT;
270                         break;
271                 }
272                 // end FILE block
273                 else if (state == FILEBLOCK) {
274                         // should have a complete file specification
275                         if (!cfi.have_path) THROW_ERROR("Must specify a path for file");
276                         else if (!cfi.have_load_addr) THROW_ERROR("Must specify a load address for file");
277
278                         // have an entire file spec, copy it into the host spec
279                         pcf->next = pc->files;
280                         pc->files = pcf;
281
282                         ci.have_files = 1;
283
284                         // done
285                         state = HOSTBLOCK;
286                         break;
287                 }
288
289         //
290         // The various things that go inside blocks
291         //
292         
293         case ASSERTION:
294                 if (state == GLOBALBLOCK) {
295                         // no global assertions ATM
296                         THROW_ERROR("Unknown directive");
297                 } else if (state == HOSTBLOCK) {
298                         // no host assertions ATM
299                         if (strsame(name,"nospew")) {
300                                 if (ci.have_pacing) THROW_ERROR("Directive nospew incompatible with pacing ");
301                                 if (ci.have_nospew) THROW_ERROR("Repeated directive");
302                                 ci.have_nospew=1;
303                                 pc->nospew++;
304                         } else THROW_ERROR("Unknown directive");
305                 } else if (state == FILEBLOCK) {
306                         if (strsame(name,"linux")) {
307                                 if (!cfi.have_path) THROW_ERROR("A path to a
308 valid kernel must precede linux");
309
310                                 do_linux_kernel(pc,pcf);
311                                 cfi.have_load_addr=1;
312                                 cfi.have_offset=1;
313                                 cfi.have_length=1;
314                                 ci.have_run_addr=1;
315                         }else{
316                         THROW_ERROR("Unknown directive");
317                         }
318                 } else THROW_ERROR("Unknown directive");
319                 break;
320
321         case ASSIGNMENT:
322                 if (state == GLOBALBLOCK) {
323                         // no global assignments ATM
324                         THROW_ERROR("Unknown directive");
325                 } else if (state == HOSTBLOCK) {
326                         // ethernet address
327                         if (strsame(name, "ethernet") || strsame(name, "mac")) {
328                                 if (type != MACADDR && type != MACADDR_PARTIAL)
329                                         THROW_ERROR("Directive must be followed by a (partial or complete) ethernet address");
330                                 else if (ci.have_mac) THROW_ERROR("Repeated directive");
331
332                                 if (type == MACADDR) {
333                                         // set MAC address; this is non-partial, so len = ETH_ALEN
334                                         bcopy(pvalue->mac_address, pc->mac, ETH_ALEN);
335                                         pc->partial_mac_len = ETH_ALEN;
336                                 } else {
337                                         bcopy(pvalue->pm.mac_address, pc->mac, pvalue->pm.mac_len);
338                                         pc->partial_mac_len = pvalue->pm.mac_len;
339                                 }
340
341                                 ci.have_mac = 1;
342                         }
343                         // execute address
344                         else if (strsame(name, "execute") || strsame(name, "run")) {
345                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by a memory address");
346                                 else if (ci.have_run_addr==2) THROW_ERROR("Repeated directive");
347
348                                 // set address
349                                 pc->run_addr = pvalue->number;
350                                 ci.have_run_addr = 2;
351                         }
352                         else if (strsame(name, "blocksize")) {
353                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by an integer ");
354                                 else if (ci.have_blocksize) THROW_ERROR("Repeated directive");
355
356                                 // set address
357                                 pc->blocklen = pvalue->number;
358                                 ci.have_blocksize = 1;
359                         }
360                         else if (strsame(name, "framesize")) {
361                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by an integer ");
362                                 else if (ci.have_framesize) THROW_ERROR("Repeated directive");
363
364                                 // set size
365                                 pc->framelen = pvalue->number;
366                                 ci.have_framesize = 1;
367                         }
368                         else if (strsame(name,"pacing")) {
369                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by an integer ");
370                                 if (ci.have_nospew) THROW_ERROR("Directive pacing incompatible with nospew ");
371                                 if (ci.have_pacing) THROW_ERROR("Repeated directive");
372                                 pc->pacing = pvalue->number;
373                                 ci.have_pacing =1;
374                         }
375                         else THROW_ERROR("Unknown directive");
376                 } else if (state == FILEBLOCK) {
377                         // path
378                         if (strsame(name, "path")) {
379                                 if (type != TEXT) THROW_ERROR("Directive must be followed by a filename");
380                                 else if (cfi.have_path) THROW_ERROR("Repeated directive");
381
382                                 // set filename
383                                 pcf->path = pvalue->text;
384                                 cfi.have_path = 1;
385                         }
386                         // load address
387                         else if (strsame(name, "load")) {
388                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by a memory address");
389                                 else if (cfi.have_load_addr) THROW_ERROR("Repeated directive");
390
391                                 // set load address
392                                 pcf->load_addr = pvalue->number;
393                                 cfi.have_load_addr = 1;
394                         }
395                         // offset
396                         else if (strsame(name, "offset")) {
397                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by a memory address");
398                                 else if (cfi.have_offset) THROW_ERROR("Repeated directive");
399
400                                 // set offset
401                                 pcf->offset = pvalue->number;
402                                 cfi.have_offset = 1;
403                         }
404                         // length
405                         else if (strsame(name, "length")) {
406                                 if (type != NUMBER) THROW_ERROR("Directive must be followed by a memory address");
407                                 else if (cfi.have_length) THROW_ERROR("Repeated directive");
408
409                                 // set length
410                                 pcf->length = pvalue->number;
411                                 cfi.have_length = 1;
412                         }
413                         else THROW_ERROR("Unknown directive");
414                 }
415                 break;
416
417                 default: THROW_ERROR("Mistake");
418         }
419         
420
421 }