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