186 lines
5.9 KiB
Python
186 lines
5.9 KiB
Python
#
|
|
# Copyright (C) 2018 Red Hat, Inc.
|
|
#
|
|
# This copyrighted material is made available to anyone wishing to use,
|
|
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
# the GNU General Public License v.2, or (at your option) any later version.
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
# Public License for more details. You should have received a copy of the
|
|
# GNU General Public License along with this program; if not, write to the
|
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
# source code or documentation are not subject to the GNU General Public
|
|
# License and may only be used or replicated with the express permission of
|
|
# Red Hat, Inc.
|
|
#
|
|
import shlex
|
|
import glob
|
|
|
|
from collections import OrderedDict
|
|
from pyanaconda.core.constants import CMDLINE_APPEND, CMDLINE_LIST, CMDLINE_FILES
|
|
|
|
__all__ = ['KernelArguments', 'kernel_arguments']
|
|
|
|
|
|
# options might have the inst. prefix (used to differentiate
|
|
# boot options for the installer from other boot options)
|
|
BOOT_ARG_PREFIX = "inst."
|
|
|
|
|
|
class KernelArguments():
|
|
"""The kernel arguments.
|
|
|
|
Hold boot arguments as an OrderedDict.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._data = OrderedDict()
|
|
self._args_with_prefix = set()
|
|
|
|
@classmethod
|
|
def from_defaults(cls):
|
|
"""Load the default files.
|
|
|
|
:return: an instance of KernelArguments
|
|
"""
|
|
args = cls()
|
|
args.read(CMDLINE_FILES)
|
|
return args
|
|
|
|
@classmethod
|
|
def from_string(cls, cmdline):
|
|
"""Load the given cmdline.
|
|
|
|
:param cmdline: a string with the kernel cmdline
|
|
:return: an instance of KernelArguments
|
|
"""
|
|
args = cls()
|
|
args.read_string(cmdline)
|
|
return args
|
|
|
|
def read(self, filenames):
|
|
"""Read and parse a file name (or a list of file names).
|
|
|
|
Files that can't be read are silently ignored. The names
|
|
can contain \\*, ?, and character ranges expressed with [].
|
|
|
|
:param filenames: a file name or a list of file names
|
|
:return: a list of successfully read files
|
|
"""
|
|
readfiles = []
|
|
if isinstance(filenames, str):
|
|
filenames = [filenames]
|
|
|
|
# Expand any filename globs
|
|
filenames = [f for g in filenames for f in glob.glob(g)]
|
|
|
|
for f in filenames:
|
|
try:
|
|
self.read_string(open(f).read())
|
|
readfiles.append(f)
|
|
except OSError:
|
|
continue
|
|
|
|
return readfiles
|
|
|
|
def read_string(self, cmdline):
|
|
"""Read and parse a string.
|
|
|
|
:param cmdline: a string with the kernel command line
|
|
"""
|
|
cmdline = cmdline.strip()
|
|
# if the BOOT_IMAGE contains a space, pxelinux will strip one of the
|
|
# quotes leaving one at the end that shlex doesn't know what to do
|
|
# with
|
|
(left, middle, right) = cmdline.rpartition("BOOT_IMAGE=")
|
|
if right.count('"') % 2:
|
|
cmdline = left + middle + '"' + right
|
|
|
|
# shlex doesn't properly handle \\ (it removes them)
|
|
# which scrambles the spaces used in labels so use underscores
|
|
cmdline = cmdline.replace("\\x20", "_")
|
|
|
|
lst = shlex.split(cmdline)
|
|
|
|
for i in lst:
|
|
prefix_used = False
|
|
# drop the inst. prefix (if found)
|
|
if i.startswith(BOOT_ARG_PREFIX):
|
|
i = i[len(BOOT_ARG_PREFIX):]
|
|
prefix_used = True
|
|
|
|
if "=" in i:
|
|
(key, val) = i.split("=", 1)
|
|
else:
|
|
key = i
|
|
val = None
|
|
|
|
if prefix_used:
|
|
self._args_with_prefix.add(key)
|
|
|
|
# Some duplicate args create a space separated string
|
|
if key in CMDLINE_APPEND and self._data.get(key, None):
|
|
if val:
|
|
self._data[key] = self._data[key] + " " + val
|
|
# Some arguments can contain spaces so adding them in one string is not that helpful
|
|
elif key in CMDLINE_LIST:
|
|
if val:
|
|
if not self._data.get(key, None):
|
|
self._data[key] = []
|
|
self._data[key].append(val)
|
|
else:
|
|
self._data[key] = val
|
|
|
|
def is_enabled(self, arg):
|
|
"""Return boolean value for the given argument.
|
|
|
|
Rules:
|
|
- 0, off, not present -> False
|
|
- the rest -> True
|
|
"""
|
|
# None is stored when arg is present but had no value when parsing.
|
|
# So the "miss" value must be something else.
|
|
val = self._data.get(arg, False)
|
|
if val in ["0", "off", False]:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def get(self, arg, default=None):
|
|
"""Return the value of the given argument.
|
|
|
|
Propagates the call verbatim to the underlying dictionary.
|
|
"""
|
|
return self._data.get(arg, default)
|
|
|
|
def __contains__(self, arg):
|
|
"""Check for presence of an argument.
|
|
|
|
Propagates the call verbatim to the underlying dictionary.
|
|
"""
|
|
return arg in self._data
|
|
|
|
def items(self):
|
|
"""Return an iterator over all arguments.
|
|
|
|
Propagates the call verbatim to the underlying dictionary.
|
|
"""
|
|
return self._data.items()
|
|
|
|
def items_raw(self):
|
|
"""Return an iterator over all arguments in their raw form (with prefixes).
|
|
|
|
TODO: DO NOT USE THIS METHOD! This workaround will be removed
|
|
when lack of 'inst.' prefix is not supported.
|
|
"""
|
|
for key, val in self._data.items():
|
|
if key in self._args_with_prefix:
|
|
yield ("{}{}".format(BOOT_ARG_PREFIX, key), val)
|
|
continue
|
|
|
|
yield (key, val)
|
|
|
|
|
|
kernel_arguments = KernelArguments.from_defaults()
|