#!/usr/bin/env python3

from importlib.resources import path
from pickletools import int4
import sys
import argparse
import subprocess
import os
import platform
import errno

from kabihelper import check_dirty_symbols
from kabihelper import parse_ko_symbols
from kabihelper import parse_whitelist_symbols
from kabihelper import tree_printer

dir_root = "/usr/share/uos-kabi-whitelist/whitelist-files"
format_kn = "5.10.0-amd64-desktop #20.01.41.32-oem"

# kernel release: "5.10.0-amd64-desktop"
# kernel version: "#20.01.41.32-oem"

udir="/proc/sys/kernel/uosversion"
def parse_wtfile_from_kernel_version(kernel_release, kernel_version, sub_version=None,kv_oldminor=None):
    kernel_arch = kernel_release.split('-')[1]
    kv = kernel_version.split()[0]
    kv = kv[1:]
    kv_list = kv.split('.')

    # old 4.19 kernel-name-format
    if len(kv_list) < 4:
        kv_major = "20"
        try:
            kvfile = open(udir,"r")
            # kvfile.readline() : '1050\n'
            kv_minor = kvfile.readline().rstrip()[2:]
            if kv_oldminor!=None:
                kv_minor = kv_oldminor
            kvfile.close()
            print("# Info: Kernel Version list len < 4,read uos version from /proc/sys/kernel/uosversion");
        except Exception as e:
            print("Read uos sub_version failed. Error message:<%s>" % file, file=sys.stderr)
    else:
        kv_major = kv_list[0]
        kv_minor = kv_list[2]
        print("# Info: Read Kernel version:");
        print(kv)

    kv_major = int(kv_major)

    if sub_version:
        kv_minor = sub_version

    dir_major="v%d" % (kv_major)
    dir_wtfiles = os.path.join(dir_root, dir_major, kernel_arch)

    wt_files = []
    for root, dirs, files in os.walk(dir_wtfiles):
        for file in files:
            wt_files.append(file)

    if not wt_files:
        return None

    wtfile = None
    for file in wt_files:
        try:
            if kv_minor.rstrip() == file.rstrip():
                wtfile = file
                break
        except ValueError as e:
            print("Warning: Whitelist repos has been polluted with the file: <%s>" % file, file=sys.stderr)
            
    if not wtfile:
        raise Exception("Error: Can't find whitelist match the version {} {},please use -l to show whitelists.".format(kv_major,kv_minor))

    wtfile = os.path.join(dir_wtfiles, wtfile)
    return wtfile

def parse_whitelist_file(wl_file, kn_version, sub_version, kv_oldminor=None):
    if wl_file:
        return wl_file

    if kn_version:
        kn_list = kn_version.split()
        if len(kn_list) != 2:
            print("Failed. Bad kernel-version format: %s" % kn_version, file=sys.stderr)
            print("Good format example: %s" % format_kn, file=sys.stderr)
            exit(1)
        else:
            return parse_wtfile_from_kernel_version(kn_list[0], kn_list[1], None, kv_oldminor)

    kernel_release = platform.uname().release
    kernel_version = platform.uname().version

    return parse_wtfile_from_kernel_version(kernel_release, kernel_version, sub_version, kv_oldminor)

def main():
    parser = argparse.ArgumentParser()


    parser.add_argument('-l', '--print-ls',
                        help='print the all whitelists',
                        action='store_true')
    parser.add_argument('-m', '--ko-files',
                        help='ko files',
                        nargs='+', default=[])

    parser.add_argument('-w', '--print-ws',
                        help='print the whitelist symbols',
                        action='store_true')
    parser.add_argument('-k', '--print-ks',
                        help='print the ko symbols',
                        action='store_true')
    parser.add_argument('-d', '--print-ds',
                        help='print the dirty symbols',
                        action='store_true')

    parser.add_argument('-o', '--oldminor',
                        help='specify the kernel old-minor number, such as "41" or "50-u3" of "%s"' % format_kn,
                        type=str, default=None)

    group_wf = parser.add_mutually_exclusive_group()
    group_wf.add_argument('-i', '--whitelist-file',
                        help='specify the whitelist file', default=None)

    group_wf.add_argument('-s', '--kernel-version',
                        help='specify the kernel version, such as "%s"' % format_kn, default=None)

    group_wf.add_argument('-v', '--sub-version',
                        help='specify the kernel subversion number, such as "41" of "%s"' % format_kn,
                        type=str, default=None)

    args = parser.parse_args()

    if args.print_ls and (args.print_ws or args.print_ks or args.print_ds):
        print("Error: Must specify the action only with [-l].", file=sys.stderr)
        exit(1)

    if not (args.print_ws or args.print_ls or args.print_ks or args.print_ds):
        print("Error: Need specify one or more action: -h -l -w -k -d.", file=sys.stderr)
        exit(1)

    if not args.ko_files and (args.print_ds or args.print_ks):
        print("Error: Must specify at least one module with the action -k/-d.", file=sys.stderr)
        exit(1)

    if args.ko_files and not args.print_ds and not args.print_ks:
        print("Error: Must specify the action [-k/-d] with the module.", file=sys.stderr)
        exit(1)


    # for debug
    check_dir = os.getenv('KWCHECK_DIR')
    if check_dir:
        global dir_root
        dir_root = check_dir
    if args.print_ls:
        tree_printer(dir_root)
        exit(0)
    # print(dir_root)

    # print(parse_wtfile_from_kernel_version("5.10.0-amd64-desktop", "#20.01.41.32-oem"))

    if args.whitelist_file:
        wl_type = "specified whitelist file"
    elif args.kernel_version:
        wl_type = "specified kernel version"
    elif args.sub_version:
        wl_type = "specified kernel subversion"
    else:
        wl_type = "current kernel"

    try:
        wtfile = parse_whitelist_file(args.whitelist_file, args.kernel_version, args.sub_version ,args.oldminor)
    except Exception as e:
        print("Parse whitelist failed. Error message:", file=sys.stderr)
        print(e, file=sys.stderr)
        exit(1)

    # print("parse-test: %s" % wtfile2)

    # print('\n'.join(syms))

    if not wtfile:
        print("Error: Can't find the whitelist file.", file=sys.stderr)
        exit(1)
    else:
        try:
            syms_white = parse_whitelist_symbols(wtfile)
        except Exception as e:
            print("Parse whitelist symbols failed. Error message:" , file=sys.stderr)
            print(e, file=sys.stderr)
            exit(1)

    try:
        syms_ko = parse_ko_symbols(args.ko_files)
    except Exception as e:
            print("Parse ko symbols failed. Error message:", file=sys.stderr)
            print(e, file=sys.stderr)
            exit(1)

    syms_dirty = check_dirty_symbols(syms_white, syms_ko)

    whitelist = '\n'.join(syms_white)
    kolist = '\n'.join(syms_ko)
    dlist = '\n'.join(syms_dirty)

    try:
        if args.print_ws:
            print('# whitelist infos:(type: %s): %s' % (wl_type, wtfile))
            print('# Count: whitelist-symbols:%d' % len(syms_white))
            print('# Whitelist-symbols:')
            print(whitelist, flush=True)

        if args.print_ks:
            print('# ko: %s' % ' '.join(args.ko_files))
            print('# Count: ko-symbols:%d' % len(syms_ko))
            print('# ko-symbols:')
            print(kolist, flush=True)

        if args.print_ds:
            print('# current kernel version: %s %s' % (platform.uname().release, platform.uname().version))
            print('# whitelist(type: %s): %s' % (wl_type, wtfile))
            print('# ko: %s' % ' '.join(args.ko_files))
            print('# Count: dirty-symbols:%d ko-symbols:%d whitelist-symbols:%d' % (len(syms_dirty), len(syms_ko), len(syms_white)))
            print('# Dirty-symbols:')
            print(dlist, flush=True)

    except Exception as e:
            # print("Exception caught. Error message:", file=sys.stderr)
            # print(e, file=sys.stderr)
            pass
if __name__ == '__main__':
    sys.exit(main())
