/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2012 Milan Jurik. All rights reserved.
*/
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 Toomas Soome <tsoome@me.com>
*/
/*
* Loader menu management.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <errno.h>
#include <limits.h>
#include <alloca.h>
#include <unistd.h>
#include <libbe.h>
#include <ficl.h>
#include <ficlplatform/emu.h>
#include "bootadm.h"
extern int bam_rootlen;
extern int bam_alt_root;
extern char *rootbuf;
extern char *bam_root;
struct menu_entry {
int entry;
char *title;
char *bootfs;
};
/* Menu related sub commands */
};
struct col_info {
const char *col_name;
};
/*
* all columns output format
*/
struct hdr_info {
};
static void
{
size_t i;
for (i = 0; i < NUM_COLS; i++) {
continue;
if (first) {
} else
}
(void) putchar('\n');
}
static void
{
size_t i;
for (i = 0; i < NUM_COLS; i++) {
sizeof (wchar_t));
if (sz > 0) {
if (wcsw > 0)
else
} else {
}
}
}
}
static void
{
int i;
for (i = 0; i < NUM_COLS; i++)
}
for (i = 0; i < NUM_COLS; i++)
}
static void
{
int i = -1;
int rv;
if (rv != BE_SUCCESS)
return;
i++;
break;
if (parsable)
(void) printf("%d;%s;%s;%s\n", i,
else
(void) printf("%-*d %-*s %-*s %-*s\n",
}
}
static void
{
if (!parsable) {
init_hdr_cols(&hdr);
}
}
{
char *title;
char *bootfs;
char *ptr;
int i = 0;
return (BAM_ERROR);
/*
* menu.lst entry is on two lines, one for title, one for bootfs
* so we process both lines in succession.
*/
do {
return (ret);
}
*ptr = '\0';
return (BAM_ERROR);
}
*ptr++ = '\0';
return (BAM_ERROR);
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
*ptr = '\0';
return (BAM_ERROR);
}
*ptr++ = '\0';
return (BAM_ERROR);
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
return (ret);
}
void
{
}
}
{
char *special;
char *zmntpt;
char *osdev;
char *osroot;
STAILQ_INIT(&menu);
/*
* Check arguments
*/
return (BAM_ERROR);
}
if (osroot) {
/* fixup bam_root so that it points at osroot */
bam_error(_("cannot resolve path %s: %s\n"),
return (BAM_ERROR);
}
bam_alt_root = 1;
}
}
bam_error(_("cannot find menu\n"));
return (BAM_ERROR);
}
bam_error(_("only ZFS root is supported\n"));
return (BAM_ERROR);
}
bam_error(_("cant find special file for mount-point %s\n"),
return (BAM_ERROR);
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
sizeof (clean_menu_root));
/*
* update_entry is special case, its used by installer
* and needs to create menu.lst file for loader
*/
return (BAM_ERROR);
}
/*
* If listing the menu, display the menu location
*/
bam_print(_("the location for the active menu is: %s\n"),
/*
* We already checked the following case in
* check_subcmd_and_suboptions() above. Complete the
* final step now.
*/
}
/*
* Once the sub-cmd handler has run
* only the line field is guaranteed to have valid values
*/
if (is_sparc()) {
bam_error(_("%s operation unsupported on SPARC "
"machines\n"), subcmd);
} else {
}
if (is_sparc()) {
bam_error(_("%s operation unsupported on SPARC "
"machines\n"), subcmd);
} else {
/*
* Compress all arguments passed in the largv[] array
* into one string that can then be appended to the
* end of the kernel$ string the routine to enable the
* hypervisor will build.
*
* This allows the caller to supply arbitrary unparsed
* arguments, such as dom0 memory settings or APIC
* options.
*
* This concatenation will be done without ANY syntax
* checking whatsoever, so it's the responsibility of
* the caller to make sure the arguments are valid and
* do not duplicate arguments the conversion routines
* may create.
*/
if (largc > 0) {
int extra_len, i;
/*
* Allocate space for argument strings,
* intervening spaces and terminating NULL.
*/
for (i = 1; i < largc; i++) {
}
}
}
} else
BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
fcn, clean_menu_root));
/* ret = menu_write(clean_menu_root, menu); */
}
if (pool) {
}
return (ret);
}
/*
* To suppress output from ficl. We do not want to see messages
* from interpreting loader config.
*/
/*ARGSUSED*/
static void
{
}
/*ARGSUSED*/
static error_t
{
char *val;
char *rest;
int optval;
*val++ = '\0';
}
errno = 0;
return (BAM_ERROR);
}
break;
}
return (BAM_ERROR);
}
bam_error(_("out of memory\n"));
return (BAM_ERROR);
}
bam_error(_("out of memory\n"));
return (BAM_ERROR);
}
if (ret != 0)
return (ret);
errno = 0;
return (BAM_ERROR);
}
bam_root);
bam_error(_("failed to open file: %s: %s\n"),
return (BAM_ERROR);
}
/*
* timeout=-1 is to disable auto boot in illumos, but
* loader needs "NO" to disable auto boot.
*/
if (optval == -1)
else
if (rv < 0) {
bam_error(_("write to file failed: %s: %s\n"),
} else
if (rv < 0) {
bam_error(_("failed to close file: %s: %s\n"),
}
return (BAM_SUCCESS);
}
return (BAM_ERROR);
}
static int
{
int ret;
tmpdir = "/tmp";
if (ret < 0) {
return (BE_ERR_NOMEM);
}
ret = BE_ERR_NOMEM;
goto out;
}
if (ret != BE_SUCCESS) {
goto out;
}
break;
be_node->be_node_name) != 0) {
ret = BE_ERR_NOMEM;
goto out;
}
ret = BE_ERR_NOMEM;
goto out;
}
if (ret == BE_ERR_MOUNTED) {
/*
* if BE is mounted, dir does not point to correct directory
*/
}
out:
return (ret);
}
static int
{
int ret;
return (BE_SUCCESS);
return (BE_ERR_NOMEM);
ret = BE_ERR_NOMEM;
goto out;
}
out:
return (ret);
}
/*
* display details of menu entry or single property
*/
static error_t
{
int mounted;
}
return (BAM_ERROR);
}
bam_error(_("error setting up forth interpreter\n"));
goto done;
}
/* should only get FICL_VM_STATUS_OUT_OF_TEXT */
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("error interpreting boot config\n"));
goto done;
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("error interpreting boot config\n"));
goto done;
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("error interpreting boot config\n"));
goto done;
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("error interpreting boot config\n"));
goto done;
}
ret = BAM_SUCCESS;
if (*setting == '\0')
goto done;
}
if (*setting == '\0')
goto done;
}
}
if (*setting == '\0')
goto done;
}
}
if (*setting == '\0')
goto done;
}
if (*setting == '\0') {
goto done;
}
if (*setting == '\0') {
(void) printf("Xen args: \"%s\"\n",
getenv("xen_cmdline"));
goto done;
}
if (*setting == '\0') {
(void) printf("Kernel: %s\n",
getenv("bootfile"));
goto done;
}
} else {
if (*setting == '\0') {
goto done;
}
}
}
if (*setting == '\0') {
goto done;
}
}
(void) printf("\nModules:\n");
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("error interpreting boot config\n"));
goto done;
}
ret = BAM_SUCCESS;
goto done;
}
/* if we got here with setting string, its unknown property */
if (*setting != '\0') {
} else
ret = BAM_SUCCESS;
done:
bf_fini();
if (mounted != BE_ERR_MOUNTED) {
(void) bam_umount_be(dir);
}
}
return (ret);
}
/*ARGSUSED*/
static error_t
{
int i, e = -1;
return (ret);
}
return (BAM_ERROR);
}
} else {
return (BAM_ERROR);
}
break;
break;
}
bam_error(_("no matching entry found\n"));
return (BAM_ERROR);
}
}
/*
* For now this is just stub entry to support grub interface, the
* known consumer is installer ict.py code, calling as:
* bootadm update-menu -R /a -Z -o rdisk
* Later this can be converted to do something useful.
*/
/*ARGSUSED*/
static error_t
{
int rv;
if (rv != BE_SUCCESS)
return (BAM_ERROR);
return (BAM_ERROR);
}
}
}
return (BAM_SUCCESS);
}
/*ARGSUSED*/
static error_t
{
char *env, *o;
/*
* if opt == NULL, remove transient config
*/
return (BAM_SUCCESS);
}
return (BAM_ERROR);
return (BAM_ERROR);
}
bam_error(_("Error setting up forth interpreter\n"));
return (ret);
}
/*
* need to check current boot config, so fire up the ficl
* if its xen setup, we add option to boot-args list, not replacing it.
*/
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
bf_fini();
if (opt[0] == '-') {
return (BAM_ERROR);
} else
return (BAM_SUCCESS);
}
/*
* it should be the case with "kernel args"
* so, we split the opt at first space
* and store bootfile= and boot-args=
*/
if (o == NULL) {
return (BAM_ERROR);
return (BAM_SUCCESS);
}
*o++ = '\0';
return (BAM_ERROR);
} else
return (ret);
}
static error_t
{
struct menu_entry *m;
int ret;
/*
* which can be:
* "" - list default entry
* number - use for entry number
* property name
*/
if (*which != '\0') {
char *rest;
errno = 0;
bam_error(_("invalid boot entry number: %s\n"),
which);
return (BAM_ERROR);
}
} else
}
/* find default entry */
if (entry == -1) {
if (ret != BE_SUCCESS) {
bam_error(_("No BE's found\n"));
return (BAM_ERROR);
}
entry++;
break;
break; /* found active node */
}
bam_error(_("None of BE nodes is marked active\n"));
return (BAM_ERROR);
}
} else {
break;
if (m == NULL) {
bam_error(_("no matching entry found\n"));
return (BAM_ERROR);
}
}
return (list_menu_entry(m, setting));
}
/*ARGSUSED*/
static error_t
{
return (BAM_SUCCESS);
}
/*ARGSUSED*/
static error_t
{
char *env;
int ret;
return (BAM_ERROR);
return (BAM_ERROR);
}
bam_error(_("Error setting up forth interpreter\n"));
return (BAM_ERROR);
}
/*
* need to check current boot config, so fire up the ficl
* if its xen setup, we add option to boot-args list, not replacing it.
*/
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
bam_error(_("Error interpreting boot config\n"));
bf_fini();
return (BAM_ERROR);
}
bf_fini();
return (BAM_ERROR); /* error, cant write config */
}
errno = 0;
/*
* on write error, remove file to ensure we have bootable config.
* note we dont mind if config exists, it will get updated
*/
if (errno != 0)
goto error;
/*
* really simple and stupid console conversion.
*/
opt);
opt);
else
opt);
} else
if (errno != 0)
goto error;
if (errno != 0)
goto error;
if (errno != 0)
goto error;
if (errno != 0) {
return (BAM_ERROR);
}
return (BAM_SUCCESS);
return (BAM_ERROR);
}