|
|
1.1 root 1: /* Boot script parser for Mach. */
2:
3: /* Written by Shantanu Goel ([email protected]). */
4:
5: #include <mach/mach_types.h>
6: #if !KERNEL || OSKIT_MACH
7: #include <string.h>
8: #endif
9: #include "boot_script.h"
10:
11:
12: /* This structure describes a symbol. */
13: struct sym
14: {
15: /* Symbol name. */
16: const char *name;
17:
18: /* Type of value returned by function. */
19: int type;
20:
21: /* Symbol value. */
22: int val;
23:
24: /* For function symbols; type of value returned by function. */
25: int ret_type;
26:
27: /* For function symbols; if set, execute function at the time
28: of command execution, not during parsing. A function with
29: this field set must also have `no_arg' set. Also, the function's
30: `val' argument will always be NULL. */
31: int run_on_exec;
32: };
33:
34: /* Additional values symbols can take.
35: These are only used internally. */
36: #define VAL_SYM 10 /* symbol table entry */
37: #define VAL_FUNC 11 /* function pointer */
38:
39: /* This structure describes an argument. */
40: struct arg
41: {
42: /* Argument text copied verbatim. 0 if none. */
43: char *text;
44:
45: /* Type of value assigned. 0 if none. */
46: int type;
47:
48: /* Argument value. */
49: int val;
50: };
51:
52: /* List of commands. */
53: static struct cmd **cmds = 0;
54:
55: /* Amount allocated for `cmds'. */
56: static int cmds_alloc = 0;
57:
58: /* Next available slot in `cmds'. */
59: static int cmds_index = 0;
60:
61: /* Symbol table. */
62: static struct sym **symtab = 0;
63:
64: /* Amount allocated for `symtab'. */
65: static int symtab_alloc = 0;
66:
67: /* Next available slot in `symtab'. */
68: static int symtab_index = 0;
69:
70: /* Create a task and suspend it. */
71: static int
72: create_task (struct cmd *cmd, int *val)
73: {
74: int err = boot_script_task_create (cmd);
75: *val = (int) cmd->task;
76: return err;
77: }
78:
79: /* Resume a task. */
80: static int
81: resume_task (struct cmd *cmd, int *val)
82: {
83: return boot_script_task_resume (cmd);
84: }
85:
86: /* Resume a task when the user hits return. */
87: static int
88: prompt_resume_task (struct cmd *cmd, int *val)
89: {
90: return boot_script_prompt_task_resume (cmd);
91: }
92:
93: /* List of builtin symbols. */
94: static struct sym builtin_symbols[] =
95: {
96: { "task-create", VAL_FUNC, (int) create_task, VAL_TASK, 0 },
97: { "task-resume", VAL_FUNC, (int) resume_task, VAL_NONE, 1 },
98: { "prompt-task-resume", VAL_FUNC, (int) prompt_resume_task, VAL_NONE, 1 },
99: };
100: #define NUM_BUILTIN (sizeof (builtin_symbols) / sizeof (builtin_symbols[0]))
101:
102: /* Free CMD and all storage associated with it.
103: If ABORTING is set, terminate the task associated with CMD,
104: otherwise just deallocate the send right. */
105: static void
106: free_cmd (struct cmd *cmd, int aborting)
107: {
108: if (cmd->task)
109: boot_script_free_task (cmd->task, aborting);
110: if (cmd->args)
111: {
112: int i;
113: for (i = 0; i < cmd->args_index; i++)
114: boot_script_free (cmd->args[i], sizeof *cmd->args[i]);
115: boot_script_free (cmd->args, sizeof cmd->args[0] * cmd->args_alloc);
116: }
117: if (cmd->exec_funcs)
118: boot_script_free (cmd->exec_funcs,
119: sizeof cmd->exec_funcs[0] * cmd->exec_funcs_alloc);
120: boot_script_free (cmd, sizeof *cmd);
121: }
122:
123: /* Free all storage allocated by the parser.
124: If ABORTING is set, terminate all tasks. */
125: static void
126: cleanup (int aborting)
127: {
128: int i;
129:
130: for (i = 0; i < cmds_index; i++)
131: free_cmd (cmds[i], aborting);
132: boot_script_free (cmds, sizeof cmds[0] * cmds_alloc);
133: cmds = 0;
134: cmds_index = cmds_alloc = 0;
135:
136: for (i = 0; i < symtab_index; i++)
137: boot_script_free (symtab[i], sizeof *symtab[i]);
138: boot_script_free (symtab, sizeof symtab[0] * symtab_alloc);
139: symtab = 0;
140: symtab_index = symtab_alloc = 0;
141: }
142:
143: /* Add PTR to the list of pointers PTR_LIST, which
144: currently has ALLOC amount of space allocated to it, and
145: whose next available slot is INDEX. If more space
146: needs to to allocated, INCR is the amount by which
147: to increase it. Return 0 on success, non-zero otherwise. */
148: static int
149: add_list (void *ptr, void ***ptr_list, int *alloc, int *index, int incr)
150: {
151: if (*index == *alloc)
152: {
153: void **p;
154:
155: *alloc += incr;
156: p = boot_script_malloc (*alloc * sizeof (void *));
157: if (! p)
158: {
159: *alloc -= incr;
160: return 1;
161: }
162: if (*ptr_list)
163: {
164: memcpy (p, *ptr_list, *index * sizeof (void *));
165: boot_script_free (*ptr_list, (*alloc - incr) * sizeof (void *));
166: }
167: *ptr_list = p;
168: }
169: *(*ptr_list + *index) = ptr;
170: *index += 1;
171: return 0;
172: }
173:
174: /* Create an argument with TEXT, value type TYPE, and value VAL.
175: Add the argument to the argument list of CMD. */
176: static struct arg *
177: add_arg (struct cmd *cmd, char *text, int type, int val)
178: {
179: struct arg *arg;
180:
181: arg = boot_script_malloc (sizeof (struct arg));
182: if (arg)
183: {
184: arg->text = text;
185: arg->type = type;
186: arg->val = val;
187: if (add_list (arg, (void ***) &cmd->args,
188: &cmd->args_alloc, &cmd->args_index, 5))
189: {
190: boot_script_free (arg, sizeof *arg);
191: return 0;
192: }
193: }
194: return arg;
195: }
196:
197: /* Search for the symbol NAME in the symbol table. */
198: static struct sym *
199: sym_lookup (const char *name)
200: {
201: int i;
202:
203: for (i = 0; i < symtab_index; i++)
204: if (! strcmp (name, symtab[i]->name))
205: return symtab[i];
206: return 0;
207: }
208:
209: /* Create an entry for symbol NAME in the symbol table. */
210: static struct sym *
211: sym_enter (const char *name)
212: {
213: struct sym *sym;
214:
215: sym = boot_script_malloc (sizeof (struct sym));
216: if (sym)
217: {
218: memset (sym, 0, sizeof (struct sym));
219: sym->name = name;
220: if (add_list (sym, (void ***) &symtab, &symtab_alloc, &symtab_index, 20))
221: {
222: boot_script_free (sym, sizeof *sym);
223: return 0;
224: }
225: }
226: return sym;
227: }
228:
229: /* Parse the command line CMDLINE. */
230: int
231: boot_script_parse_line (void *hook, char *cmdline)
232: {
233: char *p, *q;
234: int error;
235: struct cmd *cmd;
236: struct arg *arg;
237:
238: /* Extract command name. Ignore line if it lacks a command. */
239: for (p = cmdline; *p == ' ' || *p == '\t'; p++)
240: ;
241: if (*p == '#')
242: /* Ignore comment line. */
243: return 0;
244:
245: #if 0
246: if (*p && *p != ' ' && *p != '\t' && *p != '\n')
247: {
248: printf ("(bootstrap): %s\n", cmdline);
249: }
250: #endif
251:
252: for (q = p; *q && *q != ' ' && *q != '\t' && *q != '\n'; q++)
253: ;
254: if (p == q)
255: return 0;
256:
257: *q = '\0';
258:
259: /* Allocate a command structure. */
260: cmd = boot_script_malloc (sizeof (struct cmd));
261: if (! cmd)
262: return BOOT_SCRIPT_NOMEM;
263: memset (cmd, 0, sizeof (struct cmd));
264: cmd->hook = hook;
265: cmd->path = p;
266: p = q + 1;
267:
268: for (arg = 0;;)
269: {
270: if (! arg)
271: {
272: /* Skip whitespace. */
273: while (*p == ' ' || *p == '\t')
274: p++;
275:
276: /* End of command line. */
277: if (! *p || *p == '\n')
278: {
279: /* Add command to list. */
280: if (add_list (cmd, (void ***) &cmds,
281: &cmds_alloc, &cmds_index, 10))
282: {
283: error = BOOT_SCRIPT_NOMEM;
284: goto bad;
285: }
286: return 0;
287: }
288: }
289:
290: /* Look for a symbol. */
291: if (arg || (*p == '$' && (*(p + 1) == '{' || *(p + 1) == '(')))
292: {
293: char end_char = (*(p + 1) == '{') ? '}' : ')';
294: struct sym *sym = 0;
295:
296: for (p += 2;;)
297: {
298: char c;
299: int i, val, type;
300: struct sym *s;
301:
302: /* Parse symbol name. */
303: for (q = p; *q && *q != '\n' && *q != end_char && *q != '='; q++)
304: ;
305: if (p == q || ! *q || *q == '\n'
306: || (end_char == '}' && *q != '}'))
307: {
308: error = BOOT_SCRIPT_SYNTAX_ERROR;
309: goto bad;
310: }
311: c = *q;
312: *q = '\0';
313:
314: /* See if this is a builtin symbol. */
315: for (i = 0; i < NUM_BUILTIN; i++)
316: if (! strcmp (p, builtin_symbols[i].name))
317: break;
318:
319: if (i < NUM_BUILTIN)
320: s = &builtin_symbols[i];
321: else
322: {
323: /* Look up symbol in symbol table.
324: If no entry exists, create one. */
325: s = sym_lookup (p);
326: if (! s)
327: {
328: s = sym_enter (p);
329: if (! s)
330: {
331: error = BOOT_SCRIPT_NOMEM;
332: goto bad;
333: }
334: }
335: }
336:
337: /* Only values are allowed in ${...} constructs. */
338: if (end_char == '}' && s->type == VAL_FUNC)
339: return BOOT_SCRIPT_INVALID_SYM;
340:
341: /* Check that assignment is valid. */
342: if (c == '=' && s->type == VAL_FUNC)
343: {
344: error = BOOT_SCRIPT_INVALID_ASG;
345: goto bad;
346: }
347:
348: /* For function symbols, execute the function. */
349: if (s->type == VAL_FUNC)
350: {
351: if (! s->run_on_exec)
352: {
353: (error
354: = ((*((int (*) (struct cmd *, int *)) s->val))
355: (cmd, &val)));
356: if (error)
357: goto bad;
358: type = s->ret_type;
359: }
360: else
361: {
362: if (add_list (s, (void ***) &cmd->exec_funcs,
363: &cmd->exec_funcs_alloc,
364: &cmd->exec_funcs_index, 5))
365: {
366: error = BOOT_SCRIPT_NOMEM;
367: goto bad;
368: }
369: type = VAL_NONE;
370: goto out;
371: }
372: }
373: else if (s->type == VAL_NONE)
374: {
375: type = VAL_SYM;
376: val = (int) s;
377: }
378: else
379: {
380: type = s->type;
381: val = s->val;
382: }
383:
384: if (sym)
385: {
386: sym->type = type;
387: sym->val = val;
388: }
389: else if (arg)
390: {
391: arg->type = type;
392: arg->val = val;
393: }
394:
395: out:
396: p = q + 1;
397: if (c == end_char)
398: {
399: /* Create an argument if necessary.
400: We create an argument if the symbol appears
401: in the expression by itself.
402:
403: NOTE: This is temporary till the boot filesystem
404: servers support arguments. When that happens,
405: symbol values will only be printed if they're
406: associated with an argument. */
407: if (! arg && end_char == '}')
408: {
409: if (! add_arg (cmd, 0, type, val))
410: {
411: error = BOOT_SCRIPT_NOMEM;
412: goto bad;
413: }
414: }
415: arg = 0;
416: break;
417: }
418: if (s->type != VAL_FUNC)
419: sym = s;
420: }
421: }
422: else
423: {
424: char c;
425:
426: /* Command argument; just copy the text. */
427: for (q = p;; q++)
428: {
429: if (! *q || *q == ' ' || *q == '\t' || *q == '\n')
430: break;
431: if (*q == '$' && *(q + 1) == '{')
432: break;
433: }
434: c = *q;
435: *q = '\0';
436:
437: /* Add argument to list. */
438: arg = add_arg (cmd, p, VAL_NONE, 0);
439: if (! arg)
440: {
441: error = BOOT_SCRIPT_NOMEM;
442: goto bad;
443: }
444: if (c == '$')
445: p = q;
446: else
447: {
448: if (c)
449: p = q + 1;
450: else
451: p = q;
452: arg = 0;
453: }
454: }
455: }
456:
457:
458: bad:
459: free_cmd (cmd, 1);
460: cleanup (1);
461: return error;
462: }
463:
464: /* Ensure that the command line buffer can accommodate LEN bytes of space. */
465: #define CHECK_CMDLINE_LEN(len) \
466: { \
467: if (cmdline_alloc - cmdline_index < len) \
468: { \
469: char *ptr; \
470: int alloc, i; \
471: alloc = cmdline_alloc + len - (cmdline_alloc - cmdline_index) + 100; \
472: ptr = boot_script_malloc (alloc); \
473: if (! ptr) \
474: { \
475: error = BOOT_SCRIPT_NOMEM; \
476: goto done; \
477: } \
478: memcpy (ptr, cmdline, cmdline_index); \
479: for (i = 0; i < argc; ++i) \
480: argv[i] = ptr + (argv[i] - cmdline); \
481: boot_script_free (cmdline, cmdline_alloc); \
482: cmdline = ptr; \
483: cmdline_alloc = alloc; \
484: } \
485: }
486:
487: /* Execute commands previously parsed. */
488: int
489: boot_script_exec ()
490: {
491: int cmd_index;
492:
493: for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
494: {
495: char **argv, *cmdline;
496: int i, argc, cmdline_alloc;
497: int cmdline_index, error, arg_index;
498: struct cmd *cmd = cmds[cmd_index];
499:
500: /* Skip command if it doesn't have an associated task. */
501: if (cmd->task == 0)
502: continue;
503:
504: /* Allocate a command line and copy command name. */
505: cmdline_index = strlen (cmd->path) + 1;
506: cmdline_alloc = cmdline_index + 100;
507: cmdline = boot_script_malloc (cmdline_alloc);
508: if (! cmdline)
509: {
510: cleanup (1);
511: return BOOT_SCRIPT_NOMEM;
512: }
513: memcpy (cmdline, cmd->path, cmdline_index);
514:
515: /* Allocate argument vector. */
516: argv = boot_script_malloc (sizeof (char *) * (cmd->args_index + 2));
517: if (! argv)
518: {
519: boot_script_free (cmdline, cmdline_alloc);
520: cleanup (1);
521: return BOOT_SCRIPT_NOMEM;
522: }
523: argv[0] = cmdline;
524: argc = 1;
525:
526: /* Build arguments. */
527: for (arg_index = 0; arg_index < cmd->args_index; arg_index++)
528: {
529: struct arg *arg = cmd->args[arg_index];
530:
531: /* Copy argument text. */
532: if (arg->text)
533: {
534: int len = strlen (arg->text);
535:
536: if (arg->type == VAL_NONE)
537: len++;
538: CHECK_CMDLINE_LEN (len);
539: memcpy (cmdline + cmdline_index, arg->text, len);
540: argv[argc++] = &cmdline[cmdline_index];
541: cmdline_index += len;
542: }
543:
544: /* Add value of any symbol associated with this argument. */
545: if (arg->type != VAL_NONE)
546: {
547: char *p, buf[50];
548: int len;
549: mach_port_t name;
550:
551: if (arg->type == VAL_SYM)
552: {
553: struct sym *sym = (struct sym *) arg->val;
554:
555: /* Resolve symbol value. */
556: while (sym->type == VAL_SYM)
557: sym = (struct sym *) sym->val;
558: if (sym->type == VAL_NONE)
559: {
560: error = BOOT_SCRIPT_UNDEF_SYM;
561: goto done;
562: }
563: arg->type = sym->type;
564: arg->val = sym->val;
565: }
566:
567: /* Print argument value. */
568: switch (arg->type)
569: {
570: case VAL_STR:
571: p = (char *) arg->val;
572: len = strlen (p);
573: break;
574:
575: case VAL_TASK:
576: case VAL_PORT:
577: if (arg->type == VAL_TASK)
578: /* Insert send right to task port. */
579: error = boot_script_insert_task_port
580: (cmd, (task_t) arg->val, &name);
581: else
582: /* Insert send right. */
583: error = boot_script_insert_right (cmd,
584: (mach_port_t) arg->val,
585: &name);
586: if (error)
587: goto done;
588:
589: i = name;
590: p = buf + sizeof (buf);
591: len = 0;
592: do
593: {
594: *--p = i % 10 + '0';
595: len++;
596: }
597: while (i /= 10);
598: break;
599:
600: default:
601: error = BOOT_SCRIPT_BAD_TYPE;
602: goto done;
603: }
604: len++;
605: CHECK_CMDLINE_LEN (len);
606: memcpy (cmdline + cmdline_index, p, len - 1);
607: *(cmdline + cmdline_index + len - 1) = '\0';
608: if (! arg->text)
609: argv[argc++] = &cmdline[cmdline_index];
610: cmdline_index += len;
611: }
612: }
613:
614: /* Terminate argument vector. */
615: argv[argc] = 0;
616:
617: /* Execute the command. */
618: if (boot_script_exec_cmd (cmd->hook, cmd->task, cmd->path,
619: argc, argv, cmdline, cmdline_index))
620: {
621: error = BOOT_SCRIPT_EXEC_ERROR;
622: goto done;
623: }
624:
625: error = 0;
626:
627: done:
628: boot_script_free (cmdline, cmdline_alloc);
629: boot_script_free (argv, sizeof (char *) * (cmd->args_index + 2));
630: if (error)
631: {
632: cleanup (1);
633: return error;
634: }
635: }
636:
637: for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
638: {
639: int i;
640: struct cmd *cmd = cmds[cmd_index];
641:
642: /* Execute functions that want to be run on exec. */
643: for (i = 0; i < cmd->exec_funcs_index; i++)
644: {
645: struct sym *sym = cmd->exec_funcs[i];
646: int error = ((*((int (*) (struct cmd *, int *)) sym->val))
647: (cmd, 0));
648: if (error)
649: {
650: cleanup (1);
651: return error;
652: }
653: }
654: }
655:
656: cleanup (0);
657: return 0;
658: }
659:
660: /* Create an entry for the variable NAME with TYPE and value VAL,
661: in the symbol table. */
662: int
663: boot_script_set_variable (const char *name, int type, int val)
664: {
665: struct sym *sym = sym_enter (name);
666:
667: if (sym)
668: {
669: sym->type = type;
670: sym->val = val;
671: }
672: return sym ? 0 : 1;
673: }
674:
675:
676: /* Define the function NAME, which will return type RET_TYPE. */
677: int
678: boot_script_define_function (const char *name, int ret_type,
679: int (*func) (const struct cmd *cmd, int *val))
680: {
681: struct sym *sym = sym_enter (name);
682:
683: if (sym)
684: {
685: sym->type = VAL_FUNC;
686: sym->val = (int) func;
687: sym->ret_type = ret_type;
688: sym->run_on_exec = ret_type == VAL_NONE;
689: }
690: return sym ? 0 : 1;
691: }
692:
693:
694: /* Return a string describing ERR. */
695: char *
696: boot_script_error_string (int err)
697: {
698: switch (err)
699: {
700: case BOOT_SCRIPT_NOMEM:
701: return "no memory";
702:
703: case BOOT_SCRIPT_SYNTAX_ERROR:
704: return "syntax error";
705:
706: case BOOT_SCRIPT_INVALID_ASG:
707: return "invalid variable in assignment";
708:
709: case BOOT_SCRIPT_MACH_ERROR:
710: return "mach error";
711:
712: case BOOT_SCRIPT_UNDEF_SYM:
713: return "undefined symbol";
714:
715: case BOOT_SCRIPT_EXEC_ERROR:
716: return "exec error";
717:
718: case BOOT_SCRIPT_INVALID_SYM:
719: return "invalid variable in expression";
720:
721: case BOOT_SCRIPT_BAD_TYPE:
722: return "invalid value type";
723: }
724: return 0;
725: }
726:
727: #ifdef BOOT_SCRIPT_TEST
728: #include <stdio.h>
729:
730: int
731: boot_script_exec_cmd (void *hook,
732: mach_port_t task, char *path, int argc,
733: char **argv, char *strings, int stringlen)
734: {
735: int i;
736:
737: printf ("port = %d: ", (int) task);
738: for (i = 0; i < argc; i++)
739: printf ("%s ", argv[i]);
740: printf ("\n");
741: return 0;
742: }
743:
744: void
745: main (int argc, char **argv)
746: {
747: char buf[500], *p;
748: int len;
749: FILE *fp;
750: mach_port_t host_port, device_port;
751:
752: if (argc < 2)
753: {
754: fprintf (stderr, "Usage: %s <script>\n", argv[0]);
755: exit (1);
756: }
757: fp = fopen (argv[1], "r");
758: if (! fp)
759: {
760: fprintf (stderr, "Can't open %s\n", argv[1]);
761: exit (1);
762: }
763: host_port = 1;
764: device_port = 2;
765: boot_script_set_variable ("host-port", VAL_PORT, (int) host_port);
766: boot_script_set_variable ("device-port", VAL_PORT, (int) device_port);
767: boot_script_set_variable ("root-device", VAL_STR, (int) "hd0a");
768: boot_script_set_variable ("boot-args", VAL_STR, (int) "-ad");
769: p = buf;
770: len = sizeof (buf);
771: while (fgets (p, len, fp))
772: {
773: int i, err;
774:
775: i = strlen (p) + 1;
776: err = boot_script_parse_line (0, p);
777: if (err)
778: {
779: fprintf (stderr, "error %s\n", boot_script_error_string (err));
780: exit (1);
781: }
782: p += i;
783: len -= i;
784: }
785: boot_script_exec ();
786: exit (0);
787: }
788: #endif /* BOOT_SCRIPT_TEST */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.