--- a/usr/src/Makefile.master Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/Makefile.master Thu Apr 21 23:16:03 2011 -0600
@@ -125,6 +125,14 @@
ROOTPYTHONVENDORSOLINSTALLTARGETLIBNVPAIR = $(ROOTPYTHONVENDORSOLINSTALLTARGET)/libnvpair
ROOTPYTHONVENDORSOLINSTALLTARGETSHADOW = $(ROOTPYTHONVENDORSOLINSTALLTARGET)/shadow
ROOTPYTHONVENDORINSTALLTRANSFER = $(ROOTPYTHONVENDORSOLINSTALL)/transfer
+ROOTPYTHONVENDORBOOTMGMT= $(ROOTPYTHONVENDOR)/bootmgmt
+ROOTPYTHONVENDORBOOTMGMTBKND= $(ROOTPYTHONVENDORBOOTMGMT)/backend
+ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN= $(ROOTPYTHONVENDORBOOTMGMTBKND)/autogen
+ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS= $(ROOTPYTHONVENDORBOOTMGMTBKND)/bootvars
+ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC= $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/sparc
+ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86= $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/x86
+ROOTPYTHONVENDORBOOTMGMTBKNDFW= $(ROOTPYTHONVENDORBOOTMGMTBKND)/fw
+ROOTPYTHONVENDORBOOTMGMTBKNDLOADER= $(ROOTPYTHONVENDORBOOTMGMTBKND)/loader
ROOTAUTOINST= $(ROOT)/usr/share/auto_install
ROOTAUTOINSTSCPROFILES= $(ROOTAUTOINST)/sc_profiles
ROOTSBIN= $(ROOT)/sbin
--- a/usr/src/lib/Makefile Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/lib/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -25,7 +25,8 @@
include $(SRC)/Makefile.master
-SUBDIRS= install_common \
+SUBDIRS= bootmgmt \
+ install_common \
install_utils \
libaiscf \
libaiscf_pymod \
--- a/usr/src/lib/Makefile.lib Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/lib/Makefile.lib Thu Apr 21 23:16:03 2011 -0600
@@ -76,6 +76,10 @@
ROOTPYTHONVENDORSOLINSTALLTARGETCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORSOLINSTALLTARGET)/%)
ROOTPYTHONVENDORSOLINSTALLTARGETLIBS= $(CPYTHONLIBS:%=$(ROOTPYTHONVENDORSOLINSTALLTARGET)/%)
+ROOTPYTHONVENDORBOOTMGMTTARGETMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTTARGET)/%)
+ROOTPYTHONVENDORBOOTMGMTTARGETCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTTARGET)/%)
+ROOTPYTHONVENDORBOOTMGMTTARGETLIBS= $(CPYTHONLIBS:%=$(ROOTPYTHONVENDORMGMTTARGET)/%)
+
DYNLIBLINK = $(DYNLIB:%$(VERS)=%)
COSDYNLIBLINK = $(COSDYNLIB:%$(VERS)=%)
@@ -112,6 +116,10 @@
$(ROOTPYTHONVENDORSOLINSTALLMODS):= OWNER = root
$(ROOTPYTHONVENDORSOLINSTALLMODS):= GROUP = bin
+$(ROOTPYTHONVENDORBOOTMGMTMODS):= FILEMODE = 444
+$(ROOTPYTHONVENDORBOOTMGMTMODS):= OWNER = root
+$(ROOTPYTHONVENDORBOOTMGMTMODS):= GROUP = bin
+
LORDER= lorder
TSORT= tsort
AWK= awk
@@ -178,6 +186,7 @@
$(ROOTPYTHONVENDORINSTALLLIBS) := PNAME = pics
$(ROOTPYTHONVENDORSOLINSTALLLIBS) := PNAME = pics
$(ROOTPYTHONVENDORSOLINSTALLTARGETLIBS) := PNAME = pics
+$(ROOTPYTHONVENDORBOOTMGMTTARGETLIBS) := PNAME = pics
# build rule for "portable" source
objs/${ARCH}/%.o profs/${ARCH}/%.o pics/${ARCH}/%.o: %.c
--- a/usr/src/lib/Makefile.targ Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/lib/Makefile.targ Thu Apr 21 23:16:03 2011 -0600
@@ -151,6 +151,30 @@
$(ROOTPYTHONVENDORINSTALLTRANSFER):
$(INS.dir)
+$(ROOTPYTHONVENDORBOOTMGMT):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKND):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDFW):
+ $(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER):
+ $(INS.dir)
+
$(ROOTPYTHONTERMUI):
$(INS.dir)
@@ -206,6 +230,30 @@
$(ROOTPYTHONVENDORINSTALLTRANSFER)/%: %
$(CP_P.file)
+$(ROOTPYTHONVENDORBOOTMGMT)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKND)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDFW)/%: %
+ $(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)/%: %
+ $(CP_P.file)
+
$(ROOTPYTHONTERMUI)/%: %
$(CP_P.file)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,78 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.lib
+
+SUBDIRS= backend
+
+.PARALLEL: $(SUBDIRS)
+
+all:= TARGET= all
+check:= TARGET= check
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+install:= TARGET= install
+install_h:= TARGET= install_h
+lint:= TARGET= lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+PYMODS= __init__.py \
+ bootarchive.py \
+ bootconfig.py \
+ bootinfo.py \
+ bootloader.py \
+ bootutil.py \
+ pysol.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMT)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMT)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+all: $(SUBDIRS) $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMT) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS) $(SUBDIRS)
+
+install_h:
+
+$(SUBDIRS): FRC
+ cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,85 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+A Python package for management of all things Boot
+"""
+
+class BootmgmtError(Exception):
+ def __init__(self, msg, xcpt=None):
+ self.msg = msg
+ self.xcpt = xcpt
+
+ def __str__(self):
+ if not self.xcpt is None:
+ return self.msg + '(' + str(self.xcpt) + ')'
+ else:
+ return self.msg
+
+class BootmgmtNotSupportedError(BootmgmtError):
+ pass
+
+class BootmgmtArgumentError(BootmgmtError):
+ pass
+
+class BootmgmtMissingInfoError(BootmgmtError):
+ pass
+
+class BootmgmtUnsupportedOperationError(BootmgmtError):
+ pass
+
+class BootmgmtMalformedPropertyNameError(BootmgmtError):
+ pass
+
+class BootmgmtMalformedPropertyValueError(BootmgmtError):
+ def __init__(self, propname, propval):
+ super(self, BootmgmtMalformedPropertyValueError).__init__(
+ 'Invalid value specified for property "%s": %s' %
+ (str(propname), str(propval)))
+ self.propname = propname
+ self.propval = propval
+
+class BootmgmtReadError(BootmgmtError):
+ pass
+
+class BootmgmtWriteError(BootmgmtError):
+ pass
+
+class BootmgmtUnsupportedPlatformError(BootmgmtError):
+ pass
+
+class BootmgmtInterfaceCodingError(BootmgmtError):
+ pass
+
+class BootmgmtConfigReadError(BootmgmtError):
+ pass
+
+class BootmgmtConfigWriteError(BootmgmtError):
+ pass
+
+class BootmgmtIncompleteBootConfigError(BootmgmtError):
+ pass
+
+class BootmgmtUnsupportedPropertyError(BootmgmtUnsupportedOperationError):
+ pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,77 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../Makefile.lib
+
+SUBDIRS= autogen \
+ bootvars \
+ fw \
+ loader
+
+.PARALLEL: $(SUBRDIRS)
+
+all:= TARGET= all
+check:= TARGET= check
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+install:= TARGET= install
+install_h:= TARGET= install_h
+lint:= TARGET= lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+PYMODS= __init__.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKND)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKND)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(SUBDIRS) $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKND): $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKND) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS) $(SUBDIRS)
+
+install_h:
+
+$(SUBDIRS): FRC
+ cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,27 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various backend interfaces for pybootmgmt
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/autogen/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,57 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+PYMODS= __init__.py \
+ solaris.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/autogen/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,42 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+BootInstance autogenerator backend interfaces for pybootmgmt
+"""
+
+import sys
+
+autogen_backends = [ 'solaris' ]
+
+class BootInstanceAutogenFactory(object):
+ @staticmethod
+ def autogen(bootconfig):
+ instance_list = []
+ for backend in autogen_backends:
+ modname = __name__ + '.' + backend
+ __import__(modname, level=0)
+ ns = sys.modules[modname]
+ instance_list += ns.autogenerate_boot_instances(bootconfig)
+ return instance_list
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/autogen/solaris.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,65 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+BootInstance autogenerator Solaris backend for pybootmgmt
+"""
+
+import libbe_py
+
+from ...bootconfig import BootConfig, SolarisDiskBootInstance
+from ...bootutil import LoggerMixin
+
+class SolarisBootInstanceAutogenerator(LoggerMixin):
+ pass
+
+def autogenerate_boot_instances(bootconfig):
+ sbia = SolarisBootInstanceAutogenerator()
+
+ # XXX - Handle bootconfig instances that have boot_class != disk
+ if bootconfig.boot_class != BootConfig.BOOT_CLASS_DISK:
+ raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+ # Use libbe_py to get the list of boot environments, then iterate
+ # over the list, creating a new SolarisDiskBootInstance for each
+ # Note that the title will just be the last portion of the bootfs
+ # (i.e. <pool>/ROOT/<title>)
+ retcode, belist = libbe_py.beList()
+ if retcode != 0:
+ sbia._debug('libbe_py.beList() failed; return code was ' + str(retcode))
+ return []
+
+ inst_list = []
+ for bootenv in belist:
+ if bootenv.get('orig_be_name', None) is None:
+ continue # skip over snapshots
+ bootinst = SolarisDiskBootInstance(None, title=bootenv['orig_be_name'],
+ bootfs=bootenv['root_ds'])
+ if bootenv['active'] is True:
+ sbia._debug('default boot instance is:\n' + str(bootinst))
+ bootinst.default = True
+
+ inst_list.append(bootinst)
+
+ return inst_list
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,75 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+SUBDIRS= sparc \
+ x86
+
+.PARALLEL: $(SUBRDIRS)
+
+all:= TARGET= all
+check:= TARGET= check
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+install:= TARGET= install
+install_h:= TARGET= install_h
+lint:= TARGET= lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+PYMODS= __init__.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(SUBDIRS) $(PYCMODS)
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND): $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS) $(SUBDIRS)
+
+install_h:
+
+$(SUBDIRS): FRC
+ cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,47 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Boot variables backend support for pybootmgmt
+"""
+
+import sys
+from ... import bootutil
+from ... import BootmgmtArgumentError
+
+class BackendBootVarsFactory(object):
+ @staticmethod
+ def get(sysroot, arch, osname):
+ """Returns an instance of bootinfo.BootVariables corresponding to the
+ architecture and system root passed in."""
+ if arch is None:
+ arch = bootutil.get_current_arch_string()
+ if osname is None:
+ raise BootmgmtArgumentError('osname cannot be None')
+
+ bvmod = __name__ + '.' + arch + '.' + osname
+ __import__(bvmod, level=0)
+ ns = sys.modules[bvmod]
+ # bootvars_backend returns the *class*
+ return ns.bootvars_backend()(sysroot)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/sparc/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,57 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../../Makefile.lib
+
+PYMODS= __init__.py \
+ solaris.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/sparc/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,27 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+SPARC Boot variables backend support for pybootmgmt
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/sparc/solaris.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,36 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+SPARC Solaris Boot variables backend support for pybootmgmt
+"""
+
+import sys
+from .... import bootinfo
+
+class OBPBootVariables(bootinfo.BootVariables):
+ pass
+
+def bootvars_backend():
+ return OBPBootVariables
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/x86/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,57 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../../Makefile.lib
+
+PYMODS= __init__.py \
+ solaris.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/x86/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,27 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+x86 Boot variables backend support for pybootmgmt
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/x86/solaris.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,216 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+x86 Solaris Boot variables backend support for pybootmgmt
+"""
+
+import tempfile
+import sys
+import logging
+import shutil
+from .... import bootinfo
+from .... import BootmgmtMalformedPropertyNameError, BootmgmtArgumentError
+from .... import BootmgmtReadError, BootmgmtWriteError
+
+logger = logging.getLogger('bootmgmt')
+
+class BootenvBootVariables(bootinfo.BootVariables):
+ """This class supports manipulation of boot variables stored in the
+ <root>/boot/solaris/bootenv.rc file."""
+
+ BOOTENV_RC = '/boot/solaris/bootenv.rc'
+
+ def __init__(self, sysroot=None):
+ self.BOOTENV_DOT_RC = sysroot + BootenvBootVariables.BOOTENV_RC
+ self._dirty = False
+ super(BootenvBootVariables, self).__init__(sysroot)
+
+ @property
+ def dirty(self):
+ return self._dirty
+
+ @dirty.setter
+ def dirty(self, value):
+ if not type(value) is bool:
+ raise ValueError('dirty is a bool')
+ if self._dirty != value:
+ logger.debug(self.__class__.__name__ + ': dirty => %s' % str(value))
+ self._dirty = value
+
+ def setprop(self, propname, value):
+ # if the property name contains whitespace, it's invalid
+ if len(set(propname).intersection(' \t')) > 0:
+ raise BootmgmtMalformedPropertyNameError('Invalid property name' +
+ ' ("%s")' % propname)
+ if value is None: # Not allowed -- there must be a real value here
+ raise BootmgmtArgumentError('value must not be None')
+
+ if propname in self._vardict:
+ if self._vardict[propname][1] != value:
+ # All we need to do is update the value portion of the list
+ # (this automatically "updates" the value stored in _rawlines)
+ self._vardict[propname][1] = value
+ self.dirty = True
+ else:
+ proplist = [propname, value]
+ # The _vardict must use the same object that's added to _rawlines
+ # so that updates are seamless across both containers
+ self._vardict[propname] = proplist
+ self._rawlines.append(proplist)
+ self.dirty = True
+
+
+
+ def getprop(self, propname):
+ val_list = self._vardict.get(propname, None)
+ # The values stored in the dictionary are 2-element lists
+ # The first element is the prop name and the second is the value
+ if not val_list is None and len(val_list) == 2:
+ return val_list[1]
+ else:
+ return None
+
+ def delprop(self, propname):
+ if propname in self._vardict:
+ # Clear the list first so that _rawlines is "updated" to contain
+ # a 0-length list for this property
+ del self._vardict[propname][:]
+ # Now remove the property from the _vardict dict
+ del self._vardict[propname]
+ self.dirty = True
+
+ def _read(self):
+ """Reads the set of properties from the bootenv.rc file under
+ sysroot. Keeps a copy of the file in memory so comments can
+ be preserved."""
+ bvfile = self.BOOTENV_DOT_RC
+ self._rawlines = []
+ self._vardict = {}
+ try:
+ with open(bvfile) as berc:
+ for rawline in berc:
+ nextline = rawline.strip()
+ # skip comment lines
+ if len(nextline) > 0 and nextline[0] != '#':
+ # Store the property in the _rawlines list as a
+ # list so that we can make changes via the _vardict
+ # The form of a line is:
+ # setprop <propname> <propval>
+ # If we find a line that's malformed, ignore it and
+ # continue
+ try:
+ keyword, prop, val = nextline.split(None, 2)
+ except ValueError:
+ logger.debug('Malformed line in ' + bvfile +
+ ': "%s"' % nextline)
+ continue
+
+ if keyword != 'setprop':
+ logger.debug('Malformed line in ' + bvfile +
+ ': "%s"' % nextline)
+ continue
+
+ newbep = [prop, val]
+ self._rawlines.append(newbep)
+ self._vardict[prop] = newbep
+ else:
+ self._rawlines.append(rawline)
+
+
+ except IOError as e:
+ raise BootmgmtReadError('Error while loading boot variables ' +
+ 'from ' + bvfile, e)
+
+ def write(self, inst, alt_dir=None):
+ # Open a new file that will contain the new bootenv.rc, dump all
+ # the variables to that file, then move that file over to be the
+ # real bootenv.rc
+
+ if not alt_dir is None:
+ try:
+ fileobj = tempfile.NamedTemporaryFile(dir=alt_dir, delete=False)
+ except IOError as err:
+ raise BootmgmtWriteError('Error while writing to temporary ' +
+ 'bootenv.rc (%s)' % fileobj.name, err)
+ bvfile = fileobj.name
+ bvtempfile = bvfile
+ else:
+ # BOOTENV_DOT_RC has a leading slash:
+ bvfile = inst.rootpath + self.BOOTENV_DOT_RC
+ bvtempfile = bvfile + '.new'
+ try:
+ fileobj = open(bvtempfile, 'w')
+ except IOError as err:
+ raise BootmgmtWriteError('Error while writing to temporary ' +
+ 'bootenv.rc (%s)' % bvtempfile, err)
+
+ try:
+ with fileobj as berc:
+ # Write each line to the output file -- if the item in
+ # _rawlines is a list, construct a setprop command string,
+ # otherwise, just copy it verbatim
+ for line in self._rawlines:
+ if type(line) is list and len(line) == 2:
+ berc.write('setprop ' + line[0] + ' ' + line[1] + '\n')
+ elif type(line) is str:
+ berc.write(line) # newline is already part of line
+
+ except IOError as err:
+ raise BootmgmtWriteError('Error while writing to temporary ' +
+ 'bootenv.rc (%s)' % bvtempfile, err)
+
+ # Now move the file over the become the new bootenv.rc:
+ try:
+ if not alt_dir is None:
+ shutil.move(bvtempfile, bvfile)
+ self.dirty = False
+ except IOError as ioe:
+ try:
+ # Try to clean up by deleting the temporary file
+ os.remove(bvtempfile)
+ except OSError as ose:
+ logger.debug("Couldn't clean up temporary bootenv.rc: " +
+ ose.strerror)
+ pass
+ raise BootmgmtWriteError('Error while moving the temporary ' +
+ 'bootenv.rc (%s) to %s' %
+ (bvtempfile, bvfile), ioe)
+
+ if not alt_dir is None:
+ return ('file', bvfile, inst,
+ '%(systemroot)s' + BootenvBootVariables.BOOTENV_RC,
+ 'root', 'sys', 0644)
+ else:
+ return None
+
+ def __len__(self):
+ return len(self._vardict)
+
+ def __iter__(self):
+ classic_dict = [(x, z) for (x,[y,z]) in self._vardict.items()]
+ return classic_dict.__iter__()
+
+def bootvars_backend():
+ return BootenvBootVariables
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,59 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+PYMODS= __init__.py \
+ bios.py \
+ obp.py \
+ uefi64.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDFW)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDFW)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDFW) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDFW)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDFW) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,76 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+System firmware backend support for pybootmgmt
+"""
+
+from ...bootutil import get_current_arch_string
+from ... import BootmgmtUnsupportedPlatformError
+from ... import pysol
+import sys
+
+class BackendFWFactory(object):
+ @staticmethod
+ def get(fw_name):
+ """Returns an instance of bootinfo.SystemFirmware corresponding to the
+ firmware name passed in. If fw_name is None, the system firmware type
+ is autodetected and the appropriate child of SystemFirmware is returned
+ """
+ if fw_name is None:
+ curarch = get_current_arch_string()
+ # If this is a SPARC system, the appropriate class is obp
+ if curarch == 'sparc':
+ from . import obp
+ return obp.firmware_backend()('obp')
+ elif curarch == 'x86':
+ # If this is an x86 system and the efi-systype property exists,
+ # then this is a UEFI system and the property value specifies the
+ # bit width.
+ try:
+ efisystype = pysol.di_find_root_prop('efi-systype')
+ except IOError as e:
+ # Problem while trying to get the property
+ # Set efisystype to None to force BIOS
+ efisystype = None
+
+ if efisystype is None:
+ from . import bios
+ return bios.firmware_backend()('bios')
+ else:
+ uefi_string = 'uefi' + efisystype
+ fwmod = __name__ + '.' + uefi_string
+ __import__(fwmod, level=0)
+ ns = sys.modules[fwmod]
+ return ns.firmware_backend()(uefi_string)
+ else:
+ raise BootmgmtUnsupportedPlatformError('Unknown platform '
+ '"%s"' % curarch)
+ else:
+ fwmod = __name__ + '.' + fw_name
+ __import__(fwmod, level=0)
+ ns = sys.modules[fwmod]
+ return ns.firmware_backend()(fw_name)
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/bios.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,50 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+x86 BIOS firmware backend for pybootmgmt
+"""
+
+from ... import bootinfo
+from ... import BootmgmtUnsupportedOperationError, BootmgmtWriteError
+
+class BIOSFirmware(bootinfo.SystemFirmware):
+ def getprop(self, propname):
+ return super(BIOSFirmware, self).getprop(propname)
+
+ def setprop(self, propname, value):
+ """Setting properties is not supported on BIOS systems"""
+ if propname == SystemFirmware.PROP_BOOT_DEVICE:
+ raise BootmgmtWriteError('Properties are read-only on systems '
+ 'with BIOS firmware')
+ raise BootmgmtUnsupportedOperationError('Properties cannot be set '
+ 'on systems with BIOS firmware')
+
+ def delprop(self, propname):
+ """Deleting properties is not supported on BIOS systems"""
+ raise BootmgmtUnsupportedOperationError('Properties cannot be '
+ 'deleted on systems with BIOS firmware')
+
+def firmware_backend():
+ return BIOSFirmware
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/obp.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,35 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+OpenBoot Firmware firmware backend for pybootmgmt
+"""
+
+from ... import bootinfo
+
+class OBPFirmware(bootinfo.SystemFirmware):
+ pass
+
+def firmware_backend():
+ return OBPFirmware
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/uefi64.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,35 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+UEFI firmware backend for pybootmgmt (only 64-bit UEFI is supported)
+"""
+
+from ... import bootinfo
+
+class UEFI64Firmware(bootinfo.SystemFirmware):
+ pass
+
+def firmware_backend():
+ return UEFI64Firmware
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,60 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+PYMODS= __init__.py \
+ grub2.py \
+ legacygrub.py \
+ menulst.py \
+ sbb.py
+
+PYCMODS= $(PYMODS:%.py=%.pyc)
+ROOTPYMODS= $(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)/%)
+ROOTPYCMODS= $(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)/%)
+
+CLOBBERFILES = $(PYCMODS)
+CLEANFILES = $(CLOBBERFILES)
+
+%.pyc : %.py
+ $(RM) $@
+ $(PYTHON) -mpy_compile $<
+ @[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all: $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/__init__.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,68 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various bootloader interfaces for pybootmgmt
+"""
+
+import sys
+from ...bootutil import LoggerMixin
+
+boot_loader_backends = [ 'grub2', 'legacygrub', 'sbb' ]
+
+class BackendBootLoaderFactory(LoggerMixin):
+ @classmethod
+ def get(cls, bootconfig):
+ """Returns an instance of bootloader.BootLoader appropriate for the
+ system identified by the keyword arguments passed in. Invokes a
+ probe function for each boot loader backend, and returns the loader
+ whose probe function returned the highest weight value. If multiple
+ boot loaders' probe functions succeed, a reference to the
+ lower-weighted (deprecated) loader is stored in the higher-weighted
+ loader's old_loader member (only two loaders can be linked in this
+ manner).
+ """
+ loaderlist = []
+ loader_instance = None
+
+ for loader in boot_loader_backends:
+ blmod = __name__ + '.' + loader
+ __import__(blmod, level=0)
+ ns = sys.modules[blmod]
+ for loaderclass in ns.bootloader_classes():
+ loaderinst, loaderwt = loaderclass.probe(bootconfig=bootconfig)
+ if not loaderinst is None:
+ loaderlist.append((loaderinst, loaderwt))
+
+ # Sort the loader list by weight:
+ loaderlist.sort(key=(lambda t: t[1]))
+
+ cls._debug('loaderlist => ' + str(loaderlist))
+
+ if len(loaderlist) > 0:
+ loader_instance = loaderlist[-1][0]
+ if len(loaderlist) > 1:
+ loader_instance.old_loader = loaderlist[-2][0]
+
+ return loader_instance
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/grub2.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,37 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+GRUB2 boot loader backend for pybootmgmt
+"""
+
+from ...bootloader import BootLoader
+
+class GRUB2BootLoader(BootLoader):
+ @classmethod
+ def probe(cls, **kwargs):
+ return (None, None)
+
+def bootloader_classes():
+ return [GRUB2BootLoader]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/legacygrub.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,945 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Legacy GRUB BootLoader Implementation for pybootmgmt
+"""
+
+import tempfile
+import shutil
+import os
+import pwd
+import grp
+import gettext
+import stat
+import re
+
+from .menulst import MenuDotLst, MenuLstError, MenuLstMenuEntry, MenuLstCommand
+from ...bootloader import BootLoader, BootLoaderInstallError
+from ...bootconfig import BootConfig, DiskBootConfig, SolarisDiskBootInstance
+from ...bootconfig import ChainDiskBootInstance
+from ... import BootmgmtArgumentError
+from ... import BootmgmtUnsupportedOperationError, BootmgmtInterfaceCodingError
+from ... import BootmgmtIncompleteBootConfigError, BootmgmtConfigReadError
+from ... import BootmgmtConfigWriteError, BootmgmtMalformedPropertyValueError
+from ... import BootmgmtUnsupportedPlatformError, BootmgmtNotSupportedError
+from solaris_install import Popen, CalledProcessError
+
+_ = gettext.translation("SUNW_OST_OSCMD", "/usr/lib/locale",
+ fallback=True).gettext
+
+class LegacyGRUBBootLoader(BootLoader):
+ """Implementation of a Legacy GRUB (GRUB 0.97) BootLoader. Handles parsing
+ the menu.lst file (reading and writing), though reading it and creating
+ BootInstance objects is rather fragile"""
+
+ WEIGHT = 1 # Legacy GRUB's probe weight
+
+ MENU_LST_PATH = '/boot/grub/menu.lst'
+
+ DEFAULT_PROPDICT = {
+ 'default_command' : 'default 0',
+ 'timeout_command' : 'timeout 10',
+ 'serial_command' : '# serial --unit=0 --speed=9600',
+ 'terminal_command' : '# terminal serial',
+ 'splashimage_command' : '# splashimage /boot/grub/splash.xpm.gz',
+ # if foreground or background are used, they MUST start with
+ # a newline (see the MENU_LST_PREAMBLE, below)
+ 'foreground' : '',
+ 'background' : '',
+ 'minmem64' : '',
+ 'hidemenu' : '' }
+
+ INSTALLGRUB_NOUPDT = 4 # from src/cmd/boot/common/boot_utils.h
+ INSTALLGRUB_NOEINFO = 6 # (ditto)
+
+ DEFAULT_TIMEOUT = 10 # 10 seconds is the default timeout
+ DEFAULT_FORECOLOR = '343434'
+ DEFAULT_BACKCOLOR = 'F7FBFF'
+
+ # Supported properties for setprop()
+ SUPPORTED_PROPS = [BootLoader.PROP_CONSOLE,
+ BootLoader.PROP_SERIAL_PARAMS,
+ BootLoader.PROP_MINMEM64,
+ BootLoader.PROP_TIMEOUT,
+ BootLoader.PROP_QUIET]
+
+ MENU_LST_PREAMBLE = (
+r"""# default menu entry to boot
+%(default_command)s
+#
+# menu timeout in second before default OS is booted
+# set to -1 to wait for user input
+%(timeout_command)s
+#
+# To enable grub serial console to ttya uncomment the following lines
+# and comment out the splashimage line below
+# WARNING: do not enable grub serial console when BIOS console serial
+# redirection is active.
+%(serial_command)s
+%(terminal_command)s
+#
+# Uncomment the following line to enable GRUB splashimage on console
+%(splashimage_command)s%(foreground)s%(background)s
+#
+# To chainload another OS
+#
+# title Another OS
+# root (hd<disk no>,<partition no>)
+# chainloader +1
+#
+# To chainload a Solaris release not based on grub
+#
+# title Solaris 9
+# root (hd<disk no>,<partition no>)
+# chainloader +1
+# makeactive
+#
+# To load a Solaris instance based on grub
+# If GRUB determines if the booting system is 64-bit capable,
+# the kernel$ and module$ commands expand $ISADIR to "amd64"
+#
+# title Solaris <version>
+# findroot (pool_<poolname>,<partition no>,x) --x = Solaris root slice
+# bootfs <poolname>/ROOT/<BE_name>
+# kernel$ /platform/i86pc/kernel/$ISADIR/unix
+# module$ /platform/i86pc/$ISADIR/boot_archive
+
+#
+# To override Solaris boot args (see kernel(1M)), console device and
+# properties set via eeprom(1M) edit the "kernel" line to:
+#
+# kernel /platform/i86pc/kernel/unix <boot-args> -B prop1=val1,prop2=val2,...
+#
+%(hidemenu)s
+%(minmem64)s
+""")
+
+ @classmethod
+ def probe(cls, **kwargs):
+ """Probe for Legacy GRUB files for use with the BootConfig passed
+ in"""
+
+ bootconfig = kwargs.get('bootconfig', None)
+
+ if (bootconfig is None or bootconfig.boot_class is None):
+ return (None, None)
+
+ if (bootconfig.boot_class == BootConfig.BOOT_CLASS_DISK and
+ not bootconfig.boot_fstype is None):
+ return LegacyGRUBBootLoader._probe_disk(**kwargs)
+ elif bootconfig.boot_class == BootConfig.BOOT_CLASS_ODD:
+ return LegacyGRUBBootLoader._probe_odd(**kwargs)
+ else:
+ raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+ @classmethod
+ def _probe_odd(cls, **kwargs):
+ """This Legacy GRUB probe function searches the ODD's root, looking for
+ the Legacy GRUB menu.lst and stage1 and stage2 files"""
+
+ bootconfig = kwargs.get('bootconfig', None)
+
+ root = bootconfig.get_root()
+
+ cls._debug('_probe_odd(): odd_image_root=%s' % root)
+
+ try:
+ cls._probe_generic(root, root,
+ ['stage2_eltorito'])
+ except BootmgmtNotSupportedError:
+ return (None, None)
+
+ return (LegacyGRUBBootLoader(**kwargs), LegacyGRUBBootLoader.WEIGHT)
+
+ @classmethod
+ def _probe_disk(cls, **kwargs):
+ """This Legacy GRUB probe function searches the ZFS top-level dataset
+ for a menu.lst file. If that's not present, we search the system root
+ for /boot/grub/stage1 and /boot/grub/stage2 -- essential files that
+ should exist if Legacy GRUB is the active boot loader.
+ """
+
+ bootconfig = kwargs.get('bootconfig', None)
+
+ fstype = bootconfig.boot_fstype
+
+ if fstype == 'ufs':
+ menuroot = bootconfig.get_root()
+ dataroot = menuroot
+ elif fstype == 'zfs':
+ menuroot = bootconfig.zfstop
+ dataroot = bootconfig.get_root()
+ else:
+ return (None, None)
+
+ cls._debug('_probe_disk():'
+ ' menuroot=%s, dataroot=%s' % (menuroot, dataroot))
+
+ try:
+ cls._probe_generic(menuroot, dataroot,
+ ['stage1', 'stage2'])
+ except BootmgmtNotSupportedError:
+ return (None, None)
+
+ # XXX - In addition to the loader files themselves, we need to ensure
+ # XXX - that we have access to the installgrub program in the currently-
+ # XXX - running system (otherwise, we'll have no way to install Legacy
+ # XXX - GRUB).
+
+ return (LegacyGRUBBootLoader(**kwargs), LegacyGRUBBootLoader.WEIGHT)
+
+ @classmethod
+ def _probe_generic(cls, menuroot, dataroot, datafiles):
+
+ # Both the menu root and the data root locations must be specified
+ if menuroot is None or dataroot is None:
+ raise BootmgmtNotSupportedError('menuroot or dataroot is None')
+
+ menulst = menuroot + LegacyGRUBBootLoader.MENU_LST_PATH
+ try:
+ open(menulst).close()
+ except IOError as ioerr:
+ cls._debug(('Error opening %s: ' % menulst) + ioerr.strerror)
+
+ try:
+ for datafile in datafiles:
+ open(dataroot + '/boot/grub/' + datafile).close()
+ except IOError as ioerr:
+ cls._debug(str(ioerr))
+ raise BootmgmtNotSupportedError('IOError when checking for '
+ 'datafiles')
+
+ def _serial_parameters(self):
+ """Parse serial parameters and return a tuple of (serial port number,
+ serial port speed, data bits, parity (one of 'no', 'odd' or 'even'),
+ stop bits (0 or 1)) (all tuple members must be strings).
+
+ The form of the serial_params property is:
+
+ serial_params | A tuple containing (<portspec>,<speed>,<d>,
+ | <p>,<s>,<f>).
+ | <portspec> is currently defined to be a
+ | number (valid valid depend on the platform,
+ | but 0 is ttya (com1) and 1 is ttyb (com2)).
+ | Serial console parameters (<d>=data bits,
+ | <p>=parity ('N','E','O'),<s>=stop bits (0,1),
+ | <f>=flow control ('H','S',None) for hardware,
+ | software, or none). The default is:
+ | (0,9600,8,'N',1,None).
+ """
+
+ params = self.getprop('serial_params')
+
+ if not params is None:
+ if not params[BootLoader.PROP_SP_PARITY] is None:
+ try:
+ parity = {'N' : 'no',
+ 'E' : 'even',
+ 'O' : 'odd'}[params[BootLoader.PROP_SP_PARITY]]
+ except KeyError:
+ self._debug('Bad parity value in serial_params')
+ parity = None
+
+ ret_params = (params[BootLoader.PROP_SP_PORT],
+ params[BootLoader.PROP_SP_SPEED],
+ params[BootLoader.PROP_SP_DATAB],
+ parity,
+ params[BootLoader.PROP_SP_STOPB])
+
+ # if port is not indicated, force use of port 0.
+ if ret_params[0] is None:
+ ret_params[0] = '0'
+
+ return ret_params
+
+ return ('0', '9600', None, None, None)
+
+ def __init__(self, **kwargs):
+ self.pkg_names = [ 'system/boot/grub', 'SUNWgrub' ]
+ self.name = 'Legacy GRUB'
+ self._menufile = None
+ super(LegacyGRUBBootLoader, self).__init__(**kwargs)
+ self._bl_props[BootLoader.PROP_BOOT_TARGS] = 'bios'
+
+ def new_config(self):
+ """The configuration for Legacy GRUB consists solely of the menu.lst
+ file. The default new configuration is an empty menu.lst file,
+ with a graphical splashscreen and appropriate fore/back colors."""
+ self._bl_props[BootLoader.PROP_CONSOLE] = BootLoader.PROP_CONSOLE_GFX
+
+ def load_config(self):
+ """Load boot instances and GRUB properties from the menu.lst file"""
+ if self._boot_config.boot_class == BootConfig.BOOT_CLASS_DISK:
+ self._load_config_disk()
+ self.dirty = False # We just loaded a clean config from disk!
+ else:
+ raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+ def _write_config(self, basepath):
+ """The only file that needs to be written is the menu.lst file, using
+ information from the BootConfig instance to which we have a reference.
+ Information from the boot loader's properties is also used to
+ determine which commands are emitted at the global level"""
+
+ if self._boot_config is None:
+ msg = ('Cannot _write_config(%s) - _boot_config is None' %
+ str(basepath))
+ self._debug(msg)
+ raise BootmgmtInterfaceCodingError(msg)
+
+ # Determine the type of boot configuration we're dealing with, then
+ # determine the filesystem type that will hold the menu.lst. Only
+ # then can we tell the caller the appropriate path to copy it into.
+ if self._boot_config.boot_class == BootConfig.BOOT_CLASS_DISK:
+ return self._write_config_disk(basepath)
+ elif self._boot_config.boot_class == BootConfig.BOOT_CLASS_ODD:
+ return self._write_config_odd(basepath)
+ else:
+ raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+ # Legacy GRUB methods for dealing with a disk-based BootConfig
+ def _menu_lst_dir_disk(self):
+ fstype = self._boot_config.boot_fstype
+ if fstype != 'zfs' and fstype != 'ufs':
+ raise BootmgmtUnsupportedOperationError('Unknown filesystem: %s'
+ % fstype)
+ if fstype == 'zfs':
+ menu_lst_dir = self._boot_config.zfstop
+ elif fstype == 'ufs':
+ menu_lst_dir = self._boot_config.get_root()
+
+ return menu_lst_dir
+
+ def _load_config_disk(self):
+
+ menu_lst = (self._menu_lst_dir_disk() +
+ LegacyGRUBBootLoader.MENU_LST_PATH)
+ try:
+ self._menufile = LegacyGRUBMenuFile(menu_lst)
+ except IOError as err:
+ raise BootmgmtConfigReadError('Error while processing the %s file' %
+ menu_lst, err)
+ except MenuLstError as err:
+ raise BootmgmtConfigReadError('Error while processing the %s '
+ 'file: %s' % (menu_lst, str(err)))
+
+ default_index = None
+ boot_instances = []
+ # Extract the properties and entries from the parsed file:
+ for entity in self._menufile.entities():
+ if isinstance(entity, MenuLstMenuEntry):
+ # Make sure we have everything first:
+ # XXX - Recognize non-disk boot instances in the menu.lst
+ argdict = {}
+ if entity.find_command('bootfs'):
+ for cmd in entity.commands():
+ # Skip non-commands
+ if not isinstance(cmd, MenuLstCommand):
+ continue
+
+ if cmd.get_command() == 'title':
+ argdict['title'] = ' '.join(cmd.get_args())
+ elif ((cmd.get_command() == 'kernel$' or
+ cmd.get_command() == 'kernel') and
+ len(cmd.get_args()) > 0):
+ argdict['kernel'] = cmd.get_args()[0]
+ argdict['kargs'] = ' '.join(cmd.get_args()[1:])
+ elif (cmd.get_command() == 'module$' or
+ cmd.get_command() == 'module'):
+ argdict['boot_archive'] = ' '.join(cmd.get_args())
+ elif cmd.get_command() == 'bootfs':
+ argdict['bootfs'] = ' '.join(cmd.get_args())
+ argdict['fstype'] = 'zfs'
+ elif cmd.get_command() == 'findroot':
+ # XXX - Handle "BE_XXXX" and other hints
+ argstring = ''.join(cmd.get_args())
+ (pool, subs) = re.subn(
+ r'.*pool_([^,)]+).*', r'\1', argstring)
+ if subs > 0:
+ argdict['signature'] = 'pool_' + pool
+ argdict['rpool'] = pool
+ argdict['fstype'] = 'zfs'
+
+ elif entity.find_command('chainloader'):
+ for cmd in entity.commands():
+ if cmd.get_command() == 'title':
+ argdict['title'] = ' '.join(cmd.get_args())
+ elif (cmd.get_command() == 'root' or
+ cmd.get_command() == 'rootnoverify'):
+ argdict['root'] = ' '.join(cmd.get_args())
+ elif cmd.get_command() == 'chainloader':
+ argdict['chainload'] = ' '.join(cmd.get_args())
+
+ elif entity.find_command('findroot'):
+ # XXX - Look up ZFS bootfs property for the pool
+ raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+ if not argdict.get('chainload', None) is None:
+ inst = ChainDiskBootInstance(None, **argdict)
+ else:
+ inst = SolarisDiskBootInstance(None, **argdict)
+
+ boot_instances += [inst]
+
+ elif isinstance(entity, MenuLstCommand) is True:
+ # Add the command as a property
+ arglist = entity.get_args()
+ if not arglist is None and len(arglist) > 0:
+ argstring = ' '.join(arglist)
+ else:
+ argstring = ''
+ # XXX - Don't use setprop here; if the entity contains a
+ # XXX - command we don't recognize, we'll get an exception
+ # XXX - instead, just record that command as-is so it can
+ # XXX - be replayed when the menu.lst is written
+ # XXX - In any case, we need to parse the command and turn it
+ # XXX - into a valid property key/value.
+ if entity.get_command() == 'default':
+ default_index = argstring
+ else:
+ self.setprop(entity.get_command(), argstring)
+
+ if not default_index is None:
+ try:
+ default_index = int(default_index)
+ self._debug('default GRUB entry is: ' + str(default_index))
+ except ValueError:
+ self._debug("Could not convert `default' index (%s) to "
+ "an int -- setting to 0" % default_index)
+ default_index = 0
+ if default_index < len(self._boot_config.boot_instances):
+ boot_instances[default_index].default = True
+ elif len(boot_instances) > 0:
+ # Set the default to be the first one
+ boot_instances[0].default = True
+
+ # Add the boot instances to the BootConfig instance:
+ self._boot_config.add_boot_instance(boot_instances)
+
+ def _write_config_disk(self, basepath):
+ """This is a disk-based configuration. We support ZFS or UFS root,
+ so figure out which filesystem we're dealing with, and write the
+ menu.lst file to that location."""
+
+ fstype = self._boot_config.boot_fstype
+ if fstype != 'zfs' and fstype != 'ufs':
+ raise BootmgmtUnsupportedOperationError('Unknown filesystem: %s'
+ % fstype)
+ if fstype == 'zfs':
+ menu_lst_dir = self._boot_config.zfstop
+ elif fstype == 'ufs':
+ menu_lst_dir = self._boot_config.get_root()
+
+ tuples = self._write_config_generic(basepath, menu_lst_dir)
+
+ if tuples is None:
+ self._debug('No tuples returned from _write_config_generic')
+ return None
+
+ for idx, item in enumerate(tuples):
+ if (item[BootConfig.IDX_FILETYPE] is BootConfig.OUTPUT_TYPE_FILE and
+ item[BootConfig.IDX_DESTNAME] ==
+ LegacyGRUBBootLoader.MENU_LST_PATH):
+ # Make a copy of the tuple so we can change it:
+ item = list(item)
+
+ if fstype == 'zfs':
+ item[BootConfig.IDX_DESTNAME] = (
+ '%(' + DiskBootConfig.TOKEN_ZFS_RPOOL_TOP_DATASET + ')s'
+ + LegacyGRUBBootLoader.MENU_LST_PATH)
+ elif fstype == 'ufs':
+ # The BootInstance included in the 6-tuple will be the first
+ # SolarisDiskBootInstance in the list held in the associated
+ # BootConfig's boot_instances list (this may need to be
+ # revisited).
+ inst = None
+ for inst in self._boot_config.boot_instances:
+ if isinstance(inst, SolarisDiskBootInstance) is True:
+ break
+
+ item[BootConfig.IDX_DESTNAME] = (
+ '%(' + BootConfig.TOKEN_SYSTEMROOT + ')s' +
+ LegacyGRUBBootLoader.MENU_LST_PATH)
+ item[BootConfig.IDX_INSTANCE] = inst
+
+ # Update item in the list:
+ item = tuple(item)
+ tuples[idx] = item
+
+ return tuples
+
+ def _write_config_generic(self, basepath, menu_lst_dir):
+
+ # If basepath is not None, the menu.lst should be written to a file
+ # under basepath (instead of to the actual location)
+ if basepath is None:
+ realmenu = menu_lst_dir + LegacyGRUBBootLoader.MENU_LST_PATH
+ tempmenu = realmenu + '.new'
+
+ try:
+ # Don't open the new menu.lst over the old -- create a
+ # temporary file, then, if the write is successful, move the
+ # temporary file over the old one.
+ outfile = open(tempmenu, 'w')
+ self._write_menu_lst(outfile)
+ outfile.close()
+ except IOError as err:
+ raise BootmgmtConfigWriteError("Couldn't write to %s" %
+ tempmenu, err)
+
+ try:
+ shutil.move(tempmenu, realmenu)
+ except IOError as err:
+ try:
+ os.remove(tempmenu)
+ except OSError as oserr:
+ self._debug('Error while trying to remove %s: %s' %
+ (tempmenu, oserr.strerror))
+ raise BootmgmtConfigWriteError("Couldn't move %s to %s" %
+ (tempmenu, realmenu), err)
+
+ # Move was successful, so now set the owner and mode properly:
+ try:
+ os.chmod(realmenu, 0644)
+ os.chown(realmenu, pwd.getpwnam('root').pw_uid,
+ grp.getgrnam('root').gr_gid)
+ except OSError as oserr:
+ raise BootmgmtConfigWriteError("Couldn't set mode/perms on "
+ + realmenu, oserr)
+
+ return None
+
+ # basepath is set to a path. Use it to form the path to a temporary
+ # file
+ try:
+ tmpfile = tempfile.NamedTemporaryFile(dir=basepath, delete=False)
+ self._write_menu_lst(tmpfile)
+ tmpfile.close()
+ except IOError as err:
+ raise BootmgmtConfigWriteError("Couldn't create a temporary "
+ 'file for menu.lst', err)
+
+ return [(BootConfig.OUTPUT_TYPE_FILE,
+ tmpfile.name,
+ None,
+ LegacyGRUBBootLoader.MENU_LST_PATH,
+ 'root',
+ 'root',
+ 0644)]
+
+
+ def _write_config_odd(self, basepath):
+
+ if basepath is None:
+ raise BootmgmtInterfaceCodingError('basepath must not be None for '
+ 'ODDBootConfig boot configs')
+
+ odd_root = self._boot_config.get_root()
+ tuples = self._write_config_generic(basepath, odd_root)
+
+ # Now add the stage2_eltorito file to the tuples list:
+ try:
+ tmpfile = tempfile.NamedTemporaryFile(dir=basepath, delete=False)
+ tmpfile.close()
+ shutil.copy(odd_root + '/boot/grub/stage2_eltorito',
+ tmpfile.name)
+ except IOError as err:
+ raise BootmgmtConfigWriteError('Error while trying to copy '
+ 'Legacy GRUB El Torito stage2', err)
+
+ tuples += [(BootConfig.OUTPUT_TYPE_BIOS_ELTORITO,
+ tmpfile.name,
+ None,
+ None,
+ None,
+ None,
+ None)]
+
+ return tuples
+
+ # Generic support methods for all BootConfig classes
+
+ def _write_menu_lst(self, outfile):
+ propdict = LegacyGRUBBootLoader.DEFAULT_PROPDICT.copy()
+
+ minmem64 = self._bl_props.get(BootLoader.PROP_MINMEM64, None)
+ if not minmem64 is None:
+ propdict['minmem64'] = 'min_mem64 ' + str(minmem64)
+
+ timeout = self._bl_props.get(BootLoader.PROP_TIMEOUT, None)
+ if timeout is None:
+ timeout = LegacyGRUBBootLoader.DEFAULT_TIMEOUT
+ propdict['timeout_command'] = 'timeout ' + str(timeout)
+
+ hidemenu = self._bl_props.get(BootLoader.PROP_QUIET, None)
+ if hidemenu is True:
+ propdict['hidemenu'] = 'hiddenmenu'
+
+ consprop = self._bl_props.get(BootLoader.PROP_CONSOLE, None)
+ if consprop is None or consprop == BootLoader.PROP_CONSOLE_GFX:
+ propdict['splashimage_command'] = (
+ 'splashimage /boot/grub/splash.xpm.gz')
+ propdict['foreground'] = (
+ '\nforeground ' + LegacyGRUBBootLoader.DEFAULT_FORECOLOR)
+ propdict['background'] = (
+ '\nbackground ' + LegacyGRUBBootLoader.DEFAULT_BACKCOLOR)
+ elif consprop == BootLoader.PROP_CONSOLE_SERIAL:
+ params = self._serial_parameters()
+
+ sercmd = 'serial --unit=%s' % params[BootLoader.PROP_SP_PORT]
+
+ if not params[BootLoader.PROP_SP_SPEED] is None:
+ sercmd += ' --speed=%s' % params[BootLoader.PROP_SP_SPEED]
+ if not params[BootLoader.PROP_SP_DATAB] is None:
+ sercmd += ' --word=%s' % params[BootLoader.PROP_SP_DATAB]
+ if not params[BootLoader.PROP_SP_PARITY] is None:
+ sercmd += ' --parity=%s' % params[BootLoader.PROP_SP_PARITY]
+ if not params[BootLoader.PROP_SP_STOPB] is None:
+ sercmd += ' --stop=%s' % params[BootLoader.PROP_SP_STOPB]
+
+ propdict['serial_command'] = sercmd
+ propdict['terminal_command'] = 'terminal serial'
+
+ # iterate through the list of boot instances in the BootConfig
+ # instance, adding an entry for each one:
+ entries = ''
+ for idx, inst in enumerate(self._boot_config.boot_instances):
+ # XXX - Do validation of kernel against kernel under
+ # self.rootpath (if specified)
+ # XXX - Also: should we verify that a 64-bit kernel is being
+ # used with a 64-bit boot archive and similarly for 32-bit?
+ # XXX - Use the signature attribute
+ if inst.default is True:
+ propdict['default_command'] = 'default ' + str(idx)
+ entries += 'title ' + inst.title + '\n'
+ entries += self._generate_entry(inst)
+ entries += '\n'
+
+ outfile.write(LegacyGRUBBootLoader.MENU_LST_PREAMBLE % propdict)
+ outfile.write(entries)
+
+ # Menu-entry generator infrastructure
+
+ def _generate_entry(self, instance):
+ """Use the BootInstance's class name to find the entry-generator method.
+ Entry generator functions are responsible for producing a string
+ with the rest of the entry (the title is printed by the caller)"""
+
+ instclsname = instance.__class__.__name__
+ method_name = '_generate_entry_' + instclsname
+ entry_generator = self.__class__.__dict__.get(method_name, None)
+ if not entry_generator is None:
+ # It's a method, so self must be passed explicitly
+ return entry_generator(self, instance)
+ else:
+ self._debug('No entry generator (%s) for class %s' %
+ (method_name, instclsname))
+ return ''
+
+ def _generate_entry_generic(self, inst, kargs):
+
+ ostr = ''
+ kargs = '' if kargs is None else kargs
+
+ try:
+ inst.kernel = inst.kernel % {'karch' : "$ISADIR"}
+ except KeyError:
+ # If somehow another Python conversion specifier snuck in,
+ # raise an exception
+ raise BootmgmtMalformedPropertyValueError('kernel', inst.kernel)
+
+ if not inst.kernel.find("$ISADIR") is -1:
+ ostr += 'kernel$ ' + inst.kernel
+ else:
+ ostr += 'kernel ' + inst.kernel
+
+ # kargs already has a leading space from the initialization, above
+ ostr += (' ' if not kargs == '' else '') + kargs + '\n'
+
+ try:
+ inst.boot_archive = inst.boot_archive % {'karch' : "$ISADIR"}
+ except KeyError:
+ # If somehow another Python conversion specifier snuck in,
+ # raise an exception
+ raise BootmgmtMalformedPropertyValueError('boot_archive',
+ inst.boot_archive)
+
+ if not inst.boot_archive.find("$ISADIR") is -1:
+ ostr += 'module$ ' + inst.boot_archive + '\n'
+ else:
+ ostr += 'module ' + inst.boot_archive + '\n'
+
+ return ostr
+
+
+ def _generate_entry_SolarisDiskBootInstance(self, inst):
+ "Menu-entry generator function for SolarisDiskBootInstance instances"
+
+ ostr = ''
+ kargs = ''
+ if inst.fstype == 'zfs':
+ if inst.bootfs is None:
+ raise BootmgmtIncompleteBootConfigError('bootfs property '
+ 'is missing')
+ ostr += 'bootfs ' + inst.bootfs + '\n'
+
+ if inst.kargs is None:
+ kargs = '-B $ZFS-BOOTFS'
+ elif inst.kargs.find('$ZFS-BOOTFS') is -1:
+ # XXX - This is very simplistic and should be revisited
+ kargs = '-B $ZFS-BOOTFS ' + inst.kargs
+ else:
+ kargs = inst.kargs
+ elif not inst.kargs is None and inst.kargs.strip() != '':
+ kargs = inst.kargs
+
+ ostr += self._generate_entry_generic(inst, kargs)
+
+ return ostr
+
+ def _generate_entry_SolarisODDBootInstance(self, inst):
+ return self._generate_entry_generic(inst, inst.kargs)
+
+ def _generate_entry_ChainDiskBootInstance(self, inst):
+ """Menu-entry generator for ChainDiskBootInstance instances. This is a
+ VERY simple use of the rootnoverify and chainloader Legacy GRUB
+ commands. Chainloading is only supported to a specific, numbered
+ drive and physical sector offset/count."""
+
+ if inst.chaininfo is None:
+ raise BootmgmtIncompleteBootConfigError('chaininfo property is '
+ 'missing')
+ if not type(inst.chaininfo) is tuple or len(inst.chaininfo) == 0:
+ raise BootmgmtArgumentError('chaininfo must be a non-zero-length '
+ 'tuple')
+ if not type(inst.chaininfo[0]) is int:
+ raise BootmgmtArgumentError('chaininfo[0] must be an int')
+ if len(inst.chaininfo) > 1 and not type(inst.chaininfo[1]) is int:
+ raise BootmgmtArgumentError('chaininfo[1] must be an int')
+
+ diskstr = '(hd' + str(inst.chaininfo[0])
+
+ if len(inst.chaininfo) > 1:
+ diskstr += ',' + str(inst.chaininfo[1])
+
+ diskstr += ')'
+
+ ostr = 'rootnoverify ' + diskstr + '\n'
+ ostr += 'chainloader '
+ if int(inst.chainstart) != 0:
+ ostr += str(int(inst.chainstart))
+ ostr += '+' + str(int(inst.chaincount)) + '\n'
+ return ostr
+
+ def _prop_validate(self, key, value=None, validate_value=False):
+ if key == BootLoader.PROP_BOOT_TARGS:
+ if validate_value is True and value != 'bios':
+ raise BootmgmtUnsupportedPlatformError(value + ' is not a '
+ 'supported firmware type for ' + self.__class__.__name__)
+ return
+ super(self.__class__, self)._prop_validate(key, value, validate_value)
+
+ # Property-related methods
+
+ def setprop(self, key, value):
+ """Set a Boot Loader property. Raises BootmgmtUnsupportedPropertyError
+ if the property is not supported"""
+
+ self._prop_validate(key, value, True)
+
+ # XXX - Check that the value for each property is well-formed
+ if self._bl_props.get(key, None) != value:
+ self._bl_props[key] = value
+ self.dirty = True
+
+ def getprop(self, key):
+ "Get a Boot Loader property"
+
+ self._prop_validate(key)
+
+ return self._bl_props.get(key, None)
+
+ def delprop(self, key):
+ "Delete a Boot Loader property"
+
+ self._prop_validate(key)
+
+ # Check pseudoproperties:
+ if key == BootLoader.PROP_BOOT_TARGS:
+ raise BootmgmtUnsupportedOperationError("key `%s' may not be "
+ "deleted" % key)
+ try:
+ del self._bl_props[key]
+ self.dirty = True
+ except KeyError as err:
+ raise BootmgmtUnsupportedOperationError("key `%s' does not exist"
+ % key, err)
+
+ # BootLoader installation methods
+
+ def install(self, location):
+
+ if isinstance(location, basestring):
+ try:
+ filemode = os.stat(location).st_mode
+ except OSError as err:
+ raise BootLoaderInstallError('Error stat()ing %s' % location,
+ err)
+
+ if stat.S_ISDIR(filemode):
+ # We have been given an output directory. Produce the menu.lst
+ # there.
+ return self._write_config(location)
+
+ # XXX - Handle other types of boot_class here (copying pxegrub,
+ # stage2_eltorito)
+ elif stat.S_ISCHR(filemode):
+ self._write_loader(location)
+ # Now write the menu.lst:
+ self._write_config(None)
+ else:
+ raise BootmgmtArgumentError('Invalid location argument (%s)'
+ % location)
+ else:
+ for devname in location:
+ try:
+ filemode = os.stat(devname).st_mode
+ except OSError as err:
+ self._debug('Error stat()ing %s' % devname)
+ raise BootLoaderInstallError('Error stat()ing %s' % devname,
+ err)
+ if stat.S_ISCHR(filemode):
+ self._write_loader(devname)
+ else:
+ raise BootmgmtArgumentError('%s is not a characters-special'
+ ' file' % devname)
+
+ self._write_config(None)
+
+ return None
+
+ # _write_loader performs the real guts of boot loader installation
+ def _write_loader(self, devname):
+ """Invoke installgrub to write stage1 and stage2 to disk. If
+ devname is a p0 node, pass -m to installgrub"""
+
+ # Transform the devname if s0 wasn't passed in
+ mbr = False
+ realdev = devname
+
+ if len(devname) > 2 and (devname[-2] == 'p' and devname[-1].isdigit()):
+ mbr = (devname[-1] == '0' or int(devname[-1]) > 4)
+ realdev = devname[:-2] + 's0'
+ elif len(devname) > 3 and (devname[-3] == 'p' and
+ devname[-2].isdigit() and devname[-1].isdigit()):
+ realdev = devname[:-3] + 's0'
+ mbr = True # Extended partition specified, MBR is required
+
+ args = ['/usr/sbin/installgrub']
+
+ if mbr is True:
+ args += ['-m']
+
+ # If a version is present, try to use it during installation.
+ if not self.version is None:
+ args += ['-u', self.version]
+
+ args += [self.rootpath + '/boot/grub/stage1',
+ self.rootpath + '/boot/grub/stage2',
+ realdev]
+
+ self._debug('_write_loader: Invoking command: ' + ' '.join(args))
+
+ try:
+ Popen(args, stdout=Popen.PIPE, stderr=Popen.PIPE)
+ except CalledProcessError as cpe:
+ self._debug('_write_loader: Return code = %d' % cpe.returncode)
+ if cpe.returncode != LegacyGRUBBootLoader.INSTALLGRUB_NOUPDT:
+ output = ''
+ if not cpe.popen is None and not cpe.popen.stderr is None:
+ output = '\nOutput was:\n' + cpe.popen.stderr
+ raise BootLoaderInstallError('installgrub failed for '
+ 'device ' + devname + ': Return code ' +
+ str(cpe.returncode) + output)
+
+ def _get_loader_version(self, devname):
+ """[DEPRECATED AND CURRENTLY UNUSED] Invoke installgrub -ie and
+ return the first line (version string)."""
+
+ args = ['/usr/sbin/installgrub', '-ie', devname]
+ try:
+ proc = Popen(args, stdout=Popen.STORE, stderr=Popen.STORE)
+ version_string = proc.stdout.split('\n')[0]
+ return version_string
+ except CalledProcessError as cpe:
+ if cpe.returncode != LegacyGRUBBootLoader.INSTALLGRUB_NOEINFO:
+ output = ''
+ if not cpe.popen is None and not cpe.popen.stderr is None:
+ output = '\nOutput was:\n' + cpe.popen.stderr
+ self._debug('installgrub version check returned ' +
+ str(cpe.returncode) + '. Ignoring.' + output)
+ return None
+
+#
+# Legacy GRUB menu.lst
+#
+class LegacyGRUBMenuFile(MenuDotLst):
+ def __init__(self, filename='/boot/solaris/menu.lst'):
+ super(LegacyGRUBMenuFile, self).__init__(filename)
+
+ def _analyze_syntax(self):
+ """
+ Command := Keyword Arguments | VarName '=' Value
+ Arguments := Arguments [ \t]+ Argument | Argument
+ Keyword := 'blocklist' | 'boot' | 'bootfs' | 'bootp' |
+ 'cat' | 'chainloader' | 'cmp' | 'color' |
+ 'configfile' | 'debug' | 'default' |
+ 'device' | 'dhcp' | 'displayapm' |
+ 'displaymem' | 'embed' | 'fallback' |
+ 'find' | 'findroot' | 'fstest' | 'geometry' |
+ 'halt' | 'help' | 'hiddenmenu' | 'hide' |
+ 'ifconfig' | 'impsprobe' | 'initrd' |
+ 'install' | 'ioprobe' | 'kernel$' |
+ 'kernel' | 'lock' | 'makeactive' | 'map' |
+ 'md5crypt' | 'min_mem64' | 'module$' |
+ 'module' | 'modulenounzip' | 'pager' |
+ 'partnew' | 'parttype' | 'password' |
+ 'pause' | 'quit' | 'rarp' | 'read' |
+ 'reboot' | 'root' | 'rootnoverify' |
+ 'savedefault' | 'serial' | 'setkey' |
+ 'setup' | 'terminal' | 'terminfo' |
+ 'testload' | 'testvbe' | 'tftpserver' |
+ 'timeout' | 'title' | 'unhide' |
+ 'uppermem' | 'vbeprobe'
+ Argument := [^ \t\n]+
+ VarName := [A-Za-z0-9]+
+ Value := [^\n]+
+ """
+ # XXX - Currently a NOP
+
+
+def bootloader_classes():
+ return [LegacyGRUBBootLoader]
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/menulst.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,259 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+menu.lst parser implementation for pybootmgmt
+"""
+
+import re
+
+class MenuLstError(Exception):
+ def __init__(self, msg):
+ super(LegacyGRUBMenuError, self).__init__()
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+
+class MenuLstCommand(object):
+ """A menu.lst command and its arguments from the menu.lst file"""
+
+ def __init__(self, command, args=None):
+ self._command = command
+ self._args = list(args) if not args is None else []
+
+ def get_command(self):
+ return self._command
+
+ def get_args(self):
+ return self._args
+
+ def __str__(self):
+ if not self._command is None and not self._args is None:
+ return self._command + ' ' + ' '.join(self._args)
+ elif not self._command is None:
+ return self._command
+ else:
+ return ''
+
+ def __repr__(self):
+ ostr = ('(' + repr(self._command) + ',' +
+ repr(self._args) + ')')
+ return ostr
+
+class MenuLstMenuEntry(object):
+ """Representation of a menu.lst menu entry, which consists of a list of
+ MenuLstCommand objects (the first of which must be the 'title' command).
+
+ <instance>._cmdlist = [MenuLstCommand #1, MenuLstCommand #2, ...]
+ """
+
+ def __init__(self, args=None):
+ if args is None:
+ self._cmdlist = []
+ else:
+ self._cmdlist = list(args) # make a copy
+
+ def add_command(self, mlcmd):
+ self._cmdlist.append(mlcmd)
+
+ def add_non_command(self, noncmd):
+ "Add a string (blank line or comment) to the command list"
+ self._cmdlist.append(noncmd)
+
+ def find_command(self, name):
+ for cmd in self._cmdlist:
+ if isinstance(cmd, MenuLstCommand) and name == cmd.get_command():
+ return True
+ return False
+
+ def commands(self):
+ return self._cmdlist
+
+ def __str__(self):
+ ostr = 'MenuLstMenuEntry {\n'
+ for cmd in self._cmdlist:
+ ostr += '\t' + str(cmd).rstrip('\n') + '\n'
+ ostr += '}'
+ return ostr
+
+ def __repr__(self):
+ ostr = '['
+ i = 0
+ if len(self._cmdlist) >= 1:
+ for i in range(len(self._cmdlist) - 1):
+ ostr += repr(self._cmdlist[i]) + ', '
+ if len(self._cmdlist) >= 2:
+ ostr += repr(self._cmdlist[i + 1])
+ ostr += ']'
+ return ostr
+
+
+class MenuDotLst(object):
+
+ def __init__(self, filename):
+ self.target = self
+ self._line = 0
+ self._last = ''
+ self._entitylist = [] # per-instance list of entities
+ self._filename = filename
+ self._parse()
+
+ def entities(self):
+ "Return a list of entities encapsulated by this MenuDotLst"
+ return self._entitylist
+
+ def add_command(self, cmd):
+ "Add a MenuLstCommand to the entitylist"
+ self._entitylist.append(cmd)
+
+ def __str__(self):
+ ostr = ''
+ for entity in self._entitylist:
+ ostr += str(entity) + '\n'
+ return ostr
+
+ def __repr__(self):
+ return (repr(self._entitylist))
+
+ def _parse(self):
+ "Parse the menu.lst file"
+ fileobj = open(self._filename)
+ try:
+ for line in fileobj:
+ self._parse_line(line)
+ self._parse_line(None) # end of file reached
+ finally:
+ fileobj.close()
+
+ self._analyze_syntax()
+
+ def _analyze_syntax(self):
+ "This can be overridden in child classes, if needed"
+ pass
+
+ @staticmethod
+ def _process_escapes(istr):
+ res = ''
+ newline_escaped = False
+ for idx in range(len(istr)):
+ #
+ # Legacy GRUB allows escaping the newline
+ #
+ # We're guaranteed to always have a character
+ # after the backslash, so no try is needed here.
+ if istr[idx] == '\\' and istr[idx + 1] == '\n':
+ newline_escaped = True
+ res += ' '
+ elif istr[idx] != '\n':
+ res += istr[idx]
+ return res, newline_escaped
+
+ def _parse_line(self, nextline):
+ """Parses a line of a menu.lst configuration file.
+ The grammar of the menu.lst configuration file is simple:
+ Comments are lines that begin with '#' (with or without
+ preceeding whitespace), and commands are non-comments that
+ include one or more non-whitespace character sequences,
+ separated by whitespace. The commands are not checked
+ for semantic correctness. They're just stored for later
+ analysis.
+
+ The parser works as follows:
+
+ If the line is None, parsing is complete, so save the last
+ entry processed to the statement list.
+
+ If the line (stripped of any leading whitespace) starts with
+ '#', then it's a comment, so save it and return.
+
+ Split the line into a command portion and an optional
+ argument(s) portion, taking care to process escape sequences
+ (including the special escape of the newline)
+
+ If the line begins with the 'title' keyword, then a new entry
+ is created and saved so that future commands can be added to
+ it.
+
+ If an entry is active (if we're parsing after a title command),
+ add the current command and arguments to the current entry.
+
+ If an entry is not active, add the command and arguments to
+ the statement list.
+
+ The configuration file entity list contains commands and
+ entries::
+
+ [(MenuLstCommand|CommentString)*, (MenuLstMenuEntry|CommentString)*]
+
+ Comments and blank space is stored as a plain string.
+ """
+
+ if nextline is None:
+ # If there's still text in the last-line buffer,
+ # we must have had a dangling backslash
+ if self._last != '':
+ raise MenuLstError('Dangling backslash detected')
+ return
+
+ self._line += 1
+
+ # Remove the comment portion of the line
+ stripped = nextline.strip()
+ if stripped == '' or stripped[0] == '#':
+ self.target.add_non_command(nextline)
+ return
+
+ # Remove escape sequences from the line:
+ try:
+ nextline, newline_escaped = (
+ self._process_escapes(nextline))
+ except IndexError:
+ # The error must have been a dangling backslash
+ raise MenuLstError('Dangling backslash detected')
+
+ # If the newline was escaped, save the string for later
+ if newline_escaped:
+ self._last += nextline
+ return # Wait for the next line
+ else:
+ nextline = self._last + nextline
+ self._last = ''
+
+ pattern = (r'([^ \t"]+"([^\\"]|\\.)*"[^ \t]*|'
+ r'([^ \t\\"]|\\.)*"[^"]*(?![^"]*")|'
+ # r'"([^ \t\\"]|\\.)*(?![^"]*[^\\]")|'
+ r'[^ "\t]+)')
+ argv = [v[0] for v in re.compile(pattern).findall(nextline)]
+
+ if (len(argv) > 0):
+ if argv[0] == 'title':
+ self.target = MenuLstMenuEntry()
+ self._entitylist.append(self.target)
+ # Check argv[0] (if it exists) for an equals sign, since
+ # that's a valid way to set certain variables
+ argv = argv[0].split('=', 1) + argv[1:]
+
+ self.target.add_command(MenuLstCommand(argv[0], argv[1:]))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/sbb.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,56 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Loader backend for the SPARC Boot Block (SBB)
+"""
+
+from ...bootloader import BootLoader
+
+class OBPBootLoader(BootLoader):
+ pass
+
+class WanbootBootLoader(OBPBootLoader):
+ @staticmethod
+ def probe(**kwargs):
+ return (None, None)
+
+class HSFSBootLoader(OBPBootLoader):
+ @staticmethod
+ def probe(**kwargs):
+ return (None, None)
+
+class UFSBootLoader(OBPBootLoader):
+ @staticmethod
+ def probe(**kwargs):
+ return (None, None)
+
+class ZFSBootLoader(OBPBootLoader):
+ @staticmethod
+ def probe(**kwargs):
+ return (None, None)
+
+def bootloader_classes():
+ return [WanbootBootLoader, HSFSBootLoader, UFSBootLoader, ZFSBootLoader]
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootarchive.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,33 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+* Creation and manipulation of the Solaris boot archive
+
+* Manipulation of the Solaris Boot Archive cache, designed to speed up
+archive maintainence operations by caching the contents of the boot
+archive (which consists of compressed objects on x86), allowing faster
+update of an archive when changed objects are detected
+"""
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootconfig.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,797 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Abstractions for boot configuration management. A BootConfig object aggregates
+the boot configuration for a system. Implementations are provided for active
+systems (systems that boot from disk devices) as well as for installation image
+creation (i.e. for network-based installation images, optical disc installation
+images, and USB-based installation images).
+BootConfig provides access, through abstractions, to the system boot loader,
+general system boot configuration variables, and the set of bootable instances
+that the boot loader provides a means to boot.
+"""
+
+import collections
+import gettext
+import unittest
+
+from . import BootmgmtNotSupportedError, BootmgmtArgumentError
+from . import BootmgmtMissingInfoError
+from . import bootinfo, bootloader
+from .bootutil import LoggerMixin, get_current_arch_string
+
+_ = gettext.translation("SUNW_OST_OSCMD", "/usr/lib/locale",
+ fallback=True).gettext
+
+class BootConfig(LoggerMixin):
+ """Abstract base class for boot configuration classes"""
+
+ # Valid flags to pass to __init__:
+ (BCF_CREATE,
+ BCF_ONESHOT,
+ BCF_AUTOGEN,
+ BCF_MIGRATE) = range(4)
+
+ BOOT_CLASS_DISK = 'disk'
+ BOOT_CLASS_NET = 'net'
+ BOOT_CLASS_ODD = 'odd'
+
+ TOKEN_SYSTEMROOT = 'systemroot'
+
+ OUTPUT_TYPE_FILE = 'file'
+ OUTPUT_TYPE_BIOS_ELTORITO = 'bios-eltorito-image'
+
+ # Tuple indices for each tuple in the list output by the install() method
+ (IDX_FILETYPE,
+ IDX_TEMPNAME,
+ IDX_INSTANCE,
+ IDX_DESTNAME,
+ IDX_USER,
+ IDX_GROUP,
+ IDX_MODE) = range(7)
+
+ @property
+ def dirty(self):
+ return self._dirty
+
+ @dirty.setter
+ def dirty(self, val):
+ if not self._dirty and val is True:
+ self._debug('dirty set')
+ self._dirty = True
+ else:
+ self._dirty = val
+
+ def __init__(self, flags, **kwargs):
+ """
+ Initialize the basic set of attributes for all BootConfig classes
+ -----------------------------------------------------------------
+ Argument| Valid Value(s)
+ --------+--------------------------------------------------------
+ flags | <Tuple of flags that modify the behavior of this
+ | BootConfig object> [tuple]
+ |
+ | Allowed values in the tuple:
+ | ----------------------------
+ | BootConfig.BCF_CREATE: Create a new boot
+ | configuration. If one already
+ | exists, it will be discarded.
+ | If this flag is not present and
+ | no boot configuration exists,
+ | an exception
+ | (BootmgmtConfigurationReadError)
+ | will be raised.
+ | BootConfig.BCF_ONESHOT:If True, only the final boot
+ | configuration files will be
+ | created when the
+ | commit_boot_config() is called.
+ | This is useful for one-shot
+ | creation of boot configurations
+ | for installation media.
+ | BootConfig.BCF_AUTOGEN:The set of boot instances will be
+ | automatically generated by
+ | scanning the system. The list of
+ | boot instances found by the scan
+ | will be available after
+ | the BootConfig object is created.
+ | This set of boot instances can
+ | then be tailored before calling
+ | commit_boot_config(). Note that
+ | the set of BootInstance objects
+ | added to this object's boot_instances
+ | are only guaranteed to include
+ | Solaris boot instances.
+ | BootConfig.BCF_MIGRATE:If supported, the existing boot
+ | configuration will be migrated
+ | from an older form to a newer
+ | form (i.e. conversion from legacy
+ | GRUB's menu.lst to GRUB2's
+ | configuration format). The conversion
+ | is performed at commit_boot_config()
+ | time. Cannot be used with the
+ | BCF_CREATE flag.
+ |
+ platform| [optional] The target system architecture / firmware for
+ (keyword| which this boot configuration will be written. Useful
+ arg) | for the creation of boot configuration files for network
+ | boot instances, or when a boot configuration is created
+ | on a system of a different architecture / firmware than
+ | the target. If this is not supplied, the assumption is
+ | that the boot configuration will be for the current
+ | system. Supported tuple values are:
+ | { ('sparc', 'obp' (or None)),
+ | ('x86', 'bios' (or None)),
+ | ('x86', 'uefi64') }
+ | If a particular BootConfig subclass does not support
+ | manipulation of a different platform's boot
+ | configuration, a BootmgmtNotSupportedError will be
+ | raised.
+ -----------------------------------------------------------------
+ """
+
+ self.boot_instances = []
+ self.boot_class = kwargs.get('boot_class', None)
+ self.boot_fstype = kwargs.get('boot_fstype', None)
+ self._platform = kwargs.get('platform', None)
+ self._dirty = False
+ self._flags = flags
+ self.boot_loader = self._get_boot_loader(**kwargs)
+
+ if (BootConfig.BCF_CREATE in self._flags and
+ BootConfig.BCF_MIGRATE in self._flags):
+ raise BootmgmtUnsupportedOperationError(
+ 'Migration cannot be combined with creation')
+
+ if BootConfig.BCF_CREATE in self._flags:
+ self._new_boot_config(**kwargs)
+ else:
+ # _load_boot_config raises BootmgmtConfigurationReadError
+ self._load_boot_config(**kwargs)
+
+ if BootConfig.BCF_AUTOGEN in self._flags:
+ self._autogenerate_config(**kwargs)
+
+ def _autogenerate_config(self, **kwargs):
+ from .backend.autogen import BootInstanceAutogenFactory
+ inst_list = BootInstanceAutogenFactory.autogen(self)
+ if len(inst_list) > 0:
+ self.boot_instances += inst_list
+ self.dirty = True
+
+ def _load_boot_config(self, **kwargs):
+ """[Implemented by child classes] Loads the boot configuration (with
+ guidance from kwargs)."""
+ pass
+
+ def _new_boot_config(self, **kwargs):
+ """[Implemented by child classes] Initializes this instance with
+ a new boot configuration (with guidance from kwargs)."""
+ pass
+
+ def _get_boot_loader(self, **kwargs):
+ """Initializes this instance's boot_loader"""
+ loader = bootloader.BootLoader.get(bootconfig=self)
+ self._debug('loader = ' + str(loader))
+ return loader
+
+ def get_root(self):
+ "Return the root directory where this BootConfig is be stored"
+ return None
+
+ def add_boot_instance(self, boot_instances, where=-1):
+ """
+ Adds one or more new boot instances to this boot configuration.
+ boot_instance is a list of BootInstance references or a single
+ BootInstance reference.
+ The where argument determines where in the ordered list of boot
+ instances this boot instance entry will be placed. The default
+ is -1, which means the end of the list of boot instances. If
+ where is a callable object, it will be used to iterate through
+ the boot instances list. If it returns a negative value, the
+ boot_instance will be inserted BEFORE that item; If it returns
+ a positive value, the boot_instance will be inserted AFTER that
+ item; If it returns 0, no insertion will occur at that point
+ in the iteration. The arguments to the function are
+ (<current_boot_instance_in_the_iteration>, boot_instance).
+ """
+
+ if isinstance(boot_instances, collections.Iterable):
+ for instance in boot_instances:
+ self._add_one_boot_instance(instance, where)
+ # If where is not -1, make sure we increment it so that
+ # subsequent boot instances are placed in the boot_instances
+ # array in the proper order
+ if where > -1:
+ where = where + 1
+ else:
+ self._add_one_boot_instance(boot_instances, where)
+
+
+ def _add_one_boot_instance(self, boot_instance, where=-1):
+
+ prevDefault = None
+
+ if boot_instance.default is True:
+ # Find previous default (if any)
+ defaults = [x for x in self.boot_instances
+ if x.default is True]
+ if len(defaults) > 0:
+ # XXX - assert here that len(defaults) is 1?
+ prevDefault = defaults[0]
+ self._debug("Previous default was:\n%s" % str(prevDefault))
+
+ if type(where) is int:
+ # where is an index if it's > 0
+ where = where if where >= 0 else len(self.boot_instances)
+ self.boot_instances[where:where] = [boot_instance]
+ boot_instance._bootconfig = self
+ self.dirty = True
+ elif callable(where):
+ for index, inst in enumerate(self.boot_instances):
+ whereval = where(inst, boot_instance)
+ if whereval < 0:
+ self.boot_instances[index:index] = [boot_instance]
+ boot_instance._bootconfig = self
+ self.dirty = True
+ break
+ elif whereval > 0:
+ self.boot_instances[index + 1: index + 1] = [boot_instance]
+ boot_instance._bootconfig = self
+ self.dirty = True
+ break
+ else:
+ raise BootmgmtArgumentError('The where parameter is malformed')
+
+ if not prevDefault is None:
+ prevDefault.default = False
+
+ def delete_boot_instance(self, filter_func, all=True):
+ """
+ Deletes one or more boot instances from the boot configuration.
+ filter_func is a function that takes a single argument (a
+ BootInstance). If it returns True, that BootInstance is removed
+ from the boot configuration.
+ The 'all' parameter is True if all matching boot instances are
+ to be deleted. If False, only the first instance for which
+ filter_func returns True is deleted.
+ For example:
+ delete_boot_instance(lambda x: x.title() == 'snv_158')
+ will delete all boot instances whose titles match 'snv_158'.
+ """
+ if all is True:
+ oldlen = len(self.boot_instances)
+ self.boot_instances = filter(lambda x: not filter_func(x),
+ self.boot_instances)
+ if oldlen != len(self.boot_instances):
+ self.dirty = True
+ else:
+ for idx, inst in enumerate(self.boot_instances):
+ if filter_func(inst) is True:
+ del self.boot_instances[idx]
+ self.dirty = True
+ return
+
+ def modify_boot_instance(self, filter_func, mod_func):
+ """
+ Applies mod_func to all boot instances for which filter_func
+ returns True.
+ This is shorthand for:
+
+ for bi in filter(filter_func, bootconfig_obj.boot_instances):
+ mod_func(bi)
+
+ (The (single) argument to filter_func and mod_func is a
+ BootInstance ref).
+ """
+ for inst in filter(filter_func, self.boot_instances):
+ mod_func(inst)
+
+ def commit_boot_config(self, temp_dir=None, boot_devices=None):
+ """Writes the boot configuration (including boot instances and boot
+ loader settings) to stable storage.
+
+ If this object was created with the BootConfig.BC_ONESHOT flag,
+ then only the final boot configuration file(s) will be created
+ (i.e. the Legacy GRUB menu.lst, GRUB2 configuration file, or
+ SPARC menu.lst)-- any other state files that store boot
+ configuration information will not be written (customizations to
+ this boot configuration may not persist). This may prevent
+ incremental modifications to the boot configuration from
+ being possible (depending on the boot loader in use). BC_ONESHOT
+ should only be used when creating a boot configuration that is
+ not intended to change (i.e. when creating a boot configuration
+ for use with an install image, not a running system).
+
+ boot_devices is a list of strings, each of which is the path
+ to a character-special device where the boot loader should be
+ installed. This argument should be omitted when the consumer
+ desires to write the boot configuration files to a temporary
+ directory.
+
+ If temp_dir is not None, then the set of files that constitute
+ the boot configuration is written to a temporary directory (and
+ not to the official location(s)). (temp_dir must exist or an
+ IOError will be raised). When files are written to temp_dir,
+ commit_boot_config() returns a list of 7-tuples of the following
+ form:
+ (<type>, <srcpath>, <object>, <destpath>, <uid>, <gid>, <mode>)
+ Each of these tuples describes a boot configuration file that
+ was written, along with the required system-relative path where
+ it must be copied. This enables a consumer to install the
+ file(s) into the correct place(s) on a system or install image
+ without having to hard-code knowledge of the filenames)).
+ For example:
+ [('file', '/tmp/bc.Jzx1cZa/boot/solaris/bootenv.rc',
+ <bootmgmt.bootconfig.SolarisDiskBootInstance object at ...>,
+ '%(systemroot)s/boot/solaris/bootenv.rc', 'root', 'sys', 0644)]
+
+ The <type> value in the tuple identifies the type of file in
+ the tuple. This is useful for conveying platform-specific
+ attributes of a particular file. For example, <type> could be
+ 'eltorito' to identify an eltorito boot image, which a consumer
+ would recognize and then use to supply the argument to
+ `mkisofs -b`. See child class definitions for additional <type>
+ values. Only <type>='file' is defined at the BootConfig level.
+
+ The <object> element, if not None, is a reference to a
+ BootInstance object. It provides clarification for tokens that
+ are embedded in the destination path. In the above example, the
+ `systemroot' token refers to the root path of the Solaris BE
+ identified by the SolarisDiskBootInstance object in the tuple.
+ In this example, including the object allows the consumer to
+ resolve the root path location.
+
+ Note the use of Python string-formatting tokens, for which a
+ consumer must supply definitions. The following list of tokens
+ are defined at the BootConfig level; child classes may define
+ additional tokens:
+ ----------------------------------------------------------------
+ Token Name | Meaning
+ ------------------+---------------------------------------------
+ systemroot | The path to a boot instance's mounted root
+ | filesystem. [string]
+ ----------------------------------------------------------------
+ """
+ if temp_dir is None:
+ if self.dirty is True:
+ for inst in self.boot_instances:
+ if (not inst.boot_vars is None and
+ inst.boot_vars.dirty is True):
+ inst.boot_vars.write(inst=inst)
+
+ if self.dirty is True or self.boot_loader.dirty is True:
+ self.boot_loader.install(boot_devices)
+
+ return None
+ else:
+ tuple_list = []
+ if self.dirty is True:
+ for inst in self.boot_instances:
+ if (not inst.boot_vars is None and
+ inst.boot_vars.dirty is True):
+ tuple_list.append(inst.boot_vars.write(inst, temp_dir))
+
+ if self.dirty is True or self.boot_loader.dirty is True:
+ tuple_list.append(self.boot_loader.install(temp_dir))
+
+ return tuple_list
+
+
+ def __str__(self):
+ s = 'State: ' + ('dirty' if self.dirty else 'clean') + '\n'
+ s += 'Class: ' + (self.boot_class
+ if not self.boot_class is None else 'none') + '\n'
+ s += 'FSType: ' + ((self.boot_fstype
+ if not self.boot_fstype is None else 'unknown') +
+ '\n')
+ s += 'Boot instances: ' + str(len(self.boot_instances)) + '\n'
+ for idx, inst in enumerate(self.boot_instances):
+ s += '===[ Instance ' + str(idx) + ' ]===\n'
+ s += '\t' + str(inst).replace('\n', '\n\t') + '\n'
+ s += '===[ End Boot Instances ]===\n'
+ if not self.boot_loader is None:
+ s += 'Boot loader:\n'
+ s += str(self.boot_loader) + '\n'
+ else:
+ s += 'No boot loader'
+ return s
+
+
+class DiskBootConfig(BootConfig):
+ """A class for managing the boot configuration stored on hard disk-like
+ storage devices. Handles Solaris boot configurations stored in ZFS root
+ pools and on devices with a UFS root filesystem"""
+
+ # Key names for keyword arguments to the constructor
+ ARG_ZFS_RPNAME = 'rpname' # ZFS root pool name
+ ARG_ZFS_TLDPATH = 'tldpath' # ZFS top-level dataset mounted path
+ ARG_ZFS_SYSROOT_PATH = 'zfspath' # ZFS system root mounted path
+ ARG_UFS_ROOT = 'ufsroot' # UFS root mounted path
+
+ # Tokens returned from commit_boot_config()
+ TOKEN_ZFS_RPOOL_TOP_DATASET = 'rpool_top_dataset'
+
+ def __init__(self, flags, **kwargs):
+ # DiskBootConfig does not support platforms other than native
+ platform = kwargs.get('platform', None)
+ if not platform is None:
+ raise BootmgmtNotSupportedError(self.__class__.__name__ + ' does '
+ 'not support cross-platform operations')
+
+ fstype = None
+
+ if (DiskBootConfig.ARG_ZFS_RPNAME in kwargs and
+ DiskBootConfig.ARG_ZFS_TLDPATH in kwargs and
+ DiskBootConfig.ARG_ZFS_SYSROOT_PATH in kwargs):
+ fstype = 'zfs'
+ self.sysroot = kwargs[DiskBootConfig.ARG_ZFS_SYSROOT_PATH]
+ self.zfstop = kwargs[DiskBootConfig.ARG_ZFS_TLDPATH]
+ self.zfsrp = kwargs[DiskBootConfig.ARG_ZFS_RPNAME]
+ elif DiskBootConfig.ARG_UFS_ROOT in kwargs:
+ fstype = 'ufs'
+ self.sysroot = kwargs[DiskBootConfig.ARG_UFS_ROOT]
+
+ if fstype is None:
+ raise BootmgmtNotSupportedError('The filesystem type supplied '
+ 'to the DiskBootConfig constructor was not recognized')
+
+ super(self.__class__, self).__init__(flags,
+ boot_class=BootConfig.BOOT_CLASS_DISK,
+ boot_fstype=fstype, **kwargs)
+ def get_root(self):
+ return self.sysroot
+
+ def _load_boot_config(self, **kwargs):
+ """Loads the boot configuration"""
+ self.boot_loader.load_config()
+
+ def _new_boot_config(self, **kwargs):
+ """Initializes this instance with a new boot configuration"""
+ self.boot_loader.new_config()
+
+
+class ODDBootConfig(BootConfig):
+ """A class for managing the boot configuration stored on optical disk
+ storage devices. Handles Solaris boot configurations stored on DVD
+ media"""
+
+ def __init__(self, flags, **kwargs):
+ ""
+ # Weed out unsupported flags:
+ if BootConfig.BCF_MIGRATE in flags:
+ raise BootmgmtUnsupportedOperationError(self.__class__.__name__ +
+ ': Migration is not supported')
+
+ # Save the image's root directory:
+ self.odd_image_root = kwargs.get('oddimage_root', None)
+ if self.odd_image_root is None:
+ raise BootmgmtArgumentError('Missing oddimage_root argument')
+
+ super(self.__class__, self).__init__(flags,
+ boot_class=BootConfig.BOOT_CLASS_ODD,
+ **kwargs)
+
+ def get_root(self):
+ return self.odd_image_root
+
+
+
+###############################################################################
+
+
+class BootInstance(LoggerMixin):
+ """BootInstance is the core abstraction for a bootable instance of an
+ operating system. BootConfig objects aggregate zero or more
+ BootInstance objects. A BootInstance should always be associated
+ with a BootConfig object (so that if a consumer modifies a
+ BootInstance, the BootConfig is notified that its state has
+ changed so it can write updated configuration information at
+ BootConfig.commit_boot_config()-time). This association is made
+ when a BootInstance is passed to BootConfig.add_boot_instance()."""
+
+ # Valid attributes ('default' is a special case)
+ _attributes = { 'title' : None }
+
+ def __init__(self, rootpath, **kwargs):
+ # If the child class added its own set of attributes, just append to
+ # it; overwise, set it to the default set from this class
+ (self.__dict__.setdefault('_attributes', {})
+ .update(BootInstance._attributes))
+
+ self._bootconfig = None
+ self._default = False
+ self.boot_vars = None
+ self.rootpath = None
+
+ # Only add attributes to this instance if they were not overridden
+ # by the caller's keyword arguments
+ for key, value in self._attributes.items():
+ # If the key wasn't already set in this instance (in
+ # init_from_rootpath), init it to a default value here
+ if not key in kwargs and not key in self.__dict__:
+ self._debug('DEFAULT: Setting %s="%s"' %
+ (str(key), str(value)))
+ self.__setattr__(key, value)
+
+ # If the user passed in keyword args, add them as attributes
+ for key, value in kwargs.items():
+ # If the key wasn't already set in this instance (in
+ # init_from_rootpath), init it to a default value here
+ if key in self.__dict__:
+ continue
+ self._debug('KWARGS: Setting %s="%s"' % (str(key), str(value)))
+ self.__setattr__(key, value)
+
+ # init_from_rootpath must be called after all instance variables
+ # have been set
+ if not rootpath is None:
+ self.init_from_rootpath(rootpath)
+
+ @property
+ def default(self):
+ return self._default
+
+ def init_from_rootpath(self, rootpath):
+ """Initialize the boot instance with information from the root path
+ given."""
+ self.rootpath = rootpath
+ self.boot_vars = self._get_boot_vars(rootpath)
+
+ def _set_default(self, value):
+ """By setting this BootInstance to be the default, the previous default
+ must be disabled. This property setter function does just that by
+ using the _bootconfig reference to find the list of boot instances,
+ locating the previous default, and setting it to False. Note that
+ only setting the default to True triggers this search (otherwise, we
+ could end up in an infinite recursive loop)."""
+
+ if not value is False and not value is True:
+ raise BootmgmtArgumentError('default must be either True or False')
+
+ if self._default == value: # Nothing to do
+ return
+
+ if value is False:
+ self._default = False
+ else:
+ self._default = True
+
+ if not self._bootconfig is None:
+ for inst in self._bootconfig.boot_instances:
+ if not inst is self and inst.default is True:
+ self._debug('Found previous default -- clearing it')
+ # The following assignment's side-effect marks the
+ # BootConfig dirty
+ inst.default = False
+ return
+
+ if not self._bootconfig is None:
+ # If we got here, then either the value is False or it was True
+ # and no previous default was found, so mark the BootConfig dirty
+ self._bootconfig.dirty = True
+
+ def _get_boot_vars(self, rootpath):
+ """Initializes this instance's boot_vars"""
+ return bootinfo.BootVariables.get(sysroot=rootpath)
+
+ def __setattr__(self, key, value):
+ """Intercept the set attribute method to enforce setting of a particular
+ set of boot instance attributes (the union of those defined in child
+ classes and this class). 'default' is treated specially (see
+ _set_default())"""
+
+ if key == 'default':
+ self._debug('key="%s" value="%s"' % (key, value))
+ return self._set_default(value)
+
+ self.__dict__[key] = value
+ if key in self._attributes and not self._bootconfig is None:
+ self._debug('key="%s" value="%s"' % (key, value))
+ if not self._bootconfig is None:
+ self._bootconfig.dirty = True
+
+ def __delattr__(self, key):
+ """Delete an attribute from this BootInstance. 'default' is treated
+ specially, as with __setattr__."""
+
+ if key == 'default':
+ self._debug('delete key="default"')
+ self._set_default(False) # Same effect as setting it to False
+ return
+
+ if not key in self.__dict__:
+ raise AttributeError(str(key))
+
+ del self.__dict__[key]
+
+ if key in self._attributes and not self._bootconfig is None:
+ self._debug('delete key="%s"' % key)
+ if not self._bootconfig is None:
+ self._bootconfig.dirty = True
+
+ def __str__(self):
+ s = ''
+ if self.default is True: # only print default if it's True
+ s += 'default = True\n'
+ for item in self._attributes.keys():
+ s += str(item) + ' = '
+ s += str(self.__dict__.get(item, '<Not Defined>')) + '\n'
+ if not self.boot_vars is None:
+ s += '===[ %d Boot variables ]===\n' % len(self.boot_vars)
+ s += '\n'.join(map(str, self.boot_vars))
+ s += '\n===[ End Boot Variables ]==='
+ return s
+
+
+class ChainDiskBootInstance(BootInstance):
+ """A boot instance of a chainloaded operating system"""
+
+ _attributes = { 'chaininfo' : None,
+ 'chainstart' : '0',
+ 'chaincount' : '1' }
+
+ def __init__(self, rootpath=None, **kwargs):
+ """rootpath is not supported, so should remain `None'"""
+ (self.__dict__.setdefault('_attributes', {})
+ .update(ChainDiskBootInstance._attributes))
+ super(ChainDiskBootInstance, self).__init__(None, **kwargs)
+
+
+class SolarisBootInstance(BootInstance):
+ """Abstraction for a Solaris Boot Instance. Supported attributes are:
+ - kernel [string] [optional]
+ - boot_archive [string] [optional]
+ - kargs [string] [optional]: Kernel argument string
+ - signature [string] [optional]: The "boot signature" of this
+ boot instance.
+ """
+
+ if get_current_arch_string() == 'x86':
+ _attributes = {
+ 'kernel' : '/platform/i86pc/kernel/%(karch)s/unix',
+ 'boot_archive' : '/platform/i86pc/%(karch)s/boot_archive',
+ 'kargs' : None,
+ 'signature' : None,
+ }
+ else:
+ _attributes = {}
+
+
+ def __init__(self, rootpath, **kwargs):
+ # If the child class added its own set of attributes, just append to
+ # it; overwise, set it to the default set from this class
+ (self.__dict__.setdefault('_attributes', {})
+ .update(SolarisBootInstance._attributes))
+
+ super(SolarisBootInstance, self).__init__(rootpath, **kwargs)
+
+
+class SolarisDiskBootInstance(SolarisBootInstance):
+ """Abstraction for a Disk-based Solaris Boot Instance. Additional
+ attributes supported are:
+ - fstype [string] [required]: One of: [ 'ufs', 'zfs' ]
+ - If fstype == 'zfs':
+ * bootfs [string] [required]
+ """
+ _attributes = { 'fstype' : 'zfs',
+ 'bootfs' : None }
+
+ def __init__(self, rootpath, **kwargs):
+ # If the child class added its own set of attributes, just append to
+ # it; overwise, set it to the default set from this class
+ (self.__dict__.setdefault('_attributes', {})
+ .update(SolarisDiskBootInstance._attributes))
+
+ super(SolarisDiskBootInstance, self).__init__(rootpath, **kwargs)
+
+ if self.fstype == 'zfs':
+ if not 'bootfs' in kwargs:
+ raise BootmgmtMissingInfoError('missing bootfs arg')
+ # Make sure bootfs appears to be well-formed
+ bootfs_spec = kwargs['bootfs'].split('/', 2)
+ if len(bootfs_spec) != 3 or bootfs_spec[1] != 'ROOT':
+ raise BootmgmtArgumentError('Invalid bootfs: %s' %
+ kwargs['bootfs'])
+
+ # If title is STILL None, try an alternate (the last component of the
+ # bootfs):
+ try:
+ self.title = self.bootfs.split('/', 2)[2]
+ except:
+ pass
+
+ def init_from_rootpath(self, rootpath):
+ # Invoke the parent's init_from_rootpath first
+ super(SolarisBootInstance, self).init_from_rootpath(rootpath)
+
+ # self.title is guaranteed to have been initialized to something
+ if not self.title is None:
+ return # No need to get the title from /etc/release
+
+ try:
+ alt_title = self.bootfs.split('/', 2)[2]
+ except:
+ alt_title = None
+
+ self.title = solaris_release_name(rootpath, alt_title)
+
+
+class SolarisNetBootInstance(SolarisBootInstance):
+ """Abstraction for a Network-based Solaris Boot Instance.
+ """
+ def __init__(self, rootpath, **kwargs):
+ super(SolarisNetBootInstance, self).__init__(rootpath, **kwargs)
+
+
+class SolarisODDBootInstance(SolarisBootInstance):
+ """Abstraction for an optical-disc-based Solaris Boot Instance
+ """
+ def __init__(self, rootpath, **kwargs):
+ super(SolarisODDBootInstance, self).__init__(rootpath, **kwargs)
+
+ def init_from_rootpath(self, rootpath):
+ # Invoke the parent's init_from_rootpath first
+ super(SolarisBootInstance, self).init_from_rootpath(rootpath)
+
+ # self.title is guaranteed to have been initialized to something
+ if not self.title is None:
+ return
+
+ self.title = solaris_release_name(rootpath, None)
+
+
+def solaris_release_name(rootpath, alt_title):
+ # On a disk-based instance, the title can be derived from the
+ # first line of the /etc/release file, if it exists
+ title = alt_title if not alt_title is None else 'Oracle Solaris'
+ try:
+ with open(rootpath + '/etc/release') as etcrelease:
+ title = etcrelease.readline().strip()
+ except IOError:
+ pass
+
+ return title
+
+
+###############################################################################
+#################################### TESTS ##################################
+###############################################################################
+
+class TestBootConfig(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ print 'Done with ' + str(self)
+
+ def test_flags(self):
+ pass
+
+def testSuite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestBootConfig)
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootinfo.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,97 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various classes that provide access to system- and boot-related information
+"""
+
+import logging
+
+from .pysol import devfs_bootdev_get_list
+from . import BootmgmtUnsupportedOperationError
+
+logger = logging.getLogger('bootmgmt')
+
+class SystemFirmware(object):
+ PROP_BOOT_DEVICE = 'boot-device'
+
+ @staticmethod
+ def get(firmware_name=None):
+ from .backend.fw import BackendFWFactory
+ return BackendFWFactory.get(firmware_name)
+
+ def __init__(self, fw_name=None):
+ self._fw_name = fw_name
+
+ @property
+ def fw_name(self):
+ return self._fw_name
+
+ def getprop(self, propname):
+ if not propname == SystemFirmware.PROP_BOOT_DEVICE:
+ raise BootmgmtUnsupportedOperationError('Property not supported')
+ # libdevinfo's devfs_bootdev_get_list returns an ordered list of
+ # boot devices. The form of the return value is a tuple of tuples:
+ # ((<physpath>, (<logicalpath1>, <logicalpath2>, ...), ...)
+ if propname == SystemFirmware.PROP_BOOT_DEVICE:
+ return devfs_bootdev_get_list()
+
+ def setprop(self, propname, value): pass
+ def delprop(self, propname): pass
+
+###############################################################################
+
+class BootVariables(object):
+ @staticmethod
+ def get(sysroot=None, arch=None, osname='solaris'):
+ if sysroot is None:
+ return None
+ from .backend.bootvars import BackendBootVarsFactory
+ return BackendBootVarsFactory.get(sysroot, arch, osname)
+
+ def __init__(self, sysroot=None):
+ self.dirty = False
+ self._sysroot = sysroot
+ self._vardict = {}
+ if not self._sysroot is None:
+ self._read()
+
+ def _read(self):
+ pass
+
+ def load(self, sysroot):
+ self._sysroot = sysroot
+ self._read()
+
+ def write(self, inst, alt_dir=None):
+ return None
+
+ def getprop(self, propname):
+ return None
+
+ def setprop(self, propname, value):
+ pass
+
+ def delprop(self, propname):
+ pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootloader.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,182 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Boot loader abstraction for pybootmgmt
+"""
+
+from .backend.loader import BackendBootLoaderFactory
+from .bootinfo import SystemFirmware
+from .bootutil import LoggerMixin
+from . import BootmgmtError, BootmgmtNotSupportedError
+from . import BootmgmtUnsupportedPropertyError
+
+class BootLoader(LoggerMixin):
+
+ PROP_MINMEM64 = 'minmem64'
+ PROP_TIMEOUT = 'timeout'
+
+ PROP_CONSOLE = 'console'
+ PROP_CONSOLE_TEXT = 'text'
+ PROP_CONSOLE_GFX = 'graphics'
+ PROP_CONSOLE_SERIAL = 'serial'
+
+ PROP_QUIET = 'quiet'
+
+ PROP_BOOT_TARGS = 'boot-targets'
+
+ # When console=serial, this property should be set:
+ PROP_SERIAL_PARAMS = 'serial_params'
+ # Indices for the serial_params tuple:
+ PROP_SP_PORT = 0
+ PROP_SP_SPEED = 1
+ PROP_SP_DATAB = 2
+ PROP_SP_PARITY = 3
+ PROP_SP_STOPB = 4
+ PROP_SP_FLOWC = 5
+
+
+ @staticmethod
+ def get(**kwargs):
+ return BackendBootLoaderFactory.get(**kwargs)
+
+ @classmethod
+ def probe(cls, **kwargs):
+ return (None, None)
+
+ @property
+ def dirty(self):
+ return self._dirty
+
+ @dirty.setter
+ def dirty(self, value):
+ if not type(value) is bool:
+ raise ValueError('dirty must be a bool')
+ if value != self._dirty:
+ self._debug('dirty => ' + str(value))
+ self._dirty = value
+
+ def __init__(self, **kwargs):
+ self._dirty = False
+ self.old_loader = None
+ self.devices = kwargs.get('devices', None)
+ self.firmware = SystemFirmware.get(kwargs.get('fwtype', None))
+ self.version = None
+ self._boot_config = kwargs.get('bootconfig', None)
+ self._bl_props = {}
+
+ # If there is a root dir defined in the bootconfig passed in,
+ # and no rootpath, set rootpath to the bootconfig's root.
+ bc_sysroot = '/'
+ if (not self._boot_config is None and
+ not self._boot_config.get_root() is None):
+ bc_sysroot = self._boot_config.get_root()
+
+ self.rootpath = kwargs.get('rootpath', bc_sysroot)
+
+
+ def _write_config(self, basepath):
+ pass
+
+ def load_config(self):
+ pass
+
+ def new_config(self):
+ pass
+
+ def migrate_config(self):
+ pass
+
+ # Property-related methods
+ def _prop_validate(self, key, value=None, validate_value=False):
+ props = getattr(self, 'SUPPORTED_PROPS', [])
+ if not key in props:
+ raise BootmgmtUnsupportedPropertyError(key + ' is not a supported '
+ 'property')
+
+ def setprop(self, key, value):
+ pass
+
+ def getprop(self, key):
+ pass
+
+ def delprop(self, key):
+ pass
+
+ def install(self, location):
+ """Install the boot loader onto a disk or set of disks. location
+ is either a single string or a list of strings (each of which
+ is a character-special device path, which the boot loader
+ will use to determine where to install itself) or a path to a
+ directory that will hold the boot loader files and
+ configuration files (see below).
+ If the version property is set in this BootLoader instance and
+ this boot loader supports recording the version number at boot
+ loader installation time, each location specified will be queried
+ for boot loader version information.
+ The boot loader will be installed to each installation location
+ with a version older than the version specified.
+
+ Note that for some boot configurations (i.e. net and ODD),
+ the boot loader isn't installed per-se; the loader and its
+ support files are copied to a well-known location, and that
+ location is used as an input to other programs or configuration
+ stores (i.e. mkisofs is given the path to a boot image; the
+ DHCP server is configured with the name of the NBP (i.e. pxegrub)
+ and configuration file(s) for PXE-compliant boot environments,
+ etc.). For these cases, install() will return a set of tuples,
+ each of the following form:
+ (<type>, <srcpath>, <dstpath>, <owner>, <group>, <mode>)
+ <type> depends on the platform and boot configuration that this
+ BootLoader is associated with (defined in child classes).
+ <srcpath> is the path from which the file should be copied.
+ <dstpath> is the path to which the file should be copied (tokens
+ are embedded in the path to instruct the consumer of the final
+ location (see the commit_boot_config() documentation in children
+ of BootConfig for a list of tokens and their expansions)).
+ <owner>, <group>, and <mode> are the uid, gid, and permissions
+ mode bits (respectively) that should be set on the destination
+ file.
+ """
+ pass
+
+
+#
+# BootLoader Exceptions
+#
+
+class BootLoaderInstallError(BootmgmtError):
+ pass
+
+class BootPartitionAccessError(BootLoaderInstallError):
+ pass
+
+class BootDeviceNotFoundError(BootLoaderInstallError):
+ pass
+
+class BootLoaderFeatureUnsupportedError(BootmgmtNotSupportedError):
+ pass
+
+class BootLoaderUnsupportedPartTypeError(BootLoaderFeatureUnsupportedError):
+ pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootutil.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,52 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various support code for the bootmgmt package
+"""
+
+import logging
+import platform
+import os
+
+from . import BootmgmtNotSupportedError
+
+def get_current_arch_string():
+ proc = platform.processor()
+ if proc == 'i386':
+ return 'x86'
+ elif proc == 'sparc':
+ return 'sparc'
+ raise BootmgmtNotSupportedError('Unsupported platform: ' + proc)
+
+class LoggerMixin(object):
+
+ logger = logging.getLogger('bootmgmt')
+
+ if not os.environ.get('BOOTMGMT_DEBUG', None) is None:
+ logging.basicConfig(level=logging.DEBUG)
+
+ @classmethod
+ def _debug(cls, log_msg):
+ LoggerMixin.logger.debug(cls.__name__ + ': ' + log_msg)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/pysol.py Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,650 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"Solaris-specific library wrappers and functions"
+
+from ctypes import *
+import gettext
+import errno
+import platform
+import os
+
+_ = gettext.translation("SUNW_OST_OSLIB", "/usr/lib/locale",
+ fallback=True).gettext
+
+(LIBZFS_INIT_FAILURE,
+ ) = range(1)
+
+_msgs = {
+ LIBZFS_INIT_FAILURE: _('libzfs initialization failure')
+}
+
+#
+# From sys/fstyp.h
+#
+FSTYPSZ = 16
+
+_libc = CDLL('libc.so', use_errno=True)
+_platwidth = platform.architecture()[0][:2]
+_libc.fopen.restype = c_void_p
+_libc.free.argtypes = [ c_void_p ]
+_libc.free.restype = None
+_statvfs_syscall = 'statvfs' + ('64' if _platwidth == '32' else '')
+# print 'platwidth = `%s\' | _syscall = `%s\'' % (_platwidth, _syscall)
+_statvfs = getattr(_libc, _statvfs_syscall)
+
+
+def ctype2dict(ctype_struct_instance):
+ dictresult = {}
+
+ # A very important side-effect of calling getattr() here is
+ # that the ctypes API creates copies of the data we're
+ # retrieving from the "structure". If it did not do that,
+ # calling libc APIs that use static data areas would cause
+ # the resulting Python strings to refer to the same storage
+ # area.
+ for x in [x for (x, y) in ctype_struct_instance._fields_]:
+ dictresult[x] = getattr(ctype_struct_instance, x)
+
+ return dictresult
+
+# ==============================[ statvfs ]===============================
+
+class StatVFS_Result(Structure):
+ """From the manpage:
+ int statvfs(const char *restrict path, struct statvfs *restrict buf);
+
+ u_long f_bsize; /* preferred file system block size */
+ u_long f_frsize; /* fundamental filesystem block
+ (size if supported) */
+ fsblkcnt_t f_blocks; /* total # of blocks on file system
+ in units of f_frsize */
+ fsblkcnt_t f_bfree; /* total # of free blocks */
+ fsblkcnt_t f_bavail; /* # of free blocks avail to
+ non-privileged user */
+ fsfilcnt_t f_files; /* total # of file nodes (inodes) */
+ fsfilcnt_t f_ffree; /* total # of free file nodes */
+ fsfilcnt_t f_favail; /* # of inodes avail to
+ non-privileged user*/
+ u_long f_fsid; /* file system id (dev for now) */
+ char f_basetype[FSTYPSZ]; /* target fs type name,
+ null-terminated */
+ u_long f_flag; /* bit mask of flags */
+ u_long f_namemax; /* maximum file name length */
+ char f_fstr[32]; /* file system specific string */
+ if not 64-bit process
+ u_long f_filler[16]; /* reserved for future expansion */
+ endif
+
+ We always use the 64-bit statvfs variant (statvfs64 in 32-bit
+ processes and statvfs in 64-bit processes)
+
+ """
+
+ _fields_ = [ ("f_bsize", c_ulong),
+ ("f_frsize", c_ulong),
+ ("f_blocks", c_ulonglong),
+ ("f_bfree", c_ulonglong),
+ ("f_bavail", c_ulonglong),
+ ("f_files", c_ulonglong),
+ ("f_ffree", c_ulonglong),
+ ("f_favail", c_ulonglong),
+ ("f_fsid", c_ulong),
+ ("f_basetype", c_char * FSTYPSZ),
+ ("f_flag", c_ulong),
+ ("f_namemax", c_ulong),
+ ("f_fstr", c_char * 32),
+ ("f_filler", c_int * 16) ]
+
+
+def statvfs(path):
+ """Returns a dictionary whose members are the result of the Solaris
+ statvfs() call"""
+
+ result = StatVFS_Result()
+
+ if _statvfs(path, pointer(result)) != 0:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+
+ return ctype2dict(result)
+
+
+# ===========================[ mnttab functions ]============================
+
+
+class SolarisMntTab(Structure):
+ "Python ctype expression of the Solaris mnttab structure"
+ _fields_ = [('mnt_special', c_char_p),
+ ('mnt_mountp', c_char_p),
+ ('mnt_fstype', c_char_p),
+ ('mnt_mntopts', c_char_p),
+ ('mnt_time', c_char_p)]
+
+class SolarisExtMntTab(Structure):
+ "Python ctype expression of the Solaris extmnttab structure"
+ _fields_ = [('mnt_special', c_char_p),
+ ('mnt_mountp', c_char_p),
+ ('mnt_fstype', c_char_p),
+ ('mnt_mntopts', c_char_p),
+ ('mnt_time', c_char_p),
+ ('mnt_major', c_uint),
+ ('mnt_minor', c_uint)]
+
+def mnttab_err_decode(code):
+ """Decodes the following error codes from mnttab.h:
+ #define MNT_TOOLONG 1 /* entry exceeds MNT_LINE_MAX */
+ #define MNT_TOOMANY 2 /* too many fields in line */
+ #define MNT_TOOFEW 3 /* too few fields in line */
+ """
+ if code == 1:
+ return 'Entry exceeds 1024 characters'
+ elif code == 2:
+ return 'Too many fields in line'
+ elif code == 3:
+ return 'Too few fields in line'
+ else:
+ return 'Unknown mnttab error'
+
+
+def mnttab_open(mtab='/etc/mnttab'):
+ global _mnttab_FILE
+ _mnttab_FILE = c_void_p(_libc.fopen(mtab, 'r'))
+ if _mnttab_FILE.value is None:
+ raise IOError(get_errno(), mtab + ': ' +
+ os.strerror(get_errno()))
+
+def getmntent():
+ """Returns the next mnttab entry as a dictionary whose keys
+ are:
+ mnt_special
+ mnt_mountp
+ mnt_fstype
+ mnt_mntopts
+ mnt_time
+ or None if there are no more entries
+ """
+ mntent = SolarisMntTab()
+ r = _libc.getmntent(_mnttab_FILE, byref(mntent))
+ if r < 0: # EOF
+ return None
+ elif r > 0: # Error
+ raise IOError(r, mnttab_err_decode(r))
+
+ return ctype2dict(mntent)
+
+
+def getmntany(**attrs):
+ """Returns a mnttab entry matching the attributes passed in, or
+ None if no entry matches.
+ """
+ mntent = SolarisMntTab()
+ mntmatch = SolarisMntTab()
+ for x in attrs.keys():
+ mntent.__setattr__(x, attrs[x])
+ r = _libc.getmntany(_mnttab_FILE, byref(mntmatch), byref(mntent))
+ if r < 0: # EOF
+ return None
+ elif r > 0:
+ raise IOError(r, mnttab_err_decode(r))
+
+ return ctype2dict(mntmatch)
+
+
+def getextmntent():
+ """Returns the next extmnttab entry as a dictionary whose keys
+ are:
+ mnt_special
+ mnt_mountp
+ mnt_fstype
+ mnt_mntopts
+ mnt_time
+ mnt_major
+ mnt_minor
+ or None if there are no more entries.
+ """
+ extmnt = SolarisExtMntTab()
+ r = _libc.getextmntent(_mnttab_FILE, byref(extmnt), sizeof(extmnt))
+ if r < 0: # EOF
+ return None
+ elif r > 0:
+ raise IOError(r, mnttab_err_decode(r))
+
+ return ctype2dict(extmnt)
+
+
+def resetmnttab(reopen=False):
+ """Rewinds the mnttab file to the beginning
+ if reopen is True, the mnttab file is closed, then reopened
+ """
+ if reopen is True:
+ mnttab_close()
+ mnttab_open()
+ else:
+ _libc.rewind(_mnttab_FILE)
+
+
+def mnttab_close():
+ _libc.fclose(_mnttab_FILE)
+
+
+# ==============================[devlink walking]============================
+
+_libdi = CDLL('libdevinfo.so', use_errno=True)
+_libdi.di_devlink_init.restype = c_void_p
+_libdi.di_devlink_init.argtypes = [ c_char_p, c_int ]
+_libdi.di_devlink_path.restype = c_char_p
+_libdi.di_devlink_path.argtypes = [ c_void_p ]
+# The walker function returns an int and takes 2 void *'s: the di_devlink_t,
+# and the arg
+_devlink_walkfunc_type = CFUNCTYPE(c_int, c_void_p, c_void_p)
+_libdi.di_devlink_walk.argtypes = [ c_void_p, c_char_p, c_char_p, c_int,
+ c_void_p, _devlink_walkfunc_type ]
+# from <libdevinfo.h>:
+DI_MAKE_LINK = 1
+DI_PRIMARY_LINK = 1
+DI_SECONDARY_LINK = 2
+DI_WALK_CONTINUE = 0
+DI_WALK_TERMINATE = -3
+
+def di_devlink_init(drvname=None, flags=0):
+ """Initialize the libdevinfo devlink interfaces"""
+ hdl = c_void_p(_libdi.di_devlink_init(drvname, flags))
+ if hdl.value is None:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+ return hdl
+
+
+def di_devlink_path(dl):
+ """Return a string that is the path corresponding to the devlink
+ passed in
+ """
+ r = _libdi.di_devlink_path(dl)
+ if r is None:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+ return r
+
+
+def di_devlink_walk(hdl, pattern, path, flags, walk_arg, walkfunc):
+ """Conduct a walk of all devlinks that patch the given pattern and
+ that pertain to the given path. Note that since ctypes passes
+ arguments to callbacks as 'int's, those arguments must be converted
+ into a useful Python type (passing a string, then reconstituting
+ a list from that string, for example).
+ """
+
+ wf = _devlink_walkfunc_type(walkfunc)
+ r = _libdi.di_devlink_walk(hdl, pattern, path, flags, walk_arg, wf)
+ if r < 0:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+
+
+def di_devlink_fini(hdl):
+ """Performs cleanup after use of the devlink interfaces"""
+ _libdi.di_devlink_fini(hdl)
+
+# ==========================[ libdevinfo subset ]=============================
+
+_minor_walkfunc_type = CFUNCTYPE(c_int, c_void_p, c_void_p, c_void_p)
+
+_libdi.di_init.restype = c_void_p
+_libdi.di_init.argtypes = [ c_char_p, c_int ]
+_libdi.di_fini.argtypes = [ c_void_p ]
+_libdi.di_devfs_path.restype = c_void_p
+_libdi.di_devfs_path.argtypes = [ c_void_p ]
+_libdi.di_devfs_path_free.argtypes = [ c_void_p ]
+_libdi.di_minor_spectype.argtypes = [ c_void_p ]
+_libdi.di_minor_name.restype = c_char_p
+_libdi.di_minor_name.argtypes = [ c_void_p ]
+_libdi.di_minor_next.restype = c_void_p
+_libdi.di_minor_next.argtypes = [ c_void_p, c_void_p ]
+_libdi.di_walk_minor.argtypes = [ c_void_p, c_char_p, c_int, c_void_p,
+ _minor_walkfunc_type ]
+_libdi.di_prop_lookup_strings.argtypes = [ c_ulong, c_void_p,
+ c_char_p, c_void_p ]
+class struct_boot_dev(Structure):
+ _fields_ = [('bootdev_element', c_char_p),
+ ('bootdev_trans', POINTER(c_char_p))]
+pp_struct_boot_dev = POINTER(POINTER(struct_boot_dev))
+_libdi.devfs_bootdev_get_list.argtypes = [ c_char_p, POINTER(pp_struct_boot_dev) ]
+_libdi.devfs_bootdev_free_list.argtypes = [ pp_struct_boot_dev ]
+_libdi.devfs_bootdev_free_list.restype = None
+
+
+S_IFBLK = 0x6000 # from <sys/stat.h>
+DDI_NT_BLOCK = "ddi_block" # from <sys/sunddi.h>
+
+# from <sys/devinfo_impl.h>:
+DIIOC = (0xdf << 8)
+DINFOSUBTREE = (DIIOC | 0x01)
+DINFOMINOR = (DIIOC | 0x02)
+DINFOPROP = (DIIOC | 0x04)
+DINFOPATH = (DIIOC | 0x08)
+DINFOCPYALL = (DINFOSUBTREE | DINFOPROP | DINFOMINOR)
+
+# from <sys/ddipropdefs.h>
+DDI_DEV_T_ANY = -2
+DDI_DEV_T_NONE = -1
+
+def _di_minor_spectype(minor):
+ """Returns the type (block or char) of the special node whose minor
+ info is passed in
+ """
+ return _libdi.di_minor_spectype(minor)
+
+
+def di_init(path='/', flags=(DINFOPATH|DINFOCPYALL)):
+ """Initialize the device tree snapshot, starting from the device path
+ specified. flags are (DINFOPATH|DINFOCPYALL) by default.
+ """
+ hdl = c_void_p(_libdi.di_init(path, flags))
+ if hdl.value is None:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+ return hdl
+
+
+def di_minor_is_block(minor):
+ return (True if _di_minor_spectype(minor) == S_IFBLK else False)
+
+
+def di_minor_name(minor):
+ "Returns the string name of the minor passed in"
+ return _libdi.di_minor_name(minor)
+
+
+def di_devfs_path(node):
+ "Returns the string name of the node passed in"
+ r = c_void_p(_libdi.di_devfs_path(node))
+ if r.value is None:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+ rs = c_char_p(r.value).value
+ _libdi.di_devfs_path_free(r)
+ return rs
+
+
+def di_minor_next(node, minor=None):
+ """Returns the next minor node, relative to the minor passed in.
+ If minor isn't specified, or is passed in as None, the first minor
+ associated with the node is returned. None is returned when no
+ more minors exist.
+ """
+ r = c_void_p(_libdi.di_minor_next(node, minor))
+ if r.value is None:
+ e = get_errno()
+ # NXIO is a special case-- an indicator of no more minors
+ if e == errno.ENXIO:
+ return None
+ else:
+ raise IOError(e, os.strerror(e))
+ return r
+
+def di_walk_minor(rootnode, minortype, walkarg, walkfunc, flag=0):
+ """Perform a walk of all minor nodes attached to device nodes
+ in a subtree rooted at `root'. walkargs should be a simple Python
+ type, since it will need to be reconstituted in the walkfunc callback.
+ """
+
+ wf = _minor_walkfunc_type(walkfunc)
+ r = _libdi.di_walk_minor(rootnode, minortype, flag, walkarg, wf)
+ print r
+ if r < 0:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+
+
+def di_fini(hdl):
+ _libdi.di_fini(hdl)
+
+
+def devfs_bootdev_get_list():
+
+ bdl = pp_struct_boot_dev()
+
+ rv = _libdi.devfs_bootdev_get_list('/', pointer(bdl))
+
+ # No exception is raised on error, because there is no errno
+ # value that accompanies the failure
+ if rv < 0 or bool(bdl) is False:
+ return None
+
+ i = 0
+ bootdevs = []
+ while not bool(bdl[i]) is False:
+ physical = bdl[i][0].bootdev_element
+ j = 0
+ logicals = []
+ while not bool(bdl[i][0].bootdev_trans[j]) is False:
+ logicals.append(bdl[i][0].bootdev_trans[j])
+ j += 1
+ bootdevs.append((physical, tuple(logicals)))
+ i += 1
+
+ _libdi.devfs_bootdev_free_list(bdl)
+
+ return tuple(bootdevs)
+
+
+def di_prop_lookup_strings(dev, node, prop_name, prop_data):
+ rv = _libdi.di_prop_lookup_strings(dev, node, prop_name, prop_data)
+ if rv < 0:
+ raise IOError(get_errno(), os.strerror(get_errno()))
+ return rv
+
+def di_find_root_prop(propname):
+ return di_find_prop(propname, '/')
+
+def di_find_prop(propname, root):
+ hdl = di_init(root, DINFOPROP)
+ propval = None
+ try:
+ # This is a bit tricky, sicne di_prop_lookup_strings returns ONE
+ # string with several embedded NUL terminators (if multiple strings
+ # are returned). To deal with this in ctypes, we use a pointer to
+ # a c_void_p as the last parameter and manually pull out each string
+ # To pull out each string, we cast the c_void_p to a c_char_p, which
+ # automatically gets us the first string (ctypes extracts it for us)
+ # To get subsequent strings, we need to manually index the pointer,
+ # adding the length of the previous string + 2 (one to get us to the
+ # NUL and one more to get us past it).
+ value = pointer(c_void_p())
+ rv = di_prop_lookup_strings(DDI_DEV_T_ANY, hdl, propname,
+ value)
+ stringvalue = cast(value.contents, c_char_p)
+ if rv == 1:
+ propval = stringvalue.value
+ elif rv > 1:
+ stringlen = len(stringvalue.value)
+ propval = [stringvalue.value]
+ rv -= 1
+ while rv > 0:
+ stringvalue = cast(c_voidp(value.contents.value +
+ stringlen + 2),
+ c_char_p)
+ propval.append(stringvalue.value)
+ stringlen = len(stringvalue.value)
+ rv -= 1
+ except IOError as e:
+ if e.errno == errno.ENXIO:
+ propval = None
+ else:
+ raise
+ finally:
+ di_fini(hdl)
+ return propval
+
+
+# ==============================[ libfstyp ]==================================
+
+_libfstyp = CDLL('libfstyp.so.1', use_errno=True)
+_libfstyp.fstyp_init.argtypes = [ c_int, c_uint64, c_char_p, c_void_p ]
+_libfstyp.fstyp_ident.argtypes = [ c_void_p, c_char_p, c_void_p ]
+_libfstyp.fstyp_fini.argtypes = [ c_void_p ]
+_libfstyp.fstyp_strerror.argtypes = [ c_void_p, c_int ]
+_libfstyp.fstyp_strerror.restype = c_char_p
+
+def fstyp_init(fd, offset=0, module_dir=None):
+ "Returns a handle that can be used with other fstyp functions"
+ handle = c_void_p()
+ r = _libfstyp.fstyp_init(fd, offset, module_dir, byref(handle))
+ if r != 0:
+ raise IOError(r, _libfstyp.fstyp_strerror(r))
+ return handle
+
+def fstyp_ident(handle, name=None):
+ result = c_char_p(0)
+ r = _libfstyp.fstyp_ident(handle, name, byref(result))
+ if r == 1: # No Match Found
+ return None
+ elif r != 0:
+ raise IOError(r, _libfstyp.fstyp_strerror(handle, r))
+ return result.value
+
+def fstyp_fini(handle):
+ _libfstyp.fstyp_fini(handle)
+
+
+# ================================[ isalist ]=================================
+
+SI_ISALIST = 514 # return supported isa list
+ISALIST_LEN = 257 # max buffer size, as per manpage
+
+_libc.sysinfo.argtypes = [ c_int, c_char_p, c_long ]
+_libc.sysinfo.restype = c_int
+
+def isalist():
+ "Returns a list of ISAs supported on the currently-running system"
+
+ b = create_string_buffer(ISALIST_LEN)
+ r = _libc.sysinfo(SI_ISALIST, b, ISALIST_LEN)
+ if r < 0:
+ raise OSError(get_errno(), os.strerror(get_errno()))
+ return b.value.split()
+
+
+# =================================[ libscf ]=================================
+
+SCF_STATE_STRING_ONLINE = 'online'
+
+_libscf = CDLL('libscf.so.1', use_errno=True)
+_libscf.smf_get_state.argtypes = [ c_char_p ]
+_libscf.smf_get_state.restype = c_void_p # Why c_void_p ? See comment below.
+_libscf.smf_enable_instance.argtypes = [ c_char_p, c_int ]
+_libscf.smf_enable_instance.restype = c_int
+_libscf.scf_error.argtypes = None
+_libscf.scf_error.restype = c_int
+_libscf.scf_strerror.argtypes = [ c_int ]
+_libscf.scf_strerror.restype = c_char_p
+
+
+class SCFError(Exception):
+ "Wrap for smf errors"
+
+ def __init__(self, errcode, errstring):
+ self.errno = errcode
+ self.strerror = errstring
+
+
+def smf_get_state(svc):
+ "Returns the current state of the specified service"
+
+ # Need to store the result in a c_void_p because if a c_char_p were
+ # specified as the return type, it would be automatically converted
+ # to a Python string and we'd love the original pointer value, which
+ # we need for the call to free()
+
+ alloced_str = c_void_p(_libscf.smf_get_state(svc))
+ if alloced_str.value == 0:
+ return None
+ result = c_char_p(alloced_str.value).value
+ _libc.free(alloced_str)
+ return result
+
+
+def smf_enable_instance(svc, flags=0):
+ "Enables the given smf service with the specified flags"
+
+ ret = _libscf.smf_enable_instance(svc, flags)
+ if ret == -1:
+ scferr = _libscf.scf_error()
+ raise SCFError(scferr, _libscf.scf_strerror(scferr))
+ else:
+ return ret
+
+
+# =================================[ libzfs ]=================================
+
+_libzfs = CDLL('libzfs.so.1', use_errno=True)
+_libzfs.libzfs_init.restype = c_void_p
+_libzfs.zfs_open.restype = c_void_p
+_libzfs.zfs_open.argtypes = [ c_void_p, c_char_p, c_int ]
+_libzfs.zfs_close.argtypes = [ c_void_p ]
+zif_callback = CFUNCTYPE(c_int, c_void_p, c_void_p)
+_libzfs.zfs_iter_filesystems.argtypes = [ c_void_p, zif_callback, c_void_p ]
+
+ZFS_TYPE_FILESYSTEM = 1
+ZFS_TYPE_SNAPSHOT = 2
+ZFS_TYPE_VOLUME = 4
+ZFS_TYPE_POOL = 8
+
+def libzfs_init():
+ hdl = _libzfs.libzfs_init()
+ if hdl.value is None:
+ raise IOError(0, _msgs[LIBZFS_INIT_FAILURE])
+ return hdl
+
+def zfs_open(handle, devpath, type=ZFS_TYPE_FILESYSTEM): pass
+
+def libzfs_fini(handle):
+ _libzfs.libzfs_fini(handle)
+
+
+__all__ = [ "statvfs",
+ "mnttab_open",
+ "mnttab_close",
+ "getmntent",
+ "getmntany",
+ "getextmntent",
+ "resetmnttab",
+ "DI_MAKE_LINK",
+ "DI_PRIMARY_LINK",
+ "DI_SECONDARY_LINK",
+ "DI_WALK_CONTINUE",
+ "DI_WALK_TERMINATE",
+ "di_devlink_init",
+ "di_devlink_path",
+ "di_devlink_walk",
+ "di_devlink_fini",
+ "DDI_NT_BLOCK",
+ "DINFOPATH",
+ "DINFOCPYALL",
+ "di_init",
+ "di_minor_is_block",
+ "di_minor_name",
+ "di_devfs_path",
+ "di_minor_next",
+ "di_walk_minor",
+ "di_fini",
+ "devfs_bootdev_get_list",
+ "fstyp_init",
+ "fstyp_ident",
+ "fstyp_fini",
+ "isalist" ]
--- a/usr/src/pkg/Makefile Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/pkg/Makefile Thu Apr 21 23:16:03 2011 -0600
@@ -197,7 +197,8 @@
# complete reprocessing of all manifests because they'll fail command
# dependency checking.
#
-PM_TRANSFORMS= publish restart_fmri defaults extract_metadata
+PM_TRANSFORMS= publish restart_fmri defaults extract_metadata \
+ disable_depend_checks
PM_INC= transforms
PKGMOG_DEFINES= \
--- a/usr/src/pkg/manifests/system-library-install.mf Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/pkg/manifests/system-library-install.mf Thu Apr 21 23:16:03 2011 -0600
@@ -31,6 +31,7 @@
dir path=usr group=sys
dir path=usr/lib
dir path=usr/lib/python2.6
+dir path=usr/lib/python2.6/vendor-packages/bootmgmt
dir path=usr/lib/python2.6/vendor-packages
dir path=usr/lib/python2.6/vendor-packages/osol_install
dir path=usr/lib/python2.6/vendor-packages/solaris_install
@@ -195,6 +196,54 @@
file path=usr/lib/python2.6/vendor-packages/solaris_install/transfer/prog.pyc
file path=usr/lib/python2.6/vendor-packages/solaris_install/transfer/svr4.py
file path=usr/lib/python2.6/vendor-packages/solaris_install/transfer/svr4.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootarchive.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootarchive.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootconfig.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootconfig.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootinfo.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootinfo.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootloader.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootloader.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootutil.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootutil.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/pysol.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/pysol.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/solaris.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/solaris.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/solaris.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/solaris.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/solaris.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/solaris.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/bios.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/bios.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/obp.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/obp.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/uefi64.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/uefi64.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/grub2.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/grub2.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/legacygrub.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/legacygrub.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/menulst.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/menulst.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/sbb.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/sbb.pyc
file path=usr/share/install/ai.dtd mode=0444 group=sys
file path=usr/share/install/configuration.dtd mode=0444 group=sys
file path=usr/share/install/dc.dtd mode=0444 group=sys
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/transforms/disable_depend_checks Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,26 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+# This is a workaround the existing bug in pkgdepend - 18194
+<transform file path=usr/lib/python2.6/vendor-packages/bootmgmt/.+ -> default pkg.depend.bypass-generate .*>
+