|
|
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.4 ! root 297: unsigned i;
! 298: int type;
1.1.1.2 root 299: long val;
1.1 root 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
1.1.1.2 root 354: = ((*((int (*) (struct cmd *, long *)) s->val))
1.1 root 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;
1.1.1.2 root 376: val = (long) s;
1.1 root 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
1.1.1.3 root 489: boot_script_exec (void)
1.1 root 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
1.1.1.2 root 663: boot_script_set_variable (const char *name, int type, long val)
1.1 root 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;
1.1.1.2 root 686: sym->val = (long) func;
1.1 root 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.