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