bootadm.c revision 8c1b68848fbe4bab4d66af1f05f66b6a8cf5f018
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 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 <sys/systeminfo.h>
#include <pwd.h>
#include <grp.h>
#include <device_info.h>
#include <libintl.h>
#include <locale.h>
#include <assert.h>
#include "message.h"
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#endif /* TEXT_DOMAIN */
/* Type definitions */
/* Primary subcmds */
typedef enum {
BAM_MENU = 3,
} subcmd_t;
/* GRUB menu per-line classification */
typedef enum {
BAM_INVALID = 0,
} menu_flag_t;
/* struct for menu.lst contents */
typedef struct line {
int lineNum; /* Line number in menu.lst */
int entryNum; /* menu boot entry #. ENTRY_INIT if not applicable */
char *cmd;
char *sep;
char *arg;
char *line;
} line_t;
typedef struct entry {
} entry_t;
typedef struct {
} menu_t;
typedef enum {
OPT_ABSENT = 0, /* No option */
OPT_REQ, /* option required */
OPT_OPTIONAL /* option may or may not be present */
} option_t;
typedef enum {
BAM_SUCCESS = 0,
BAM_WRITE = 2
} error_t;
typedef struct {
char *subcmd;
#define BAM_MAXLINE 8192
#define LINE_INIT 0 /* lineNum initial value */
#define MULTI_BOOT "/platform/i86pc/multiboot"
#define BOOT_ARCHIVE "/platform/i86pc/boot_archive"
#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 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 INSTALLGRUB "/sbin/installgrub"
/*
* Default file attributes
*/
#define DEFAULT_DEV_UID 0 /* user root */
/*
* Menu related
* menu_cmd_t and menu_cmds must be kept in sync
*/
typedef enum {
DEFAULT_CMD = 0,
} menu_cmd_t;
static char *menu_cmds[] = {
"default", /* DEFAULT_CMD */
"timeout", /* TIMEOUT_CMD */
"title", /* TITLE_CMD */
"root", /* ROOT_CMD */
"kernel", /* KERNEL_CMD */
"module", /* MODULE_CMD */
" ", /* SEP_CMD */
"#", /* COMMENT_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"
#define BAM_HDR "---------- ADDED BY BOOTADM - DO NOT EDIT ----------"
#define BAM_FTR "---------------------END BOOTADM--------------------"
#define BAM_OLDDEF "BOOTADM SAVED DEFAULT: "
/* Globals */
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_force;
static int bam_verbose;
static int bam_check;
static int bam_smf_check;
static int bam_lock_fd = -1;
static int bam_update_all;
/* function prototypes */
static void bam_lock(void);
static void bam_unlock(void);
static int is_readonly(char *);
static int is_amd64(void);
static void append_to_flist(filelist_t *, char *);
#if defined(__sparc)
static void sparc_abort(void);
#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;
static void
usage(void)
{
/* archive usage */
prog);
#ifndef __sparc
/* x86 only */
#endif
}
int
{
(void) textdomain(TEXT_DOMAIN);
} else {
prog++;
}
if (geteuid() != 0) {
bam_exit(1);
}
bam_lock();
/*
* Don't depend on caller's umask
*/
(void) umask(0022);
#if defined(__sparc)
/*
* There are only two valid invocations of bootadm
* on SPARC:
*
* - SPARC diskless server creating boot_archive for i386 clients
* - archive creation call during reboot of a SPARC system
*
* The latter should be a NOP
*/
if (bam_cmd != BAM_ARCHIVE) {
sparc_abort();
}
#endif
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);
}
#if defined(__sparc)
static void
sparc_abort(void)
{
bam_exit(1);
}
#endif
/*
* 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 '?':
error = 1;
break;
default :
error = 1;
break;
}
}
/*
* 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);
}
bam_rootlen = 1;
}
/* verify that subcmd is valid */
break;
}
return (BAM_ERROR);
}
/* 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 */
/*
* Check arguments
*/
return (BAM_ERROR);
}
mnted = 0;
/*
* If the user provides an alternate root, we
* assume they know what they are doing and we
* use it. Else we check if there is an
* for the GRUB menu
*/
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);
}
/*
* 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 defined(__sparc)
/*
* A NOP if called on SPARC during reboot
*/
return (BAM_SUCCESS);
sparc_abort();
#endif
/*
* 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;
bam_update_all = 0;
return (ret);
}
/*PRINTFLIKE1*/
static void
{
}
/*PRINTFLIKE1*/
static void
{
}
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/filesyste/boot-archive
* SMF service, ignore amd64 modules unless we are booted amd64.
*/
return (0);
/*
* 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_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 (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 rval;
/*
* 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;
}
}
static void
create_newstat(void)
{
int error;
if (error) {
/*
* Not fatal - we can still create archive
*/
}
}
static void
{
/* XXX shouldn't we use FTW_MOUNT ? */
/*
* 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)
*/
/*
* 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);
}
/*
* At this point we need an update - so save new stat data
* However, if only checking (-n), don't save new stat data.
*/
if (!bam_check)
return (1);
}
static error_t
create_ramdisk(char *root)
{
/*
* Setup command args for create_ramdisk.ksh
*/
return (BAM_ERROR);
}
/* chop off / at the end */
} else
return (BAM_ERROR);
}
/*
* Verify that the archive has been created
*/
return (BAM_ERROR);
}
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_newboot(char *root)
{
/*
* We can't boot without MULTI_BOOT
*/
if (bam_verbose)
return (0);
}
/*
* We can't generate archive without GRUB_DIR
*/
if (bam_verbose)
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 GRUB boot OS,
* don't care on sparc except for diskless clients
*/
if (!is_newboot(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 */
}
return (ret);
}
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);
}
/*
* First update archive for current root
*/
/*
* 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.
*/
}
out:
}
return (ret);
}
static void
{
} else {
}
}
static void
{
/* unlink from list */
else
else
}
static entry_t *
{
return (ent);
}
return (ent);
}
static void
{
if (ent)
}
/*
* 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
*/
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;
} 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 {
}
} else if (flag != BAM_INVALID) {
/*
* For header comments, the entry# is "fixed up"
* by the subsequent title
*/
} else {
}
/* 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);
}
static int
char *title,
char *root,
char *kernel,
char *module)
{
char linebuf[BAM_MAXLINE];
}
return (BAM_ERROR);
}
return (BAM_ERROR);
}
} else {
}
/*
* The syntax for comments is #<comment>
*/
if (root) {
}
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;
}
}
static char *
get_special(char *mountp)
{
return (0);
}
if (*mountp == '\0')
else
return (NULL);
}
}
static 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 grubdisk and module (don't care about kernel line).
* 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 */
}
/* root command found, match grub disk */
continue;
}
} else {
/* no root command, see if root is optional */
if (root_opt == 0) {
continue;
}
}
continue;
}
/* check for matching module entry (failsafe or normal) */
continue;
}
break; /* match found */
}
*entry_num = i;
return (ent);
}
static int
{
int i;
char linebuf[BAM_MAXLINE];
/* note: don't match on title, it's updated on upgrade */
/* replace title of exiting entry and delete root line */
if (root_opt) { /* root line not needed */
} else
}
return (i);
}
/*ARGSUSED*/
static error_t
{
int entry;
char failsafe[256];
return (BAM_ERROR);
}
return (BAM_ERROR);
}
/* add the entry for normal Solaris */
/* add the entry for failsafe archive */
"%s/boot/x86.miniroot-safe", osroot);
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];
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;
char kernbuf[1024];
/* If no option, delete exiting reboot menu entry */
0, &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 */
"/platform/i86pc/boot_archive");
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 */
}
/*ARGSUSED*/
static error_t
{
char *val;
return (BAM_ERROR);
}
*val = '\0';
} else {
return (BAM_ERROR);
}
*val = '=';
}
/*
* 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()
*/
/* if (bam_verbose) XXX */
}
}
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
*/
static char *
{
int n;
if (buf) {
}
return (buf);
}
static void *
{
void *ptr;
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);
amd64 = 1;
else
amd64 = 0;
return (amd64);
}
static void
{
else
}