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