bootadm.c revision d876c67df282cf8c0136415d482d92d16ee59a0b
/*
* 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>
#if !defined(_OPB)
#endif
#include <pwd.h>
#include <grp.h>
#include <device_info.h>
#include <locale.h>
#include <assert.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"
/* 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_backup_menu "/etc/lu/GRUB_backup_menu"
#define GRUB_slice_mntpt "/tmp/GRUB_slice_mntpt"
#define LU_ACTIVATE_FILE "/etc/lu/DelayUpdate/activate.sh"
#define GRUB_fdisk "/etc/lu/GRUB_fdisk"
#define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
#define INSTALLGRUB "/sbin/installgrub"
/*
* 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 */
};
#define OPT_ENTRY_NUM "entry"
/*
* archive 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;
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 int bam_debug;
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_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_print(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 char *s_strdup(char *);
static int is_readonly(char *);
static int is_amd64(void);
static int is_sun4u(void);
static int is_sun4v(void);
static void append_to_flist(filelist_t *, char *);
#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 '?':
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);
}
}
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);
}
static char *
{
char *mntpt;
int p, l, f;
*mnted = 0;
/*
* physlice, logslice, fs_type args may be NULL
*/
if (physlice)
if (logslice)
if (fs_type)
return (NULL);
}
return (NULL);
}
p = sizeof ("PHYS_SLICE=") - 1;
l = sizeof ("LOG_SLICE=") - 1;
f = sizeof ("LOG_FSTYP=") - 1;
continue;
}
continue;
}
continue;
}
}
return (NULL);
}
if (physlice) {
}
if (logslice) {
}
if (fs_type) {
}
/*
* Check if the slice is already mounted
*/
goto error;
}
break;
}
}
if (mntpt) {
return (mntpt);
}
/*
* GRUB slice is not mounted, we need to mount it now.
* First create the mountpoint
*/
goto error;
}
}
goto error;
}
*mnted = 1;
return (mntpt);
if (physlice) {
}
if (logslice) {
}
if (fs_type) {
}
return (NULL);
}
static void
int mnted,
char *mntpt,
char *physlice,
char *logslice,
char *fs_type)
{
/*
* If we have not dealt with GRUB slice
* we have nothing to do - just return.
*/
return;
/*
* If we mounted the filesystem earlier in mount_grub_slice()
* unmount it now.
*/
if (mnted) {
mntpt);
}
}
}
if (physlice)
if (logslice)
if (fs_type)
}
static char *
use_stubboot(void)
{
int mnted;
return (NULL);
}
/*
* Check if stubboot is mounted. If not, mount it
*/
return (NULL);
}
mnted = 0;
mnted = 1;
break;
}
}
if (mnted)
return (STUBBOOT);
/*
* Stubboot is not mounted, mount it now.
*/
STUBBOOT);
return (NULL);
}
return (STUBBOOT);
}
static void
{
/*
* Check if we did a temp mount of an unmounted device.
* If yes, print the block device and fstype for that device
* else it is already mounted, so we print the path to the GRUB menu.
*/
if (mnted) {
} else {
}
}
/*
* NOTE: A single "/" is also considered a trailing slash and will
* be deleted.
*/
static void
{
}
}
static error_t
{
int mnted; /* set if we did a mount */
char *rootpath;
/*
* Menu sub-command only applies to GRUB (i.e. x86)
*/
return (BAM_ERROR);
}
/*
* Check arguments
*/
return (BAM_ERROR);
}
mnted = 0;
/*
* Check for the menu.list file:
*
* 1. Check for a GRUB_slice file, be it on / or
* on the user-provided alternate root.
* 2. Use the alternate root, if given.
* 3. Check /stubboot
* 4. Use /
*/
if (bam_alt_root) {
} else {
}
} else if (bam_alt_root) {
menu_root = use_stubboot();
} else {
}
return (BAM_ERROR);
}
/*
* If listing the menu, display the active menu
* location
*/
}
/*
* Special handling for setting timeout and default
*/
usage();
return (BAM_ERROR);
}
} else if (largc != 0) {
usage();
return (BAM_ERROR);
}
if (ret != BAM_SUCCESS)
return (ret);
/*
* Once the sub-cmd handler has run
* only the line field is guaranteed to have valid values
*/
else
}
return (ret);
}
static error_t
char *subcmd,
char *opt)
{
/*
* 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*/
static 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;
return (BAM_ERROR);
} else {
return (BAM_SUCCESS);
}
}
}
/*
* Preserve attributes of existing file if possible,
* If all fails, fall back on hard-coded defaults.
*/
} else {
} else {
if (bam_verbose)
}
} else {
if (bam_verbose)
}
}
/* 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_sun4u()) {
} else if (is_sun4v()) {
} 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];
if (bam_debug)
return (BAM_ERROR);
}
/* skip blank lines */
continue;
}
return (BAM_ERROR);
}
return (BAM_SUCCESS);
}
static error_t
{
int n, rval;
/*
* 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);
}
if (bam_debug)
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)
{
/*
* We can't create an archive without the create_ramdisk script
*/
if (bam_verbose)
return (0);
}
return (1);
}
/*
* Need to call this for anything that operates on the GRUB menu
*/
int
{
/*
* GRUB_DIR is required to modify the menu
*/
if (bam_debug)
return (0);
}
return (1);
}
static int
is_readonly(char *root)
{
/*
* Check for RDONLY filesystem
* When in doubt assume it is not readonly
*/
if (bam_verbose)
return (0);
}
return (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.
* Note: statvfs() does not always report the truth
*/
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 void
restore_grub_slice(void)
{
int mnted; /* set if we did a mount */
return;
}
/*
* If we are doing an luactivate, don't attempt to restore GRUB or else
* we may not be able to get to DCA boot environments. Let luactivate
*/
return;
}
mnted = 0;
return;
}
return;
}
/*
* The menu is missing - we need to do a restore
*/
return;
}
return;
}
return;
}
/* Success */
}
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 {
}
}
static void
{
/* unlink from list */
else
else
}
static entry_t *
{
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
{
return (0);
}
}
/*
* 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;
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 {
/*
* We only compare for the length of "module"
* so that "module$" will also match.
*/
"xen.gz"))
menu_cmds[CHAINLOADER_CMD]) == 0)
}
}
}
/* record default, old default, and entry line ranges */
}
}
static void
{
int lineNum;
int entryNum;
int old_default_value;
return;
}
/*
* Get the value of the default command
*/
default_lp = lp;
}
/*
* If not boot 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;
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 *root,
char *kernel,
char *mod_kernel,
char *module)
{
char linebuf[BAM_MAXLINE];
}
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 (root) {
}
if (mod_kernel != NULL) {
}
return (entryNum);
}
static error_t
{
int deleted;
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 *
open_diskmap(char *root)
{
/* make sure we have a map file */
}
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);
}
static char *
{
char *grubdisk; /* (hd#,#,#) */
char *slice;
char *grubhd;
int fdiskpart;
int found = 0;
return (NULL);
if (slice)
*slice = '\0';
if (grubhd)
else
found = 1;
break;
}
}
if (slice)
*slice = 's';
if (found == 0) {
if (bam_verbose)
}
if (fdiskpart == -1)
return (NULL);
if (slice) {
} else
/* if root not on bootdev, change GRUB disk to 0 */
if (!on_bootdev)
return (grubdisk);
}
static char *
{
return (NULL);
if (cp)
break;
}
}
char *
get_special(char *mountp)
{
return (0);
}
if (*mountp == '\0')
else
return (NULL);
}
}
char *
{
char *grubdisk;
return (NULL);
}
return (grubdisk);
}
/*
* Check if root is on the boot device
* Return 0 (false) on error
*/
static int
{
int ret;
return (0);
if (bootp)
*bootp = '\0';
return (0);
return (ret);
}
/*
* look for matching bootadm entry with specified parameters
* Here are the rules (based on existing usage):
* - If title is specified, match on title only
* - Else, match on kernel, grubdisk and module. Note that, if root_opt is
* non-zero, the absence of root line is considered a match.
*/
static entry_t *
{
int i;
/* find matching entry */
/* first line of entry must be bootadm comment */
continue;
}
/* advance to title line */
if (title) {
break;
continue; /* check title only */
}
continue;
/* root command found, match grub disk */
continue;
}
} else {
/* 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;
}
return (ent);
}
static int
{
int i, change_kernel = 0;
char linebuf[BAM_MAXLINE];
/* note: don't match on title, it's updated on upgrade */
/*
* We may be upgrading a kernel from multiboot to
* directboot. Look for a multiboot entry.
*/
change_kernel = 1;
}
}
/* replace title of exiting entry and delete root line */
if (root_opt) { /* root line not needed */
} else
}
if (change_kernel) {
/*
* We're upgrading from multiboot to directboot.
*/
kernel);
}
module);
}
}
return (i);
}
/*ARGSUSED*/
static error_t
{
int entry;
char failsafe[256];
return (BAM_ERROR);
}
return (BAM_ERROR);
}
/* add the entry for normal Solaris */
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 char *
read_grub_root(void)
{
char buf[BAM_MAXLINE];
char *rootstr;
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
/*
* Copy buf here as check below may trash the buffer
*/
}
return (rootstr);
}
static void
{
int entry = 0; /* default is 0 */
char linebuf[BAM_MAXLINE];
} else {
}
if (lp)
}
static void
{
int entry;
char *str;
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=# switches default entry to 1
* else treated as boot-args and setup a temperary menu entry
* and make it the default
*/
#define REBOOT_TITLE "Solaris_reboot_transient"
/*ARGSUSED*/
static error_t
{
int entry;
/* 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 */
}
/*
* add a new menu entry base on opt and make it the default
*/
/*
* 1. First get root disk name from mnttab
* 2. Translate disk name to grub name
* 3. Add the new menu entry
*/
if (rootdev) {
}
} else {
/*
* This is an LU BE. The GRUB_root file
* contains entry for GRUB's "root" cmd.
*/
grubdisk = read_grub_root();
}
return (BAM_ERROR);
}
/* add an entry for Solaris reboot */
if (bam_direct == BAM_DIRECT_DBOOT) {
if (opt[0] == '-') {
/* It's an option - first see if boot-file is set */
!= BAM_SUCCESS)
return (BAM_ERROR);
if (kernbuf[0] == '\0')
BUFSIZ);
} 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.
*/
BUFSIZ) != BAM_SUCCESS)
return (BAM_ERROR);
if (args_buf[0] != '\0') {
BUFSIZ);
}
}
} 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 = ' ';
BUFSIZ);
} else {
return (BAM_ERROR);
if (args_buf[0] != '\0') {
BUFSIZ);
}
}
} else {
return (BAM_ERROR);
}
}
} else {
MULTI_BOOT, opt);
}
return (BAM_ERROR);
}
return (BAM_WRITE);
}
static error_t
{
char prefix[BAM_MAXLINE];
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;
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;
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 old_space;
buf[0] = '\0';
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);
}
}
entryNum = -1;
&entryNum);
break;
}
}
return (BAM_ERROR);
}
old_args++;
}
/* Simply report what was found */
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 */
/*
* 1. First get root disk name from mnttab
* 2. Translate disk name to grub name
* 3. Add the new menu entry
*/
if (rootdev) {
}
} else {
/*
* This is an LU BE. The GRUB_root file
* contains entry for GRUB's "root" cmd.
*/
grubdisk = read_grub_root();
}
goto done;
}
if (optnum == KERNEL_CMD) {
} else {
}
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)
return (rv);
}
/*ARGSUSED*/
static error_t
{
char *val;
*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 = '=';
}
return (rv);
} else {
}
}
/*
* 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
{
}
static 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);
}
static 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 int
is_sun4u(void)
{
static int sun4u = -1;
if (sun4u != -1)
return (sun4u);
if (bam_alt_platform) {
sun4u = 1;
}
} else {
sun4u = 1;
}
}
if (sun4u == -1)
sun4u = 0;
return (sun4u);
}
static int
is_sun4v(void)
{
static int sun4v = -1;
if (sun4v != -1)
return (sun4v);
if (bam_alt_platform) {
sun4v = 1;
}
} else {
sun4v = 1;
}
}
if (sun4v == -1)
sun4v = 0;
return (sun4v);
}
static void
{
else
}
#if !defined(_OPB)
/*ARGSUSED*/
static void
ucode_install(char *root)
{
int i;
continue;
continue;
return;
return;
}
}
#endif