2N/A#! /usr/bin/python2.6
2N/A#
2N/A# CDDL HEADER START
2N/A#
2N/A# The contents of this file are subject to the terms of the
2N/A# Common Development and Distribution License (the "License").
2N/A# You may not use this file except in compliance with the License.
2N/A#
2N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A# or http://www.opensolaris.org/os/licensing.
2N/A# See the License for the specific language governing permissions
2N/A# and limitations under the License.
2N/A#
2N/A# When distributing Covered Code, include this CDDL HEADER in each
2N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A# If applicable, add the following below this CDDL HEADER, with the
2N/A# fields enclosed by brackets "[]" replaced with your own identifying
2N/A# information: Portions Copyright [yyyy] [name of copyright owner]
2N/A#
2N/A# CDDL HEADER END
2N/A#
2N/A# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A#
2N/A
2N/A"""
2N/Ax86 Solaris Boot variables backend support for pybootmgmt
2N/A"""
2N/A
2N/Aimport logging
2N/Aimport os
2N/Aimport tempfile
2N/Aimport shutil
2N/Aimport sys
2N/Afrom bootmgmt import bootinfo
2N/Afrom bootmgmt import (BootmgmtMalformedPropertyNameError, BootmgmtArgumentError,
2N/A BootmgmtReadError, BootmgmtWriteError)
2N/A
2N/Alogger = logging.getLogger('bootmgmt')
2N/A
2N/A
2N/Aclass BootenvBootVariables(bootinfo.BootVariables):
2N/A """This class supports manipulation of boot variables stored in the
2N/A <root>/boot/solaris/bootenv.rc file."""
2N/A
2N/A BOOTENV_RC = '/boot/solaris/bootenv.rc'
2N/A
2N/A def __init__(self, sysroot=None):
2N/A self.BOOTENV_DOT_RC = sysroot + BootenvBootVariables.BOOTENV_RC
2N/A self._dirty = False
2N/A super(BootenvBootVariables, self).__init__(sysroot)
2N/A
2N/A @property
2N/A def dirty(self):
2N/A return self._dirty
2N/A
2N/A @dirty.setter
2N/A def dirty(self, value):
2N/A if not type(value) is bool:
2N/A raise ValueError('dirty is a bool')
2N/A if self._dirty != value:
2N/A logger.debug(self.__class__.__name__ + ': dirty => %s'
2N/A % str(value))
2N/A self._dirty = value
2N/A
2N/A def setprop(self, propname, value):
2N/A # if the property name contains whitespace, it's invalid
2N/A if len(set(propname).intersection(' \t')) > 0:
2N/A raise BootmgmtMalformedPropertyNameError('Invalid property name' +
2N/A ' ("%s")' % propname)
2N/A if value is None: # Not allowed -- there must be a real value here
2N/A raise BootmgmtArgumentError('value must not be None')
2N/A
2N/A if propname in self._vardict:
2N/A if self._vardict[propname][1] != value:
2N/A # All we need to do is update the value portion of the list
2N/A # (this automatically "updates" the value stored in _rawlines)
2N/A self._vardict[propname][1] = value
2N/A self.dirty = True
2N/A else:
2N/A proplist = [propname, value]
2N/A # The _vardict must use the same object that's added to _rawlines
2N/A # so that updates are seamless across both containers
2N/A self._vardict[propname] = proplist
2N/A self._rawlines.append(proplist)
2N/A self.dirty = True
2N/A
2N/A def getprop(self, propname):
2N/A val_list = self._vardict.get(propname, None)
2N/A # The values stored in the dictionary are 2-element lists
2N/A # The first element is the prop name and the second is the value
2N/A if not val_list is None and len(val_list) == 2:
2N/A return val_list[1]
2N/A else:
2N/A return None
2N/A
2N/A def delprop(self, propname):
2N/A if propname in self._vardict:
2N/A # Clear the list first so that _rawlines is "updated" to contain
2N/A # a 0-length list for this property
2N/A del self._vardict[propname][:]
2N/A # Now remove the property from the _vardict dict
2N/A del self._vardict[propname]
2N/A self.dirty = True
2N/A
2N/A def _read(self):
2N/A """Reads the set of properties from the bootenv.rc file under
2N/A sysroot. Keeps a copy of the file in memory so comments can
2N/A be preserved."""
2N/A bvfile = self.BOOTENV_DOT_RC
2N/A self._rawlines = []
2N/A self._vardict = {}
2N/A try:
2N/A with open(bvfile) as berc:
2N/A for rawline in berc:
2N/A nextline = rawline.strip()
2N/A # skip comment lines
2N/A if len(nextline) > 0 and nextline[0] != '#':
2N/A # Store the property in the _rawlines list as a
2N/A # list so that we can make changes via the _vardict
2N/A # The form of a line is:
2N/A # setprop <propname> <propval>
2N/A # If we find a line that's malformed, ignore it and
2N/A # continue
2N/A try:
2N/A keyword, prop, val = nextline.split(None, 2)
2N/A except ValueError:
2N/A logger.debug('Malformed line in ' + bvfile +
2N/A ': "%s"' % nextline)
2N/A continue
2N/A
2N/A if keyword != 'setprop':
2N/A logger.debug('Malformed line in ' + bvfile +
2N/A ': "%s"' % nextline)
2N/A continue
2N/A
2N/A newbep = [prop, val]
2N/A self._rawlines.append(newbep)
2N/A self._vardict[prop] = newbep
2N/A else:
2N/A self._rawlines.append(rawline)
2N/A
2N/A except IOError as e:
2N/A raise BootmgmtReadError('Error while loading boot variables ' +
2N/A 'from ' + bvfile, e)
2N/A
2N/A def write(self, inst, alt_dir=None):
2N/A # Open a new file that will contain the new bootenv.rc, dump all
2N/A # the variables to that file, then move that file over to be the
2N/A # real bootenv.rc
2N/A
2N/A if not alt_dir is None:
2N/A try:
2N/A fileobj = tempfile.NamedTemporaryFile(dir=alt_dir,
2N/A delete=False)
2N/A except IOError as err:
2N/A raise BootmgmtWriteError('Error while writing to temporary ' +
2N/A 'bootenv.rc (%s)' % fileobj.name, err)
2N/A bvfile = fileobj.name
2N/A bvtempfile = bvfile
2N/A else:
2N/A # BOOTENV_DOT_RC has a leading slash:
2N/A bvfile = self.BOOTENV_DOT_RC
2N/A bvtempfile = bvfile + '.new'
2N/A try:
2N/A fileobj = open(bvtempfile, 'w')
2N/A except IOError as err:
2N/A raise BootmgmtWriteError('Error while writing to temporary ' +
2N/A 'bootenv.rc (%s)' % bvtempfile, err)
2N/A
2N/A try:
2N/A with fileobj as berc:
2N/A # Write each line to the output file -- if the item in
2N/A # _rawlines is a list, construct a setprop command string,
2N/A # otherwise, just copy it verbatim
2N/A for line in self._rawlines:
2N/A if type(line) is list and len(line) == 2:
2N/A berc.write('setprop ' + line[0] + ' ' + line[1] + '\n')
2N/A elif type(line) is str:
2N/A berc.write(line) # newline is already part of line
2N/A
2N/A except IOError as err:
2N/A raise BootmgmtWriteError('Error while writing to temporary ' +
2N/A 'bootenv.rc (%s)' % bvtempfile, err)
2N/A
2N/A # Now move the file over the become the new bootenv.rc:
2N/A try:
2N/A if alt_dir is None:
2N/A shutil.move(bvtempfile, bvfile)
2N/A self.dirty = False
2N/A except IOError as ioe:
2N/A try:
2N/A # Try to clean up by deleting the temporary file
2N/A os.remove(bvtempfile)
2N/A except OSError as ose:
2N/A logger.debug("Couldn't clean up temporary bootenv.rc: " +
2N/A ose.strerror)
2N/A pass
2N/A raise BootmgmtWriteError('Error while moving the temporary ' +
2N/A 'bootenv.rc (%s) to %s' %
2N/A (bvtempfile, bvfile), ioe)
2N/A
2N/A if not alt_dir is None:
2N/A return [('file', bvfile, inst,
2N/A '%(systemroot)s' + BootenvBootVariables.BOOTENV_RC,
2N/A 'root', 'sys', 0644)]
2N/A else:
2N/A return None
2N/A
2N/A def __len__(self):
2N/A return len(self._vardict)
2N/A
2N/A def __iter__(self):
2N/A classic_dict = [(x, z) for (x, [y, z]) in self._vardict.items()]
2N/A return classic_dict.__iter__()
2N/A
2N/A
2N/Adef bootvars_backend():
2N/A return BootenvBootVariables