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