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