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