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