diff --git a/tools/kexttool/kext.py b/tools/kexttool/kext.py index a7034a9..297072d 100644 --- a/tools/kexttool/kext.py +++ b/tools/kexttool/kext.py @@ -4,9 +4,11 @@ import os import glob class KextSource: - __kext_info = {} - __src_dir_path = '' - + src_languages = { + 'C': [ '.c' ], + 'CXX': [ '.cpp' ], + 'ASM': [ '.S' ], + } def scan(path): result = [] @@ -29,11 +31,34 @@ class KextSource: self.__kext_info = yaml.safe_load(info_fp) + def __extension_is_of_lang(ext, lang_name): + for name, exts in KextSource.src_languages.items(): + if name == lang_name and ext in exts: + return True + + return False + + + def __extension_get_lang(ext): + for name, exts in src_languages.items(): + if ext in exts: + return name + + return None + + def name(self): return self.__kext_info['name'] - def id(self): + def description(self): + return self.__kext_info['description'] + + + def id(self, underscores=False): + if underscores: + return self.__kext_info['id'].replace('.', '_').replace('-', '_') + return self.__kext_info['id'] @@ -49,5 +74,33 @@ class KextSource: return self.__kext_info['sources'] + def source_languages(self): + langs = [] + + for src in self.__kext_info['sources']: + ext = os.path.splitext(src)[1] + lang = KextSource.__extension_get_lang(ext) + if lang == None: + continue + + langs.append(lang) + + return langs + + + def sources_filepath(self, lang=None): + sources = [] + for s in self.__kext_info['sources']: + if lang != None: + ext = os.path.splitext(s)[1] + if KextSource.__extension_is_of_lang(lang, ext): + continue + + full_path = os.path.join(self.__src_dir_path, s) + sources.append(full_path) + + return sources + + def src_dirpath(self): return self.__src_dir_path diff --git a/tools/kexttool/scan.py b/tools/kexttool/scan.py index 4fbdcc0..a0b8d23 100644 --- a/tools/kexttool/scan.py +++ b/tools/kexttool/scan.py @@ -4,7 +4,7 @@ from kexttool import kext def list_all(pretty=False): cwd = os.getcwd() - scan_arg = os.path.join(cwd, 'extensions', '**/extension.yaml') + scan_arg = os.path.join(cwd, 'kexts', '**/extension.yaml') nr_extensions = 0 diff --git a/tools/kexttool/select.py b/tools/kexttool/select.py index 04d3b6b..922f31a 100644 --- a/tools/kexttool/select.py +++ b/tools/kexttool/select.py @@ -1,19 +1,155 @@ -import inquirer +import yaml import os -from kexttool import kext +from kexttool.kext import KextSource -def select_kexts(): - all_kexts = [ k.id() for k in kext.KextSource.scan(os.path.join(os.getcwd(), 'extensions')) ] +selection_file_path = 'build/extensions.yaml' +kext_makefile_path = 'build/extensions.mk' + + +def description_to_list(kext): + return list(filter(None, kext.description().split('\n'))) + + +def create_selection_file(): + if not os.path.isdir('build'): + os.mkdir('build') + + all_kexts = KextSource.scan(os.path.join(os.getcwd(), 'kexts')) if len(all_kexts) == 0: print('No kernel extensions available.') return + + out_file = open(selection_file_path, 'w') + selection_info = {} + + for k in all_kexts: + k_info = { + 'description': description_to_list(k), + 'build': None + } + + selection_info[k.id()] = k_info + + out_file.write(yaml.dump(selection_info, default_flow_style=False)) + + print('A list of available kexts has been created at {}'.format(selection_file_path)) + print('For each kext that you want to build, set the "build" key to:') + print(' * internal: build the kext into the kernel image, to be loaded during boot.') + print(' * external: build the kext into a standalone executable image, to be loaded at runtime.') + print(' * null (or any other value): don\'t build the kext.') + return + + +def find_kext_by_id(all_kexts, ident): + for k in all_kexts: + if k.id() == ident: + return k + + return None + + +def external_kext_src_variable_name(kext, lang): + return 'KEXT_{}_{}_SRC'.format(kext.id(underscores=True), lang) + + +def external_kext_obj_variable_name(kext): + return 'KEXT_{}_OBJ'.format(kext.id(underscores=True)) + + +def write_external_kext_source_list(fp, kext, lang): + sources = kext.sources_filepath(lang) + + fp.write('{} := \\\n'.format(external_kext_src_variable_name(kext, lang))) + + for i, src in enumerate(sources): + fp.write('\t{}'.format(src)) + + if i != len(sources) - 1: + fp.write(' \\') + fp.write('\n') + + +def write_external_kext_variables(fp, kexts): + for kext in kexts: + fp.write('{} := \\\n'.format(external_kext_obj_variable_name(kext))) + sources = kext.sources_filepath() + + for i, src in enumerate(sources): + obj = os.path.splitext(src)[0] + '.o' + fp.write('\t$(BUILD_DIR)/{}'.format(obj)) + + if i != len(sources) - 1: + fp.write(' \\') + fp.write('\n') - questions = [ - inquirer.Checkbox('kexts', - message="Select kexts to include in the kernel image", - choices=all_kexts, - ) - ] - answers = inquirer.prompt(questions) - print(answers["kexts"]) + +def create_kext_makefile(): + all_kexts = KextSource.scan(os.path.join(os.getcwd(), 'kexts')) + selection_info = {} + + try: + selection_fp = open(selection_file_path, 'r') + selection_info = yaml.safe_load(selection_fp) + except Exception as e: + print('E: cannot load extension selection data ({})'.format(e)) + return + + try: + makefile_fp = open(kext_makefile_path, 'w') + except Exception as e: + print('E: cannot write extension makefile ({})'.format(e)) + return + + internal_source_files = [] + external_kexts = [] + + print('the following kernel extensions will be built:') + for kext_id, build_info in selection_info.items(): + try: + kext_info = find_kext_by_id(all_kexts, kext_id) + if kext_info == None: + print('W: cannot find kernel extension with ID "{}"'.format(kext_id)) + continue + + if 'build' not in build_info: + continue + + if build_info['build'] != 'internal' and build_info['build'] != 'external': + continue + + if build_info['build'] == 'internal': + internal_source_files += kext_info.sources_filepath() + elif build_info['build'] == 'external': + external_kexts.append(kext_info) + else: + continue + + print(' * {} ({})'.format(kext_info.id(), build_info['build'])) + except Exception as e: + print(e) + continue + + if len(internal_source_files) > 0: + makefile_fp.write('INTERNAL_KEXT_OBJ := \\\n') + + for i, src in enumerate(internal_source_files): + obj = os.path.splitext(src)[0] + '.o' + makefile_fp.write('\t$(BUILD_DIR)/{}'.format(obj)) + + if i != len(internal_source_files) - 1: + makefile_fp.write(' \\') + makefile_fp.write('\n') + + write_external_kext_variables(makefile_fp, external_kexts) + + for kext in external_kexts: + makefile_fp.write('\nkexts/{}: $({})\n'.format(kext.id(), external_kext_obj_variable_name(kext))) + + + +def select_kexts(): + if os.path.isfile(selection_file_path): + create_kext_makefile() + else: + create_selection_file() diff --git a/tools/socks.kexttool b/tools/socks.kexttool index 5f4bd43..9cf7663 100755 --- a/tools/socks.kexttool +++ b/tools/socks.kexttool @@ -3,7 +3,6 @@ # -*- mode: python -*- import glob -import inquirer import os import sys from kexttool import kext, scan, select