bootadm.c revision 7975540170826a816320fa0cf07ba06a66c4252d
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* bootadm(1M) is a new utility for managing bootability of
* Solaris *Newboot* environments. It has two primary tasks:
* - Allow end users to manage bootability of Newboot Solaris instances
* - Provide services to other subsystems in Solaris (primarily Install)
*/
/* Headers */
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <limits.h>
#include <signal.h>
#include <libnvpair.h>
#include <ftw.h>
#include <fcntl.h>
#include <strings.h>
#include <utime.h>
#include <sys/systeminfo.h>
#include <dirent.h>
#include <ctype.h>
#include <libgen.h>
#include <sys/sysmacros.h>
#if !defined(_OPB)
#endif
#include <pwd.h>
#include <grp.h>
#include <device_info.h>
#include <sys/efi_partition.h>
#include <locale.h>
#include "message.h"
#include "bootadm.h"
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#endif /* TEXT_DOMAIN */
/* Type definitions */
/* Primary subcmds */
typedef enum {
BAM_MENU = 3,
} subcmd_t;
typedef enum {
OPT_ABSENT = 0, /* No option */
OPT_REQ, /* option required */
OPT_OPTIONAL /* option may or may not be present */
} option_t;
typedef struct {
char *subcmd;
int unpriv; /* is this an unprivileged command */
#define LINE_INIT 0 /* lineNum initial value */
#define MENU_TMP "/boot/grub/menu.lst.tmp"
#define RAMDISK_SPECIAL "/ramdisk"
#define STUBBOOT "/stubboot"
#define GRUBSIGN_DIR "/boot/grub/bootsign"
#define GRUBSIGN_BACKUP "/etc/bootsign"
#define GRUBSIGN_UFS_PREFIX "rootfs"
#define GRUBSIGN_ZFS_PREFIX "pool_"
#define GRUBSIGN_LU_PREFIX "BE_"
#define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
#define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
#define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
/* lock related */
#define BAM_LOCK_FILE "/var/run/bootadm.lock"
#define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
#define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
#define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
#define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
#define GRUB_slice "/etc/lu/GRUB_slice"
#define GRUB_fdisk "/etc/lu/GRUB_fdisk"
#define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
#define INSTALLGRUB "/sbin/installgrub"
typedef enum zfs_mnted {
ZFS_MNT_ERROR = -1,
LEGACY_MOUNTED = 1,
} zfs_mnted_t;
/*
* The following two defines are used to detect and create the correct
* boot archive when safemode patching is underway. LOFS_PATCH_FILE is a
* contracted private interface between bootadm and the install
* consolidation. It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
* is applied.
*/
#define LOFS_PATCH_FILE "/var/run/.patch_loopback_mode"
#define LOFS_PATCH_MNT "/var/run/.patch_root_loopbackmnt"
/*
* Default file attributes
*/
#define DEFAULT_DEV_UID 0 /* user root */
/*
* Menu related
* menu_cmd_t and menu_cmds must be kept in sync
*/
char *menu_cmds[] = {
"default", /* DEFAULT_CMD */
"timeout", /* TIMEOUT_CMD */
"title", /* TITLE_CMD */
"root", /* ROOT_CMD */
"kernel", /* KERNEL_CMD */
"kernel$", /* KERNEL_DOLLAR_CMD */
"module", /* MODULE_CMD */
"module$", /* MODULE_DOLLAR_CMD */
" ", /* SEP_CMD */
"#", /* COMMENT_CMD */
"chainloader", /* CHAINLOADER_CMD */
"args", /* ARGS_CMD */
"findroot", /* FINDROOT_CMD */
};
#define OPT_ENTRY_NUM "entry"
/*
* exec_cmd related
*/
typedef struct {
} filelist_t;
#define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
#define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
#define FILE_STAT "boot/solaris/filestat.ramdisk"
#define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
/* Globals */
int bam_verbose;
int bam_force;
int bam_debug;
static char *prog;
static char *bam_root;
static int bam_rootlen;
static int bam_root_readonly;
static int bam_alt_root;
static char *bam_subcmd;
static char *bam_opt;
static char **bam_argv;
static int bam_argc;
static int bam_check;
static int bam_smf_check;
static int bam_lock_fd = -1;
static int bam_zfs;
static int bam_update_all;
static int bam_alt_platform;
static char *bam_platform;
/* function prototypes */
static void parse_args_internal(int, char *[]);
static void parse_args(int, char *argv[]);
static error_t bam_archive(char *, char *);
static void bam_exit(int);
static void bam_lock(void);
static void bam_unlock(void);
static int exec_cmd(char *, filelist_t *);
static void linelist_free(line_t *);
static void filelist_free(filelist_t *);
static error_t update_archive(char *, char *);
static error_t list_archive(char *, char *);
static error_t update_all(char *, char *);
static char *expand_path(const char *);
static long s_strtol(char *);
static int is_amd64(void);
static char *get_machine(void);
static void append_to_flist(filelist_t *, char *);
static int ufs_add_to_sign_list(char *sign);
#if !defined(_OPB)
static void ucode_install();
#endif
/* Menu related sub commands */
static subcmd_defn_t menu_subcmds[] = {
};
/* Archive related sub commands */
static subcmd_defn_t arch_subcmds[] = {
};
static struct {
int need_update;
} walk_arg;
struct safefile {
char *name;
};
#define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
static void
usage(void)
{
/* archive usage */
"\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
"\t%s list-archive [-R altroot [-p platform>]]\n", prog);
#if !defined(_OPB)
/* x86 only */
#endif
}
int
{
(void) textdomain(TEXT_DOMAIN);
} else {
prog++;
}
/*
* Don't depend on caller's umask
*/
(void) umask(0022);
switch (bam_cmd) {
case BAM_MENU:
break;
case BAM_ARCHIVE:
break;
default:
usage();
bam_exit(1);
}
if (ret != BAM_SUCCESS)
bam_exit(1);
bam_unlock();
return (0);
}
/*
* Equivalence of public and internal commands:
* update-archive -- -a update
* list-archive -- -a list
* set-menu -- -m set_option
* list-menu -- -m list_entry
* update-menu -- -m update_entry
*/
static struct cmd_map {
char *bam_cmdname;
int bam_cmd;
char *bam_subcmd;
} cmd_map[] = {
};
/*
* Commands syntax published in bootadm(1M) are parsed here
*/
static void
{
/* command conforming to the final spec */
/*
* Map commands to internal table.
*/
while (cmp->bam_cmdname) {
break;
}
cmp++;
}
usage();
bam_exit(1);
}
argc--;
argv++;
}
}
/*
* A combination of public and private commands are parsed here.
* The internal syntax and the corresponding functionality are:
* -a update -- update-archive
* -a list -- list-archive
* -a update-all -- (reboot to sync all mounted OS archive)
* -m update_entry -- update-menu
* -m list_entry -- list-menu
* -m update_temp -- (reboot -- [boot-args])
* -m delete_all_entries -- (called from install)
*/
static void
{
int c, error;
extern char *optarg;
/* Suppress error message from getopt */
opterr = 0;
error = 0;
switch (c) {
case 'a':
if (bam_cmd) {
error = 1;
}
bam_subcmd = optarg;
break;
case 'd':
if (bam_debug) {
error = 1;
}
break;
case 'f':
if (bam_force) {
error = 1;
}
bam_force = 1;
break;
case 'm':
if (bam_cmd) {
error = 1;
}
bam_subcmd = optarg;
break;
case 'n':
if (bam_check) {
error = 1;
}
bam_check = 1;
break;
case 'o':
if (bam_opt) {
error = 1;
}
break;
case 'v':
if (bam_verbose) {
error = 1;
}
bam_verbose = 1;
break;
case 'C':
bam_smf_check = 1;
break;
case 'R':
if (bam_root) {
error = 1;
break;
error = 1;
break;
}
bam_alt_root = 1;
break;
case 'p':
bam_alt_platform = 1;
error = 1;
}
break;
case 'Z':
bam_zfs = 1;
break;
case '?':
error = 1;
break;
default :
error = 1;
break;
}
}
/*
* An alternate platform requires an alternate root
*/
if (bam_alt_platform && bam_alt_root == 0) {
usage();
bam_exit(0);
}
/*
* A command option must be specfied
*/
if (!bam_cmd) {
usage();
bam_exit(0);
}
error = 1;
}
if (error) {
usage();
bam_exit(1);
}
bam_exit(1);
}
/*
* -n implies verbose mode
*/
if (bam_check)
bam_verbose = 1;
}
static error_t
char *subcmd,
char *opt,
{
int i;
return (BAM_ERROR);
}
usage();
return (BAM_ERROR);
usage();
return (BAM_ERROR);
}
usage();
return (BAM_ERROR);
}
bam_rootlen = 1;
}
/* verify that subcmd is valid */
break;
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
/*
* Currently only privileged commands need a lock
*/
bam_lock();
/* subcmd verifies that opt is appropriate */
if (opt)
else
return (BAM_ERROR);
}
}
return (BAM_SUCCESS);
}
/*
* NOTE: A single "/" is also considered a trailing slash and will
* be deleted.
*/
static void
{
}
}
static error_t
{
char clean_menu_root[PATH_MAX];
char *special;
char *zmntpt;
char *osdev;
char *osroot;
const char *fcn = "bam_menu()";
/*
* Menu sub-command only applies to GRUB (i.e. x86)
*/
return (BAM_ERROR);
}
/*
* Check arguments
*/
return (BAM_ERROR);
}
if (osroot) {
/* fixup bam_root so that it points at osroot */
return (BAM_ERROR);
}
bam_alt_root = 1;
}
}
/*
* We support menu on PCFS (under certain conditions), but
* not the OS root
*/
return (BAM_ERROR);
}
return (BAM_ERROR);
}
/*
* We no longer use the GRUB slice file. If it exists, then
* the user is doing something that is unsupported (such as
* standard upgrading an old Live Upgrade BE). If that
* happens, mimic existing behavior i.e. pretend that it is
* not a BE. Emit a warning though.
*/
if (bam_alt_root) {
} else {
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
}
sizeof (clean_menu_root));
/*
* If listing the menu, display the menu location
*/
}
/*
* We already checked the following case in
* check_subcmd_and_suboptions() above. Complete the
* final step now.
*/
} else {
}
if (ret != BAM_SUCCESS) {
goto out;
}
/*
* Once the sub-cmd handler has run
* only the line field is guaranteed to have valid values
*/
else
}
out:
if (pool) {
}
return (ret);
}
static error_t
char *subcmd,
char *opt)
{
const char *fcn = "bam_archive()";
/*
* Add trailing / for archive subcommands
*/
/*
* Check arguments
*/
if (ret != BAM_SUCCESS) {
return (BAM_ERROR);
}
if (ret != BAM_SUCCESS) {
return (ret);
}
/*
* Check archive not supported with update_all
* since it is awkward to display out-of-sync
* information for each BE.
*/
return (BAM_ERROR);
}
bam_update_all = 1;
#if !defined(_OPB)
#endif
bam_update_all = 0;
return (ret);
}
/*PRINTFLIKE1*/
void
{
}
/*PRINTFLIKE1*/
void
bam_derror(char *format, ...)
{
}
/*PRINTFLIKE1*/
void
{
}
/*PRINTFLIKE1*/
void
bam_print_stderr(char *format, ...)
{
}
static void
{
bam_unlock();
}
static void
bam_lock(void)
{
if (bam_lock_fd < 0) {
/*
* We may be invoked early in boot for archive verification.
* Proceed without the lock
*/
bam_root_readonly = 1;
return;
}
bam_exit(1);
}
(void) close(bam_lock_fd);
bam_lock_fd = -1;
bam_exit(1);
}
pid = 0;
(void) close(bam_lock_fd);
bam_lock_fd = -1;
bam_exit(1);
}
}
/* We own the lock now */
}
static void
bam_unlock(void)
{
/*
* NOP if we don't hold the lock
*/
if (bam_lock_fd < 0) {
return;
}
}
}
bam_lock_fd = -1;
}
static error_t
{
return (BAM_ERROR);
}
}
return (BAM_SUCCESS);
}
/*
* This routine writes a list of lines to a file.
* The list is *not* freed
*/
static error_t
{
int ret;
const char *fcn = "list2file()";
/* Empty GRUB menu */
return (BAM_ERROR);
} else {
return (BAM_SUCCESS);
}
}
return (BAM_SUCCESS);
}
/*
* Preserve attributes of existing file if possible,
* If all fails, fall back on hard-coded defaults.
*/
} else {
} else {
}
} else {
}
}
/* Truncate tmpfile first */
return (BAM_ERROR);
}
return (BAM_ERROR);
}
/* Now open it in append mode */
return (BAM_ERROR);
}
return (BAM_ERROR);
}
}
return (BAM_ERROR);
}
/*
* Set up desired attributes. Ignore failures on filesystems
* not supporting these operations - pcfs reports unsupported
* operations as EINVAL.
*/
if (ret == -1 &&
return (BAM_ERROR);
}
if (ret == -1 &&
return (BAM_ERROR);
}
/*
* Do an atomic rename
*/
if (ret != 0) {
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
/*
* This function should always return 0 - since we want
* to create stat data for *all* files in the list.
*/
/*ARGSUSED*/
static int
const char *file,
int flags,
{
int error;
/*
* We only want regular files
*/
return (0);
/*
* new_nvlp may be NULL if there were errors earlier
* but this is not fatal to update determination.
*/
if (error)
}
/*
* The remaining steps are only required if we haven't made a
* decision about update or if we are checking (-n)
*/
return (0);
/*
* If we are invoked as part of system/filesystem/boot-archive, then
* there are a number of things we should not worry about
*/
if (bam_smf_check) {
/* ignore amd64 modules unless we are booted amd64. */
return (0);
/* read in list of safe files */
sizeof (struct safefile));
sizeof (struct safefile));
MAXPATHLEN + MAXNAMELEN);
}
}
}
/*
* We need an update if file doesn't exist in old archive
*/
if (bam_smf_check) /* ignore new during smf check */
return (0);
if (bam_verbose)
return (0);
}
/*
* File exists in old archive. Check if file has changed
*/
if (bam_smf_check) {
return (0);
}
}
}
if (bam_verbose)
if (bam_smf_check)
else
}
return (0);
}
/*
* Check flags and presence of required files.
* trigger an update.
* Suppress stdout output if check (-n) option is set
* (as -n should only produce parseable output.)
*/
static void
check_flags_and_files(char *root)
{
/*
* if force, create archive unconditionally
*/
if (bam_force) {
if (bam_verbose && !bam_check)
return;
}
/*
* If archive is missing, create archive
*/
if (is_sparc()) {
} else {
if (bam_direct == BAM_DIRECT_DBOOT) {
if (bam_verbose && !bam_check)
return;
}
}
}
if (bam_verbose && !bam_check)
return;
}
}
static error_t
{
char buf[BAM_MAXLINE];
const char *fcn = "read_one_list()";
return (BAM_ERROR);
}
/* skip blank lines */
continue;
}
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
static error_t
{
int n, rval;
const char *fcn = "read_list()";
/*
* build and check path to extract_boot_filelist.ksh
*/
if (n >= sizeof (path)) {
return (BAM_ERROR);
}
/*
* If extract_boot_filelist is present, exec it, otherwise read
* the filelists directly, for compatibility with older images.
*/
/*
* build arguments to exec extract_boot_filelist.ksh
*/
if (bam_alt_platform)
*platarg = 0;
*rootarg = 0;
"-R %s", root);
}
if (bam_alt_platform) {
"-p %s", bam_platform);
}
if (n >= sizeof (cmd)) {
return (BAM_ERROR);
}
return (BAM_ERROR);
}
} else {
/*
* Read current lists of files - only the first is mandatory
*/
if (rval != BAM_SUCCESS)
return (rval);
}
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
static void
getoldstat(char *root)
{
char *ostat;
if (fd == -1) {
if (bam_verbose)
return;
}
return;
}
return;
}
if (error) {
return;
}
}
/*
* Checks if a file in the current (old) archive has
* been deleted from the root filesystem. This is needed for
* software like Trusted Extensions (TX) that switch early
*/
static void
check4stale(char *root)
{
char *file;
/*
* Skip stale file check during smf check
*/
if (bam_smf_check)
return;
/* Nothing to do if no old stats */
return;
continue;
if (bam_verbose)
}
}
}
static void
create_newstat(void)
{
int error;
if (error) {
/*
* Not fatal - we can still create archive
*/
}
}
static void
{
/*
* Don't follow symlinks. A symlink must refer to
* a file that would appear in the archive through
* a direct reference. This matches the archive
* construction behavior.
*/
/*
* Some files may not exist.
* For example: etc/rtc_config on a x86 diskless system
* Emit verbose message only
*/
if (bam_verbose)
}
}
}
static void
{
char *nstat;
sz = 0;
NV_ENCODE_XDR, 0);
if (error) {
return;
}
if (fd == -1) {
return;
}
return;
}
}
}
static void
clear_walk_args(void)
{
walk_arg.need_update = 0;
}
/*
* Returns:
* 0 - no update necessary
* 1 - update required.
* BAM_ERROR (-1) - An error occurred
*
* Special handling for check (-n):
* ================================
* The check (-n) option produces parseable output.
* To do this, we suppress all stdout messages unrelated
* to out of sync files.
* All stderr messages are still printed though.
*
*/
static int
update_required(char *root)
{
int need_update;
walk_arg.need_update = 0;
/*
* Without consulting stat data, check if we need update
*/
/*
* In certain deployment scenarios, filestat may not
* exist. Ignore it during boot-archive SMF check.
*/
if (bam_smf_check) {
return (0);
}
/*
* consult stat data only if we haven't made a decision
* about update. If checking (-n) however, we always
* need stat data (since we want to compare old and new)
*/
/*
* Check if the archive contains files that are no longer
* present on the root filesystem.
*/
/*
* read list of files
*/
return (BAM_ERROR);
}
/*
* At this point either the update is required
* or the decision is pending. In either case
* we need to create new stat nvlist
*/
/*
* This walk does 2 things:
* - gets new stat data for every file
* - (optional) compare old and new stat data
*/
/* done with the file list */
/*
* if we didn't succeed in creating new stat data above
* just return result of update check so that archive is built.
*/
return (need_update ? 1 : 0);
}
/*
* If no update required, discard newstat
*/
if (!walk_arg.need_update) {
return (0);
}
return (1);
}
static error_t
create_ramdisk(char *root)
{
/*
* Setup command args for create_ramdisk.ksh
*/
return (BAM_ERROR);
}
if (bam_alt_platform)
if (bam_alt_platform) {
/* chop off / at the end */
/* chop off / at the end */
} else
return (BAM_ERROR);
}
/*
* The existence of the expected archives used to be
* verified here. This check is done in create_ramdisk as
* it needs to be in sync with the altroot operated upon.
*/
return (BAM_SUCCESS);
}
/*
* Checks if target filesystem is on a ramdisk
* 1 - is miniroot
* 0 - is not
* When in doubt assume it is not a ramdisk.
*/
static int
is_ramdisk(char *root)
{
int found;
char *cp;
/*
* There are 3 situations where creating archive is
* of dubious value:
* - create boot_archive on a lofi-mounted boot_archive
* - create it on a ramdisk which is the root filesystem
* - create it on a ramdisk mounted somewhere else
* The first is not easy to detect and checking for it is not
* worth it.
* The other two conditions are handled here
*/
return (0);
}
/*
* Remove any trailing / from the mount point
*/
if (*cp == '/')
*cp = '\0';
}
found = 0;
found = 1;
break;
}
}
if (!found) {
if (bam_verbose)
return (0);
}
if (bam_verbose)
return (1);
}
return (0);
}
static int
is_boot_archive(char *root)
{
int error;
const char *fcn = "is_boot_archive()";
/*
* We can't create an archive without the create_ramdisk script
*/
if (error == -1) {
if (bam_verbose)
return (0);
}
return (1);
}
/*
* Need to call this for anything that operates on the GRUB menu
*/
int
{
const char *fcn = "is_grub()";
return (0);
}
return (1);
}
static int
{
int ret;
const char *fcn = "is_zfs()";
if (ret != 0) {
return (0);
}
return (1);
} else {
return (0);
}
}
static int
{
int ret;
const char *fcn = "is_ufs()";
if (ret != 0) {
return (0);
}
return (1);
} else {
return (0);
}
}
static int
{
int ret;
const char *fcn = "is_pcfs()";
if (ret != 0) {
return (0);
}
return (1);
} else {
return (0);
}
}
static int
is_readonly(char *root)
{
int fd;
int error;
const char *fcn = "is_readonly()";
/*
* Using statvfs() to check for a read-only filesystem is not
* reliable. The only way to reliably test is to attempt to
* create a file
*/
errno = 0;
return (1);
} else if (fd == -1) {
}
return (0);
}
static error_t
{
/*
* root must belong to a boot archive based OS,
*/
if (!is_boot_archive(root)) {
/*
* Emit message only if not in context of update_all.
* If in update_all, emit only if verbose flag is set.
*/
if (!bam_update_all || bam_verbose)
return (BAM_SUCCESS);
}
/*
* If smf check is requested when / is writable (can happen
* on first reboot following an upgrade because service
* dependency is messed up), skip the check.
*/
if (bam_smf_check && !bam_root_readonly)
return (BAM_SUCCESS);
/*
* root must be writable. This check applies to alternate
* root (-R option); bam_root_readonly applies to '/' only.
*/
if (bam_verbose)
return (BAM_SUCCESS);
}
/*
* Don't generate archive on ramdisk
*/
if (is_ramdisk(root)) {
if (bam_verbose)
return (BAM_SUCCESS);
}
/*
* Now check if updated is really needed
*/
/*
* The check command (-n) is *not* a dry run
* It only checks if the archive is in sync.
*/
if (bam_check) {
}
if (ret == 1) {
/* create the ramdisk */
}
/* if the archive is updated, save the new stat data */
}
return (ret);
}
static void
update_fdisk(void)
{
}
/*
* We are done, remove the files.
*/
}
}
static error_t
{
return (BAM_ERROR);
}
/*
* Check to see if we are in the midst of safemode patching
* If so skip building the archive for /. Instead build it
* against the latest bits obtained by creating a fresh lofs
* mount of root.
*/
goto out;
}
"/sbin/mount -F lofs -o nosub / %s", LOFS_PATCH_MNT);
}
/*
* unmount the lofs mount since there could be
* multiple invocations of bootadm -a update_all
*/
"/sbin/umount %s", LOFS_PATCH_MNT);
}
}
} else {
/*
* First update archive for current root
*/
}
goto out;
/*
* Now walk the mount table, performing archive update
* for all mounted Newboot root filesystems
*/
goto out;
}
continue;
continue;
continue;
continue;
/*
* We put a trailing slash to be consistent with root = "/"
* case, such that we don't have to print // in some cases.
*/
/*
* It's possible that other mounts may be an alternate boot
* architecture, so check it again.
*/
}
out:
/*
* Update fdisk table as we go down. Updating it when
* the system is running will confuse biosdev.
*/
update_fdisk();
/*
* It is an error for one file to be
* present and the other absent.
* It is normal for both files to be
* absent - it indicates that no fdisk
* update is required.
*/
}
return (ret);
}
static void
{
} else {
}
}
void
{
/* unlink from list */
else
else
}
static entry_t *
{
const char *fcn = "boot_entry_new()";
return (ent);
}
return (ent);
}
static void
{
if (ent)
}
/*
* Check whether cmd matches the one indexed by which, and whether arg matches
* str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
* respective *_DOLLAR_CMD is also acceptable. The arg is searched using
* strstr(), so it can be a partial match.
*/
static int
{
int ret;
const char *fcn = "check_cmd()";
return (0);
}
if (ret) {
} else {
}
return (ret);
}
static error_t
{
const char *fcn = "kernel_parser()";
return (BAM_ERROR);
}
sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
sizeof (DIRECT_BOOT_64) - 1) == 0) {
sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
return (BAM_ERROR);
} else {
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
static error_t
{
const char *fcn = "module_parser()";
return (BAM_ERROR);
}
return (BAM_SUCCESS);
/* don't emit warning for hand entries */
return (BAM_ERROR);
} else {
return (BAM_ERROR);
}
}
/*
* A line in menu.lst looks like
* [ ]*<cmd>[ \t=]*<arg>*
*/
static void
{
/*
* save state across calls. This is so that
* header gets the right entry# after title has
* been processed
*/
static int in_liveupgrade = 0;
const char *fcn = "line_parser()";
return;
}
/*
* First save a copy of the entire line.
* We use this later to set the line field.
*/
/* Eat up leading whitespace */
str++;
flag = BAM_COMMENT;
in_liveupgrade = 1;
in_liveupgrade = 0;
}
} else {
/*
* '=' is not a documented separator in grub syntax.
* However various development bits use '=' as a
* separator. In addition, external users also
* use = as a separator. So we will allow that usage.
*/
if (*str == '\0') {
break;
}
str++;
}
if (*str != '\0') {
*str = '\0';
str++;
*str = '\0';
str++;
if (*str == '\0')
else
}
}
} else {
if (in_liveupgrade) {
}
}
} else if (flag != BAM_INVALID) {
/*
* For header comments, the entry# is "fixed up"
* by the subsequent title
*/
} else {
if (*entryNum == ENTRY_INIT) {
} else {
== 0) {
arg));
menu_cmds[CHAINLOADER_CMD]) == 0) {
arg));
}
}
}
}
/* record default, old default, and entry line ranges */
}
}
void
{
int lineNum;
int entryNum;
int old_default_value;
return;
}
/*
* Get the value of the default command
*/
default_lp = lp;
}
/*
* If not a booting entry, nothing else to fix for this
* entry
*/
continue;
/*
* Record the position of the default entry.
* The following works because global
* commands like default and timeout should precede
* actual boot entries, so old_default_value
* is already known (or default cmd is missing).
*/
if (default_entry == NULL &&
old_default_value != ENTRY_INIT &&
default_entry = lp;
}
/*
* Now fixup the entry number
*/
/* fixup the bootadm header */
}
} else {
}
}
/*
* No default command in menu, simply return
*/
if (default_lp == NULL) {
return;
}
if (default_entry == NULL) {
} else {
}
/*
* The following is required since only the line field gets
* written back to menu.lst
*/
}
static menu_t *
{
return (mp);
}
/* Note: GRUB boot entry number starts with 0 */
entry = ENTRY_INIT;
len -= n - 1;
cp += n - 1;
continue;
}
}
}
return (mp);
}
static error_t
{
char *eq;
char *opt_dup;
int entryNum;
if (entry)
*entry = ENTRY_INIT;
if (title)
return (BAM_ERROR);
}
*eq = '\0';
return (BAM_ERROR);
}
} else {
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
/*
* only title lines in file are printed.
*
* If invoked with a title or entry #, all
* lines in *every* matching entry are listed
*/
static error_t
{
int entry = ENTRY_INIT;
int found;
/* opt is optional */
return (BAM_ERROR);
}
return (BAM_ERROR);
}
} else {
}
found = 0;
continue;
found = 1;
continue;
}
found = 1;
continue;
}
/*
* We set the entry value here so that all lines
* in entry get printed. If we subsequently match
* title in other entries, all lines in those
* entries get printed as well.
*/
found = 1;
continue;
}
}
if (!found) {
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
int
char *title,
char *findroot,
char *kernel,
char *mod_kernel,
char *module)
{
int lineNum;
int entryNum;
char linebuf[BAM_MAXLINE];
const char *fcn = "add_boot_entry()";
return (BAM_ERROR);
}
}
return (BAM_ERROR);
}
if (bam_direct != BAM_DIRECT_DBOOT) {
return (BAM_ERROR);
}
/* Figure the commands out from the kernel line */
k_cmd = KERNEL_CMD;
m_cmd = MODULE_CMD;
} else {
k_cmd = KERNEL_CMD;
m_cmd = MODULE_CMD;
}
} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
/*
* If it's a non-failsafe dboot kernel, use the "kernel$"
* command. Otherwise, use "kernel".
*/
} else {
k_cmd = KERNEL_CMD;
m_cmd = MODULE_CMD;
}
} else {
}
/*
* The syntax for comments is #<comment>
*/
if (mod_kernel != NULL) {
}
return (entryNum);
}
static error_t
{
int deleted;
const char *fcn = "do_delete()";
while (ent) {
/* check entry number and make sure it's a bootadm entry */
continue;
}
/* free the entry content */
do {
/* free the entry_t structure */
else
if (ent)
deleted = 1;
}
return (BAM_ERROR);
}
/*
* Now that we have deleted an entry, update
* the entry numbering and the default cmd.
*/
return (BAM_SUCCESS);
}
static error_t
{
return (BAM_SUCCESS);
}
return (BAM_ERROR);
}
return (BAM_WRITE);
}
static FILE *
create_diskmap(char *osroot)
{
const char *fcn = "create_diskmap()";
/* make sure we have a map file */
return (NULL);
if (fp) {
} else {
}
}
return (fp);
}
#define SECTOR_SIZE 512
static int
get_partition(char *device)
{
char boot_sect[SECTOR_SIZE];
/* form whole disk (p0) */
if (!is_pcfs)
*slice = '\0';
if (!is_pcfs)
*slice = 's';
/* read boot sector */
return (partno);
}
/* parse fdisk table */
for (i = 0; i < FD_NUMPART; i++) {
if (is_pcfs) { /* looking for solaris boot part */
partno = i;
break;
}
} else { /* look for solaris partition, old and new */
partno = i;
break;
}
}
}
return (partno);
}
char *
{
char *grubroot; /* (hd#,#,#) */
char *slice;
char *grubhd;
int fdiskpart;
int found = 0;
char *devname;
const char *fcn = "get_grubroot()";
return (NULL);
}
/* menu bears no resemblance to our reality */
return (NULL);
}
if (slice)
*slice = '\0';
return (NULL);
}
if (grubhd)
else
found = 1;
break;
}
}
if (slice)
*slice = 's';
if (found == 0) {
return (NULL);
}
if (fdiskpart == -1) {
return (NULL);
}
if (slice) {
} else
return (grubroot);
}
static char *
{
char *lu;
char *ufs;
char *zfs;
const char *fcn = "find_primary_common()";
return (NULL);
}
return (NULL);
}
continue;
strlen(GRUBSIGN_LU_PREFIX)) == 0) {
}
strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
}
strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
}
}
if (dirp) {
}
}
/* For now, we let Live Upgrade take care of its signature itself */
if (lu) {
}
}
static char *
{
char *ufs;
char *zfs;
char *lu;
int error;
const char *fcn = "find_backup_common()";
/*
* We didn't find it in the primary directory.
* Look at the backup
*/
if (bam_verbose) {
}
return (NULL);
}
strlen(GRUBSIGN_LU_PREFIX)) == 0) {
}
strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
}
strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
}
}
if (bfp) {
}
}
/* For now, we let Live Upgrade take care of its signature itself */
if (lu) {
}
}
static char *
find_ufs_existing(char *osroot)
{
char *sign;
const char *fcn = "find_ufs_existing()";
} else {
}
return (sign);
}
char *
{
int error;
int ret;
const char *fcn = "get_mountpoint()";
return (NULL);
}
if (ret != 0) {
return (NULL);
}
}
/*
* Mounts a "legacy" top dataset (if needed)
* Returns: The mountpoint of the legacy top dataset or NULL on error
* mnted returns one of the above values defined for zfs_mnted_t
*/
static char *
{
filelist_t flist = {0};
char *is_mounted;
int ret;
const char *fcn = "mount_legacy_dataset()";
*mnted = ZFS_MNT_ERROR;
pool);
if (ret != 0) {
return (NULL);
}
return (NULL);
}
*mnted = LEGACY_ALREADY;
/* get_mountpoint returns a strdup'ed string */
}
/*
* legacy top dataset is not mounted. Mount it now
* First create a mountpoint.
*/
ZFS_LEGACY_MNTPT, getpid());
if (ret == -1) {
if (ret == -1) {
return (NULL);
}
} else {
}
if (ret != 0) {
return (NULL);
}
*mnted = LEGACY_MOUNTED;
}
/*
* Mounts the top dataset (if needed)
* Returns: The mountpoint of the top dataset or NULL on error
* mnted returns one of the above values defined for zfs_mnted_t
*/
static char *
{
filelist_t flist = {0};
char *is_mounted;
char *mntpt;
char *zmntpt;
int ret;
const char *fcn = "mount_top_dataset()";
*mnted = ZFS_MNT_ERROR;
/*
* First check if the top dataset is a "legacy" dataset
*/
pool);
if (ret != 0) {
return (NULL);
}
}
}
pool);
if (ret != 0) {
return (NULL);
}
return (NULL);
}
*mnted = ZFS_ALREADY;
goto mounted;
}
/* top dataset is not mounted. Mount it now */
if (ret != 0) {
return (NULL);
}
*mnted = ZFS_MOUNTED;
/*FALLTHRU*/
/*
* Now get the mountpoint
*/
pool);
if (ret != 0) {
goto error;
}
goto error;
}
if (*mntpt != '/') {
goto error;
}
return (zmntpt);
return (NULL);
}
static int
{
int ret;
const char *fcn = "umount_top_dataset()";
switch (mnted) {
case LEGACY_ALREADY:
case ZFS_ALREADY:
/* nothing to do */
return (BAM_SUCCESS);
case LEGACY_MOUNTED:
if (ret != 0) {
return (BAM_ERROR);
}
if (mntpt)
return (BAM_SUCCESS);
case ZFS_MOUNTED:
if (ret != 0) {
return (BAM_ERROR);
}
return (BAM_SUCCESS);
default:
return (BAM_ERROR);
}
/*NOTREACHED*/
}
/*
* For ZFS, osdev can be one of two forms
* It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
* It can be a /dev/[r]dsk special file. We handle both instances
*/
static char *
{
filelist_t flist = {0};
char *pool;
char *cp;
char *slash;
int ret;
const char *fcn = "get_pool()";
return (NULL);
}
if (osdev[0] != '/') {
if (slash)
*slash = '\0';
return (pool);
return (NULL);
}
osdev);
if (ret != 0) {
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (pool);
}
static char *
find_zfs_existing(char *osdev)
{
char *pool;
char *mntpt;
char *sign;
const char *fcn = "find_zfs_existing()";
return (NULL);
}
return (NULL);
}
} else {
}
return (sign);
}
static char *
{
const char *fcn = "find_existing_sign()";
return (find_ufs_existing(osroot));
return (find_zfs_existing(osdev));
} else {
return (NULL);
}
}
#define MH_HASH_SZ 16
typedef enum {
MH_ERROR = -1,
} mh_search_t;
typedef struct mcache {
char *mc_special;
char *mc_mntpt;
char *mc_fstype;
} mcache_t;
typedef struct mhash {
} mhash_t;
static int
{
int i;
for (i = 0; key[i] != '\0'; i++) {
}
sum %= MH_HASH_SZ;
return (sum);
}
static mhash_t *
cache_mnttab(void)
{
char *ctds;
int idx;
int error;
char *special_dup;
const char *fcn = "cache_mnttab()";
return (NULL);
}
/* only cache ufs */
continue;
/* basename() modifies its arg, so dup it */
}
return (mhp);
}
static void
{
int i;
for (i = 0; i < MH_HASH_SZ; i++) {
/*LINTED*/
}
}
for (i = 0; i < MH_HASH_SZ; i++) {
}
}
static mh_search_t
{
int idx;
const char *fcn = "search_hash()";
return (MH_ERROR);
}
break;
}
return (MH_NOMATCH);
}
return (MH_MATCH);
}
static int
{
char *sign;
char *signline;
char signbuf[MAXNAMELEN];
int len;
int error;
const char *fcn = "check_add_ufs_sign_to_list()";
/* safe to specify NULL as "osdev" arg for UFS */
/* No existing signature, nothing to add to list */
return (0);
}
/* ignore invalid signatures */
return (0);
}
return (-1);
}
return (0);
}
/*
* slice is a basename not a full pathname
*/
static int
{
int ret;
char *mntpt;
filelist_t flist = {0};
char *fstype;
const char *fcn = "process_slice_common()";
switch (ret) {
case MH_MATCH:
return (-1);
else
return (0);
case MH_NOMATCH:
break;
case MH_ERROR:
default:
return (-1);
}
return (0);
}
/* Check if ufs */
slice);
if (bam_verbose)
return (0);
}
if (bam_verbose)
return (0);
}
if (bam_verbose)
return (0);
}
/*
* Since we are mounting the filesystem read-only, the
* the last mount field of the superblock is unchanged
* and does not need to be fixed up post-mount;
*/
slice);
if (bam_verbose)
return (0);
}
tmpmnt);
return (0);
}
return (ret);
}
static int
char *s0,
char *tmpmnt)
{
int idx;
char *cp;
const char *fcn = "process_vtoc_slices()";
continue;
}
/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
case V_SWAP:
case V_USR:
case V_BACKUP:
case V_VAR:
case V_HOME:
case V_ALTSCTR:
continue;
default:
break;
}
/* skip unmountable and readonly slices */
case V_UNMNT:
case V_RONLY:
continue;
default:
break;
}
return (-1);
}
}
return (0);
}
static int
char *s0,
char *tmpmnt)
{
int idx;
char *cp;
const char *fcn = "process_efi_slices()";
continue;
}
/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
case V_SWAP:
case V_USR:
case V_BACKUP:
case V_VAR:
case V_HOME:
case V_ALTSCTR:
continue;
default:
break;
}
/* skip unmountable and readonly slices */
case V_UNMNT:
case V_RONLY:
continue;
default:
break;
}
return (-1);
}
}
return (0);
}
/*
* s0 is a basename not a full path
*/
static int
{
int e_flag;
int v_flag;
int retval;
int err;
int fd;
const char *fcn = "process_slice0()";
return (0);
}
if (fd == -1) {
return (0);
}
switch (retval) {
case VT_EIO:
break;
case VT_EINVAL:
break;
case VT_ERROR:
break;
case VT_ENOTSUP:
e_flag = 1;
break;
case 0:
v_flag = 1;
break;
default:
break;
}
if (e_flag) {
e_flag = 0;
switch (retval) {
case VT_EIO:
break;
case VT_EINVAL:
break;
case VT_ERROR:
break;
case VT_ENOTSUP:
break;
case 0:
e_flag = 1;
break;
default:
break;
}
}
if (v_flag) {
} else if (e_flag) {
} else {
return (0);
}
return (retval);
}
/*
* Find and create a list of all existing UFS boot signatures
*/
static int
FindAllUfsSignatures(void)
{
int fd;
int ret;
int error;
const char *fcn = "FindAllUfsSignatures()";
return (0);
}
if (fd == -1) {
return (-1);
}
if (ret == -1) {
return (-1);
}
return (-1);
}
mnttab_hash = cache_mnttab();
if (mnttab_hash == NULL) {
return (-1);
}
"/tmp/bootadm_ufs_sign_mnt.%d", getpid());
if (ret == -1) {
return (-1);
}
goto fail;
}
continue;
/*
* we only look for the s0 slice. This is guranteed to
* have 's' at len - 2.
*/
continue;
}
if (ret == -1)
goto fail;
}
return (-1);
}
/* We have a list of existing GRUB signatures. Sort it first */
if (ret != 0) {
return (-1);
}
if (ret == -1) {
return (-1);
}
}
return (0);
fail:
if (dirp)
return (-1);
}
static char *
create_ufs_sign(void)
{
int signnum = -1;
char *numstr;
int i;
int ret;
int error;
const char *fcn = "create_ufs_sign()";
ret = FindAllUfsSignatures();
if (ret == -1) {
return (NULL);
}
/* Make sure the list exists and is owned by root */
INJECT_ERROR1("SIGNLIST_NOT_CREATED",
(void) unlink(UFS_SIGNATURE_LIST));
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
i = 0;
goto found;
}
/* The signature list was sorted when it was created */
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
if (signnum < 0) {
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
if (i != signnum) {
break;
}
}
if (ret == -1) {
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
}
static char *
get_fstype(char *osroot)
{
int error;
int ret;
const char *fcn = "get_fstype()";
return (NULL);
}
return (NULL);
}
if (*osroot == '\0')
else
if (ret != 0) {
return (NULL);
}
return (NULL);
}
}
static char *
create_zfs_sign(char *osdev)
{
char *pool;
const char *fcn = "create_zfs_sign()";
/*
* First find the pool name
*/
return (NULL);
}
}
static char *
{
char *sign;
const char *fcn = "create_new_sign()";
sign = create_ufs_sign();
} else {
}
return (sign);
}
static int
{
int error;
char *bdir;
char *backup_dup;
int ret;
const char *fcn = "set_backup_common()";
/* First read the backup */
return (0);
}
}
} else {
}
/*
* Didn't find the correct signature. First create
* the directory if necessary.
*/
/* dirname() modifies its argument so dup it */
if (ret == -1) {
if (ret == -1) {
return (-1);
}
}
/*
* Open the backup in append mode to add the correct
* signature;
*/
return (-1);
}
return (-1);
}
if (bam_verbose)
return (0);
}
static int
{
const char *fcn = "set_backup_ufs()";
}
static int
{
char *pool;
char *mntpt;
int ret;
const char *fcn = "set_backup_zfs()";
return (-1);
}
return (-1);
}
if (ret == 0) {
} else {
}
return (ret);
}
static int
{
const char *fcn = "set_backup()";
int ret;
} else {
ret = -1;
}
if (ret == 0) {
} else {
}
return (ret);
}
static int
{
int fd;
int error;
int ret;
const char *fcn = "set_primary_common()";
if (bam_verbose)
return (0);
} else {
}
if (ret == -1) {
return (-1);
}
}
if (fd == -1) {
return (-1);
}
if (ret != 0) {
}
if (bam_verbose)
return (0);
}
static int
{
const char *fcn = "set_primary_ufs()";
}
static int
{
char *pool;
char *mntpt;
int ret;
const char *fcn = "set_primary_zfs()";
return (-1);
}
/* Pool name must exist in the sign */
if (ret == 0) {
return (-1);
}
return (-1);
}
if (ret == 0) {
} else {
}
return (ret);
}
static int
{
const char *fcn = "set_primary()";
int ret;
} else {
ret = -1;
}
if (ret == 0) {
} else {
}
return (ret);
}
static int
ufs_add_to_sign_list(char *sign)
{
char signline[MAXNAMELEN];
int ret;
int error;
const char *fcn = "ufs_add_to_sign_list()";
strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
(void) unlink(UFS_SIGNATURE_LIST);
return (-1);
}
/*
* most failures in this routine are not a fatal error
*/
if (ret == -1) {
(void) unlink(UFS_SIGNATURE_LIST);
return (0);
}
return (0);
}
return (0);
}
return (0);
}
/* Sort the list again */
if (ret != 0) {
return (0);
}
if (ret == -1) {
return (0);
}
return (0);
}
static int
{
int ret;
const char *fcn = "set_signature()";
if (ret == -1) {
return (-1);
}
if (ret == 0) {
} else {
}
return (ret);
}
char *
{
char *grubsign; /* (<sign>,#,#) */
char *slice;
int fdiskpart;
char *sign;
char *fstype;
int ret;
const char *fcn = "get_grubsign()";
return (NULL);
}
return (NULL);
}
}
if (ret == -1) {
(void) unlink(UFS_SIGNATURE_LIST);
return (NULL);
}
if (bam_verbose)
if (fdiskpart == -1) {
return (NULL);
}
if (slice) {
} else
return (grubsign);
}
static char *
{
static char title[80];
const char *fcn = "get_title()";
goto out;
}
if (cp)
break;
}
out:
return (cp);
}
char *
get_special(char *mountp)
{
int error;
int ret;
const char *fcn = "get_special()";
return (NULL);
}
return (NULL);
}
if (*mountp == '\0')
else
if (ret != 0) {
return (NULL);
}
}
static void
free_physarray(char **physarray, int n)
{
int i;
const char *fcn = "free_physarray()";
assert(n);
for (i = 0; i < n; i++) {
}
}
static int
{
char *pool;
filelist_t flist = {0};
char *comp1;
int i;
int ret;
const char *fcn = "zfs_get_physical()";
if (special[0] == '/') {
return (-1);
}
return (-1);
}
if (ret != 0) {
return (-1);
}
return (-1);
}
} else {
}
}
continue;
break;
}
}
return (-1);
}
continue;
continue;
break;
i++;
}
if (i == 0) {
return (-1);
}
*n = i;
continue;
continue;
break;
} else {
}
}
assert(i == *n);
return (0);
}
static int
{
char *shortname;
filelist_t flist = {0};
char *meta;
char *type;
char *comp1;
char *comp2;
char *comp3;
char *comp4;
int i;
int ret;
const char *fcn = "ufs_get_physical()";
return (-1);
}
} else {
return (-1);
}
if (ret != 0) {
return (-1);
}
return (-1);
}
/*
* Check if not a mirror. We only parse a single metadevice
* if not a mirror
*/
return (-1);
}
return (-1);
}
*n = 1;
return (0);
}
/*
* Okay we have a mirror. Everything after the first line
* is a submirror
*/
return (-1);
}
i++;
}
*n = i;
free_physarray(*physarray, *n);
return (-1);
}
}
assert(i == *n);
return (0);
}
static int
{
char *special;
int ret;
const char *fcn = "get_physical()";
assert(n);
*n = 0;
return (-1);
}
/* If already a physical device nothing to do */
*n = 1;
return (0);
}
} else {
ret = -1;
}
if (ret == -1) {
} else {
int i;
assert (*n > 0);
for (i = 0; i < *n; i++) {
}
}
return (ret);
}
static int
{
int ret;
char *grubroot;
char *bootp;
const char *fcn = "is_bootdisk()";
if (bootp)
*bootp = '\0';
/*
* We just want the BIOS mapping for menu disk.
* Don't pass menu_root to get_grubroot() as the
* check that it is used for is not relevant here.
* The osroot is immaterial as well - it is only used to
* to find create_diskmap script. Everything hinges on
* "physical"
*/
return (0);
}
return (ret);
}
/*
* Check if menu is on the boot device
* Return 0 (false) on error
*/
static int
{
char **physarray;
int ret;
int n;
int i;
int on_bootdisk;
const char *fcn = "menu_on_bootdisk()";
if (ret != 0) {
return (0);
}
assert(n > 0);
on_bootdisk = 0;
for (i = 0; i < n; i++) {
on_bootdisk = 1;
}
}
free_physarray(physarray, n);
if (on_bootdisk) {
} else {
}
return (on_bootdisk);
}
void
{
const char *fcn = "bam_add_line()";
} else {
}
}
}
}
/*
* look for matching bootadm entry with specified parameters
* Here are the rules (based on existing usage):
* - If title is specified, match on title only
* Note that, if root_opt is non-zero, the absence of
* root line is considered a match.
*/
static entry_t *
char *title,
char *kernel,
char *findroot,
char *root,
char *module,
int root_opt,
int *entry_num)
{
int i;
const char *fcn = "find_boot_entry()";
if (entry_num)
/* find matching entry */
/* first line of entry must be bootadm comment */
continue;
}
/* advance to title line */
if (title) {
break;
}
continue; /* check title only */
}
continue;
INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
continue;
}
/* findroot command found, try match */
continue;
}
continue;
}
/* root cmd found, try match */
continue;
}
} else {
INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
root_opt = 0);
INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
root_opt = 1);
/* no root command, see if root is optional */
if (root_opt == 0) {
continue;
}
}
continue;
}
if (kernel &&
continue;
}
/*
* Check for matching module entry (failsafe or normal).
* If it fails to match, we go around the loop again.
* For xpv entries, there are two module lines, so we
* do the check twice.
*/
/* match found */
break;
}
}
*entry_num = i;
}
if (ent) {
} else {
}
return (ent);
}
static int
{
int i;
int change_kernel = 0;
char linebuf[BAM_MAXLINE];
const char *fcn = "update_boot_entry()";
/* note: don't match on title, it's updated on upgrade */
root_opt, &i);
/*
* We may be upgrading a kernel from multiboot to
* directboot. Look for a multiboot entry. A multiboot
* entry will not have a findroot line.
*/
MULTIBOOT_ARCHIVE, root_opt, &i);
change_kernel = 1;
}
} else if (ent) {
}
}
/* replace title of existing entry and update findroot line */
/* if no root or findroot command, create a new line_t */
} else {
}
/* kernel line */
if (change_kernel) {
/*
* We're upgrading from multiboot to directboot.
*/
kernel);
}
module);
}
}
return (i);
}
int
{
char *ospecial;
char *mspecial;
char *slash;
int root_opt;
int ret1;
int ret2;
const char *fcn = "root_optional()";
/*
* For all filesystems except ZFS, a straight compare of osroot
* and menu_root will tell us if root is optional.
* For ZFS, the situation is complicated by the fact that
* menu_root and osroot are always different
*/
goto out;
}
return (0);
}
return (0);
}
if (slash)
*slash = '\0';
out:
if (root_opt) {
} else {
}
return (root_opt);
}
/*ARGSUSED*/
static error_t
{
int entry;
char *grubsign;
char *grubroot;
char *title;
char *failsafe_kernel = NULL;
char failsafe[256];
int ret;
const char *fcn = "update_entry()";
return (BAM_ERROR);
}
/*
* It is not a fatal error if get_grubroot() fails
* We no longer rely on biosdev to populate the
* menu
*/
if (grubroot) {
} else {
}
/* add the entry for normal Solaris */
INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
if (bam_direct == BAM_DIRECT_DBOOT) {
}
} else {
}
/*
* Add the entry for failsafe archive. On a bfu'd system, the
* failsafe may be different than the installed kernel.
*/
/* Figure out where the kernel line should point */
} else {
}
}
if (failsafe_kernel != NULL) {
}
}
return (BAM_ERROR);
}
}
return (BAM_WRITE);
}
static void
{
int lineNum;
int entryNum;
int entry = 0; /* default is 0 */
char linebuf[BAM_MAXLINE];
const char *fcn = "save_default_entry()";
} else {
}
if (lp)
}
static void
{
int entry;
char *str;
const char *fcn = "restore_default_entry()";
return; /* nothing to restore */
}
/* delete saved old default line */
}
/*
* This function is for supporting reboot with args.
* The opt value can be:
* NULL delete temp entry, if present
* entry=<n> switches default entry to <n>
* else treated as boot-args and setup a temperary menu entry
* and make it the default
* Note that we are always rebooting the current OS instance
* so osroot == / always.
*/
#define REBOOT_TITLE "Solaris_reboot_transient"
/*ARGSUSED*/
static error_t
{
int entry;
char *osdev;
char *fstype;
char *sign;
char *opt_ptr;
char *path;
int ret;
const char *fcn = "update_temp()";
/* opt can be NULL */
return (BAM_ERROR);
}
/* If no option, delete exiting reboot menu entry */
return (BAM_SUCCESS);
}
return (BAM_WRITE);
}
/* if entry= is specified, set the default entry */
/* this is entry=# option */
return (ret);
} else {
return (BAM_ERROR);
}
}
/*
* add a new menu entry based on opt and make it the default
*/
return (BAM_ERROR);
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
/*
* There is no alternate root while doing reboot with args
* This version of bootadm is only delivered with a DBOOT
* version of Solaris.
*/
if (bam_direct != BAM_DIRECT_DBOOT) {
return (BAM_ERROR);
}
/* add an entry for Solaris reboot */
if (opt[0] == '-') {
/* It's an option - first see if boot-file is set */
if (ret != BAM_SUCCESS) {
return (BAM_ERROR);
}
if (kernbuf[0] == '\0')
sizeof (kernbuf));
} else if (opt[0] == '/') {
/* It's a full path, so write it out. */
/*
* If someone runs:
*
* # eeprom boot-args='-kd'
*
* we want to use the boot-args as part of the boot
* line. On the other hand, if someone runs:
*
*
* we don't need to mess with boot-args. If there's
* no space in the options string, assume we're in the
* first case.
*/
sizeof (args_buf));
if (ret != BAM_SUCCESS) {
return (BAM_ERROR);
}
if (args_buf[0] != '\0') {
sizeof (kernbuf));
}
}
} else {
/*
* It may be a partial path, or it may be a partial
* path followed by options. Assume that only options
* follow a space. If someone sends us a kernel path
* that includes a space, they deserve to be broken.
*/
*opt_ptr = '\0';
}
/*
* If there were options given, use those.
* Otherwise, copy over the default options.
*/
/* Restore the space in opt string */
*opt_ptr = ' ';
sizeof (kernbuf));
} else {
sizeof (args_buf));
INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
if (ret != BAM_SUCCESS) {
return (BAM_ERROR);
}
if (args_buf[0] != '\0') {
sizeof (kernbuf));
}
}
} else {
return (BAM_ERROR);
}
}
return (BAM_ERROR);
}
}
return (BAM_WRITE);
}
static error_t
{
char *cp;
char *str;
char prefix[BAM_MAXLINE];
const char *fcn = "set_global()";
return (BAM_ERROR);
}
}
continue;
continue;
}
continue;
if (found) {
}
}
} else {
}
return (BAM_WRITE);
}
/*
* We are changing an existing entry. Retain any prefix whitespace,
* but overwrite everything else. This preserves tabs added for
* readability.
*/
return (BAM_WRITE); /* need a write to menu */
}
/*
* expand it to a full unix path. The calling function is expected to
* output a message if an error occurs and NULL is returned.
*/
static char *
expand_path(const char *partial_path)
{
int new_path_len;
char *new_path;
const char *fcn = "expand_path()";
return (new_path);
}
return (new_path);
}
/*
* We've quickly reached unsupported usage. Try once more to
* see if we were just given a glom name.
*/
/*
* We matched both, so we actually
* want to write the $ISADIR version.
*/
}
return (new_path);
}
return (NULL);
}
/*
* The kernel cmd and arg have been changed, so
* check whether the archive line needs to change.
*/
static void
{
char *new_archive;
const char *fcn = "set_archive_line()";
break;
}
return;
}
}
return;
}
m_cmd = MODULE_CMD;
} else {
m_cmd = MODULE_CMD;
}
return;
}
}
}
/*
* Title for an entry to set properties that once went in bootenv.rc.
*/
#define BOOTENV_RC_TITLE "Solaris bootenv rc"
/*
* If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
* (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
* string, reset the value to the default. If path is a non-zero-length
* string, set the kernel or arguments.
*/
static error_t
char *path,
char *buf,
{
int entryNum;
int rv = BAM_SUCCESS;
int free_new_path = 0;
char *new_arg;
char *old_args;
char *space;
char *new_path;
char old_space;
char *fstype;
char *osdev;
char *sign;
int ret;
const char *fcn = "get_set_kernel()";
buf[0] = '\0';
INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
if (bam_direct != BAM_DIRECT_DBOOT) {
return (BAM_ERROR);
}
/*
* If a user changed the default entry to a non-bootadm controlled
* one, we don't want to mess with it. Just print an error and
* return.
*/
if (mp->curdefault) {
break;
}
return (BAM_ERROR);
}
}
0, &entryNum);
break;
}
}
return (BAM_ERROR);
}
old_args++;
}
return (BAM_SUCCESS);
}
if (old_args[0] != '\0') {
}
} else {
/*
* We need to print the kernel, so we just turn the
* first space into a '\0' and print the beginning.
* We don't print anything if it's the default kernel.
*/
*space = '\0';
}
}
return (BAM_SUCCESS);
}
/*
* First, check if we're resetting an entry to the default.
*/
if ((path[0] == '\0') ||
((optnum == KERNEL_CMD) &&
/* No previous entry, it's already the default */
return (BAM_SUCCESS);
}
/*
* Check if we can delete the entry. If we're resetting the
* kernel command, and the args is already empty, or if we're
* resetting the args command, and the kernel is already the
* default, we can restore the old default and delete the entry.
*/
if (((optnum == KERNEL_CMD) &&
sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
mp->old_rc_default);
goto done;
}
if (optnum == KERNEL_CMD) {
/*
* At this point, we've already checked that old_args
* and entryp are valid pointers. The "+ 2" is for
* a space a the string termination character.
*/
/*
* We have changed the kernel line, so we may need
* to update the archive line as well.
*/
} else {
/*
* We're resetting the boot args to nothing, so
* we only need to copy the kernel. We've already
* checked that the kernel is not the default.
*/
}
goto done;
}
/*
* Expand the kernel file to a full path, if necessary
*/
return (BAM_ERROR);
}
free_new_path = 1;
} else {
free_new_path = 0;
}
/*
* At this point, we know we're setting a new value. First, take care
* of the case where there was no previous entry.
*/
/* Similar to code in update_temp */
goto done;
}
goto done;
}
goto done;
}
if (optnum == KERNEL_CMD) {
} else {
}
INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
goto done;
}
}
goto done;
}
/*
* There was already an bootenv entry which we need to edit.
*/
if (optnum == KERNEL_CMD) {
old_args);
/*
* If we have changed the kernel line, we may need to update
* the archive line as well.
*/
} else {
}
done:
if (free_new_path)
} else {
}
return (rv);
}
static error_t
{
const char *fcn = "get_kernel()";
}
static error_t
{
const char *fcn = "set_kernel()";
}
/*ARGSUSED*/
static error_t
{
int optnum;
int optval;
char *val;
const char *fcn = "set_option()";
/* opt is set from bam_argv[0] and is always non-NULL */
*val = '\0';
}
optnum = KERNEL_CMD;
} else {
return (BAM_ERROR);
}
/*
* kernel and args are allowed without "=new_value" strings. All
* others cause errors
*/
return (BAM_ERROR);
*val = '=';
}
if (val)
else
} else {
}
} else {
}
return (rv);
}
/*
* The quiet argument suppresses messages. This is used
* when invoked in the context of other commands (e.g. list_entry)
*/
static error_t
{
char *arg;
if (!quiet)
return (BAM_ERROR);
}
done = 0;
continue;
if (!quiet)
continue;
}
continue;
/* Found global. Check for duplicates */
}
done = 1;
}
if (!done && bam_verbose)
return (ret);
}
static error_t
{
const char *fcn = "menu_write()";
}
void
{
return;
}
static void
{
while (start) {
}
}
static void
{
}
static void
{
while (ent) {
}
}
/*
* Utility routines
*/
/*
* Returns 0 on success
* Any other value indicates an error
*/
static int
{
int ret;
void (*disp)(int);
/*
* For security
* - only absolute paths are allowed
* - set IFS to space and tab
*/
if (*cmdline != '/') {
return (-1);
}
(void) putenv("IFS= \t");
/*
* We may have been exec'ed with SIGCHLD blocked
* unblock it here
*/
(void) sigemptyset(&set);
return (-1);
}
/*
*/
return (-1);
}
return (-1);
}
return (-1);
}
/*
* If we simply do a pclose() following a popen(), pclose()
* will close the reader end of the pipe immediately even
* does wait for cmd to terminate before returning though.
* When the executed command writes its output to the pipe
* there is no reader process and the command dies with
* SIGPIPE. To avoid this we read repeatedly until read
* terminates with EOF. This indicates that the command
* (writer) has closed the pipe and we can safely do a
* pclose().
*
* Since pclose() does wait for the command to exit,
* we can safely reap the exit status of the command
* from the value returned by pclose()
*/
/* s_fgets strips newlines, so insert them at the end */
} else {
}
}
if (ret == -1) {
return (-1);
}
return (WEXITSTATUS(ret));
} else {
return (-1);
}
}
/*
* Since this function returns -1 on error
* it cannot be used to convert -1. However,
* that is sufficient for what we need.
*/
static long
{
long l;
return (-1);
}
errno = 0;
return (-1);
}
return (l);
}
/*
* Wrapper around fputs, that adds a newline (since fputs doesn't)
*/
static int
{
char linebuf[BAM_MAXLINE];
}
/*
* Wrapper around fgets, that strips newlines returned by fgets
*/
char *
{
int n;
if (buf) {
}
return (buf);
}
void *
{
void *ptr;
bam_exit(1);
}
return (ptr);
}
void *
{
bam_exit(1);
}
return (ptr);
}
char *
{
char *ptr;
return (NULL);
bam_exit(1);
}
return (ptr);
}
/*
* Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
* Returns 0 otherwise
*/
static int
is_amd64(void)
{
static int amd64 = -1;
if (amd64 != -1)
return (amd64);
if (bam_alt_platform) {
}
} else {
amd64 = 1;
}
}
if (amd64 == -1)
amd64 = 0;
return (amd64);
}
static char *
get_machine(void)
{
static int cached = -1;
if (cached == 0)
return (mbuf);
if (bam_alt_platform) {
return (bam_platform);
} else {
cached = 1;
}
}
if (cached == -1) {
mbuf[0] = '\0';
cached = 0;
}
return (mbuf);
}
int
is_sparc(void)
{
static int issparc = -1;
if (issparc != -1)
return (issparc);
if (bam_alt_platform) {
issparc = 1;
}
} else {
issparc = 1;
else
issparc = 0;
}
return (issparc);
}
static void
{
else
}
#if !defined(_OPB)
/*ARGSUSED*/
static void
ucode_install(char *root)
{
int i;
continue;
continue;
return;
return;
}
}
#endif