1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
24 */
25
26 /*
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28 * Copyright 2016 Toomas Soome <tsoome@me.com>
29 * Copyright 2017 RackTop Systems.
30 */
31
32 /*
33 * Loader menu management.
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <wchar.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <alloca.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/queue.h>
47 #include <libbe.h>
48 #include <ficl.h>
49 #include <ficlplatform/emu.h>
50
51 #include "bootadm.h"
52
53 extern int bam_rootlen;
54 extern int bam_alt_root;
55 extern char *rootbuf;
56 extern char *bam_root;
57
58 #define BOOT_DIR "/boot"
59 #define CONF_DIR BOOT_DIR "/conf.d"
60 #define MENU BOOT_DIR "/menu.lst"
61 #define TRANSIENT BOOT_DIR "/transient.conf"
62 #define XEN_CONFIG CONF_DIR "/xen"
63
64 struct menu_entry {
65 int entry;
66 char *title;
67 char *bootfs;
68 STAILQ_ENTRY(menu_entry) next;
69 };
70 STAILQ_HEAD(menu_lst, menu_entry);
71
72 static error_t set_option(struct menu_lst *, char *, char *);
73 static error_t list_entry(struct menu_lst *, char *, char *);
74 static error_t update_entry(struct menu_lst *, char *, char *);
75 static error_t update_temp(struct menu_lst *, char *, char *);
76 static error_t list_setting(struct menu_lst *menu, char *, char *);
77 static error_t disable_hyper(struct menu_lst *, char *, char *);
78 static error_t enable_hyper(struct menu_lst *, char *, char *);
79
80 /* Menu related sub commands */
81 static subcmd_defn_t menu_subcmds[] = {
82 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
83 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
84 "update_entry", OPT_REQ, update_entry, 0, /* menu */
85 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
86 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
87 "disable_hypervisor", OPT_ABSENT, disable_hyper, 0, /* menu */
88 "enable_hypervisor", OPT_ABSENT, enable_hyper, 0, /* menu */
89 NULL, 0, NULL, 0 /* must be last */
90 };
91
92 #define NUM_COLS (4)
93 struct col_info {
94 const char *col_name;
95 size_t width;
96 };
97
98 /*
99 * all columns output format
100 */
101 struct hdr_info {
102 struct col_info cols[NUM_COLS];
103 };
104
105 static void
106 print_hdr(struct hdr_info *hdr_info)
107 {
108 boolean_t first = B_TRUE;
109 size_t i;
110
111 for (i = 0; i < NUM_COLS; i++) {
112 struct col_info *col_info = &hdr_info->cols[i];
113 const char *name = col_info->col_name;
114 size_t width = col_info->width;
115
116 if (name == NULL)
117 continue;
118
119 if (first) {
120 (void) printf("%-*s", width, name);
121 first = B_FALSE;
122 } else
123 (void) printf(" %-*s", width, name);
124 }
125 (void) putchar('\n');
126 }
127
128 static void
129 init_hdr_cols(struct hdr_info *hdr)
130 {
131 struct col_info *col = hdr->cols;
132 size_t i;
133
134 col[0].col_name = _("Index");
135 col[1].col_name = _("Default");
136 col[2].col_name = _("Dataset");
137 col[3].col_name = _("Menu");
138 col[4].col_name = NULL;
139
140 for (i = 0; i < NUM_COLS; i++) {
141 const char *name = col[i].col_name;
142 col[i].width = 0;
143
144 if (name != NULL) {
145 wchar_t wname[128];
146 size_t sz = mbstowcs(wname, name, sizeof (wname) /
147 sizeof (wchar_t));
148 if (sz > 0) {
149 int wcsw = wcswidth(wname, sz);
150 if (wcsw > 0)
151 col[i].width = wcsw;
152 else
153 col[i].width = sz;
154 } else {
155 col[i].width = strlen(name);
156 }
157 }
158 }
159 }
160
161 static void
162 count_widths(struct hdr_info *hdr, struct menu_lst *menu)
163 {
164 size_t len[NUM_COLS];
165 struct menu_entry *entry;
166 int i;
167
168 for (i = 0; i < NUM_COLS; i++)
169 len[i] = hdr->cols[i].width + 1;
170
171 STAILQ_FOREACH(entry, menu, next) {
172 size_t bootfs_len = strlen(entry->bootfs);
173 if (bootfs_len > len[2])
174 len[2] = bootfs_len + 1;
175 }
176
177 for (i = 0; i < NUM_COLS; i++)
178 hdr->cols[i].width = len[i];
179 }
180
181 static void
182 print_menu_nodes(boolean_t parsable, struct hdr_info *hdr,
183 struct menu_lst *menu)
184 {
185 struct menu_entry *entry;
186 int i = 0;
187 int rv;
188 be_node_list_t *be_nodes, *be_node;
189
190 rv = be_list(NULL, &be_nodes);
191 if (rv != BE_SUCCESS)
192 return;
193
194 STAILQ_FOREACH(entry, menu, next) {
195 boolean_t active = B_FALSE;
196
197 for (be_node = be_nodes; be_node;
198 be_node = be_node->be_next_node)
199 if (strcmp(be_node->be_root_ds, entry->bootfs) == 0)
200 if (be_node->be_active_on_boot)
201 active = B_TRUE;
202
203 if (parsable)
204 (void) printf("%d;%s;%s;%s\n", i,
205 active == B_TRUE ? "*" : "-",
206 entry->bootfs, entry->title);
207 else
208 (void) printf("%-*d %-*s %-*s %-*s\n",
209 hdr->cols[0].width, i,
210 hdr->cols[1].width,
211 active == B_TRUE ? "*" : "-",
212 hdr->cols[2].width, entry->bootfs,
213 hdr->cols[3].width, entry->title);
214
215 i++;
216 }
217 be_free_list(be_nodes);
218 }
219
220 static void
221 print_nodes(boolean_t parsable, struct menu_lst *menu)
222 {
223 struct hdr_info hdr;
224
225 if (!parsable) {
226 init_hdr_cols(&hdr);
227 count_widths(&hdr, menu);
228 print_hdr(&hdr);
229 }
230
231 print_menu_nodes(parsable, &hdr, menu);
232 }
233
234 error_t
235 menu_read(struct menu_lst *menu, char *menu_path)
236 {
237 FILE *fp;
238 struct menu_entry *mp;
239 char buf[PATH_MAX];
240 char *title;
241 char *bootfs;
242 char *ptr;
243 int i = 0;
244 int ret = BAM_SUCCESS;
245
246 fp = fopen(menu_path, "r");
247 if (fp == NULL)
248 return (BAM_ERROR);
249
250 /*
251 * menu.lst entry is on two lines, one for title, one for bootfs
252 * so we process both lines in succession.
253 */
254 do {
255 if (fgets(buf, PATH_MAX, fp) == NULL) {
256 if (!feof(fp))
257 ret = BAM_ERROR;
258 (void) fclose(fp);
259 return (ret);
260 }
261 ptr = strchr(buf, '\n');
262 if (ptr != NULL)
263 *ptr = '\0';
264
265 ptr = strchr(buf, ' ');
266 if (ptr == NULL) {
267 (void) fclose(fp);
268 return (BAM_ERROR);
269 }
270 *ptr++ = '\0';
271 if (strcmp(buf, "title") != 0) {
272 (void) fclose(fp);
273 return (BAM_ERROR);
274 }
275 if ((title = strdup(ptr)) == NULL) {
276 (void) fclose(fp);
277 return (BAM_ERROR);
278 }
279
280 if (fgets(buf, PATH_MAX, fp) == NULL) {
281 free(title);
282 (void) fclose(fp);
283 return (BAM_ERROR);
284 }
285
286 ptr = strchr(buf, '\n');
287 if (ptr != NULL)
288 *ptr = '\0';
289
290 ptr = strchr(buf, ' ');
291 if (ptr == NULL) {
292 free(title);
293 (void) fclose(fp);
294 return (BAM_ERROR);
295 }
296 *ptr++ = '\0';
297 if (strcmp(buf, "bootfs") != 0) {
298 free(title);
299 (void) fclose(fp);
300 return (BAM_ERROR);
301 }
302 if ((bootfs = strdup(ptr)) == NULL) {
303 free(title);
304 (void) fclose(fp);
305 return (BAM_ERROR);
306 }
307 if ((mp = malloc(sizeof (struct menu_entry))) == NULL) {
308 free(title);
309 free(bootfs);
310 (void) fclose(fp);
311 return (BAM_ERROR);
312 }
313 mp->entry = i++;
314 mp->title = title;
315 mp->bootfs = bootfs;
316 STAILQ_INSERT_TAIL(menu, mp, next);
317 } while (feof(fp) == 0);
318
319 (void) fclose(fp);
320 return (ret);
321 }
322
323 void
324 menu_free(struct menu_lst *menu)
325 {
326 struct menu_entry *entry;
327 STAILQ_FOREACH(entry, menu, next) {
328 STAILQ_REMOVE_HEAD(menu, next);
329 free(entry->title);
330 free(entry->bootfs);
331 free(entry);
332 }
333 }
334
335 error_t
336 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
337 {
338 error_t ret;
339 char menu_path[PATH_MAX];
340 char clean_menu_root[PATH_MAX];
341 char menu_root[PATH_MAX];
342 struct stat sb;
343 error_t (*f)(struct menu_lst *, char *, char *);
344 char *special;
345 char *pool = NULL;
346 zfs_mnted_t zmnted;
347 char *zmntpt;
348 char *osdev;
349 char *osroot;
350 const char *fcn = "bam_loader_menu()";
351 struct menu_lst menu = {0};
352
353 STAILQ_INIT(&menu);
354
355 /*
356 * Check arguments
357 */
358 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
359 if (ret == BAM_ERROR) {
360 return (BAM_ERROR);
361 }
362
363 assert(bam_root);
364
365 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
366 osdev = osroot = NULL;
367
368 if (strcmp(subcmd, "update_entry") == 0) {
369 assert(opt);
370
371 osdev = strtok(opt, ",");
372 assert(osdev);
373 osroot = strtok(NULL, ",");
374 if (osroot) {
375 /* fixup bam_root so that it points at osroot */
376 if (realpath(osroot, rootbuf) == NULL) {
377 bam_error(_("cannot resolve path %s: %s\n"),
378 osroot, strerror(errno));
379 return (BAM_ERROR);
380 }
381 bam_alt_root = 1;
382 bam_root = rootbuf;
383 bam_rootlen = strlen(rootbuf);
384 }
385 }
386
387 if (stat(menu_root, &sb) == -1) {
388 bam_error(_("cannot find menu\n"));
389 return (BAM_ERROR);
390 }
391
392 if (!is_zfs(menu_root)) {
393 bam_error(_("only ZFS root is supported\n"));
394 return (BAM_ERROR);
395 }
396
397 assert(strcmp(menu_root, bam_root) == 0);
398 special = get_special(menu_root);
399 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
400 if (special == NULL) {
401 bam_error(_("cant find special file for mount-point %s\n"),
402 menu_root);
403 return (BAM_ERROR);
404 }
405 pool = strtok(special, "/");
406 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
407 if (pool == NULL) {
408 free(special);
409 bam_error(_("cant find pool for mount-point %s\n"), menu_root);
410 return (BAM_ERROR);
411 }
412 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
413
414 zmntpt = mount_top_dataset(pool, &zmnted);
415 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
416 if (zmntpt == NULL) {
417 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
418 free(special);
419 return (BAM_ERROR);
420 }
421 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
422
423 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
424 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
425
426 elide_trailing_slash(menu_root, clean_menu_root,
427 sizeof (clean_menu_root));
428
429 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
430
431 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
432 (void) strlcat(menu_path, MENU, sizeof (menu_path));
433
434 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
435
436 /*
437 * update_entry is special case, its used by installer
438 * and needs to create menu.lst file for loader
439 */
440 if (menu_read(&menu, menu_path) == BAM_ERROR &&
441 strcmp(subcmd, "update_entry") != 0) {
442 bam_error(_("cannot find menu file: %s\n"), menu_path);
443 if (special != NULL)
444 free(special);
445 return (BAM_ERROR);
446 }
447
448 /*
449 * If listing the menu, display the menu location
450 */
451 if (strcmp(subcmd, "list_entry") == 0)
452 bam_print(_("the location for the active menu is: %s\n"),
453 menu_path);
454
455 /*
456 * We already checked the following case in
457 * check_subcmd_and_suboptions() above. Complete the
458 * final step now.
459 */
460 if (strcmp(subcmd, "set_option") == 0) {
461 assert(largc == 1 && largv[0] && largv[1] == NULL);
462 opt = largv[0];
463 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
464 (strcmp(subcmd, "list_setting") != 0)) {
465 assert(largc == 0 && largv == NULL);
466 }
467
468 /*
469 * Once the sub-cmd handler has run
470 * only the line field is guaranteed to have valid values
471 */
472 if (strcmp(subcmd, "update_entry") == 0) {
473 ret = f(&menu, menu_root, osdev);
474 } else if (strcmp(subcmd, "upgrade") == 0) {
475 ret = f(&menu, bam_root, menu_root);
476 } else if (strcmp(subcmd, "list_entry") == 0) {
477 ret = f(&menu, menu_path, opt);
478 } else if (strcmp(subcmd, "list_setting") == 0) {
479 ret = f(&menu, ((largc > 0) ? largv[0] : ""),
480 ((largc > 1) ? largv[1] : ""));
481 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
482 if (is_sparc()) {
483 bam_error(_("%s operation unsupported on SPARC "
484 "machines\n"), subcmd);
485 ret = BAM_ERROR;
486 } else {
487 ret = f(&menu, bam_root, NULL);
488 }
489 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
490 if (is_sparc()) {
491 bam_error(_("%s operation unsupported on SPARC "
492 "machines\n"), subcmd);
493 ret = BAM_ERROR;
494 } else {
495 char *extra_args = NULL;
496
497 /*
498 * Compress all arguments passed in the largv[] array
499 * into one string that can then be appended to the
500 * end of the kernel$ string the routine to enable the
501 * hypervisor will build.
502 *
503 * This allows the caller to supply arbitrary unparsed
504 * arguments, such as dom0 memory settings or APIC
505 * options.
506 *
507 * This concatenation will be done without ANY syntax
508 * checking whatsoever, so it's the responsibility of
509 * the caller to make sure the arguments are valid and
510 * do not duplicate arguments the conversion routines
511 * may create.
512 */
513 if (largc > 0) {
514 int extra_len, i;
515
516 for (extra_len = 0, i = 0; i < largc; i++)
517 extra_len += strlen(largv[i]);
518
519 /*
520 * Allocate space for argument strings,
521 * intervening spaces and terminating NULL.
522 */
523 extra_args = alloca(extra_len + largc);
524
525 (void) strcpy(extra_args, largv[0]);
526
527 for (i = 1; i < largc; i++) {
528 (void) strcat(extra_args, " ");
529 (void) strcat(extra_args, largv[i]);
530 }
531 }
532
533 ret = f(&menu, bam_root, extra_args);
534 }
535 } else
536 ret = f(&menu, NULL, opt);
537
538 if (ret == BAM_WRITE) {
539 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
540 fcn, clean_menu_root));
541 /* ret = menu_write(clean_menu_root, menu); */
542 }
543
544 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
545 assert((is_zfs(menu_root)) ^ (pool == NULL));
546 if (pool) {
547 (void) umount_top_dataset(pool, zmnted, zmntpt);
548 free(special);
549 }
550
551 menu_free(&menu);
552 return (ret);
553 }
554
555 /*
556 * To suppress output from ficl. We do not want to see messages
557 * from interpreting loader config.
558 */
559
560 /*ARGSUSED*/
561 static void
562 ficlTextOutSilent(ficlCallback *cb, char *text)
563 {
564 }
565
566 /*ARGSUSED*/
567 static error_t
568 set_option(struct menu_lst *menu, char *dummy, char *opt)
569 {
570 char path[PATH_MAX];
571 char *val;
572 char *rest;
573 int optval;
574 struct menu_entry *entry;
575 nvlist_t *be_attrs;
576 FILE *fp;
577 int rv, ret = BAM_SUCCESS;
578
579 assert(menu);
580 assert(opt);
581 assert(dummy == NULL);
582
583 val = strchr(opt, '=');
584 if (val != NULL) {
585 *val++ = '\0';
586 }
587
588 if (strcmp(opt, "default") == 0) {
589 errno = 0;
590 optval = strtol(val, &rest, 10);
591 if (errno != 0 || *rest != '\0') {
592 bam_error(_("invalid boot entry number: %s\n"), val);
593 return (BAM_ERROR);
594 }
595 STAILQ_FOREACH(entry, menu, next) {
596 if (entry->entry == optval)
597 break;
598 }
599 if (entry == NULL) {
600 bam_error(_("invalid boot entry number: %s\n"), val);
601 return (BAM_ERROR);
602 }
603 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
604 bam_error(_("out of memory\n"));
605 return (BAM_ERROR);
606 }
607 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
608 entry->title) != 0) {
609 bam_error(_("out of memory\n"));
610 nvlist_free(be_attrs);
611 return (BAM_ERROR);
612 }
613 ret = be_activate(be_attrs);
614 nvlist_free(be_attrs);
615 if (ret != 0)
616 ret = BAM_ERROR;
617 return (ret);
618 } else if (strcmp(opt, "timeout") == 0) {
619 errno = 0;
620 optval = strtol(val, &rest, 10);
621 if (errno != 0 || *rest != '\0') {
622 bam_error(_("invalid timeout: %s\n"), val);
623 return (BAM_ERROR);
624 }
625
626 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
627 bam_root);
628
629 fp = fopen(path, "w");
630 if (fp == NULL) {
631 bam_error(_("failed to open file: %s: %s\n"),
632 path, strerror(errno));
633 return (BAM_ERROR);
634 }
635 /*
636 * timeout=-1 is to disable auto boot in illumos, but
637 * loader needs "NO" to disable auto boot.
638 */
639 if (optval == -1)
640 rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
641 else
642 rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
643
644 if (rv < 0) {
645 bam_error(_("write to file failed: %s: %s\n"),
646 path, strerror(errno));
647 (void) fclose(fp);
648 ret = BAM_ERROR;
649 } else
650 rv = fclose(fp);
651
652 if (rv < 0) {
653 bam_error(_("failed to close file: %s: %s\n"),
654 path, strerror(errno));
655 ret = BAM_ERROR;
656 }
657 if (ret == BAM_ERROR)
658 (void) unlink(path);
659
660 return (BAM_SUCCESS);
661 }
662
663 bam_error(_("invalid option: %s\n"), opt);
664 return (BAM_ERROR);
665 }
666
667 static int
668 bam_mount_be(struct menu_entry *entry, char **dir)
669 {
670 nvlist_t *be_attrs = NULL;
671 const char *tmpdir = getenv("TMPDIR");
672 const char *tmpname = "bam.XXXXXX";
673 be_node_list_t *be_node, *be_nodes = NULL;
674 int ret;
675
676 *dir = NULL;
677 if (tmpdir == NULL)
678 tmpdir = "/tmp";
679
680 ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
681 if (ret < 0) {
682 return (BE_ERR_NOMEM);
683 }
684 *dir = mkdtemp(*dir);
685
686 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
687 ret = BE_ERR_NOMEM;
688 goto out;
689 }
690
691 ret = be_list(NULL, &be_nodes);
692 if (ret != BE_SUCCESS) {
693 goto out;
694 }
695
696 for (be_node = be_nodes; be_node;
697 be_node = be_node->be_next_node)
698 if (strcmp(be_node->be_root_ds, entry->bootfs) == 0)
699 break;
700
701 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
702 be_node->be_node_name) != 0) {
703 ret = BE_ERR_NOMEM;
704 goto out;
705 }
706
707 if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
708 ret = BE_ERR_NOMEM;
709 goto out;
710 }
711
712 ret = be_mount(be_attrs);
713 if (ret == BE_ERR_MOUNTED) {
714 /*
715 * if BE is mounted, dir does not point to correct directory
716 */
717 (void) rmdir(*dir);
718 free(*dir);
719 *dir = NULL;
720 }
721 out:
722 if (be_nodes != NULL)
723 be_free_list(be_nodes);
724 nvlist_free(be_attrs);
725 return (ret);
726 }
727
728 static int
729 bam_umount_be(char *dir)
730 {
731 nvlist_t *be_attrs;
732 int ret;
733
734 if (dir == NULL) /* nothing to do */
735 return (BE_SUCCESS);
736
737 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
738 return (BE_ERR_NOMEM);
739
740 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
741 ret = BE_ERR_NOMEM;
742 goto out;
743 }
744
745 ret = be_unmount(be_attrs);
746 out:
747 nvlist_free(be_attrs);
748 return (ret);
749 }
750
751 /*
752 * display details of menu entry or single property
753 */
754 static error_t
755 list_menu_entry(struct menu_entry *entry, char *setting)
756 {
757 int ret = BAM_SUCCESS;
758 char *ptr, *dir;
759 char buf[MAX_INPUT];
760 ficlVm *vm;
761 int mounted;
762
763 mounted = bam_mount_be(entry, &dir);
764 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
765 if (dir != NULL) {
766 (void) rmdir(dir);
767 free(dir);
768 }
769 bam_error(_("%s is not mounted\n"), entry->title);
770 return (BAM_ERROR);
771 }
772
773 vm = bf_init("", ficlTextOutSilent);
774 if (vm == NULL) {
775 bam_error(_("error setting up forth interpreter\n"));
776 ret = BAM_ERROR;
777 goto done;
778 }
779
780 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
781 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
782 entry->bootfs);
783 ret = ficlVmEvaluate(vm, buf);
784 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
785 bam_error(_("error interpreting boot config\n"));
786 ret = BAM_ERROR;
787 goto done;
788 }
789 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
790 ret = ficlVmEvaluate(vm, buf);
791 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
792 bam_error(_("error interpreting boot config\n"));
793 ret = BAM_ERROR;
794 goto done;
795 }
796 (void) snprintf(buf, MAX_INPUT, "start");
797 ret = ficlVmEvaluate(vm, buf);
798 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
799 bam_error(_("error interpreting boot config\n"));
800 ret = BAM_ERROR;
801 goto done;
802 }
803 (void) snprintf(buf, MAX_INPUT, "boot");
804 ret = ficlVmEvaluate(vm, buf);
805 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
806 bam_error(_("error interpreting boot config\n"));
807 ret = BAM_ERROR;
808 goto done;
809 }
810
811 ret = BAM_SUCCESS;
812 if (*setting == '\0')
813 (void) printf("\nTitle: %s\n", entry->title);
814 else if (strcasecmp(setting, "title") == 0) {
815 (void) printf("%s\n", entry->title);
816 goto done;
817 }
818
819 ptr = getenv("autoboot_delay");
820 if (ptr != NULL) {
821 char *timeout = "-1";
822
823 if (strcasecmp(ptr, "NO") != 0)
824 timeout = ptr;
825
826 if (*setting == '\0')
827 (void) printf("Timeout: %s\n", timeout);
828 else if (strcasecmp(setting, "timeout") == 0) {
829 (void) printf("%s\n", timeout);
830 goto done;
831 }
832
833 }
834 ptr = getenv("console");
835 if (ptr != NULL) {
836 if (*setting == '\0')
837 (void) printf("Console: %s\n", ptr);
838 else if (strcasecmp(setting, "console") == 0) {
839 (void) printf("%s\n", ptr);
840 goto done;
841 }
842 }
843
844 if (*setting == '\0')
845 (void) printf("Bootfs: %s\n", entry->bootfs);
846 else if (strcasecmp(setting, "bootfs") == 0) {
847 (void) printf("%s\n", entry->bootfs);
848 goto done;
849 }
850
851 ptr = getenv("xen_kernel");
852 if (ptr != NULL) {
853 if (*setting == '\0') {
854 (void) printf("Xen kernel: %s\n", ptr);
855 } else if (strcasecmp(setting, "xen_kernel") == 0) {
856 (void) printf("%s\n", ptr);
857 goto done;
858 }
859
860 if (*setting == '\0') {
861 (void) printf("Xen args: \"%s\"\n",
862 getenv("xen_cmdline"));
863 } else if (strcasecmp(setting, "xen_cmdline") == 0) {
864 (void) printf("%s\n", getenv("xen_cmdline"));
865 goto done;
866 }
867
868 if (*setting == '\0') {
869 (void) printf("Kernel: %s\n",
870 getenv("bootfile"));
871 } if (strcasecmp(setting, "kernel") == 0) {
872 (void) printf("%s\n", getenv("bootfile"));
873 goto done;
874 }
875 } else {
876 ptr = getenv("kernelname");
877 if (ptr != NULL) {
878 if (*setting == '\0') {
879 (void) printf("Kernel: %s\n", ptr);
880 } else if (strcasecmp(setting, "kernel") == 0) {
881 (void) printf("%s\n", ptr);
882 goto done;
883 }
884 }
885 }
886
887 ptr = getenv("boot-args");
888 if (ptr != NULL) {
889 if (*setting == '\0') {
890 (void) printf("Boot-args: \"%s\"\n", ptr);
891 } else if (strcasecmp(setting, "boot-args") == 0) {
892 (void) printf("%s\n", ptr);
893 goto done;
894 }
895 }
896
897 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
898 (void) printf("\nModules:\n");
899 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
900 (void) snprintf(buf, MAX_INPUT, "show-module-options");
901 ret = ficlVmEvaluate(vm, buf);
902 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
903 bam_error(_("error interpreting boot config\n"));
904 ret = BAM_ERROR;
905 goto done;
906 }
907 ret = BAM_SUCCESS;
908 goto done;
909 }
910
911 /* if we got here with setting string, its unknown property */
912 if (*setting != '\0') {
913 bam_error(_("unknown property: %s\n"), setting);
914 ret = BAM_ERROR;
915 } else
916 ret = BAM_SUCCESS;
917 done:
918 bf_fini();
919 if (mounted != BE_ERR_MOUNTED) {
920 (void) bam_umount_be(dir);
921 }
922
923 if (dir != NULL) {
924 (void) rmdir(dir);
925 free(dir);
926 }
927
928 return (ret);
929 }
930
931 /*ARGSUSED*/
932 static error_t
933 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
934 {
935 error_t ret = BAM_SUCCESS;
936 struct menu_entry *entry;
937 char *ptr, *title = NULL;
938 int i, e = -1;
939
940 if (opt == NULL) {
941 print_nodes(B_FALSE, menu);
942 return (ret);
943 }
944
945 if ((ptr = strchr(opt, '=')) == NULL) {
946 bam_error(_("invalid option: %s\n"), opt);
947 return (BAM_ERROR);
948 }
949
950 i = ptr - opt;
951 if (strncmp(opt, "entry", i) == 0) {
952 e = atoi(ptr+1);
953 } else if (strncmp(opt, "title", i) == 0) {
954 title = ptr+1;
955 } else {
956 bam_error(_("invalid option: %s\n"), opt);
957 return (BAM_ERROR);
958 }
959
960 STAILQ_FOREACH(entry, menu, next) {
961 if (title != NULL) {
962 if (strcmp(title, entry->title) == 0)
963 break;
964 } else if (entry->entry == e)
965 break;
966 }
967
968 if (entry == NULL) {
969 bam_error(_("no matching entry found\n"));
970 return (BAM_ERROR);
971 }
972
973 return (list_menu_entry(entry, ""));
974 }
975
976 /*
977 * For now this is just stub entry to support grub interface, the
978 * known consumer is installer ict.py code, calling as:
979 * bootadm update-menu -R /a -Z -o rdisk
980 * Later this can be converted to do something useful.
981 */
982 /*ARGSUSED*/
983 static error_t
984 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
985 {
986 char path[PATH_MAX];
987 char *pool = menu_root + 1;
988 be_node_list_t *be_nodes, *be_node;
989 int rv;
990 FILE *fp;
991
992 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
993 rv = be_list(NULL, &be_nodes);
994
995 if (rv != BE_SUCCESS)
996 return (BAM_ERROR);
997
998 fp = fopen(path, "w");
999 if (fp == NULL) {
1000 be_free_list(be_nodes);
1001 return (BAM_ERROR);
1002 }
1003
1004 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1005 if (strcmp(be_node->be_rpool, pool) == 0) {
1006 (void) fprintf(fp, "title %s\n", be_node->be_node_name);
1007 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1008 }
1009 }
1010
1011 be_free_list(be_nodes);
1012 (void) fclose(fp);
1013 return (BAM_SUCCESS);
1014 }
1015
1016 /*ARGSUSED*/
1017 static error_t
1018 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1019 {
1020 error_t ret = BAM_ERROR;
1021 char path[PATH_MAX];
1022 char buf[MAX_INPUT];
1023 struct mnttab mpref = { 0 };
1024 struct mnttab mp = { 0 };
1025 ficlVm *vm;
1026 char *env, *o;
1027 FILE *fp;
1028
1029 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1030 /*
1031 * if opt == NULL, remove transient config
1032 */
1033 if (opt == NULL) {
1034 (void) unlink(path);
1035 return (BAM_SUCCESS);
1036 }
1037
1038 fp = fopen(MNTTAB, "r");
1039 if (fp == NULL)
1040 return (BAM_ERROR);
1041
1042 mpref.mnt_mountp = "/";
1043 if (getmntany(fp, &mp, &mpref) != 0) {
1044 (void) fclose(fp);
1045 return (BAM_ERROR);
1046 }
1047 (void) fclose(fp);
1048
1049 vm = bf_init("", ficlTextOutSilent);
1050 if (vm == NULL) {
1051 bam_error(_("Error setting up forth interpreter\n"));
1052 return (ret);
1053 }
1054
1055 /*
1056 * need to check current boot config, so fire up the ficl
1057 * if its xen setup, we add option to boot-args list, not replacing it.
1058 */
1059 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1060 ret = ficlVmEvaluate(vm, buf);
1061 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1062 bam_error(_("Error interpreting boot config\n"));
1063 bf_fini();
1064 return (BAM_ERROR);
1065 }
1066 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1067 ret = ficlVmEvaluate(vm, buf);
1068 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1069 bam_error(_("Error interpreting boot config\n"));
1070 bf_fini();
1071 return (BAM_ERROR);
1072 }
1073 (void) snprintf(buf, MAX_INPUT, "start");
1074 ret = ficlVmEvaluate(vm, buf);
1075 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1076 bam_error(_("Error interpreting boot config\n"));
1077 bf_fini();
1078 return (BAM_ERROR);
1079 }
1080 (void) snprintf(buf, MAX_INPUT, "boot");
1081 ret = ficlVmEvaluate(vm, buf);
1082 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1083 bam_error(_("Error interpreting boot config\n"));
1084 bf_fini();
1085 return (BAM_ERROR);
1086 }
1087 bf_fini();
1088
1089 if (opt[0] == '-') {
1090 env = getenv("xen_kernel");
1091 fp = fopen(path, "w");
1092 if (fp == NULL)
1093 return (BAM_ERROR);
1094
1095 if (env != NULL) {
1096 env = getenv("boot-args");
1097 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1098 } else
1099 (void) fprintf(fp, "boot-args=\"%s\"\n", opt);
1100 (void) fclose(fp);
1101 return (BAM_SUCCESS);
1102 }
1103
1104 /*
1105 * it should be the case with "kernel args"
1106 * so, we split the opt at first space
1107 * and store bootfile= and boot-args=
1108 */
1109 env = getenv("xen_kernel");
1110
1111 o = strchr(opt, ' ');
1112 if (o == NULL) {
1113 fp = fopen(path, "w");
1114 if (fp == NULL)
1115 return (BAM_ERROR);
1116 (void) fprintf(fp, "bootfile=\"%s\"\n", opt);
1117 (void) fclose(fp);
1118 return (BAM_SUCCESS);
1119 }
1120 *o++ = '\0';
1121 fp = fopen(path, "w");
1122 if (fp == NULL)
1123 return (BAM_ERROR);
1124 (void) fprintf(fp, "bootfile=\"%s\"\n", opt);
1125
1126 if (env != NULL) {
1127 env = getenv("boot-args");
1128 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1129 } else
1130 (void) fprintf(fp, "boot-args=\"%s\"\n", o);
1131
1132 (void) fflush(fp);
1133 (void) fclose(fp);
1134 return (ret);
1135 }
1136
1137 static error_t
1138 list_setting(struct menu_lst *menu, char *which, char *setting)
1139 {
1140 int entry = -1;
1141 struct menu_entry *m;
1142 be_node_list_t *be_nodes, *be_node = NULL;
1143 int ret;
1144
1145 assert(which);
1146 assert(setting);
1147
1148 /*
1149 * which can be:
1150 * "" - list default entry
1151 * number - use for entry number
1152 * property name
1153 */
1154 if (*which != '\0') {
1155 if (isdigit(*which)) {
1156 char *rest;
1157 errno = 0;
1158 entry = strtol(which, &rest, 10);
1159 if (errno != 0 || *rest != '\0') {
1160 bam_error(_("invalid boot entry number: %s\n"),
1161 which);
1162 return (BAM_ERROR);
1163 }
1164 } else
1165 setting = which;
1166 }
1167
1168 /* find default entry */
1169 if (entry == -1) {
1170 ret = be_list(NULL, &be_nodes);
1171 if (ret != BE_SUCCESS) {
1172 bam_error(_("No BE's found\n"));
1173 return (BAM_ERROR);
1174 }
1175 STAILQ_FOREACH(m, menu, next) {
1176 entry++;
1177 for (be_node = be_nodes; be_node;
1178 be_node = be_node->be_next_node)
1179 if (strcmp(be_node->be_root_ds, m->bootfs) == 0)
1180 break;
1181 if (be_node->be_active_on_boot == B_TRUE)
1182 break; /* found active node */
1183 }
1184 be_free_list(be_nodes);
1185 if (be_node == NULL) {
1186 bam_error(_("None of BE nodes is marked active\n"));
1187 return (BAM_ERROR);
1188 }
1189 } else {
1190 STAILQ_FOREACH(m, menu, next)
1191 if (m->entry == entry)
1192 break;
1193
1194 if (m == NULL) {
1195 bam_error(_("no matching entry found\n"));
1196 return (BAM_ERROR);
1197 }
1198 }
1199
1200 return (list_menu_entry(m, setting));
1201 }
1202
1203 /*ARGSUSED*/
1204 static error_t
1205 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1206 {
1207 char path[PATH_MAX];
1208
1209 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1210 (void) unlink(path);
1211 return (BAM_SUCCESS);
1212 }
1213
1214 /*ARGSUSED*/
1215 static error_t
1216 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1217 {
1218 ficlVm *vm;
1219 char path[PATH_MAX];
1220 char buf[MAX_INPUT];
1221 char *env;
1222 FILE *fp;
1223 struct mnttab mpref = { 0 };
1224 struct mnttab mp = { 0 };
1225 int ret;
1226
1227 fp = fopen(MNTTAB, "r");
1228 if (fp == NULL)
1229 return (BAM_ERROR);
1230
1231 mpref.mnt_mountp = "/";
1232 if (getmntany(fp, &mp, &mpref) != 0) {
1233 (void) fclose(fp);
1234 return (BAM_ERROR);
1235 }
1236 (void) fclose(fp);
1237
1238 vm = bf_init("", ficlTextOutSilent);
1239 if (vm == NULL) {
1240 bam_error(_("Error setting up forth interpreter\n"));
1241 return (BAM_ERROR);
1242 }
1243
1244 /*
1245 * need to check current boot config, so fire up the ficl
1246 * if its xen setup, we add option to boot-args list, not replacing it.
1247 */
1248 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1249 ret = ficlVmEvaluate(vm, buf);
1250 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1251 bam_error(_("Error interpreting boot config\n"));
1252 bf_fini();
1253 return (BAM_ERROR);
1254 }
1255 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1256 ret = ficlVmEvaluate(vm, buf);
1257 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1258 bam_error(_("Error interpreting boot config\n"));
1259 bf_fini();
1260 return (BAM_ERROR);
1261 }
1262 (void) snprintf(buf, MAX_INPUT, "start");
1263 ret = ficlVmEvaluate(vm, buf);
1264 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1265 bam_error(_("Error interpreting boot config\n"));
1266 bf_fini();
1267 return (BAM_ERROR);
1268 }
1269 (void) snprintf(buf, MAX_INPUT, "boot");
1270 ret = ficlVmEvaluate(vm, buf);
1271 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1272 bam_error(_("Error interpreting boot config\n"));
1273 bf_fini();
1274 return (BAM_ERROR);
1275 }
1276 bf_fini();
1277
1278 (void) mkdir(CONF_DIR, 0755);
1279 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1280 fp = fopen(path, "w");
1281 if (fp == NULL) {
1282 return (BAM_ERROR); /* error, cant write config */
1283 }
1284
1285 errno = 0;
1286 /*
1287 * on write error, remove file to ensure we have bootable config.
1288 * note we dont mind if config exists, it will get updated
1289 */
1290 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1291 if (errno != 0)
1292 goto error;
1293
1294 /*
1295 * really simple and stupid console conversion.
1296 * it really has to be gone, it belongs to milestone/xvm properties.
1297 */
1298 env = getenv("console");
1299 if (env != NULL) {
1300 if (strcmp(env, "ttya") == 0)
1301 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1302 opt);
1303 else if (strcmp(env, "ttyb") == 0)
1304 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1305 opt);
1306 else
1307 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1308 opt);
1309 } else
1310 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1311 if (errno != 0)
1312 goto error;
1313
1314 (void) fprintf(fp,
1315 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1316 if (errno != 0)
1317 goto error;
1318
1319 (void) fprintf(fp,
1320 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1321 if (errno != 0)
1322 goto error;
1323
1324 (void) fclose(fp);
1325 if (errno != 0) {
1326 (void) unlink(path);
1327 return (BAM_ERROR);
1328 }
1329 return (BAM_SUCCESS);
1330 error:
1331 (void) fclose(fp);
1332 (void) unlink(path);
1333 return (BAM_ERROR);
1334 }