]> git.saurik.com Git - apple/system_cmds.git/blob - mach_init.tproj/parser.c
system_cmds-175.tar.gz
[apple/system_cmds.git] / mach_init.tproj / parser.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * bootstrap -- fundamental service initiator and port server
26 * Mike DeMoney, NeXT, Inc.
27 * Copyright, 1990. All rights reserved.
28 *
29 * parser.c -- configuration file parser
30 */
31 #import <mach/boolean.h>
32 #import <mach/port.h>
33
34 #import <string.h>
35 #import <libc.h>
36 #import <ctype.h>
37 #import <stdio.h>
38
39 #import "lists.h"
40 #import "bootstrap_internal.h"
41 #import "error_log.h"
42 #import "parser.h"
43
44
45 #ifndef ASSERT
46 #define ASSERT(p)
47 #endif
48
49 #define MAX_TOKEN_LEN 128
50
51 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
52 #define LAST_ELEMENT(x) ((x)[NELEM(x)-1])
53 #define STREQ(a, b) (strcmp(a, b) == 0)
54 #define NEW(type, num) ((type *)ckmalloc(sizeof(type) * (num)))
55
56 typedef enum {
57 ASSIGN_TKN, EOF_TKN, FORWARD_TKN, INIT_TKN, NUM_TKN,
58 RESTARTABLE_TKN, SELF_TKN, SEMICOLON_TKN, SERVER_TKN, SERVICES_TKN,
59 STRING_TKN, ERROR_TKN, PRI_TKN
60 } token_t;
61
62 typedef struct {
63 char *string;
64 token_t token;
65 } keyword_t;
66
67 static keyword_t keywords[] = {
68 { "forward", FORWARD_TKN },
69 { "init", INIT_TKN },
70 { "priority", PRI_TKN },
71 { "restartable", RESTARTABLE_TKN },
72 { "self", SELF_TKN },
73 { "server", SERVER_TKN },
74 { "services", SERVICES_TKN },
75 { NULL, ERROR_TKN }
76 };
77
78 static FILE *conf;
79 static int (*charget)(void);
80 static const char *default_conf_ptr;
81
82 static char token_string[MAX_TOKEN_LEN];
83 static token_t current_token;
84 static int token_num;
85 static int token_priority;
86 static int peekc;
87
88 static int get_from_conf(void);
89 static int get_from_default(void);
90 static boolean_t parse_conf_file(void);
91 static boolean_t parse_self(void);
92 static boolean_t parse_server(void);
93 static boolean_t parse_service(server_t *serverp);
94 static boolean_t parse_pri(void);
95 static void advance_token(void);
96 static token_t keyword_lookup(void);
97
98 /*
99 * init_config -- read configuration file and build-up initial server and
100 * service lists
101 *
102 * If can't find a suitable bootstrap.conf, use configuration given in
103 * bootstrap.c to get system booted so someone can correct bootstrap.conf
104 */
105 void
106 init_config(void)
107 {
108 boolean_t parse_ok;
109
110 conf = fopen(conf_file, "r");
111 if (conf != NULL)
112 charget = get_from_conf;
113 else {
114 error("Can't open %s -- using default configuration", conf_file);
115 charget = get_from_default;
116 }
117
118 parse_ok = parse_conf_file();
119 if ( ! parse_ok && charget == get_from_conf) {
120 error("Can't parse %s -- using default configuration", conf_file);
121 charget = get_from_default;
122 init_lists();
123 peekc = 0;
124 parse_ok = parse_conf_file();
125 }
126 if ( ! parse_ok )
127 fatal("Can't parse default configuration file");
128 }
129
130 /*
131 * Function pointer "charget" points at get_from_conf or get_from_default
132 */
133 static int
134 get_from_conf(void)
135 {
136 return getc(conf);
137 }
138
139 static int
140 get_from_default(void)
141 {
142 int c;
143
144 if (default_conf_ptr == NULL)
145 default_conf_ptr = default_conf;
146
147 if (c = *default_conf_ptr)
148 default_conf_ptr++;
149 if (c == '\0')
150 c = EOF;
151 return c;
152 }
153
154 /*
155 * What follows is a simple recursive descent parser
156 * ("we don't need no stinkin' yacc")
157 */
158 static boolean_t
159 parse_conf_file(void)
160 {
161 boolean_t parse_ok, good_parse;
162
163 /*
164 * Configuration file syntax (and parsing routines).
165 *
166 * (parse_conf_file)
167 * CONF_FILE := STMT [ ; STMT ]* [ ; ]
168 * STMT := SERVER | SERVICE | SELF | forward | initpri
169 *
170 * (parse_server)
171 * SERVER := [ restartable ] ( server | init ) SERVER_PATH_ARGS [ SERVICE ]
172 *
173 * (parse_service)
174 * SERVICE := services [ SERVICE_DECL ]+
175 * SERVICE_DECL := SERVICE_NAME
176 *
177 * (parse_self)
178 * SELF := self [ priority = NUM ] SERVICE_DECL
179 *
180 * Or more simply, just a list of:
181 *
182 * [[restartable] (server|init) SERVER_PATH_ARGS] [ priority = NUM ]
183 * [services SERVICE_NAME [ SERVICE_NAME [ = NUM ]]*] ;
184 *
185 * self [ SERVICE_NAME ]+
186 *
187 * [ forward ]
188 *
189 */
190 advance_token();
191 if (current_token == EOF_TKN) {
192 error("Empty configuration file: %s", conf_file);
193 return FALSE;
194 }
195
196 good_parse = TRUE;
197 while (current_token != EOF_TKN) {
198 parse_ok = TRUE;
199 switch (current_token) {
200 case RESTARTABLE_TKN:
201 case SERVER_TKN:
202 case INIT_TKN:
203 parse_ok = parse_server();
204 break;
205 case SERVICES_TKN:
206 parse_ok = parse_service(NULL);
207 break;
208 case SELF_TKN:
209 parse_ok = parse_self();
210 break;
211 case FORWARD_TKN:
212 forward_ok = TRUE;
213 advance_token();
214 break;
215 case SEMICOLON_TKN:
216 advance_token();
217 break;
218 case EOF_TKN:
219 break;
220 default:
221 parse_error(token_string, "start of new declaration");
222 parse_ok = FALSE;
223 break;
224 }
225 switch (current_token) {
226 case SEMICOLON_TKN:
227 advance_token();
228 break;
229 case EOF_TKN:
230 break;
231 default:
232 if (parse_ok)
233 parse_error(token_string, "expected ';'");
234 /* Try to re-sync with input */
235 while (current_token != SEMICOLON_TKN && current_token != EOF_TKN)
236 advance_token();
237 parse_ok = FALSE;
238 break;
239 }
240 if (! parse_ok)
241 good_parse = FALSE;
242 }
243 return good_parse;
244 }
245
246 static boolean_t
247 parse_self(void)
248 {
249 name_t name;
250
251 ASSERT(current_token == SELF_TKN);
252 advance_token(); /* Skip SELF_TKN */
253 if (current_token == PRI_TKN) {
254 boolean_t ok;
255 ok = parse_pri();
256 if (!ok)
257 return FALSE;
258 init_priority = token_priority;
259 }
260 while (current_token == STRING_TKN) {
261 if (strlen(token_string) >= sizeof(name_t)) {
262 parse_error(token_string, "Service name too long");
263 return FALSE;
264 }
265 if (lookup_service_by_name(&bootstraps, token_string) != NULL)
266 {
267 parse_error(token_string, "Service name previously declared");
268 return FALSE;
269 }
270 strcpy(name, token_string);
271 advance_token();
272 (void) new_service(&bootstraps, name, MACH_PORT_NULL, ACTIVE, SELF,
273 NULL_SERVER);
274 }
275 return TRUE;
276 }
277
278 static boolean_t
279 parse_server(void)
280 {
281 server_t *serverp;
282 servertype_t servertype = SERVER;
283
284 if (current_token == RESTARTABLE_TKN) {
285 advance_token();
286 servertype = RESTARTABLE;
287 }
288 switch (current_token) {
289 case SERVER_TKN:
290 advance_token();
291 break;
292 case INIT_TKN:
293 if (find_init_server() != NULL) {
294 parse_error(token_string,
295 "Can't specify multiple init servers");
296 return FALSE;
297 }
298 if (servertype == RESTARTABLE) {
299 parse_error(token_string,
300 "Init server can not be restartable");
301 return FALSE;
302 }
303 servertype = ETCINIT;
304 advance_token();
305 break;
306 default:
307 parse_error(token_string, "expected \"server\" or \"init\"");
308 return FALSE;
309 }
310 if (current_token == PRI_TKN) {
311 boolean_t ok;
312 ok = parse_pri();
313 if (!ok)
314 return FALSE;
315 } else
316 token_priority = BASEPRI_USER;
317 if (current_token != STRING_TKN) {
318 parse_error(token_string,
319 "expected string giving server to exec");
320 return FALSE;
321 }
322 serverp = new_server(servertype, token_string, token_priority);
323 advance_token();
324 if (current_token == SERVICES_TKN)
325 return parse_service(serverp);
326 return TRUE;
327 }
328
329 static boolean_t
330 parse_service(server_t *serverp)
331 {
332 name_t name;
333
334 ASSERT(current_token == SERVICES_TKN);
335 advance_token(); /* Skip SERVICES_TKN */
336 while (current_token == STRING_TKN) {
337 if (strlen(token_string) >= sizeof(name_t)) {
338 parse_error(token_string, "Service name too long");
339 return FALSE;
340 }
341 if (lookup_service_by_name(&bootstraps, token_string) != NULL)
342 {
343 parse_error(token_string, "Service name previously declared");
344 return FALSE;
345 }
346 strcpy(name, token_string);
347 advance_token();
348 (void) new_service(&bootstraps, name, MACH_PORT_NULL, !ACTIVE,
349 DECLARED, serverp);
350 }
351 return TRUE;
352 }
353
354 /*
355 * Parse priority=NUM
356 */
357 static boolean_t
358 parse_pri(void)
359 {
360 ASSERT(current_token == PRI_TKN);
361 advance_token(); /* Skip PRI_TKN */
362 if (current_token != ASSIGN_TKN) {
363 parse_error(token_string, "expected '='");
364 return FALSE;
365 }
366 advance_token(); /* Skip = */
367 if (current_token != NUM_TKN) {
368 parse_error(token_string, "expected NUM");
369 return FALSE;
370 }
371 advance_token(); /* Skip NUM */
372 token_priority = token_num;
373 return TRUE;
374 }
375 /*
376 * advance_token -- advances input to next token
377 * Anything from a '#' on is comment and ignored
378 *
379 * On return:
380 * current_token contains token_t of next token
381 * token_string contains string value of next token
382 * if token was number, token_num contains numeric value of token
383 */
384 static void
385 advance_token(void)
386 {
387 char *cp;
388
389 again:
390 while (peekc == '\0' || isspace(peekc))
391 peekc = (*charget)();
392
393 /* Skip comments */
394 if (peekc == '#') {
395 while (peekc != EOF && peekc != '\n')
396 peekc = (*charget)();
397 goto again;
398 }
399
400 cp = token_string;
401 *cp = '\0';
402
403 if (peekc == EOF) {
404 current_token = EOF_TKN;
405 return;
406 }
407
408 if (isalpha(peekc) || peekc == '\\') {
409 /*
410 * this only allows names to be alphanumerics, '_', and
411 * backslash escaped characters.
412 * If you want something fancier, use "..."
413 */
414 current_token = STRING_TKN; /* guarantee it's not ERROR_TKN */
415 for (; isalnum(peekc) || peekc == '_' || peekc == '\\';
416 peekc = (*charget)()) {
417 if (cp >= &LAST_ELEMENT(token_string)) {
418 cp = token_string;
419 parse_error(token_string, "token too long");
420 current_token = ERROR_TKN;
421 }
422 if (peekc == '\\')
423 peekc = (*charget)();
424 *cp++ = peekc;
425 }
426 *cp = '\0';
427 if (current_token != ERROR_TKN)
428 current_token = keyword_lookup();
429 return;
430 }
431
432 /* Handle "-quoted strings */
433 if (peekc == '"') {
434 peekc = (*charget)();
435 for (; peekc != EOF && peekc != '"'; peekc = (*charget)()) {
436 if (cp >= &LAST_ELEMENT(token_string)) {
437 cp = token_string;
438 parse_error(token_string, "token too long");
439 current_token = ERROR_TKN;
440 }
441 if (peekc == '\\')
442 peekc = (*charget)();
443 if (peekc == '\n') {
444 cp = token_string;
445 parse_error(token_string, "Missing \"");
446 current_token = ERROR_TKN;
447 }
448 *cp++ = peekc;
449 }
450 if (peekc == EOF) {
451 cp = token_string;
452 parse_error(token_string, "Missing \"");
453 current_token = ERROR_TKN;
454 } else
455 peekc = (*charget)(); /* skip closing " */
456 *cp = '\0';
457 if (current_token != ERROR_TKN)
458 current_token = STRING_TKN;
459 return;
460 }
461
462 if (isdigit(peekc)) {
463 for (token_num = 0; isdigit(peekc); peekc = (*charget)())
464 token_num = token_num * 10 + peekc - '0';
465 current_token = NUM_TKN;
466 return;
467 }
468
469 if (peekc == ';') {
470 peekc = (*charget)();
471 current_token = SEMICOLON_TKN;
472 return;
473 }
474
475 if (peekc == '=') {
476 peekc = (*charget)();
477 current_token = ASSIGN_TKN;
478 return;
479 }
480
481 current_token = ERROR_TKN;
482 return;
483 }
484
485 static token_t
486 keyword_lookup(void)
487 {
488 keyword_t *kwp;
489
490 for (kwp = keywords; kwp->string; kwp++)
491 if (STREQ(kwp->string, token_string))
492 return kwp->token;
493 return STRING_TKN;
494 }
495