diff --git a/.vscode/settings.json b/.vscode/settings.json index 6f714f0..90fa232 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,10 @@ { - "window.title": "openwrtBuild" + "window.title": "openwrtBuild", + "cSpell.words": [ + "infile", + "isfile", + "openwrt", + "regen", + "sysbackup" + ] } \ No newline at end of file diff --git a/openwrt-build-gui.py b/openwrt-build-gui.py deleted file mode 100755 index 974b15d..0000000 --- a/openwrt-build-gui.py +++ /dev/null @@ -1,253 +0,0 @@ -#!/usr/bin/env python3 -import tkinter as tk -from tkinter import filedialog -from tkinter import ttk -import os.path -import csv -import json -import gzip -import urllib.request - -##################################################################### -######################## VARIABLES ################################## -##################################################################### - -# Enable file caching and some dev output -debug=True - -##################################################################### -######################## FUNCTIONS ################################## -##################################################################### - -def get_toh(): - """ - Retrieves the openwrt.org table of hardware and returns it as a list of dicts - """ - toh_cache_file = "sources/toh.tsv" - if debug is True and os.path.isfile(toh_cache_file): - with open(toh_cache_file) as infile: - toh = json.load(infile) - else: - toh_gz_handle = urllib.request.urlopen("https://openwrt.org/_media/toh_dump_tab_separated.gz") - toh_handle = gzip.open(toh_gz_handle, mode='rt', encoding='ISO-8859-1') - toh_dict = csv.DictReader(toh_handle, delimiter='\t') - # Convert the DictReader object to a native list of dicts - toh = list(toh_dict) - - # Sanitize it - junk = ['', 'nan', 'NULL', '-', '?', '¿', ' '] - toh = [d for d in toh if d['target'] not in junk and d['subtarget'] not in junk] - - # Save to cache file - with open(toh_cache_file, 'w') as outfile: - json.dump(toh, outfile) - - return toh - -##################################################################### -######################## CLASSES #################################### -##################################################################### - -class DeviceFrame(tk.Frame): - """This class renders the device information frame""" - - def __init__(self, parent, *args, **kwargs): - super().__init__(parent, *args, **kwargs) - - # Make a dict to hold our widgets - self.widgets = {} - - # Get the Table of Hardware (toh) data frame from openwrt.org - self.toh = get_toh() - - # Create device frame and widgets - self.device_frame = tk.LabelFrame(self, text="Select Device") - self.device_frame.grid(row=0, column=0, sticky=(tk.W + tk.E)) - self.info_frame = tk.LabelFrame(self, text="Info") - self.info_frame.grid(row=1, column=0, sticky=(tk.W + tk.E)) - self.version_frame = tk.LabelFrame(self, text="Version") - self.version_frame.grid(row=2, column=0, sticky=(tk.W + tk.E)) - - self.targets_widget(parent) - self.subtargets_widget(parent) - self.info_widget(parent) - self.version_widget(parent) - - # Create some traces to repopulate the subtarget menu and info widget - parent.target.trace("w", lambda var_name, var_index, operation: self.subtargets_widget(parent, regen=True)) - parent.subtarget.trace("w", lambda var_name, var_index, operation: self.info_widget(parent, regen=True)) - - # Place the widgets in the device frame - self.widgets['Target'].grid(row=0, column=0) - self.widgets['Subtarget'].grid(row=1, column=0) - self.widgets['Version'].grid(row=0, column=0) - self.widgets['Info'].grid(row=0, column=1) - - - def targets_widget(self, parent): - """A Combobox of targets from the ToH""" - - targets = [k['target'] for k in self.toh] - targets = sorted(set(targets)) - parent.target.set(targets[0]) - - self.widgets['Target'] = LabelInput( - self.device_frame, - label='Target:', - input_class=ttk.Combobox, - input_var=parent.target, - options_list=targets - ) - - - def subtargets_widget(self, parent, regen=None): - """A Combobox of subtargets of the current target""" - regen = regen or False - subtargets = [d['subtarget'] for d in self.toh if d['target'] == parent.target.get()] - subtargets = sorted(set(subtargets)) - parent.subtarget.set(subtargets[0]) - if regen is True: - self.widgets['Subtarget'].input['values'] = subtargets - return - self.widgets['Subtarget'] = LabelInput( - self.device_frame, - label='Subtarget:', - input_class=ttk.Combobox, - input_var=parent.subtarget, - options_list=subtargets - ) - - - def info_widget(self, parent, regen=None): - """An InfoBox of info about the current subtarget""" - regen = regen or False - # Add any stat that you wish to display to this list - stats = {'devicetype': 'Type', 'brand': 'Brand', 'model': 'Model', 'cpu': 'CPU', 'supportedsincerel': 'First Release', - 'supportedcurrentrel': 'Current Release', 'packagearchitecture': 'Arch', 'wikideviurl': 'Wiki'} - for device in self.toh: - if device['target'] == parent.target.get() and device['subtarget'] == parent.subtarget.get(): - break - # Stringify - stats_str = '\n'.join('{}: {}'.format(stats[k], v) for k, v in device.items() if k in stats) - parent.subtarget_info.set(stats_str) - if regen is True: - return - self.widgets['Info'] = LabelInput( - self.device_frame, - label='', - input_class=ttk.Label, - input_var=parent.subtarget_info, - textvariable=parent.subtarget_info, - ) - - - def version_widget(self, parent): - """A Combobox of version numbers""" - # A space-delim'd string of version numbers TODO: make automatic from scraped data (repo tags?) - versions = 'snapshot 12.09' - - parent.version.set('snapshot') - - self.widgets['Version'] = LabelInput( - self.version_frame, - label='', - input_class=ttk.Combobox, - input_var=parent.version, - options_list=versions - ) - - - -class LabelInput(tk.Frame): - """A widget containing a label and input together.""" - - def __init__(self, parent, *args, label='', input_class=ttk.Entry, - input_var=None, input_args=None, label_args=None, options_list=None, - **kwargs): - super().__init__(parent) - input_args = input_args or {} - label_args = label_args or {} - options_list = options_list or [] - self.variable = input_var - - if input_class in (ttk.Checkbutton, ttk.Button, ttk.Radiobutton): - input_args["text"] = label - input_args["variable"] = input_var - else: - self.label = ttk.Label(self, text=label, **label_args) - self.label.grid(row=0, column=0, sticky=(tk.W + tk.E)) - input_args["textvariable"] = input_var - if input_class in (ttk.OptionMenu, ttk.Combobox): - input_args["values"] = options_list - - - if input_class is ttk.OptionMenu: - self.input = input_class(self, self.variable, *options_list) - else: - self.input = input_class(self, **input_args) - - self.input.grid(row=1, column=0, sticky=(tk.W + tk.E)) - self.columnconfigure(0, weight=1) - - def grid(self, sticky=(tk.E + tk.W), **kwargs): - super().grid(sticky=sticky, **kwargs) - - def get(self): - if self.variable: - return self.variable.get() - elif type(self.input) == tk.Text: - return self.input.get('1.0', tk.END) - else: - return self.input.get() - - def set(self, value, *args, **kwargs): - if type(self.variable) == tk.BooleanVar: - self.variable.set(bool(value)) - elif self.variable: - self.variable.set(value, *args, **kwargs) - elif type(self.input).__name__.endswith('button'): - if value: - self.input.select() - else: - self.input.deselect() - elif type(self.input) == tk.Text: - self.input.delete('1.0', tk.END) - self.input.insert('1.0', value) - else: - self.input.delete(0, tk.END) - self.input.insert(0, value) - - - -class GUI(tk.Tk): - """Application root window""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # Set the window properties - self.title("openwrt-build") - self.geometry("800x600") - self.resizable(width=False, height=False) - - ttk.Label( - self, - text="OpenWRT Image Builder", - font=("TkDefaultFont", 16) - ).grid(row=0) - - # Let's store the global tk vars in this top-level class - self.target = tk.StringVar() - self.subtarget = tk.StringVar() - self.subtarget_info = tk.StringVar() - self.version = tk.StringVar() - - # Add each frame class - self.device_frame = DeviceFrame(self) - self.device_frame.grid(row=1, column=0, sticky=(tk.W + tk.E)) - - -if __name__ == '__main__': - app = GUI() - app.mainloop() - diff --git a/openwrtBuild b/openwrtBuild index bebe9f7..f625a37 100755 --- a/openwrtBuild +++ b/openwrtBuild @@ -63,31 +63,38 @@ setDefaults() { [[ -z $_filesroot ]] && _filesroot="$_builddir/files/" # Additional packages for all profiles - _packages+=("luci" "nano" "htop" "tcpdump" "diffutils" "tar" "iperf") + _packages+=("luci" "luci-ssl" "nano" "htop" "tcpdump" "diffutils" "tar" "iperf") - # If no profile is specified, use the TP-Link Archer C7 v2 dumb AP - [[ -z $_profile ]] && _profile="tplink_archer-c7-v2" + # Exit if no profile specified + [[ -z $_profile ]] && echo "You must specify a target profile (device)" && printHelpAndExit 1 + + # By default use latest release + [[ -z $_version ]] && _version="21.02.1" # Custom profiles # TP-Link Archer C7v2 WAP (dumb AP) w/ legacy drivers for better performance - if [[ "$_profile" == "tplink_archer-c7-v2" ]]; then - [[ -z $_version ]] && _version="21.02.0-rc1" + if [[ "$_profile" == "archer" ]]; then + _profile="tplink_archer-c7-v2" _target="ath79/generic" - _factory_suffix="squashfs-factory.bin" - _sysupgrade_suffix="squashfs-sysupgrade.bin" + _filesystem="squashfs" _packages+=("-dnsmasq" \ "-odhcpd" \ "-iptables" \ "-ath10k-firmware-qca988x-ct" \ - "-kmod-ath10k-ct" \ - "ath10k-firmware-qca988x" \ - "kmod-ath10k") + "ath10k-firmware-qca988x-ct-full-htt") + # Linksys EA8300 (dumb AP) + elif [[ "$_profile" == "linksys" ]]; then + _profile="linksys_ea8300" + _target="ipq40xx/generic" + _filesystem="squashfs" + _packages+=("-dnsmasq" \ + "-odhcpd" \ + "-iptables" \ + ) # Raspberry Pi 4B router with USB->Ethernet dongle elif [[ "$_profile" == "rpi-4" ]]; then - [[ -z $_version ]] && _version="21.02.0-rc1" _target="bcm27xx/bcm2711" - _factory_suffix="ext4-factory.img" - _sysupgrade_suffix="ext4-sysupgrade.img" + _filesystem="ext4" _packages+=("kmod-usb-net-asix-ax88179" \ "kmod-usb-net-rtl8152" \ "luci-app-upnp" \ @@ -99,11 +106,9 @@ setDefaults() { "luci-app-sqm") # NanoPi R2S router elif [[ "$_profile" == "r2s" ]]; then - [[ -z $_version ]] && _version="21.02.0-rc1" _profile="friendlyarm_nanopi-r2s" _target="rockchip/armv8" - _factory_suffix="ext4-factory.img" - _sysupgrade_suffix="ext4-sysupgrade.img" + _filesystem="ext4" _packages+=("luci-app-upnp" \ "luci-app-wireguard" \ "luci-app-vpn-policy-routing" \ @@ -114,7 +119,29 @@ setDefaults() { "luci-app-statistics" \ "collectd-mod-sensors" \ "collectd-mod-thermal" \ - "lm-sensors") + "collectd-mod-conntrack" \ + "smcroute" \ + "curl" \ + "ethtool") + elif [[ "$_profile" == "r4s" ]]; then + _version="snapshot" + _profile="friendlyarm_nanopi-r4s" + _target="rockchip/armv8" + _filesystem="ext4" + _packages+=("luci-app-upnp" \ + "luci-app-wireguard" \ + "luci-app-vpn-policy-routing" \ + "-dnsmasq" \ + "dnsmasq-full" \ + "luci-app-ddns" \ + "luci-app-sqm" \ + "luci-app-statistics" \ + "collectd-mod-sensors" \ + "collectd-mod-thermal" \ + "collectd-mod-conntrack" \ + "smcroute" \ + "curl" \ + "ethtool") fi } @@ -193,18 +220,22 @@ setVars() { export _source_dir="${_source_archive%.tar.xz}" export _out_bin_dir="$_builddir/bin/$_profile-$_version/" + export _patches_dir="$_builddir/patches/" + export _files_dir="$_builddir/files/" + if [[ "$_version" == "snapshot" ]]; then local _out_prefix="$_out_bin_dir/openwrt-${_target//\//-}-$_profile" else local _out_prefix="$_out_bin_dir/openwrt-$_version-${_target//\//-}-$_profile" fi - export _factory_bin="$_out_prefix-$_factory_suffix" + + export _factory_bin="$_out_prefix-$_filesystem-factory.bin" export _factory_bin_fname="${_factory_bin##*/}" export _factory_bin_gz="$_factory_bin.gz" export _factory_bin_gz_fname="${_factory_bin_gz##*/}" - export _sysupgrade_bin="$_out_prefix-$_sysupgrade_suffix" + export _sysupgrade_bin="$_out_prefix-$_filesystem-sysupgrade.bin" export _sysupgrade_bin_fname="${_sysupgrade_bin##*/}" export _sysupgrade_bin_gz="$_sysupgrade_bin.gz" export _sysupgrade_bin_gz_fname="${_sysupgrade_bin_gz##*/}" @@ -285,6 +316,20 @@ extractImageBuilder() { } +# copyFiles() { + +# debug "${FUNCNAME[0]}" + +# declare -l _this_files_dir="$_files_dir/$_profile" + +# [[ ! -d "$_files_dir" ]] && return + +# $_profile == "r2s" + + +# } + + makeImage() { debug "${FUNCNAME[0]}" @@ -344,7 +389,7 @@ flashImage() { echo "Unmounting target device $_flash_dev partitions" debug "umount $_flash_dev?*" - sudo umount "$_flash_dev?*" + sudo umount "$_flash_dev"?* debug "sudo dd if=\"$_factory_bin\" of=\"$_flash_dev\" bs=2M conv=fsync" if sudo dd if="$_factory_bin" of="$_flash_dev" bs=2M conv=fsync; then @@ -430,11 +475,12 @@ __main() { installPrerequisites acquireImageBuilder extractImageBuilder + #copyFiles rm -rf "$_ssh_backup_path" [[ -v _ssh_backup_path ]] && sshBackup "$_ssh_backup_path" if makeImage; then - [[ -n $_ssh_upgrade_path ]] && sshUpgrade - [[ -n $_flash_dev ]] && flashImage + [[ -v _ssh_upgrade_path ]] && sshUpgrade + [[ -v _flash_dev ]] && flashImage fi } diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1411a4a..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pandas \ No newline at end of file