#!/usr/bin/python3

"""
 kernelstub
 The automatic manager for using the Linux Kernel EFI Stub to boot

 Copyright 2017-2018 Ian Santopietro <isantop@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

Please see the provided LICENSE.txt file for additional distribution/copyright
terms.

 This program will automatically keep a copy of the Linux Kernel image and your
 initrd.img located on your EFI System Partition (ESP) on an EFI-compatible
 system. The benefits of this approach include being able to boot Linux directly
 and bypass the GRUB bootloader in many cases, which can save 4-6 seconds during
 boot on a system with a fast drive. Please note that there are no guarantees
 about boot time saved using this method.

 For maximum safety, kernelstub recommends leaving an entry for GRUB in your
 system's ESP and NVRAM configuration, as GRUB allows you to modify boot
 parameters per-boot, which loading the kernel directly cannot do. The only
 other way to do this is to use an EFI shell to manually load the kernel and
 give it parameters from the EFI Shell. You can do this by using:

 fs0:> vmlinuz-image initrd=EFI/path/to/initrd/stored/on/esp options

 kernelstub will load parameters from the /etc/default/kernelstub config file.
"""

import argparse, os

from kernelstub import application

def main(options=None): # Do the thing
    kernelstub = application.Kernelstub()
    # Set up argument processing
    parser = argparse.ArgumentParser(
        description = "Automatic Kernel EFIstub manager")
    loader_stub = parser.add_mutually_exclusive_group()
    install_loader = parser.add_mutually_exclusive_group()

    parser.add_argument(
        '-c',
        '--dry-run',
        action = 'store_true',
        dest = 'dry_run',
        help = 'Don\'t perform any actions, just simulate them.'
    )
    parser.add_argument(
        '-p',
        '--print-config',
        action = 'store_true',
        dest = 'print_config',
        help = 'Print the current configuration and exit'
    )

    parser.add_argument(
        '-e',
        dest = 'esp_path',
        metavar = 'ESP,',
        help = ''
    )
    parser.add_argument(
        '--esp-path',
        dest = 'esp_path',
        metavar = 'ESP',
        help = 'Manually specify the path to the ESP. Default is /boot/efi'
    )

    parser.add_argument(
        '-r',
        dest = 'root_path',
        metavar = 'ROOT',
        help = ''
    )
    parser.add_argument(
        '--root-path',
        dest = 'root_path',
        metavar = 'ROOT',
        help = 'The path where the root filesystem to use is mounted.'
    )

    parser.add_argument(
        '-k',
        dest = 'kernel_path',
        metavar= 'PATH,',
        help = ''
    )
    parser.add_argument(
        '--kernel-path',
        dest = 'kernel_path',
        metavar= 'PATH',
        help = 'The path to the kernel image.'
    )

    parser.add_argument(
        '-i',
        dest = 'initrd_path',
        metavar = 'PATH,',
        help = ''
    )
    parser.add_argument(
        '--initrd-path',
        dest = 'initrd_path',
        metavar = 'PATH',
        help = 'The path to the initrd image.'
    )

    parser.add_argument(
        '-o',
        dest = 'k_options',
        metavar = '"OPTIONS",',
        help = ''
    )
    parser.add_argument(
        '--options',
        dest = 'k_options',
        metavar = '"OPTIONS"',
        help = 'The total boot options to be passed to the kernel'
    )

    parser.add_argument(
        '-a',
        dest = 'add_options',
        metavar = '"OPTIONS",',
        help = ''
    )
    parser.add_argument(
        '--add-options',
        dest = 'add_options',
        metavar = '"OPTIONS"',
        help = ('Boot options to add to the configuration '
               '(if they aren\'t already present)')
   )

    parser.add_argument(
        '-d',
        dest = 'remove_options',
        metavar = '"OPTIONS"',
        help = ''
    )
    parser.add_argument(
        '--delete-options',
        dest = 'remove_options',
        metavar = '"OPTIONS"',
        help = ('Boot options to remove from the configuration '
                '(if they\'re present already)')
    )

    parser.add_argument(
        '-g',
        dest = 'log_file',
        metavar = 'LOG',
        help = ''
    )
    parser.add_argument(
        '--log-file',
        dest = 'log_file',
        metavar = 'LOG',
        help = ('The path to the log file to use. Defaults to ' 
               '/var/log/kernelstub.log')
    )

    install_loader.add_argument(
        '-l',
        '--loader',
        action = 'store_true',
        dest = 'setup_loader',
        help = 'Creates a systemd-boot compatible loader configuration'
    )
    install_loader.add_argument(
        '-n',
        '--no-loader',
        action = 'store_true',
        dest = 'off_loader',
        help = 'Turns off creating loader configuration'
    )

    loader_stub.add_argument(
        '-s',
        '--stub',
        action = 'store_true',
        dest = 'install_stub',
        help = 'Set up NVRAM entries for the copied kernel'
    )

    loader_stub.add_argument(
        '-m',
        '--manage-only',
        action = 'store_true',
        dest = 'manage_mode',
        help = 'Only copy entries, don\'t set up the NVRAM'
    )

    parser.add_argument(
        '-f',
        '--force-update',
        action = 'store_true',
        dest = 'force_update',
        help = ('Forcibly update any loader.conf to set the new entry as the ' 
               'default')
    )

    parser.add_argument(
        '-v',
        '--verbose',
        action = 'count',
        dest = 'verbosity',
        help = 'Increase program verbosity and display extra output.'
    )

    parser.add_argument(
        '--preserve-live-mode',
        action = 'store_true',
        dest = 'preserve_live',
        help = argparse.SUPPRESS
    )

    args = parser.parse_args()
    if options:
        args = parser.parse_args(options)

    if os.geteuid() != 0:
        parser.print_help()
        print('kernelstub: ERROR: You need to be root or use sudo to run ' 
              'kernelstub!')
        exit(176)

    kernelstub.main(args)

if __name__ == '__main__':
    main()
