├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── CONTRIBUTING.md ├── COPYRIGHT.txt ├── LICENSE.txt ├── README.md ├── bin └── kernelstub ├── data ├── config │ ├── kernelstub │ └── kernelstub.SAMPLE ├── initramfs │ └── zz-kernelstub └── kernel │ └── zz-kernelstub ├── debian ├── changelog ├── compat ├── conffiles ├── control ├── copyright ├── kernelstub.postinst ├── license ├── readme ├── rules └── source │ ├── format │ └── options ├── install.log ├── kernelstub ├── __init__.py ├── application.py ├── config.py ├── drive.py ├── installer.py ├── kernel_option.py ├── nvram.py └── opsys.py └── setup.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | **Distribution (run `cat /etc/os-release`):** 9 | 10 | 11 | 12 | **Related Application and/or Package Version (run `apt policy $PACKAGE NAME`):** 13 | 14 | 15 | 16 | **Issue/Bug Description:** 17 | 18 | 19 | 20 | **Steps to reproduce (if you know):** 21 | 22 | 23 | 24 | **Expected behavior:** 25 | 26 | 27 | 28 | **Other Notes:** 29 | 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Other files here: 2 | output.log 3 | .buildconfig 4 | 5 | # Debian Packaging artefacts: 6 | debian/.debhelper/ 7 | debian/kernelstub 8 | debian/*.debhelper 9 | debian/*.substvars 10 | debian/files 11 | debian/pop-boot 12 | 13 | # Editor TMP files 14 | *~ 15 | 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *$py.class 20 | 21 | # C extensions 22 | *.so 23 | 24 | # Distribution / packaging 25 | .Python 26 | env/ 27 | build/ 28 | develop-eggs/ 29 | dist/ 30 | downloads/ 31 | eggs/ 32 | .eggs/ 33 | lib/ 34 | lib64/ 35 | parts/ 36 | sdist/ 37 | var/ 38 | wheels/ 39 | *.egg-info/ 40 | .installed.cfg 41 | *.egg 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *.cover 62 | .hypothesis/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # celery beat schedule file 92 | celerybeat-schedule 93 | 94 | # SageMath parsed files 95 | *.sage.py 96 | 97 | # dotenv 98 | .env 99 | 100 | # virtualenv 101 | .venv 102 | venv/ 103 | ENV/ 104 | 105 | # Spyder project settings 106 | .spyderproject 107 | .spyproject 108 | 109 | # Rope project settings 110 | .ropeproject 111 | 112 | # mkdocs documentation 113 | /site 114 | 115 | # mypy 116 | .mypy_cache/ 117 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | The source code in this repository is to be considered a "Collective Work" under 2 | Copyright Law of the United States of America. As such, be advised that code you 3 | contribute to this repository, should it be accepted and merged, shall be 4 | considered part of the repository as a whole with regard to its context within 5 | the repository. This means that your contributions will be made available under 6 | the terms laid out in the LICENCE.md file and may be subject to change in the 7 | future. 8 | 9 | For the purposes of copyright, the person(s) listed as "Owner" of this 10 | repository are considered to be the "Author(s)". 11 | 12 | Please note that this does not diminish your copyright with regard to the code 13 | itself contributed, only to its inclusion in the repository as a whole. For 14 | example, if you contribute code, the copy of code that you contribute to the 15 | repository is then considered to be part of the collective whole. If you later 16 | wish to reuse the code in another project, be it a derivative of this work or a 17 | different work entirely, the inclusion of your code in this project does not 18 | affect your ability to do so regardless of the licence currently in effect. 19 | 20 | This document protects both the rights of the Author(s) to distribute the work 21 | according to their terms, and also the rights of Contributors to express their 22 | ideas free of the legal restrictions imposed by others. 23 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | Kernelstub 2 | 3 | Copyright 2017-2018 Ian Santopietro 4 | 5 | Collective Public Licence, Version 1 6 | 7 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION 8 | 9 | The source code in this repository is to be considered a "Collective Work" under 10 | Copyright Law of any applicable countries. As such, be advised that code you 11 | contribute to this repository, should it be accepted and merged, shall be 12 | considered part of the repository as a whole with regard to its context within 13 | the repository. This means that your contributions will be made available under 14 | the terms laid out in this file and may be subject to change in the future. 15 | 16 | For the purposes of copyright, the person(s) listed as "Owner" of this 17 | repository are considered to be the "Author(s)". 18 | 19 | Please note that this does not diminish your copyright with regard to the code 20 | itself contributed, only to its inclusion in the repository as a whole. For 21 | example, if you contribute code, the copy of code that you contribute to the 22 | repository is then considered to be part of the collective whole. If you later 23 | wish to reuse the code in another project, be it a derivative of this work or a 24 | different work entirely, the inclusion of your code in this project does not 25 | affect your ability to do so regardless of the licence currently in effect. 26 | 27 | This document protects both the rights of the Author(s) to distribute the work 28 | according to their terms, and also the rights of Contributors to express their 29 | ideas free of the legal restrictions imposed by others. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 32 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 33 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 34 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 35 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 36 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 37 | THIS SOFTWARE. 38 | 39 | This software may be distributed under additional terms than those presented here, 40 | provided those terms are not in conflict with the terms provided here. Any 41 | additional license, disclosure, terms, or disclaimer is provided at the end of 42 | this document. 43 | 44 | LICENSE 45 | 46 | Permission to use, copy, modify, and/or distribute this software for any purpose 47 | with or without fee is hereby granted, provided that the above copyright notice 48 | and this permission notice appear in all copies. 49 | 50 | 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Kernelstub 2 | 3 | Copyright 2017-2018 Ian Santopietro 4 | 5 | Collective Public Licence, Version 1 6 | 7 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION 8 | 9 | The source code in this repository is to be considered a "Collective Work" under 10 | Copyright Law of any applicable countries. As such, be advised that code you 11 | contribute to this repository, should it be accepted and merged, shall be 12 | considered part of the repository as a whole with regard to its context within 13 | the repository. This means that your contributions will be made available under 14 | the terms laid out in this file and may be subject to change in the future. 15 | 16 | For the purposes of copyright, the person(s) listed as "Owner" of this 17 | repository are considered to be the "Author(s)". 18 | 19 | Please note that this does not diminish your copyright with regard to the code 20 | itself contributed, only to its inclusion in the repository as a whole. For 21 | example, if you contribute code, the copy of code that you contribute to the 22 | repository is then considered to be part of the collective whole. If you later 23 | wish to reuse the code in another project, be it a derivative of this work or a 24 | different work entirely, the inclusion of your code in this project does not 25 | affect your ability to do so regardless of the licence currently in effect. 26 | 27 | This document protects both the rights of the Author(s) to distribute the work 28 | according to their terms, and also the rights of Contributors to express their 29 | ideas free of the legal restrictions imposed by others. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 32 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 33 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 34 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 35 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 36 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 37 | THIS SOFTWARE. 38 | 39 | This software may be distributed under additional terms than those presented here, 40 | provided those terms are not in conflict with the terms provided here. Any 41 | additional license, disclosure, terms, or disclaimer is provided at the end of 42 | this document. 43 | 44 | LICENSE 45 | 46 | Permission to use, copy, modify, and/or distribute this software for any purpose 47 | with or without fee is hereby granted, provided that the above copyright notice 48 | and this permission notice appear in all copies. 49 | 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Kernelstub 2 | 3 | The automatic manager for booting Linux on (U)EFI. 4 | 5 | Kernelstub is a utility to automatically manage your OS's EFI System Partition 6 | (ESP). It makes it simple to copy the current kernel and initramfs image onto 7 | the ESP so that they are automatically probable by most EFI boot loaders as well 8 | as the EFI firmware itself. It can also set up the system's NVRAM to add entries 9 | to the firmware boot menu for the kernel (and keep these options up to date when 10 | new kernel versions are installed). 11 | 12 | ### Installation 13 | 14 | Installation can be handled through the supplied Debian packaging as well as the 15 | python packaging. If kernelstub is packaged in your distro's repositories, you 16 | can install kernelstub through the `kernelstub` package. 17 | 18 | To build a debian package locally, first install the build dependencies. On 19 | Pop!\_OS, you can install the build dependencies using this command: 20 | ``` 21 | sudo apt build-dep kernelstub 22 | ``` 23 | 24 | Otherwise, you will need to install the following packages: 25 | ``` 26 | python3-all 27 | pyflakes3 28 | debhelper (>= 7.4.3) 29 | dh-python 30 | ``` 31 | 32 | Then use these commands to build the package: 33 | ``` 34 | git clone https://github.com/pop-os/kernelstub 35 | cd kernelstub 36 | dpkg-buildpackage -b -us -uc 37 | sudo dpkg -i ../kernelstub*.deb 38 | ``` 39 | For installation on non-debian systems, or if you prefer to use Python 40 | packaging, use: 41 | ``` 42 | git clone https://github.com/pop-os/kernelstub 43 | cd kernelstub 44 | sudo python3 setup.py install --record=installed_files.txt 45 | ``` 46 | For your convenience, this will create a list of all files installed on the 47 | system in the `installed_files.txt` file, so that you can easily remove the 48 | package later. 49 | 50 | 51 | ### Usage 52 | 53 | Usage is fairly straightforward and usually only requires running the command as 54 | root with `su`/`sudo`. If your computer requires special kernel parameters to 55 | boot, you can specify them as such: 56 | ``` 57 | sudo kernelstub -o "options_go here wrapped in-quotes" 58 | ``` 59 | Running the command with `-o` will save the options used into the user section 60 | of the configuration file (see _Configuration_ below) so that you don't need to 61 | specify them manually each time you run it. 62 | 63 | Kernelstub assumes the `quiet splash` options by default, since these generally 64 | work on every system, at least for booting up to a usable system. 65 | 66 | You can get output of the program using the `-v`/`--verbose` option. There are 67 | three levels of verbosity. The default is to only show `Warning`, `Error`, and 68 | `Critical` failure messages. With one `-v`, kernelstub will display information 69 | about its progress to the command line. Two `-v` flags will also display 70 | debugging information generally only useful to developers. 71 | 72 | By default, kernelstub will attempt to set up an entry in the system NVRAM to 73 | boot the kernel directly. If you want to use kernelstub with a separate program 74 | (e.g. `bootctl`/`systemd-boot` or rEFIt/rEFInd), you can use the `-m` flag. This 75 | causes kernelstub to copy the kernel and initrd into the ESP, but not set up any 76 | NVRAM entries. The `-l` option also explicitly sets up the configuration for 77 | `system-boot` or `gummiboot`. These options are also stored in the config file. 78 | 79 | There are other options as well, as detailed below: 80 | 81 | | Option | Action | 82 | |-------------------------------------------|--------------------------------------------------------| 83 | |`-h`, `--help` | Display the help Text | 84 | |`-c`, `--dry-run` | Don't actually copy any files or set anything up. | 85 | |`-p`, `--print-config` | Print the current configuration and exit. | 86 | |*_Path Options_* | | 87 | |`-r `, `--root-path ` | Manually specify the root filesystem path. | 88 | |`-e `, `--esp-path ` | Manually specify the ESP path.* | 89 | |`-k `, `--kernel-path ` | Manually specify the path to the kernel image. | 90 | |`-i `, `--initrd-path ` | Manually specify the path to the initrd image. | 91 | |*_Kernel Parameters_* | | 92 | |`-o `,`--options ` | Set kernel boot options.* | 93 | |`-a ` ,`--add-options | Adds new options to the list of kernel boot options.*⁺ | 94 | |`-d ` ,`--delete-options | Remove options from the list of kernel boot options.*⁺ | 95 | *_Output/logging Options_* | | 96 | |`-v`, `--verbose` | Display more information to the command line | 97 | |`-g `,`--log-file ` | Where to save the log file. | 98 | |*_Behavior Options_* | | 99 | |`-l`, `--loader` | Create a `systemd-boot`-compatible loader config.* | 100 | |`-n`, `--no-loader` | Turns off creating the loader configuration. | 101 | |`-s`, `--stub` | Set up NVRAM entries for the copied kernel. | 102 | |`-m`, `--manage-only` | Don't set up any NVRAM entries.* | 103 | |`-f`, `--force-update` | Forcefully update the main loader.conf.** | 104 | 105 | *These options save information to the config file. 106 | 107 | **This may overwrite another OS's information. 108 | 109 | ⁺Does not add options if they are already present in the configuration, or 110 | remove options if they are not present. Each option is checked individually. 111 | 112 | ### Configuration 113 | 114 | Kernelstub has a robust configuration system with multiple fallbacks for safety. 115 | By default, a sample (non-functional) configuration file is provided in 116 | `/etc/kernelstub/SAMPLE` which demonstrates the available options and 117 | the JSON syntax. The main configuration file is stored in 118 | `/etc/kernelstub/configuration`, which is created by default if it doesn't exist. 119 | Kernelstub also has a copy of the default configuration stored internally, so 120 | that it can create a config file if none already exists. 121 | 122 | When kernelstub is run with the `-o`, `-l`, `-s`, or `-m` options, this is 123 | recorded in the config file so that future automatic runs or runs without 124 | options will work correctly. Options specified on the command line always take 125 | precedence over options in the config files. 126 | 127 | Your distribution or package maintainer may additionally create a configuration 128 | template in `/etc/default/kernelstub`. This should not be edited except by 129 | maintaners. Use the standard config file instead, as this will be loaded instead 130 | of the distributor file if it exists, and options will never be saved to the 131 | distributor file. 132 | 133 | 134 | ### Return codes 135 | 136 | If kernelstub is going to be used in a scripted environment, it is useful to 137 | know what return codes it provides in the event of errors. The table below 138 | details these codes and their meaning: 139 | 140 | | Exit Code | Meaning | 141 | |-----------|--------------------------------------------------------------| 142 | | 0 | Success | 143 | | 166 | The kernel path supplied/detected was invalid | 144 | | 167 | The initrd path supplied/detected was invalid | 145 | | 168 | No kernel options found/supplied | 146 | | 169 | Malformed configuration found | 147 | | 170 | Couldn't copy kernel image to ESP | 148 | | 171 | Couldn't copy initrd image to ESP | 149 | | 172 | Couldn't create a new NVRAM entry | 150 | | 173 | Couldn't remove an old NVRAM entry | 151 | | 174 | Couldn't detect the block device file for the root partition | 152 | | 175 | Coundn't detect the block device file for the ESP | 153 | | 176 | Wasn't run as root | 154 | | 177 | Couldn't get a required UUID | 155 | 156 | 157 | ### Licence 158 | 159 | Kernelstub is available under an COLPL + ISC-based license. The full license is 160 | supplied in the LICENSE.txt file. 161 | 162 | Copyright 2017-2018 Ian Santopietro 163 | 164 | Permission to use, copy, modify, and/or distribute this software for any purpose 165 | with or without fee is hereby granted, provided that the above copyright notice 166 | and this permission notice appear in all copies. 167 | 168 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 169 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 170 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 171 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 172 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 173 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 174 | THIS SOFTWARE. 175 | -------------------------------------------------------------------------------- /bin/kernelstub: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | 24 | This program will automatically keep a copy of the Linux Kernel image and your 25 | initrd.img located on your EFI System Partition (ESP) on an EFI-compatible 26 | system. The benefits of this approach include being able to boot Linux directly 27 | and bypass the GRUB bootloader in many cases, which can save 4-6 seconds during 28 | boot on a system with a fast drive. Please note that there are no guarantees 29 | about boot time saved using this method. 30 | 31 | For maximum safety, kernelstub recommends leaving an entry for GRUB in your 32 | system's ESP and NVRAM configuration, as GRUB allows you to modify boot 33 | parameters per-boot, which loading the kernel directly cannot do. The only 34 | other way to do this is to use an EFI shell to manually load the kernel and 35 | give it parameters from the EFI Shell. You can do this by using: 36 | 37 | fs0:> vmlinuz-image initrd=EFI/path/to/initrd/stored/on/esp options 38 | 39 | kernelstub will load parameters from the /etc/default/kernelstub config file. 40 | """ 41 | 42 | import argparse, os 43 | 44 | from kernelstub import application 45 | 46 | def main(options=None): # Do the thing 47 | kernelstub = application.Kernelstub() 48 | # Set up argument processing 49 | parser = argparse.ArgumentParser( 50 | description = "Automatic Kernel EFIstub manager") 51 | loader_stub = parser.add_mutually_exclusive_group() 52 | install_loader = parser.add_mutually_exclusive_group() 53 | 54 | parser.add_argument( 55 | '-c', 56 | '--dry-run', 57 | action = 'store_true', 58 | dest = 'dry_run', 59 | help = 'Don\'t perform any actions, just simulate them.' 60 | ) 61 | parser.add_argument( 62 | '-p', 63 | '--print-config', 64 | action = 'store_true', 65 | dest = 'print_config', 66 | help = 'Print the current configuration and exit' 67 | ) 68 | 69 | parser.add_argument( 70 | '-e', 71 | dest = 'esp_path', 72 | metavar = 'ESP,', 73 | help = '' 74 | ) 75 | parser.add_argument( 76 | '--esp-path', 77 | dest = 'esp_path', 78 | metavar = 'ESP', 79 | help = 'Manually specify the path to the ESP. Default is /boot/efi' 80 | ) 81 | 82 | parser.add_argument( 83 | '-r', 84 | dest = 'root_path', 85 | metavar = 'ROOT', 86 | help = '' 87 | ) 88 | parser.add_argument( 89 | '--root-path', 90 | dest = 'root_path', 91 | metavar = 'ROOT', 92 | help = 'The path where the root filesystem to use is mounted.' 93 | ) 94 | 95 | parser.add_argument( 96 | '-k', 97 | dest = 'kernel_path', 98 | metavar= 'PATH,', 99 | help = '' 100 | ) 101 | parser.add_argument( 102 | '--kernel-path', 103 | dest = 'kernel_path', 104 | metavar= 'PATH', 105 | help = 'The path to the kernel image.' 106 | ) 107 | 108 | parser.add_argument( 109 | '-i', 110 | dest = 'initrd_path', 111 | metavar = 'PATH,', 112 | help = '' 113 | ) 114 | parser.add_argument( 115 | '--initrd-path', 116 | dest = 'initrd_path', 117 | metavar = 'PATH', 118 | help = 'The path to the initrd image.' 119 | ) 120 | 121 | parser.add_argument( 122 | '-o', 123 | dest = 'k_options', 124 | metavar = '"OPTIONS",', 125 | help = '' 126 | ) 127 | parser.add_argument( 128 | '--options', 129 | dest = 'k_options', 130 | metavar = '"OPTIONS"', 131 | help = 'The total boot options to be passed to the kernel' 132 | ) 133 | 134 | parser.add_argument( 135 | '-a', 136 | dest = 'add_options', 137 | metavar = '"OPTIONS",', 138 | help = '' 139 | ) 140 | parser.add_argument( 141 | '--add-options', 142 | dest = 'add_options', 143 | metavar = '"OPTIONS"', 144 | help = ('Boot options to add to the configuration ' 145 | '(if they aren\'t already present)') 146 | ) 147 | 148 | parser.add_argument( 149 | '-d', 150 | dest = 'remove_options', 151 | metavar = '"OPTIONS"', 152 | help = '' 153 | ) 154 | parser.add_argument( 155 | '--delete-options', 156 | dest = 'remove_options', 157 | metavar = '"OPTIONS"', 158 | help = ('Boot options to remove from the configuration ' 159 | '(if they\'re present already)') 160 | ) 161 | 162 | parser.add_argument( 163 | '-g', 164 | dest = 'log_file', 165 | metavar = 'LOG', 166 | help = '' 167 | ) 168 | parser.add_argument( 169 | '--log-file', 170 | dest = 'log_file', 171 | metavar = 'LOG', 172 | help = ('The path to the log file to use. Defaults to ' 173 | '/var/log/kernelstub.log') 174 | ) 175 | 176 | install_loader.add_argument( 177 | '-l', 178 | '--loader', 179 | action = 'store_true', 180 | dest = 'setup_loader', 181 | help = 'Creates a systemd-boot compatible loader configuration' 182 | ) 183 | install_loader.add_argument( 184 | '-n', 185 | '--no-loader', 186 | action = 'store_true', 187 | dest = 'off_loader', 188 | help = 'Turns off creating loader configuration' 189 | ) 190 | 191 | loader_stub.add_argument( 192 | '-s', 193 | '--stub', 194 | action = 'store_true', 195 | dest = 'install_stub', 196 | help = 'Set up NVRAM entries for the copied kernel' 197 | ) 198 | 199 | loader_stub.add_argument( 200 | '-m', 201 | '--manage-only', 202 | action = 'store_true', 203 | dest = 'manage_mode', 204 | help = 'Only copy entries, don\'t set up the NVRAM' 205 | ) 206 | 207 | parser.add_argument( 208 | '-f', 209 | '--force-update', 210 | action = 'store_true', 211 | dest = 'force_update', 212 | help = ('Forcibly update any loader.conf to set the new entry as the ' 213 | 'default') 214 | ) 215 | 216 | parser.add_argument( 217 | '-v', 218 | '--verbose', 219 | action = 'count', 220 | dest = 'verbosity', 221 | help = 'Increase program verbosity and display extra output.' 222 | ) 223 | 224 | parser.add_argument( 225 | '--preserve-live-mode', 226 | action = 'store_true', 227 | dest = 'preserve_live', 228 | help = argparse.SUPPRESS 229 | ) 230 | 231 | args = parser.parse_args() 232 | if options: 233 | args = parser.parse_args(options) 234 | 235 | if os.geteuid() != 0: 236 | parser.print_help() 237 | print('kernelstub: ERROR: You need to be root or use sudo to run ' 238 | 'kernelstub!') 239 | exit(176) 240 | 241 | kernelstub.main(args) 242 | 243 | if __name__ == '__main__': 244 | main() 245 | -------------------------------------------------------------------------------- /data/config/kernelstub: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "kernel_options": "quiet loglevel=0", 4 | "esp_path": "/boot/efi", 5 | "setup_loader": false, 6 | "manage_mode": true, 7 | "force_update": false, 8 | "live_mode": false, 9 | "config_rev": 2 10 | }, 11 | } -------------------------------------------------------------------------------- /data/config/kernelstub.SAMPLE: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "kernel_options": "quiet splash", 4 | "esp_path": "/boot/efi", 5 | "setup_loader": false, 6 | "manage_mode": false, 7 | "force_update": false, 8 | "live_mode": false, 9 | "config_rev": 2 10 | }, 11 | } -------------------------------------------------------------------------------- /data/initramfs/zz-kernelstub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | KERNEL="/boot/vmlinuz-$1" 4 | INITRD="$2" 5 | 6 | kernelstub \ 7 | --verbose \ 8 | --preserve-live-mode 9 | -------------------------------------------------------------------------------- /data/kernel/zz-kernelstub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | INITRD="/boot/initrd.img-$1" 4 | KERNEL="$2" 5 | 6 | kernelstub \ 7 | --verbose \ 8 | --preserve-live-mode 9 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | kernelstub (3.1.4) bionic; urgency=medium 2 | 3 | * Do live mode check immediately after parsing config 4 | 5 | -- Jeremy Soller Wed, 15 Apr 2020 09:15:19 -0600 6 | 7 | kernelstub (3.1.3) eoan; urgency=medium 8 | 9 | * Redo the old-version detection. (fix #38) 10 | 11 | -- Ian Santopietro Thu, 26 Mar 2020 12:17:00 -0600 12 | 13 | kernelstub (3.1.2) eoan; urgency=medium 14 | 15 | * Use latest kernel option if no kernel parameter is provided 16 | 17 | -- Jeremy Soller Tue, 08 Oct 2019 15:10:55 -0600 18 | 19 | kernelstub (3.1.1) eoan; urgency=medium 20 | 21 | * Check for vmlinuz and initrd.img links in /boot 22 | 23 | -- Jeremy Soller Wed, 07 Aug 2019 13:00:38 -0600 24 | 25 | kernelstub (3.1.0) bionic; urgency=medium 26 | 27 | * Add logging to systemd journald 28 | 29 | -- Ian Santopietro Tue, 25 Sep 2018 10:53:23 -0600 30 | 31 | kernelstub (3.0.0) bionic; urgency=medium 32 | 33 | * Breaking API Change in 2.3.0 34 | Existing option renamed/removed: --dry-run 35 | Short-form changed 36 | 37 | -- Ian Santopietro Wed, 13 Jun 2018 10:39:08 -0600 38 | 39 | kernelstub (2.3.0) bionic; urgency=medium 40 | 41 | * Add flag to remove kernel options from the configuration 42 | 43 | -- Ian Santopietro Wed, 13 Jun 2018 10:03:03 -0600 44 | 45 | kernelstub (2.2.1) bionic; urgency=medium 46 | 47 | * Cause kernelstub to exit without an error code if the initrd or kernel 48 | images can't be found. 49 | 50 | -- Ian Santopietro Wed, 23 May 2018 14:14:34 -0600 51 | 52 | kernelstub (2.2.0) bionic; urgency=medium 53 | 54 | * Added an option to add kernel options 55 | 56 | -- Ian Santopietro Tue, 01 May 2018 10:15:52 -0700 57 | 58 | kernelstub (2.1.0) bionic; urgency=medium 59 | 60 | * New live-mode feature to prevent automatic runs on live disks 61 | 62 | -- Ian Santopietro Tue, 17 Apr 2018 10:52:20 -0600 63 | 64 | kernelstub (2.0.8) artful; urgency=medium 65 | 66 | * Pyflakes in build process 67 | * Code review feedback 68 | * More reliable UUID detection 69 | * Less embedded sh 70 | 71 | -- Ian Santopietro Fri, 23 Feb 2018 10:43:42 -0700 72 | 73 | kernelstub (2.0.7) artful; urgency=medium 74 | 75 | * Rewrite drive detection code to be more reliable 76 | 77 | -- Ian Santopietro Thu, 15 Feb 2018 12:17:13 -0700 78 | 79 | kernelstub (2.0.6.1) artful; urgency=medium 80 | 81 | * Fix cmdline installation order 82 | 83 | -- Ian Santopietro Wed, 14 Feb 2018 11:15:08 -0700 84 | 85 | kernelstub (2.0.6) artful; urgency=medium 86 | 87 | * Fix adding stub to NVRAM (-s flag) 88 | * Re-add log file 89 | * Use conffiles for marking old files removed 90 | * Removed irrelevant ERROR output when only a single kernel is installed 91 | 92 | -- Ian Santopietro Wed, 14 Feb 2018 10:17:57 -0700 93 | 94 | kernelstub (2.0.5) artful; urgency=medium 95 | 96 | * Add options for: 97 | - Specifying Root filesystem path (for recovery) 98 | - Printing current configuration options 99 | - Turning off setting up the loader config 100 | * Update automatic hooks to use / symlinks 101 | 102 | -- Ian Santopietro Mon, 05 Feb 2018 10:36:13 -0700 103 | 104 | kernelstub (2.0.4) artful; urgency=medium 105 | 106 | * Use /vmlinuz and /initrd.img symlinks 107 | 108 | -- Ian Santopietro Thu, 01 Feb 2018 09:47:27 -0700 109 | 110 | kernelstub (2.0.3) artful; urgency=medium 111 | 112 | * Update to include quotes in update hooks 113 | 114 | -- Ian Santopietro Tue, 30 Jan 2018 14:51:17 -0700 115 | 116 | kernelstub (2.0.2) artful; urgency=medium 117 | 118 | * Update iniramfs and kernel triggers with new option names. 119 | 120 | -- Ian Santopietro Mon, 29 Jan 2018 10:27:49 -0700 121 | 122 | kernelstub (2.0.1) artful; urgency=medium 123 | 124 | * Add pop-boot package for awesome installs. 125 | 126 | -- Ian Santopietro Wed, 24 Jan 2018 11:00:12 -0700 127 | 128 | kernelstub (2.0.0) artful; urgency=medium 129 | 130 | * Re-written with new, more organized sturcture. 131 | 132 | -- Ian Santopietro Wed, 24 Jan 2018 10:59:28 -0700 133 | 134 | kernelstub (1.0.0) artful; urgency=medium 135 | 136 | * 1.0 release. 137 | * New ISC license instead of BSD. 138 | * First release from Git. 139 | 140 | -- Ian Santopietro Wed, 08 Nov 2017 09:47:42 -0700 141 | 142 | kernelstub (0.19) zesty; urgency=medium 143 | 144 | * Improve reliability on old-kernel removal 145 | 146 | -- Ian Santopietro Tue, 25 Apr 2017 15:08:26 -0600 147 | 148 | kernelstub (0.18) zesty; urgency=low 149 | 150 | * source package automatically created by stdeb 0.8.5 151 | 152 | -- Ian Santopietro Mon, 27 Mar 2017 11:06:36 -0600 153 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/conffiles: -------------------------------------------------------------------------------- 1 | /etc/default/kernelstub.SAMPLE 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: kernelstub 2 | Maintainer: Ian Santopietro 3 | Section: python 4 | Priority: optional 5 | Build-Depends: python3-all, pyflakes3, debhelper (>= 7.4.3), dh-python, python3-setuptools 6 | Standards-Version: 3.9.1 7 | 8 | Package: kernelstub 9 | Architecture: all 10 | Depends: ${misc:Depends}, ${python3:Depends}, efibootmgr, python3-debian, util-linux 11 | Recommends: python3-systemd 12 | Description: Automatic kernel efistub manager for UEFI 13 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Copyright 2017 Ian Santopietro 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | 15 | 16 | -------------------------------------------------------------------------------- /debian/kernelstub.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kernelstub \ 4 | --verbose \ 5 | --preserve-live-mode 6 | -------------------------------------------------------------------------------- /debian/license: -------------------------------------------------------------------------------- 1 | Kernelstub 2 | 3 | Copyright 2017-2018 Ian Santopietro 4 | 5 | Collective Public Licence, Version 1 6 | 7 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION 8 | 9 | The source code in this repository is to be considered a "Collective Work" under 10 | Copyright Law of any applicable countries. As such, be advised that code you 11 | contribute to this repository, should it be accepted and merged, shall be 12 | considered part of the repository as a whole with regard to its context within 13 | the repository. This means that your contributions will be made available under 14 | the terms laid out in this file and may be subject to change in the future. 15 | 16 | For the purposes of copyright, the person(s) listed as "Owner" of this 17 | repository are considered to be the "Author(s)". 18 | 19 | Please note that this does not diminish your copyright with regard to the code 20 | itself contributed, only to its inclusion in the repository as a whole. For 21 | example, if you contribute code, the copy of code that you contribute to the 22 | repository is then considered to be part of the collective whole. If you later 23 | wish to reuse the code in another project, be it a derivative of this work or a 24 | different work entirely, the inclusion of your code in this project does not 25 | affect your ability to do so regardless of the licence currently in effect. 26 | 27 | This document protects both the rights of the Author(s) to distribute the work 28 | according to their terms, and also the rights of Contributors to express their 29 | ideas free of the legal restrictions imposed by others. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 32 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 33 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 34 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 35 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 36 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 37 | THIS SOFTWARE. 38 | 39 | This software may be distributed under additional terms than those presented here, 40 | provided those terms are not in conflict with the terms provided here. Any 41 | additional license, disclosure, terms, or disclaimer is provided at the end of 42 | this document. 43 | 44 | LICENSE 45 | 46 | Permission to use, copy, modify, and/or distribute this software for any purpose 47 | with or without fee is hereby granted, provided that the above copyright notice 48 | and this permission notice appear in all copies. 49 | 50 | 51 | -------------------------------------------------------------------------------- /debian/readme: -------------------------------------------------------------------------------- 1 | ## Kernelstub 2 | 3 | The automatic manager for booting Linux on (U)EFI. 4 | 5 | Kernelstub is a utility to automatically manage your OS's EFI System Partition 6 | (ESP). It makes it simple to copy the current kernel and initramfs image onto 7 | the ESP so that they are automatically probable by most EFI boot loaders as well 8 | as the EFI firmware itself. It can also set up the system's NVRAM to add entries 9 | to the firmware boot menu for the kernel (and keep these options up to date when 10 | new kernel versions are installed). 11 | 12 | ### Installation 13 | 14 | Installation can be handled through the supplied Debian packaging as well as the 15 | python packaging. If kernelstub is packaged in your distro's repositories, you 16 | can install kernelstub through the `kernelstub` package. To build a debian 17 | package locally, use these commands: 18 | ``` 19 | git clone https://github.com/isantop/kernelstub 20 | cd kernelstub 21 | debuild -B 22 | sudo dpkg -i ../kernelstub*.deb 23 | ``` 24 | For installation on non-debian systems, or if you prefer to use Python 25 | packaging, use: 26 | ``` 27 | git clone https://github.com/isantop/kernelstub 28 | cd kernelstub 29 | sudo python3 setup.py install --record > installed_files.txt 30 | ``` 31 | For your convenience, this will create a list of all files installed on the 32 | system in the `installed_files.txt` file, so that you can easily remove the 33 | package later. 34 | 35 | 36 | ### Usage 37 | 38 | Usage is fairly straightforward and usually only requires running the command as 39 | root with `su`/`sudo`. If your computer requires special kernel parameters to 40 | boot, you can specify them as such: 41 | ``` 42 | sudo kernelstub -c "options_go here wrapped in-quotes" 43 | ``` 44 | Running the command with `-o` will save the options used into the user section 45 | of the configuration file (see _Configuration_ below) so that you don't need to 46 | specify them manually each time you run it. 47 | 48 | Kernelstub assumes the `quiet splash` options by default, since these generally 49 | work on every system, at least for booting up to a usable system. 50 | 51 | You can get output of the program using the `-v`/`--verbose` option. There are 52 | three levels of verbosity. The default is to only show `Warning`, `Error`, and 53 | `Critical` failure messages. With one `-v`, kernelstub will display information 54 | about its progress to the command line. Two `-v` flags will also display 55 | debugging information generally only useful to developers. 56 | 57 | By default, kernelstub will attempt to set up an entry in the system NVRAM to 58 | boot the kernel directly. If you want to use kernelstub with a separate program 59 | (e.g. `bootctl`/`systemd-boot` or rEFIt/rEFInd), you can use the `-m` flag. This 60 | causes kernelstub to copy the kernel and initrd into the ESP, but not set up any 61 | NVRAM entries. The `-l` option also explicitly sets up the configuration for 62 | `system-boot` or `gummiboot`. These options are also stored in the config file. 63 | 64 | There are other options as well, as detailed below: 65 | 66 | | Option | Action | 67 | |--------------------------|---------------------------------------------------| 68 | |`-h`, `--help` | Display the help Text | 69 | |`-d`, `--dry-run` | Don't actually copy any files or set anything up. | 70 | |`-e PATH`, `--esp_path` | Manually specify the ESP path.* | 71 | |`-k PATH`, `--kernelpath` | Manually specify the path to the kernel image. | 72 | |`-i PATH`, `--initrd_path`| Manually specify the path to the initrd image. | 73 | |`-o "options"`,`--options`| Set kernel boot options.* | 74 | |`-l`, `--loader` | Create a `systemd-boot`-compatible loader config.*| 75 | |`-s`, `--stub` | Set up NVRAM entries for the copied kernel. | 76 | |`-m`, `--manage-only` | Don't set up any NVRAM entries.* | 77 | |`-f`, `--force-update` | Forcefully update the main loader.conf.** | 78 | |`-v`, `--verbose` | Display more information to the command line | 79 | 80 | *These options save information to the config file. 81 | **This may overwrite another OS's information. 82 | 83 | ### Configuration 84 | 85 | Kernelstub has a robust configuration system with multiple fallbacks for safety. 86 | By default, a sample (non-functional) configuration file is provided in 87 | `/etc/kernelstub/SAMPLE` which demonstrates the available options and 88 | the JSON syntax. The main configuration file is stored in 89 | `/etc/kernelstub/configuration`, which is created by default if it doesn't exist. 90 | Kernelstub also has a copy of the default configuration stored internally, so 91 | that it can create a config file if none already exists. 92 | 93 | When kernelstub is run with the `-o`, `-l`, `-s`, or `-m` options, this is 94 | recorded in the config file so that future automatic runs or runs without 95 | options will work correctly. Options specified on the command line always take 96 | precedence over options in the config files. 97 | 98 | Your distribution or package maintainer may additionally create a configuration 99 | template in `/etc/default/kernelstub`. This should not be edited except by 100 | maintaners. Use the standard config file instead, as this will be loaded instead 101 | of the distributor file if it exists, and options will never be saved to the 102 | distributor file. 103 | 104 | 105 | ### Return codes 106 | 107 | If kernelstub is going to be used in a scripted environment, it is useful to 108 | know what return codes it provides in the event of errors. If everything 109 | appeared to work correctly and kernelstub exited successfully, it returns 0. If 110 | there was a problem parsing the configuration file, it returns 2. If there was a 111 | problem copying a file needed for installation, it returns 3. 112 | 113 | 114 | ### Licence 115 | 116 | Kernelstub is available under an ISC-based licence. The full licence is below: 117 | 118 | Copyright 2017-2018 Ian Santopietro 119 | 120 | Permission to use, copy, modify, and/or distribute this software for any purpose 121 | with or without fee is hereby granted, provided that the above copyright notice 122 | and this permission notice appear in all copies. 123 | 124 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 125 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 126 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 127 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 128 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 129 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 130 | THIS SOFTWARE. 131 | 132 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # This file was automatically generated by stdeb 0.8.5 at 4 | # Mon, 27 Mar 2017 11:06:36 -0600 5 | 6 | %: 7 | dh $@ --with python3 --buildsystem=python_distutils 8 | 9 | override_dh_auto_clean: 10 | python3 setup.py clean -a 11 | find . -name \*.pyc -exec rm {} \; 12 | 13 | override_dh_auto_build: 14 | python3 setup.py build --force 15 | 16 | override_dh_auto_test: 17 | set -ex; for python in $(shell py3versions -r); do \ 18 | $$python -Werror setup.py test; \ 19 | done 20 | 21 | override_dh_auto_install: 22 | python3 setup.py install --force --root=debian/kernelstub --no-compile -O0 --install-layout=deb 23 | 24 | override_dh_installdeb: 25 | dh_installdeb 26 | cp debian/conffiles debian/kernelstub/DEBIAN/conffiles 27 | 28 | override_dh_python2: 29 | dh_python2 --no-guessing-versions 30 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | extend-diff-ignore="\.egg-info$" -------------------------------------------------------------------------------- /install.log: -------------------------------------------------------------------------------- 1 | /usr/local/lib/python3.6/dist-packages/kernelstub/loader.py 2 | /usr/local/lib/python3.6/dist-packages/kernelstub/nvram.py 3 | /usr/local/lib/python3.6/dist-packages/kernelstub/config.py 4 | /usr/local/lib/python3.6/dist-packages/kernelstub/application.py 5 | /usr/local/lib/python3.6/dist-packages/kernelstub/drive.py 6 | /usr/local/lib/python3.6/dist-packages/kernelstub/__init__.py 7 | /usr/local/lib/python3.6/dist-packages/kernelstub/opers.py 8 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/loader.cpython-36.pyc 9 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/nvram.cpython-36.pyc 10 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/config.cpython-36.pyc 11 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/application.cpython-36.pyc 12 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/drive.cpython-36.pyc 13 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/__init__.cpython-36.pyc 14 | /usr/local/lib/python3.6/dist-packages/kernelstub/__pycache__/opers.cpython-36.pyc 15 | /usr/local/bin/kernelstub 16 | /etc/kernel/postinst.d/zz-kernelstub 17 | /etc/initramfs/post-update.d/zz-kernelstub 18 | /etc/default/kernelstub.SAMPLE 19 | /usr/local/lib/python3.6/dist-packages/kernelstub-2.0.0.egg-info 20 | -------------------------------------------------------------------------------- /kernelstub/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | """ 24 | 25 | # Code is not needed here 26 | -------------------------------------------------------------------------------- /kernelstub/application.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | 24 | This program will automatically keep a copy of the Linux Kernel image and your 25 | initrd.img located on your EFI System Partition (ESP) on an EFI-compatible 26 | system. The benefits of this approach include being able to boot Linux directly 27 | and bypass the GRUB bootloader in many cases, which can save 4-6 seconds during 28 | boot on a system with a fast drive. Please note that there are no guarantees 29 | about boot time saved using this method. 30 | 31 | For maximum safety, kernelstub recommends leaving an entry for GRUB in your 32 | system's ESP and NVRAM configuration, as GRUB allows you to modify boot 33 | parameters per-boot, which loading the kernel directly cannot do. The only 34 | other way to do this is to use an EFI shell to manually load the kernel and 35 | give it parameters from the EFI Shell. You can do this by using: 36 | 37 | fs0:> vmlinuz-image initrd=EFI/path/to/initrd/stored/on/esp options 38 | 39 | kernelstub will load parameters from the /etc/default/kernelstub config file. 40 | """ 41 | 42 | import logging, os 43 | 44 | systemd_support = False 45 | try: 46 | from systemd.journal import JournalHandler 47 | systemd_support = True 48 | 49 | except ImportError: 50 | pass 51 | 52 | import logging.handlers as handlers 53 | 54 | from . import drive as Drive 55 | from . import nvram as Nvram 56 | from . import opsys as Opsys 57 | from . import installer as Installer 58 | from . import config as Config 59 | from . import kernel_option as KernelOption 60 | 61 | class CmdLineError(Exception): 62 | pass 63 | 64 | class Kernelstub(): 65 | 66 | def parse_options(self, options): 67 | for index, option in enumerate(options): 68 | if '"' in option: 69 | matched = False 70 | itr = 1 71 | while matched == False: 72 | try: 73 | next_option = options[index + itr] 74 | option = '%s %s' % (option, next_option) 75 | options[index + itr] = "" 76 | if '"' in next_option: 77 | matched = True 78 | else: 79 | itr = itr + 1 80 | except IndexError: 81 | matched = True 82 | options[index] = option 83 | return options 84 | 85 | def main(self, args): # Do the thing 86 | 87 | log_file_path = '/var/log/kernelstub.log' 88 | if args.log_file: 89 | log_file_path = args.log_file 90 | 91 | verbosity = 0 92 | if args.verbosity: 93 | verbosity = args.verbosity 94 | if verbosity > 2: 95 | verbosity = 2 96 | 97 | if args.print_config: 98 | verbosity = 1 99 | 100 | level = { 101 | 0 : logging.WARNING, 102 | 1 : logging.INFO, 103 | 2 : logging.DEBUG, 104 | } 105 | 106 | console_level = level[verbosity] 107 | file_level = level[2] 108 | 109 | stream_fmt = logging.Formatter( 110 | '%(name)-21s: %(levelname)-8s %(message)s') 111 | file_fmt = logging.Formatter( 112 | '%(asctime)s - %(name)-21s: %(levelname)-8s %(message)s') 113 | log = logging.getLogger('kernelstub') 114 | 115 | console_log = logging.StreamHandler() 116 | console_log.setFormatter(stream_fmt) 117 | console_log.setLevel(console_level) 118 | 119 | file_log = handlers.RotatingFileHandler( 120 | log_file_path, maxBytes=(1048576*5), backupCount=5) 121 | file_log.setFormatter(file_fmt) 122 | file_log.setLevel(file_level) 123 | 124 | log.addHandler(console_log) 125 | log.addHandler(file_log) 126 | 127 | if systemd_support: 128 | journald_log = JournalHandler() 129 | journald_log.setLevel(file_level) 130 | journald_log.setFormatter(stream_fmt) 131 | log.addHandler(journald_log) 132 | 133 | log.setLevel(logging.DEBUG) 134 | 135 | log.debug('Got command line options: %s' % args) 136 | 137 | # Figure out runtime options 138 | no_run = False 139 | if args.dry_run: 140 | no_run = True 141 | 142 | config = Config.Config() 143 | configuration = config.config['user'] 144 | 145 | if args.preserve_live and configuration['live_mode']: 146 | configuration['live_mode'] = True 147 | log.warning( 148 | 'Live mode is enabled!\n' 149 | 'Kernelstub is running in live environment mode. This usually ' 150 | 'means that you are running a live disk, and kernelstub should ' 151 | 'not run. We are thus exiting with 0.\n' 152 | 'If you are not running a live disk, please run ' 153 | '`sudo kernelstub` to disable live mode.' 154 | ) 155 | exit(0) 156 | 157 | configuration['live_mode'] = False 158 | 159 | if args.esp_path: 160 | configuration['esp_path'] = args.esp_path 161 | 162 | root_path = "/" 163 | if args.root_path: 164 | root_path = args.root_path 165 | 166 | boot_path = os.path.join(root_path, 'boot') 167 | latest_option, previous_option = KernelOption.latest_option(boot_path) 168 | 169 | opsys = Opsys.OS() 170 | 171 | if args.kernel_path: 172 | log.debug( 173 | 'Manually specified kernel path:\n ' + 174 | ' %s' % args.kernel_path) 175 | opsys.kernel_path = args.kernel_path 176 | elif latest_option: 177 | opsys.kernel_path = latest_option['kernel'] 178 | else: 179 | opsys.kernel_path = os.path.join(boot_path, opsys.kernel_name) 180 | if not os.path.exists(opsys.kernel_path): 181 | opsys.kernel_path = os.path.join(root_path, opsys.kernel_name) 182 | 183 | if args.initrd_path: 184 | log.debug( 185 | 'Manually specified initrd path:\n ' + 186 | ' %s' % args.initrd_path) 187 | opsys.initrd_path = args.initrd_path 188 | elif latest_option: 189 | opsys.initrd_path = latest_option['initrd'] 190 | else: 191 | opsys.initrd_path = os.path.join(boot_path, opsys.initrd_name) 192 | if not os.path.exists(opsys.initrd_path): 193 | opsys.initrd_path = os.path.join(root_path, opsys.initrd_name) 194 | 195 | if previous_option: 196 | opsys.old_kernel_path = previous_option['kernel'] 197 | opsys.old_initrd_path = previous_option['initrd'] 198 | else: 199 | # We use the default location in / before overwriting to /boot/ 200 | # Then we can use the existing fallbacks in installer. 201 | if not os.path.exists(opsys.old_kernel_path): 202 | opsys.old_kernel_path = os.path.join( 203 | boot_path, opsys.old_kernel_name 204 | ) 205 | 206 | if not os.path.exists(opsys.old_initrd_path): 207 | opsys.old_initrd_path = os.path.join( 208 | boot_path, opsys.old_initrd_name 209 | ) 210 | 211 | if not os.path.exists(opsys.kernel_path): 212 | log.exception('Can\'t find the kernel image \'' + opsys.kernel_path + '\'! \n\n' 213 | 'Please use the --kernel-path option to specify ' 214 | 'the path to the kernel image') 215 | exit(0) 216 | 217 | if not os.path.exists(opsys.initrd_path): 218 | log.exception('Can\'t find the initrd image \'' + opsys.initrd_path + '\'! \n\n' 219 | 'Please use the --initrd-path option to specify ' 220 | 'the path to the initrd image') 221 | exit(0) 222 | 223 | # Check for kernel parameters. Without them, stop and fail 224 | if args.k_options: 225 | configuration['kernel_options'] = self.parse_options(args.k_options.split()) 226 | else: 227 | try: 228 | configuration['kernel_options'] 229 | except KeyError: 230 | error = ("cmdline was 'InvalidConfig'\n\n" 231 | "Could not find any valid configuration. This " 232 | "probably means that the configuration file is " 233 | "corrupt. Either remove it to regenerate it from" 234 | "default or fix the existing one.") 235 | log.exception(error) 236 | raise CmdLineError("No Kernel Parameters found") 237 | exit(168) 238 | 239 | log.debug(config.print_config()) 240 | 241 | if args.setup_loader: 242 | configuration['setup_loader'] = True 243 | if args.off_loader: 244 | configuration['setup_loader'] = False 245 | 246 | if args.install_stub: 247 | configuration['manage_mode'] = False 248 | if args.manage_mode: 249 | configuration['manage_mode'] = True 250 | 251 | 252 | log.debug('Checking configuration integrity...') 253 | try: 254 | kernel_opts = configuration['kernel_options'] 255 | esp_path = configuration['esp_path'] 256 | setup_loader = configuration['setup_loader'] 257 | manage_mode = configuration['manage_mode'] 258 | force = configuration['force_update'] 259 | 260 | except KeyError: 261 | log.exception( 262 | 'Malformed configuration! \n' 263 | 'The configuration we got is bad, and we can\'nt continue. ' 264 | 'Please check the config files and make sure they are correct. ' 265 | 'If you can\'t figure it out, then deleting them should fix ' 266 | 'the errors and cause kernelstub to regenerate them from ' 267 | 'Default. \n\n You can use "-vv" to get the configuration used.') 268 | log.debug('Configuration we got: \n\n%s' % config.print_config()) 269 | exit(169) 270 | 271 | 272 | if args.add_options: 273 | add_opts = args.add_options.split(" ") 274 | add_opts = config.parse_options(add_opts) 275 | for opt in add_opts: 276 | if opt not in kernel_opts: 277 | kernel_opts.append(opt) 278 | configuration['kernel_options'] = kernel_opts 279 | 280 | if args.remove_options: 281 | rem_opts = args.remove_options.split(" ") 282 | rem_opts = config.parse_options(rem_opts) 283 | kernel_opts = list(set(kernel_opts) - set(rem_opts)) 284 | configuration['kernel_options'] = kernel_opts 285 | 286 | if args.force_update: 287 | force = True 288 | if configuration['force_update'] == True: 289 | force = True 290 | 291 | log.debug('Structing objects') 292 | 293 | drive = Drive.Drive(root_path=root_path, esp_path=esp_path) 294 | nvram = Nvram.NVRAM(opsys.name, opsys.version) 295 | installer = Installer.Installer(nvram, opsys, drive) 296 | 297 | # Log some helpful information, to file and optionally console 298 | info = ( 299 | ' OS:..................%s %s\n' %(opsys.name_pretty,opsys.version) + 300 | ' Root partition:......%s\n' % drive.root_fs + 301 | ' Root FS UUID:........%s\n' % drive.root_uuid + 302 | ' ESP Path:............%s\n' % esp_path + 303 | ' ESP Partition:.......%s\n' % drive.esp_fs + 304 | ' ESP Partition #:.....%s\n' % drive.esp_num + 305 | ' NVRAM entry #:.......%s\n' % nvram.os_entry_index + 306 | ' Boot Variable #:.....%s\n' % nvram.order_num + 307 | ' Kernel Boot Options:.%s\n' % " ".join(kernel_opts) + 308 | ' Kernel Image Path:...%s\n' % opsys.kernel_path + 309 | ' Initrd Image Path:...%s\n' % opsys.initrd_path + 310 | ' Force-overwrite:.....%s\n' % str(force)) 311 | 312 | log.info('System information: \n\n%s' % info) 313 | 314 | if args.print_config: 315 | all_config = ( 316 | ' ESP Location:..................%s\n' % configuration['esp_path'] + 317 | ' Management Mode:...............%s\n' % configuration['manage_mode'] + 318 | ' Install Loader configuration:..%s\n' % configuration['setup_loader'] + 319 | ' Configuration version:.........%s\n' % configuration['config_rev']) 320 | log.info('Configuration details: \n\n%s' % all_config) 321 | exit(0) 322 | 323 | log.debug('Setting up boot...') 324 | 325 | kopts = 'root=UUID=%s ro %s' % (drive.root_uuid, " ".join(kernel_opts)) 326 | log.debug('kopts: %s' % kopts) 327 | 328 | 329 | 330 | installer.setup_kernel( 331 | kopts, 332 | setup_loader=setup_loader, 333 | overwrite=force, 334 | simulate=no_run) 335 | try: 336 | installer.backup_old( 337 | kopts, 338 | setup_loader=setup_loader, 339 | simulate=no_run) 340 | except Exception as e: 341 | log.debug('Couldn\'t back up old kernel. \nThis might just mean ' + 342 | 'You don\'t have an old kernel installed. If you do, try ' + 343 | 'with -vv to see debuging information') 344 | log.debug(e) 345 | 346 | installer.copy_cmdline(simulate=no_run) 347 | 348 | if not manage_mode: 349 | installer.setup_stub(kopts, simulate=no_run) 350 | 351 | log.debug('Saving configuration to file') 352 | 353 | config.config['user'] = configuration 354 | config.save_config() 355 | 356 | log.debug('Setup complete!\n\n') 357 | 358 | return 0 359 | -------------------------------------------------------------------------------- /kernelstub/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | """ 24 | 25 | import json, os, logging 26 | 27 | class ConfigError(Exception): 28 | pass 29 | 30 | class Config(): 31 | 32 | config_path = "/etc/kernelstub/configuration" 33 | config = {} 34 | config_default = { 35 | 'default': { 36 | 'kernel_options': ['quiet', 'splash'], 37 | 'esp_path': "/boot/efi", 38 | 'setup_loader': False, 39 | 'manage_mode': False, 40 | 'force_update' : False, 41 | 'live_mode' : False, 42 | 'config_rev' : 3 43 | } 44 | } 45 | 46 | def __init__(self, path='/etc/kernelstub/configuration'): 47 | self.log = logging.getLogger('kernelstub.Config') 48 | self.log.debug('loaded kernelstub.Config') 49 | self.config_path = path 50 | self.config = self.load_config() 51 | os.makedirs('/etc/kernelstub/', exist_ok=True) 52 | 53 | def load_config(self): 54 | self.log.info('Looking for configuration...') 55 | 56 | if os.path.exists(self.config_path): 57 | self.log.debug('Checking %s' % self.config_path) 58 | 59 | with open(self.config_path) as config_file: 60 | self.config = json.load(config_file) 61 | 62 | elif os.path.exists('/etc/default/kernelstub'): 63 | self.log.debug('Checking fallback /etc/default/kernelstub') 64 | 65 | with open('/etc/default/kernelstub', mode='r') as config_file: 66 | self.config = json.load(config_file) 67 | 68 | else: 69 | self.log.info('No configuration file found, loading defaults.') 70 | self.config = self.config_default 71 | 72 | self.log.debug('Configuration found!') 73 | try: 74 | user_config = self.config['user'] 75 | self.log.debug(user_config) 76 | except KeyError: 77 | self.config['user'] = self.config['default'].copy() 78 | 79 | try: 80 | self.log.debug('Configuration version: %s' % self.config['user']['config_rev']) 81 | if self.config['user']['config_rev'] < self.config_default['default']['config_rev']: 82 | self.log.warning("Updating old configuration.") 83 | self.config = self.update_config(self.config) 84 | self.log.info("Configuration updated successfully!") 85 | elif self.config['user']['config_rev'] == self.config_default['default']['config_rev']: 86 | self.log.debug("Configuration up to date") 87 | # Double-checking in case OEMs do bad things with the config file 88 | if type(self.config['user']['kernel_options']) is str: 89 | self.log.warning('Invalid kernel_options format!\n\n' 90 | 'Usually outdated or buggy maintainer packages from your hardware OEM. ' 91 | 'Contact your hardware vendor to inform them to fix their packages.') 92 | try: 93 | self.config['user']['kernel_options'] = self.parse_options(self.config['user']['kernel_options'].split()) 94 | except: 95 | raise ConfigError('Malformed configuration file found!') 96 | exit(169) 97 | else: 98 | raise ConfigError("Configuration cannot be understood!") 99 | except KeyError: 100 | self.log.warning("Attempting upgrade of legacy configuration.") 101 | self.config = self.update_config(self.config) 102 | self.log.info("Configuration updated successfully!") 103 | return self.config 104 | 105 | def save_config(self, path='/etc/kernelstub/configuration'): 106 | self.log.debug('Saving configuration to %s' % path) 107 | 108 | with open(path, mode='w') as config_file: 109 | json.dump(self.config, config_file, indent=2) 110 | 111 | self.log.debug('Configuration saved!') 112 | return 0 113 | 114 | def update_config(self, config): 115 | if config['user']['config_rev'] < 2: 116 | config['user']['live_mode'] = False 117 | config['default']['live_mode'] = False 118 | if config['user']['config_rev'] < 3: 119 | if type(config['user']['kernel_options']) is str: 120 | config['user']['kernel_options'] = self.parse_options(config['user']['kernel_options'].split()) 121 | if type(config['default']['kernel_options']) is str: 122 | config['default']['kernel_options'] = self.parse_options(config['default']['kernel_options'].split()) 123 | config['user']['config_rev'] = 3 124 | config['default']['config_rev'] = 3 125 | return config 126 | 127 | def parse_options(self, options): 128 | self.log.debug(options) 129 | for index, option in enumerate(options): 130 | if '"' in option: 131 | matched = False 132 | itr = 1 133 | while matched == False: 134 | try: 135 | next_option = options[index + itr] 136 | option = '%s %s' % (option, next_option) 137 | options[index + itr] = "" 138 | if '"' in next_option: 139 | matched = True 140 | else: 141 | itr = itr + 1 142 | except IndexError: 143 | matched = True 144 | options[index] = option 145 | return options 146 | 147 | def print_config(self): 148 | output_config = json.dumps(self.config, indent=2) 149 | return output_config 150 | -------------------------------------------------------------------------------- /kernelstub/drive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | """ 24 | 25 | import os, logging, subprocess 26 | 27 | class NoBlockDevError(Exception): 28 | pass 29 | 30 | class UUIDNotFoundError(Exception): 31 | pass 32 | 33 | class Drive(): 34 | 35 | drive_name = 'none' 36 | root_fs = '/' 37 | root_path = '/' 38 | root_uuid = '12345-12345-12345' 39 | esp_fs = '/boot/efi' 40 | esp_path = '/boot/efi' 41 | esp_num = 0 42 | 43 | def __init__(self, root_path="/", esp_path="/boot/efi"): 44 | self.log = logging.getLogger('kernelstub.Drive') 45 | self.log.debug('loaded kernelstub.Drive') 46 | 47 | self.esp_path = esp_path 48 | self.root_path = root_path 49 | self.log.debug('root path = %s' % self.root_path) 50 | self.log.debug('esp_path = %s' % self.esp_path) 51 | 52 | self.mtab = self.get_drives() 53 | 54 | try: 55 | self.root_fs = self.get_part_dev(self.root_path) 56 | self.esp_fs = self.get_part_dev(self.esp_path) 57 | self.drive_name = self.get_drive_dev(self.esp_fs) 58 | self.esp_num = self.esp_fs[-1] 59 | self.root_uuid = self.get_uuid(self.root_path) 60 | except NoBlockDevError as e: 61 | self.log.exception('Could not find a block device for the a ' + 62 | 'partition. This is a critical error and we ' + 63 | 'cannot continue.') 64 | self.log.debug(e) 65 | exit(174) 66 | except UUIDNotFoundError as e: 67 | self.log.exception('Could not get a UUID for the a filesystem. ' + 68 | 'This is a critical error and we cannot continue') 69 | self.log.debug(e) 70 | exit(177) 71 | 72 | 73 | self.log.debug('Root is on /dev/%s' % self.drive_name) 74 | self.log.debug('root_fs = %s ' % self.root_fs) 75 | self.log.debug('root_uuid is %s' % self.root_uuid) 76 | 77 | 78 | def get_drives(self): 79 | self.log.debug('Getting a list of drives') 80 | with open('/proc/mounts', mode='r') as proc_mounts: 81 | mtab = proc_mounts.readlines() 82 | 83 | self.log.debug(mtab) 84 | return mtab 85 | 86 | def get_part_dev(self, path): 87 | self.log.debug('Getting the block device file for %s' % path) 88 | for mount in self.mtab: 89 | drive = mount.split(" ") 90 | if drive[1] == path: 91 | part_dev = os.path.realpath(drive[0]) 92 | self.log.debug('%s is on %s' % (path, part_dev)) 93 | return part_dev 94 | raise NoBlockDevError('Couldn\'t find the block device for %s' % path) 95 | 96 | def get_drive_dev(self, esp): 97 | # Ported from bash, out of @jackpot51's firmware updater 98 | efi_name = os.path.basename(esp) 99 | efi_sys = os.readlink('/sys/class/block/%s' % efi_name) 100 | disk_sys = os.path.dirname(efi_sys) 101 | disk_name = os.path.basename(disk_sys) 102 | self.log.debug('ESP is a partition on /dev/%s' % disk_name) 103 | return disk_name 104 | 105 | def get_uuid(self, path): 106 | self.log.debug('Looking for UUID for path %s' % path) 107 | try: 108 | args = ['findmnt', '-n', '-o', 'UUID', '--mountpoint', path] 109 | result = subprocess.run(args, stdout=subprocess.PIPE) 110 | uuid = result.stdout.decode('ASCII') 111 | uuid = uuid.strip() 112 | return uuid 113 | except OSError as e: 114 | raise UUIDNotFoundError from e 115 | 116 | -------------------------------------------------------------------------------- /kernelstub/installer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | """ 24 | 25 | import os, shutil, logging, platform, gzip 26 | 27 | from pathlib import Path 28 | 29 | class FileOpsError(Exception): 30 | pass 31 | 32 | class Installer(): 33 | 34 | loader_dir = '/boot/efi/loader' 35 | entry_dir = '/boot/efi/loader/entries' 36 | os_dir_name = 'linux-kernelstub' 37 | work_dir = '/boot/efi/EFI/' 38 | old_kernel = True 39 | 40 | def __init__(self, nvram, opsys, drive): 41 | self.log = logging.getLogger('kernelstub.Installer') 42 | self.log.debug('loaded kernelstub.Installer') 43 | 44 | self.nvram = nvram 45 | self.opsys = opsys 46 | self.drive = drive 47 | 48 | self.work_dir = os.path.join(self.drive.esp_path, "EFI") 49 | self.loader_dir = os.path.join(self.drive.esp_path, "loader") 50 | self.entry_dir = os.path.join(self.loader_dir, "entries") 51 | self.os_dir_name = "%s-%s" % (self.opsys.name, self.drive.root_uuid) 52 | self.os_folder = os.path.join(self.work_dir, self.os_dir_name) 53 | self.kernel_dest = os.path.join(self.os_folder, self.opsys.kernel_name) 54 | self.initrd_dest = os.path.join(self.os_folder, self.opsys.initrd_name) 55 | 56 | if not os.path.exists(self.loader_dir): 57 | os.makedirs(self.loader_dir) 58 | if not os.path.exists(self.entry_dir): 59 | os.makedirs(self.entry_dir) 60 | 61 | 62 | def backup_old(self, kernel_opts, setup_loader=False, simulate=False): 63 | self.log.info('Backing up old kernel') 64 | 65 | old_path = Path(self.opsys.old_kernel_path).resolve() 66 | new_path = Path(self.opsys.kernel_path).resolve() 67 | if old_path == new_path: 68 | self.log.info('No old kernel found, skipping') 69 | return 0 70 | 71 | old_kernel_name = "%s-previous.efi" % self.opsys.kernel_name 72 | old_kernel_dest = os.path.join(self.os_folder, old_kernel_name) 73 | try: 74 | self.copy_files( 75 | self.opsys.old_kernel_path, 76 | old_kernel_dest, 77 | simulate=simulate) 78 | except: 79 | self.log.debug('Couldn\'t back up old kernel. There\'s ' + 80 | 'probably only one kernel installed.') 81 | self.old_kernel = False 82 | pass 83 | 84 | old_initrd_name = "%s-previous" % self.opsys.initrd_name 85 | old_initrd_dest = os.path.join(self.os_folder, old_initrd_name) 86 | try: 87 | self.copy_files( 88 | self.opsys.old_initrd_path, 89 | old_initrd_dest, 90 | simulate=simulate) 91 | except: 92 | self.log.debug('Couldn\'t back up old initrd.img. There\'s ' + 93 | 'probably only one kernel installed.') 94 | self.old_kernel = False 95 | pass 96 | 97 | if setup_loader and self.old_kernel: 98 | self.ensure_dir(self.entry_dir) 99 | linux_line = '/EFI/%s-%s/%s-previous.efi' % (self.opsys.name, 100 | self.drive.root_uuid, 101 | self.opsys.kernel_name) 102 | initrd_line = '/EFI/%s-%s/%s-previous' % (self.opsys.name, 103 | self.drive.root_uuid, 104 | self.opsys.initrd_name) 105 | self.make_loader_entry( 106 | self.opsys.name_pretty, 107 | linux_line, 108 | initrd_line, 109 | kernel_opts, 110 | os.path.join(self.entry_dir, '%s-oldkern' % self.opsys.name)) 111 | 112 | def setup_kernel(self, kernel_opts, setup_loader=False, overwrite=False, simulate=False): 113 | self.log.info('Copying Kernel into ESP') 114 | self.kernel_dest = os.path.join( 115 | self.os_folder, 116 | "%s.efi" % self.opsys.kernel_name) 117 | self.ensure_dir(self.os_folder, simulate=simulate) 118 | self.log.debug('kernel being copied to %s' % self.kernel_dest) 119 | 120 | try: 121 | arch = platform.machine() 122 | if arch == "arm64" or arch == "aarch64": 123 | self.gunzip_files( 124 | self.opsys.kernel_path, 125 | self.kernel_dest, 126 | simulate=simulate) 127 | else: 128 | self.copy_files( 129 | self.opsys.kernel_path, 130 | self.kernel_dest, 131 | simulate=simulate) 132 | 133 | except FileOpsError as e: 134 | self.log.exception( 135 | 'Couldn\'t copy the kernel onto the ESP!\n' + 136 | 'This is a critical error and we cannot continue. Check your ' + 137 | 'settings to see if there is a typo. Otherwise, check ' + 138 | 'permissions and try again.') 139 | self.log.debug(e) 140 | exit(170) 141 | 142 | self.log.info('Copying initrd.img into ESP') 143 | self.initrd_dest = os.path.join(self.os_folder, self.opsys.initrd_name) 144 | try: 145 | self.copy_files( 146 | self.opsys.initrd_path, 147 | self.initrd_dest, 148 | simulate=simulate) 149 | 150 | except FileOpsError as e: 151 | self.log.exception('Couldn\'t copy the initrd onto the ESP!\n' + 152 | 'This is a critical error and we cannot ' + 153 | 'continue. Check your settings to see if ' + 154 | 'there is a typo. Otherwise, check permissions ' + 155 | 'and try again.') 156 | self.log.debug(e) 157 | exit(171) 158 | 159 | self.log.debug('Copy complete') 160 | 161 | if setup_loader: 162 | self.log.info('Setting up loader.conf configuration') 163 | linux_line = '/EFI/%s-%s/%s.efi' % (self.opsys.name, 164 | self.drive.root_uuid, 165 | self.opsys.kernel_name) 166 | initrd_line = '/EFI/%s-%s/%s' % (self.opsys.name, 167 | self.drive.root_uuid, 168 | self.opsys.initrd_name) 169 | if simulate: 170 | self.log.info("Simulate creation of entry...") 171 | self.log.info('Loader entry: %s/%s-current\n' %(self.entry_dir, 172 | self.opsys.name) + 173 | 'title %s\n' % self.opsys.name_pretty + 174 | 'linux %s\n' % linux_line + 175 | 'initrd %s\n' % initrd_line + 176 | 'options %s\n' % kernel_opts) 177 | return 0 178 | 179 | if not overwrite: 180 | if not os.path.exists('%s/loader.conf' % self.loader_dir): 181 | overwrite = True 182 | 183 | if overwrite: 184 | self.ensure_dir(self.loader_dir) 185 | with open( 186 | '%s/loader.conf' % self.loader_dir, mode='w') as loader: 187 | 188 | default_line = 'default %s-current\n' % self.opsys.name 189 | loader.write(default_line) 190 | 191 | self.ensure_dir(self.entry_dir) 192 | self.make_loader_entry( 193 | self.opsys.name_pretty, 194 | linux_line, 195 | initrd_line, 196 | kernel_opts, 197 | os.path.join(self.entry_dir, '%s-current' % self.opsys.name)) 198 | 199 | 200 | 201 | 202 | 203 | def setup_stub(self, kernel_opts, simulate=False): 204 | self.log.info("Setting up Kernel EFISTUB loader...") 205 | self.copy_cmdline(simulate=simulate) 206 | self.nvram.update() 207 | 208 | if self.nvram.os_entry_index >= 0: 209 | self.log.info("Deleting old boot entry") 210 | self.nvram.delete_boot_entry(self.nvram.order_num, simulate) 211 | 212 | else: 213 | self.log.debug("No old entry found, skipping removal.") 214 | 215 | self.nvram.add_entry(self.opsys, self.drive, kernel_opts, simulate) 216 | self.nvram.update() 217 | nvram_lines = "\n".join(self.nvram.nvram) 218 | self.log.info('NVRAM configured, new values: \n\n%s\n' % nvram_lines) 219 | 220 | def copy_cmdline(self, simulate): 221 | self.copy_files( 222 | '/proc/cmdline', 223 | self.os_folder, 224 | simulate = simulate 225 | ) 226 | 227 | 228 | def make_loader_entry(self, title, linux, initrd, options, filename): 229 | self.log.info('Making entry file for %s' % title) 230 | with open('%s.conf' % filename, mode='w') as entry: 231 | entry.write('title %s\n' % title) 232 | entry.write('linux %s\n' % linux) 233 | entry.write('initrd %s\n' % initrd) 234 | entry.write('options %s\n' % options) 235 | self.log.debug('Entry created!') 236 | 237 | def ensure_dir(self, directory, simulate=False): 238 | if not simulate: 239 | try: 240 | os.makedirs(directory, exist_ok=True) 241 | return True 242 | except Exception as e: 243 | self.log.exception('Couldn\'t make sure %s exists.' % directory) 244 | self.log.debug(e) 245 | return False 246 | 247 | def gunzip_files(self, src, dest, simulate): # Decompress file src to dest 248 | if simulate: 249 | self.log.info('Simulate decompressing: %s => %s' % (src, dest)) 250 | return True 251 | else: 252 | try: 253 | self.log.debug('Decompressing: %s => %s' % (src, dest)) 254 | with gzip.open(src, 'rb') as in_obj: 255 | with open(dest, 'wb') as out_obj: 256 | shutil.copyfileobj(in_obj, out_obj) 257 | return True 258 | except Exception as e: 259 | self.log.debug(e) 260 | raise FileOpsError("Could not decompress one or more files.") 261 | return False 262 | 263 | 264 | def copy_files(self, src, dest, simulate): # Copy file src into dest 265 | if simulate: 266 | self.log.info('Simulate copying: %s => %s' % (src, dest)) 267 | return True 268 | else: 269 | try: 270 | self.log.debug('Copying: %s => %s' % (src, dest)) 271 | shutil.copy(src, dest) 272 | return True 273 | except Exception as e: 274 | self.log.debug(e) 275 | raise FileOpsError("Could not copy one or more files.") 276 | return False 277 | -------------------------------------------------------------------------------- /kernelstub/kernel_option.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from debian.changelog import Version 4 | import os 5 | import os.path 6 | 7 | def options(path): 8 | items={} 9 | for name in os.listdir(path): 10 | key = None 11 | if name.startswith("vmlinuz-"): 12 | key = "kernel" 13 | elif name.startswith("initrd.img-"): 14 | key = "initrd" 15 | 16 | if key is None: 17 | continue 18 | 19 | parts = name.split("-", 1) 20 | version = parts[1] 21 | 22 | if not version in items: 23 | items[version] = {} 24 | 25 | items[version][key] = os.path.join(path, name) 26 | 27 | return items 28 | 29 | def get_newest_option(opts): 30 | latest_version = None 31 | latest_option = None 32 | 33 | for version, option in opts.items(): 34 | # If option is not complete, skip 35 | if 'kernel' not in option or 'initrd' not in option: 36 | continue 37 | 38 | # If this option is newer, store this option and continue 39 | if latest_version is None or Version(version) > Version(latest_version): 40 | latest_version = version 41 | latest_option = option 42 | 43 | return latest_option, latest_version 44 | 45 | def latest_option(path): 46 | opts = options(path) 47 | latest_option, latest_version = get_newest_option(opts) 48 | 49 | if latest_version is not None: 50 | opts.pop(latest_version) 51 | previous_option = None 52 | if len(opts) > 0: 53 | previous_option, latest_version = get_newest_option(opts) 54 | 55 | return latest_option, previous_option 56 | 57 | if __name__ == "__main__": 58 | print(latest_option("/boot")) 59 | -------------------------------------------------------------------------------- /kernelstub/nvram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | """ 24 | 25 | import subprocess, logging 26 | 27 | class NVRAM(): 28 | 29 | os_entry_index = -1 30 | os_label = "" 31 | nvram = [] 32 | order_num = "0000" 33 | 34 | def __init__(self, name, version): 35 | self.log = logging.getLogger('kernelstub.NVRAM') 36 | self.log.debug('loaded kernelstub.NVRAM') 37 | 38 | self.os_label = "%s %s" % (name, version) 39 | self.update() 40 | 41 | def update(self): 42 | self.log.debug('Updating NVRAM info') 43 | self.nvram = self.get_nvram() 44 | self.find_os_entry(self.nvram, self.os_label) 45 | if self.os_entry_index >= 0: 46 | self.order_num = str(self.nvram[self.os_entry_index])[4:8] 47 | 48 | def get_nvram(self): 49 | self.log.debug('Getting NVRAM data') 50 | command = [ 51 | 'efibootmgr' 52 | ] 53 | try: 54 | return subprocess.check_output(command).decode('UTF-8').split('\n') 55 | except Exception as e: 56 | self.log.exception('Failed to retrieve NVRAM data. Are you running in a chroot?') 57 | self.log.debug(e) 58 | return [] 59 | 60 | def find_os_entry(self, nvram, os_label): 61 | self.log.debug('Finding NVRAM entry for %s' % os_label) 62 | self.os_entry_index = -1 63 | find_index = self.os_entry_index 64 | for entry in nvram: 65 | find_index = find_index + 1 66 | if os_label in entry: 67 | self.os_entry_index = find_index 68 | self.log.debug('Entry found! Index: %s' % self.os_entry_index) 69 | return find_index 70 | 71 | 72 | def add_entry(self, this_os, this_drive, kernel_opts, simulate=False): 73 | self.log.info('Creating NVRAM entry') 74 | device = '/dev/%s' % this_drive.drive_name 75 | esp_num = this_drive.esp_num 76 | entry_label = '%s %s' % (this_os.name, this_os.version) 77 | entry_linux = '\\EFI\\%s-%s\\vmlinuz.efi' % (this_os.name, this_drive.root_uuid) 78 | entry_initrd = 'EFI/%s-%s/initrd.img' % (this_os.name, this_drive.root_uuid) 79 | command = [ 80 | 'efibootmgr', 81 | '-c', 82 | '-d', device, 83 | '-p', esp_num, 84 | '-L', '%s' % entry_label, 85 | '-l', '%s' % entry_linux, 86 | '-u', 87 | 'initrd=%s %s' % (entry_initrd, kernel_opts) 88 | ] 89 | self.log.debug('NVRAM command:\n%s' % command) 90 | if not simulate: 91 | try: 92 | subprocess.run(command) 93 | except Exception as e: 94 | self.log.exception('Couldn\'t create boot entry for kernel! ' + 95 | 'This means that the system will not boot from ' + 96 | 'the new kernel directly. Do NOT reboot without ' + 97 | 'an alternate bootloader configured or fixing ' + 98 | 'this problem. More information is available in ' + 99 | 'the log or by running again with -vv') 100 | self.log.debug(e) 101 | exit(172) 102 | self.update() 103 | 104 | def delete_boot_entry(self, index, simulate): 105 | self.log.info('Deleting old boot entry: %s' % index) 106 | command = ['efibootmgr', 107 | '-B', 108 | '-b', str(index)] 109 | self.log.debug('NVRAM command:\n%s' % command) 110 | if not simulate: 111 | try: 112 | subprocess.run(command) 113 | except Exception as e: 114 | self.log.exception('Couldn\'t delete old boot entry %s. ' % index + 115 | 'This could cause problems, so kernelstub will ' + 116 | 'not continue. Check again with -vv for more info.') 117 | self.log.debug(e) 118 | exit(173) 119 | self.update() 120 | -------------------------------------------------------------------------------- /kernelstub/opsys.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | kernelstub 5 | The automatic manager for using the Linux Kernel EFI Stub to boot 6 | 7 | Copyright 2017-2018 Ian Santopietro 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any purpose 10 | with or without fee is hereby granted, provided that the above copyright notice 11 | and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 14 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 19 | THIS SOFTWARE. 20 | 21 | Please see the provided LICENSE.txt file for additional distribution/copyright 22 | terms. 23 | """ 24 | 25 | import platform 26 | 27 | class OS(): 28 | 29 | name_pretty = "Linux" 30 | name = "Linux" 31 | version = "1.0" 32 | cmdline = ['quiet', 'splash'] 33 | kernel_name = 'vmlinuz' 34 | initrd_name = 'initrd.img' 35 | old_kernel_name = 'vmlinuz.old' 36 | old_initrd_name = 'initrd.img.old' 37 | kernel_release = platform.release() 38 | kernel_path = '/vmlinuz' 39 | initrd_path = '/initrd.img' 40 | old_kernel_path = '/vmlinuz.old' 41 | old_initrd_path = '/initrd.img.old' 42 | 43 | def __init__(self): 44 | self.name_pretty = self.get_os_name() 45 | self.name = self.clean_names(self.name_pretty) 46 | self.version = self.get_os_version() 47 | self.cmdline = self.get_os_cmdline() 48 | 49 | def clean_names(self, name): 50 | # This is a list of characters we can't/don't want to have in technical 51 | # names for the OS. name_pretty will still have them. 52 | badchar = { 53 | ' ' : '_', 54 | '~' : '-', 55 | '!' : '', 56 | "'" : "", 57 | '<' : '', 58 | '>' : '', 59 | ':' : '', 60 | '"' : '', 61 | '/' : '', 62 | '\\' : '', 63 | '|' : '', 64 | '?' : '', 65 | '*' : '', 66 | 'CON' : '', 67 | 'PRN' : '', 68 | 'AUX' : '', 69 | 'NUL' : '', 70 | 'COM1' : '', 71 | 'COM2' : '', 72 | 'COM3' : '', 73 | 'COM4' : '', 74 | 'COM5' : '', 75 | 'COM6' : '', 76 | 'COM7' : '', 77 | 'COM8' : '', 78 | 'COM9' : '', 79 | 'LPT1' : '', 80 | 'LPT2' : '', 81 | 'LPT3' : '', 82 | 'LPT4' : '', 83 | 'LPT5' : '', 84 | 'LPT6' : '', 85 | 'LPT7' : '', 86 | 'LPT8' : '', 87 | 'LPT9' : '', 88 | } 89 | 90 | for char in badchar: 91 | name = name.replace(char, badchar[char]) 92 | return name 93 | 94 | def get_os_cmdline(self): 95 | with open('/proc/cmdline') as cmdline_file: 96 | cmdline_list = cmdline_file.readlines()[0].split(" ") 97 | 98 | cmdline = [] 99 | for option in cmdline_list: 100 | if not option.startswith('BOOT_IMAGE'): 101 | if not option.startswith('root='): 102 | if not option.startswith('initrd='): 103 | cmdline.append(option) 104 | return cmdline 105 | 106 | def get_os_name(self): 107 | os_release = self.get_os_release() 108 | for item in os_release: 109 | if item.startswith('NAME='): 110 | name = item.split('=')[1] 111 | return self.strip_quotes(name[:-1]) 112 | 113 | def get_os_version(self): 114 | os_release = self.get_os_release() 115 | for item in os_release: 116 | if item.startswith('VERSION_ID='): 117 | version = item.split('=')[1] 118 | return self.strip_quotes(version[:-1]) 119 | 120 | def strip_quotes(self, value): 121 | new_value = value 122 | if value.startswith('"'): 123 | new_value = new_value[1:] 124 | if value.endswith('"'): 125 | new_value = new_value[:-1] 126 | return new_value 127 | 128 | def get_os_release(self): 129 | try: 130 | with open('/etc/os-release') as os_release_file: 131 | os_release = os_release_file.readlines() 132 | except FileNotFoundError: 133 | os_release = ['NAME="%s"\n' % self.name, 134 | 'ID=linux\n', 135 | 'ID_LIKE=linux\n', 136 | 'VERSION_ID="%s"\n' % self.version] 137 | 138 | return os_release 139 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | Copyright 2017-2018 Ian Santopietro 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any purpose 7 | with or without fee is hereby granted, provided that the above copyright notice 8 | and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 13 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 14 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 16 | THIS SOFTWARE. 17 | 18 | Portions of test-related code authored by Jason DeRose 19 | """ 20 | 21 | 22 | from setuptools import setup 23 | from setuptools import Command 24 | import os, subprocess, sys 25 | 26 | TREE = os.path.dirname(os.path.abspath(__file__)) 27 | DIRS = [ 28 | 'kernelstub', 29 | 'bin'] 30 | 31 | 32 | def run_under_same_interpreter(opname, script, args): 33 | print('\n** running: {}...'.format(script), file=sys.stderr) 34 | if not os.access(script, os.R_OK | os.X_OK): 35 | print('ERROR: cannot read and execute: {!r}'.format(script), 36 | file=sys.stderr 37 | ) 38 | print('Consider running `setup.py test --skip-{}`'.format(opname), 39 | file=sys.stderr 40 | ) 41 | sys.exit(3) 42 | cmd = [sys.executable, script] + args 43 | print('check_call:', cmd, file=sys.stderr) 44 | subprocess.check_call(cmd) 45 | print('** PASSED: {}\n'.format(script), file=sys.stderr) 46 | 47 | def run_pyflakes3(): 48 | script = '/usr/bin/pyflakes3' 49 | names = [ 50 | 'setup.py', 51 | ] + DIRS 52 | args = [os.path.join(TREE, name) for name in names] 53 | run_under_same_interpreter('flakes', script, args) 54 | 55 | 56 | 57 | class Test(Command): 58 | description = 'run pyflakes3' 59 | 60 | user_options = [ 61 | ('skip-flakes', None, 'do not run pyflakes static checks'), 62 | ] 63 | 64 | def initialize_options(self): 65 | self.skip_sphinx = 0 66 | self.skip_flakes = 0 67 | 68 | def finalize_options(self): 69 | pass 70 | 71 | def run(self): 72 | if not self.skip_flakes: 73 | run_pyflakes3() 74 | 75 | setup(name='kernelstub', 76 | version='3.1.4', 77 | description='Automatic kernel efistub manager for UEFI', 78 | url='https://launchpad.net/kernelstub', 79 | author='Ian Santopietro', 80 | author_email='isantop@gmail.com', 81 | license='ISC', 82 | packages=['kernelstub'], 83 | scripts=['bin/kernelstub'], 84 | cmdclass={'test': Test}, 85 | data_files=[ 86 | ('/etc/kernel/postinst.d', ['data/kernel/zz-kernelstub']), 87 | ('/etc/initramfs/post-update.d', ['data/initramfs/zz-kernelstub']), 88 | ('/etc/default', ['data/config/kernelstub.SAMPLE'])] 89 | ) 90 | --------------------------------------------------------------------------------