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