diff options
Diffstat (limited to 'SOURCES/uki_create_addons.py')
-rwxr-xr-x | SOURCES/uki_create_addons.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/SOURCES/uki_create_addons.py b/SOURCES/uki_create_addons.py new file mode 100755 index 0000000..e30d43b --- /dev/null +++ b/SOURCES/uki_create_addons.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# This script inspects a given json proving a list of addons, and +# creates an addon for each key/value pair matching the given uki, distro and +# arch provided in input. +# +# Usage: python uki_create_addons.py input_json out_dir uki distro arch +# +# This tool requires the systemd-ukify and systemd-boot packages. +# +# Addon file +#----------- +# Each addon terminates with .addon +# Each addon contains only two types of lines: +# Lines beginning with '#' are description and thus ignored +# All other lines are command line to be added. +# The name of the end resulting addon is taken from the json hierarchy. +# For example, and addon in json['virt']['rhel']['x86_64']['hello.addon'] will +# result in an UKI addon file generated in out_dir called +# hello-virt.rhel.x86_64.addon.efi +# +# The common key, present in any sub-dict in the provided json (except the leaf dict) +# is used as place for default addons when the same addon is not defined deep +# in the hierarchy. For example, if we define test.addon (text: 'test1\n') in +# json['common']['test.addon'] = ['test1\n'] and another test.addon (text: test2) in +# json['virt']['common']['test.addon'] = ['test2'], any other uki except virt +# will have a test.addon.efi with text "test1", and virt will have a +# test.addon.efi with "test2" +# +# sbat.conf +#---------- +# This dict is containing the sbat string for *all* addons being created. +# This dict is optional, but when used has to be put in a sub-dict with +# { 'sbat' : { 'sbat.conf' : ['your text here'] }} +# It follows the same syntax as the addon files, meaning '#' is comment and +# the rest is taken as sbat string and feed to ukify. + +import os +import sys +import json +import collections +import subprocess + + +UKIFY_PATH = '/usr/lib/systemd/ukify' + +def usage(err): + print(f'Usage: {os.path.basename(__file__)} input_json output_dir uki distro arch') + print(f'Error:{err}') + sys.exit(1) + +def check_clean_arguments(input_json, out_dir): + # Remove end '/' + if out_dir[-1:] == '/': + out_dir = out_dir[:-1] + if not os.path.isfile(input_json): + usage(f'input_json {input_json} is not a file, or does not exist!') + if not os.path.isdir(out_dir): + usage(f'out_dir_dir {out_dir} is not a dir, or does not exist!') + return out_dir + +UKICmdlineAddon = collections.namedtuple('UKICmdlineAddon', ['name', 'cmdline']) +uki_addons_list = [] +uki_addons = {} +addon_sbat_string = None + +def parse_lines(lines, rstrip=True): + cmdline = '' + for l in lines: + l = l.lstrip() + if not l: + continue + if l[0] == '#': + continue + # rstrip is used only for addons cmdline, not sbat.conf, as it replaces + # return lines with spaces. + if rstrip: + l = l.rstrip() + ' ' + cmdline += l + if cmdline == '': + return '' + return cmdline + +def parse_all_addons(in_obj): + global addon_sbat_string + + for el in in_obj.keys(): + # addon found: copy it in our global dict uki_addons + if el.endswith('.addon'): + uki_addons[el] = in_obj[el] + + if 'sbat' in in_obj and 'sbat.conf' in in_obj['sbat']: + # sbat.conf found: override sbat with the most specific one found + addon_sbat_string = parse_lines(in_obj['sbat']['sbat.conf'], rstrip=False) + +def recursively_find_addons(in_obj, folder_list): + # end of recursion, leaf directory. Search all addons here + if len(folder_list) == 0: + parse_all_addons(in_obj) + return + + # first, check for common folder + if 'common' in in_obj: + parse_all_addons(in_obj['common']) + + # second, check if there is a match with the searched folder + if folder_list[0] in in_obj: + folder_next = in_obj[folder_list[0]] + folder_list = folder_list[1:] + recursively_find_addons(folder_next, folder_list) + +def parse_in_json(in_json, uki_name, distro, arch): + with open(in_json, 'r') as f: + in_obj = json.load(f) + recursively_find_addons(in_obj, [uki_name, distro, arch]) + + for addon_name, cmdline in uki_addons.items(): + addon_name = addon_name.replace(".addon","") + addon_full_name = f'{addon_name}-{uki_name}.{distro}.{arch}.addon.efi' + cmdline = parse_lines(cmdline).rstrip() + if cmdline: + uki_addons_list.append(UKICmdlineAddon(addon_full_name, cmdline)) + +def create_addons(out_dir): + for uki_addon in uki_addons_list: + out_path = os.path.join(out_dir, uki_addon.name) + cmd = [ + f'{UKIFY_PATH}', 'build', + f'--cmdline="{uki_addon.cmdline}"', + f'--output={out_path}'] + if addon_sbat_string: + cmd.append('--sbat="' + addon_sbat_string.rstrip() +'"') + + subprocess.check_call(cmd, text=True) + +if __name__ == "__main__": + argc = len(sys.argv) - 1 + if argc != 5: + usage('too few or too many parameters!') + + input_json = sys.argv[1] + out_dir = sys.argv[2] + uki_name = sys.argv[3] + distro = sys.argv[4] + arch = sys.argv[5] + + out_dir = check_clean_arguments(input_json, out_dir) + parse_in_json(input_json, uki_name, distro, arch) + create_addons(out_dir) + + |