]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/commands.c
Merge bmake-20230909
[FreeBSD/FreeBSD.git] / stand / common / commands.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 #include <stand.h>
29 #include <string.h>
30
31 #include "bootstrap.h"
32
33 const char      *command_errmsg;
34 /* XXX should have procedural interface for setting, size limit? */
35 char            command_errbuf[COMMAND_ERRBUFSZ];
36
37 static int page_file(char *filename);
38
39 /*
40  * Help is read from a formatted text file.
41  *
42  * Entries in the file are formatted as 
43
44 # Ttopic [Ssubtopic] Ddescription
45 help
46 text
47 here
48 #
49
50  *
51  * Note that for code simplicity's sake, the above format must be followed
52  * exactly.
53  *
54  * Subtopic entries must immediately follow the topic (this is used to
55  * produce the listing of subtopics).
56  *
57  * If no argument(s) are supplied by the user, the help for 'help' is displayed.
58  */
59 COMMAND_SET(help, "help", "detailed help", command_help);
60
61 static int
62 help_getnext(int fd, char **topic, char **subtopic, char **desc)
63 {
64         char    line[81], *cp, *ep;
65
66         /* Make sure we provide sane values. */
67         *topic = *subtopic = *desc = NULL;
68         for (;;) {
69                 if (fgetstr(line, 80, fd) < 0)
70                         return (0);
71
72                 if (strlen(line) < 3 || line[0] != '#' || line[1] != ' ')
73                         continue;
74
75                 cp = line + 2;
76                 while (cp != NULL && *cp != 0) {
77                         ep = strchr(cp, ' ');
78                         if (*cp == 'T' && *topic == NULL) {
79                                 if (ep != NULL)
80                                         *ep++ = 0;
81                                 *topic = strdup(cp + 1);
82                         } else if (*cp == 'S' && *subtopic == NULL) {
83                                 if (ep != NULL)
84                                         *ep++ = 0;
85                                 *subtopic = strdup(cp + 1);
86                         } else if (*cp == 'D') {
87                                 *desc = strdup(cp + 1);
88                                 ep = NULL;
89                         }
90                         cp = ep;
91                 }
92                 if (*topic == NULL) {
93                         free(*subtopic);
94                         free(*desc);
95                         *subtopic = *desc = NULL;
96                         continue;
97                 }
98                 return (1);
99         }
100 }
101
102 static int
103 help_emitsummary(char *topic, char *subtopic, char *desc)
104 {
105         int     i;
106
107         pager_output("    ");
108         pager_output(topic);
109         i = strlen(topic);
110         if (subtopic != NULL) {
111                 pager_output(" ");
112                 pager_output(subtopic);
113                 i += strlen(subtopic) + 1;
114         }
115         if (desc != NULL) {
116                 do {
117                         pager_output(" ");
118                 } while (i++ < 30);
119                 pager_output(desc);
120         }
121         return (pager_output("\n"));
122 }
123
124 static int
125 command_help(int argc, char *argv[])
126 {
127         char    buf[81];        /* XXX buffer size? */
128         int     hfd, matched, doindex;
129         char    *topic, *subtopic, *t, *s, *d;
130
131         /* page the help text from our load path */
132         snprintf(buf, sizeof(buf), "%s/boot/%s", getenv("loaddev"),
133             HELP_FILENAME);
134         if ((hfd = open(buf, O_RDONLY)) < 0) {
135                 printf("Verbose help not available, "
136                     "use '?' to list commands\n");
137                 return (CMD_OK);
138         }
139
140         /* pick up request from arguments */
141         topic = subtopic = NULL;
142         switch (argc) {
143         case 3:
144                 subtopic = strdup(argv[2]);
145                 /* FALLTHROUGH */
146         case 2:
147                 topic = strdup(argv[1]);
148                 break;
149         case 1:
150                 topic = strdup("help");
151                 break;
152         default:
153                 command_errmsg = "usage is 'help <topic> [<subtopic>]";
154                 close(hfd);
155                 return(CMD_ERROR);
156         }
157
158         /* magic "index" keyword */
159         doindex = strcmp(topic, "index") == 0? 1 : 0;
160         matched = doindex;
161
162         /* Scan the helpfile looking for help matching the request */
163         pager_open();
164         while (help_getnext(hfd, &t, &s, &d)) {
165
166                 if (doindex) {          /* dink around formatting */
167                         if (help_emitsummary(t, s, d))
168                                 break;
169
170                 } else if (strcmp(topic, t)) {
171                         /* topic mismatch */
172                         if (matched) {
173                                 /* nothing more on this topic, stop scanning */
174                                 break;
175                         }
176                 } else {
177                         /* topic matched */
178                         matched = 1;
179                         if ((subtopic == NULL && s == NULL) ||
180                             (subtopic != NULL && s != NULL &&
181                             strcmp(subtopic, s) == 0)) {
182                                 /* exact match, print text */
183                                 while (fgetstr(buf, 80, hfd) >= 0 &&
184                                     buf[0] != '#') {
185                                         if (pager_output(buf))
186                                                 break;
187                                         if (pager_output("\n"))
188                                                 break;
189                                 }
190                         } else if (subtopic == NULL && s != NULL) {
191                                 /* topic match, list subtopics */
192                                 if (help_emitsummary(t, s, d))
193                                         break;
194                         }
195                 }
196                 free(t);
197                 free(s);
198                 free(d);
199                 t = s = d = NULL;
200         }
201         free(t);
202         free(s);
203         free(d);
204         pager_close();
205         close(hfd);
206         if (!matched) {
207                 snprintf(command_errbuf, sizeof(command_errbuf),
208                     "no help available for '%s'", topic);
209                 free(topic);
210                 free(subtopic);
211                 return (CMD_ERROR);
212         }
213         free(topic);
214         free(subtopic);
215         return (CMD_OK);
216 }
217
218 COMMAND_SET(commandlist, "?", "list commands", command_commandlist);
219
220 /*
221  * Please note: although we use the pager for the list of commands,
222  * this routine is called from the ? FORTH function which then
223  * unconditionally prints some commands. This will lead to anomalous
224  * behavior. There's no 'pager_output' binding to FORTH to allow
225  * things to work right, so I'm documenting the bug rather than
226  * fixing it.
227  */
228 static int
229 command_commandlist(int argc __unused, char *argv[] __unused)
230 {
231         struct bootblk_command  **cmdp;
232         int     res;
233         char    name[20];
234
235         res = 0;
236         pager_open();
237         res = pager_output("Available commands:\n");
238         SET_FOREACH(cmdp, Xcommand_set) {
239                 if (res)
240                         break;
241                 if ((*cmdp)->c_name != NULL && (*cmdp)->c_desc != NULL) {
242                         snprintf(name, sizeof(name), "  %-15s  ",
243                             (*cmdp)->c_name);
244                         pager_output(name);
245                         pager_output((*cmdp)->c_desc);
246                         res = pager_output("\n");
247                 }
248         }
249         pager_close();
250         return (CMD_OK);
251 }
252
253 /*
254  * XXX set/show should become set/echo if we have variable
255  * substitution happening.
256  */
257
258 COMMAND_SET(show, "show", "show variable(s)", command_show);
259
260 static int
261 command_show(int argc, char *argv[])
262 {
263         struct env_var  *ev;
264         char            *cp;
265
266         if (argc < 2) {
267                 /*
268                  * With no arguments, print everything.
269                  */
270                 pager_open();
271                 for (ev = environ; ev != NULL; ev = ev->ev_next) {
272                         pager_output(ev->ev_name);
273                         cp = getenv(ev->ev_name);
274                         if (cp != NULL) {
275                                 pager_output("=");
276                                 pager_output(cp);
277                         }
278                         if (pager_output("\n"))
279                                 break;
280                 }
281                 pager_close();
282         } else {
283                 if ((cp = getenv(argv[1])) != NULL) {
284                         printf("%s\n", cp);
285                 } else {
286                         snprintf(command_errbuf, sizeof(command_errbuf),
287                             "variable '%s' not found", argv[1]);
288                         return (CMD_ERROR);
289                 }
290         }
291         return (CMD_OK);
292 }
293
294 COMMAND_SET(set, "set", "set a variable", command_set);
295
296 static int
297 command_set(int argc, char *argv[])
298 {
299         int     err;
300
301         if (argc != 2) {
302                 command_errmsg = "wrong number of arguments";
303                 return (CMD_ERROR);
304         } else {
305 #ifdef LOADER_VERIEXEC
306                 /*
307                  * Impose restrictions if input is not verified
308                  */
309                 const char *restricted[] = {
310                         "boot",
311                         "init",
312                         "loader.ve.",
313                         "rootfs",
314                         "secur",
315                         "vfs.",
316                         NULL,
317                 };
318                 const char **cp;
319                 int ves;
320
321                 ves = ve_status_get(-1);
322                 if (ves == VE_UNVERIFIED_OK) {
323 #ifdef LOADER_VERIEXEC_TESTING
324                         printf("Checking: %s\n", argv[1]);
325 #endif
326                         for (cp = restricted; *cp; cp++) {
327                                 if (strncmp(argv[1], *cp, strlen(*cp)) == 0) {
328                                         printf("Ignoring restricted variable: %s\n",
329                                             argv[1]);
330                                         return (CMD_OK);
331                                 }
332                         }
333                 }
334 #endif
335                 if ((err = putenv(argv[1])) != 0) {
336                         command_errmsg = strerror(err);
337                         return (CMD_ERROR);
338                 }
339         }
340         return (CMD_OK);
341 }
342
343 COMMAND_SET(unset, "unset", "unset a variable", command_unset);
344
345 static int
346 command_unset(int argc, char *argv[])
347 {
348         int     err;
349
350         if (argc != 2) {
351                 command_errmsg = "wrong number of arguments";
352                 return (CMD_ERROR);
353         } else {
354                 if ((err = unsetenv(argv[1])) != 0) {
355                         command_errmsg = strerror(err);
356                         return (CMD_ERROR);
357                 }
358         }
359         return (CMD_OK);
360 }
361
362 COMMAND_SET(echo, "echo", "echo arguments", command_echo);
363
364 static int
365 command_echo(int argc, char *argv[])
366 {
367         char    *s;
368         int     nl, ch;
369
370         nl = 0;
371         optind = 1;
372         optreset = 1;
373         while ((ch = getopt(argc, argv, "n")) != -1) {
374                 switch (ch) {
375                 case 'n':
376                         nl = 1;
377                         break;
378                 case '?':
379                 default:
380                         /* getopt has already reported an error */
381                         return (CMD_OK);
382                 }
383         }
384         argv += (optind);
385         argc -= (optind);
386
387         s = unargv(argc, argv);
388         if (s != NULL) {
389                 printf("%s", s);
390                 free(s);
391         }
392         if (!nl)
393                 printf("\n");
394         return (CMD_OK);
395 }
396
397 /*
398  * A passable emulation of the sh(1) command of the same name.
399  */
400
401 COMMAND_SET(read, "read", "read input from the terminal", command_read);
402
403 static int
404 command_read(int argc, char *argv[])
405 {
406         char    *prompt;
407         int     timeout;
408         time_t  when;
409         char    *cp;
410         char    *name;
411         char    buf[256];               /* XXX size? */
412         int     c;
413     
414         timeout = -1;
415         prompt = NULL;
416         optind = 1;
417         optreset = 1;
418         while ((c = getopt(argc, argv, "p:t:")) != -1) {
419                 switch (c) {
420                 case 'p':
421                         prompt = optarg;
422                         break;
423                 case 't':
424                         timeout = strtol(optarg, &cp, 0);
425                         if (cp == optarg) {
426                                 snprintf(command_errbuf,
427                                     sizeof(command_errbuf),
428                                     "bad timeout '%s'", optarg);
429                                 return (CMD_ERROR);
430                         }
431                         break;
432                 default:
433                         return (CMD_OK);
434                 }
435         }
436
437         argv += (optind);
438         argc -= (optind);
439         name = (argc > 0) ? argv[0]: NULL;
440
441         if (prompt != NULL)
442                 printf("%s", prompt);
443         if (timeout >= 0) {
444                 when = time(NULL) + timeout;
445                 while (!ischar())
446                         if (time(NULL) >= when)
447                                 return (CMD_OK); /* is timeout an error? */
448         }
449
450         ngets(buf, sizeof(buf));
451
452         if (name != NULL)
453                 setenv(name, buf, 1);
454         return (CMD_OK);
455 }
456
457 /*
458  * File pager
459  */
460 COMMAND_SET(more, "more", "show contents of a file", command_more);
461
462 static int
463 command_more(int argc, char *argv[])
464 {
465         int     i;
466         int     res;
467         char    line[80];
468
469         res = 0;
470         pager_open();
471         for (i = 1; (i < argc) && (res == 0); i++) {
472                 snprintf(line, sizeof(line), "*** FILE %s BEGIN ***\n",
473                     argv[i]);
474                 if (pager_output(line))
475                         break;
476                 res = page_file(argv[i]);
477                 if (!res) {
478                         snprintf(line, sizeof(line), "*** FILE %s END ***\n",
479                             argv[i]);
480                         res = pager_output(line);
481                 }
482         }
483         pager_close();
484
485         return (CMD_OK);
486 }
487
488 static int
489 page_file(char *filename)
490 {
491         int result;
492
493         result = pager_file(filename);
494
495         if (result == -1) {
496                 snprintf(command_errbuf, sizeof(command_errbuf),
497                     "error showing %s", filename);
498         }
499
500         return (result);
501 }
502
503 /*
504  * List all disk-like devices
505  */
506 COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev);
507
508 static int
509 command_lsdev(int argc, char *argv[])
510 {
511         int     verbose, ch, i;
512         char    line[80];
513
514         verbose = 0;
515         optind = 1;
516         optreset = 1;
517         while ((ch = getopt(argc, argv, "v")) != -1) {
518                 switch (ch) {
519                 case 'v':
520                         verbose = 1;
521                         break;
522                 case '?':
523                 default:
524                         /* getopt has already reported an error */
525                         return (CMD_OK);
526                 }
527         }
528         argv += (optind);
529         argc -= (optind);
530
531         pager_open();
532         for (i = 0; devsw[i] != NULL; i++) {
533                 if (devsw[i]->dv_print != NULL) {
534                         if (devsw[i]->dv_print(verbose))
535                                 break;
536                 } else {
537                         snprintf(line, sizeof(line), "%s: (unknown)\n",
538                             devsw[i]->dv_name);
539                         if (pager_output(line))
540                                 break;
541                 }
542         }
543         pager_close();
544         return (CMD_OK);
545 }
546
547 static int
548 command_readtest(int argc, char *argv[])
549 {
550         int fd;
551         time_t start, end;
552         char buf[512];
553         ssize_t rv, count = 0;
554
555         if (argc != 2) {
556                 snprintf(command_errbuf, sizeof(command_errbuf),
557                   "Usage: readtest <filename>");
558                 return (CMD_ERROR);
559         }
560
561         start = getsecs();
562         if ((fd = open(argv[1], O_RDONLY)) < 0) {
563                 snprintf(command_errbuf, sizeof(command_errbuf),
564                   "can't open '%s'", argv[1]);
565                 return (CMD_ERROR);
566         }
567         while ((rv = read(fd, buf, sizeof(buf))) > 0)
568                 count += rv;
569         end = getsecs();
570
571         printf("Received %zd bytes during %jd seconds\n", count, (intmax_t)end - start);
572         close(fd);
573         return (CMD_OK);
574 }
575
576 COMMAND_SET(readtest, "readtest", "Time a file read", command_readtest);
577
578 static int
579 command_quit(int argc, char *argv[])
580 {
581         exit(0);
582         return (CMD_OK);
583 }
584
585 COMMAND_SET(quit, "quit", "exit the loader", command_quit);