├── .gitignore ├── 2targets-1pikvm.md ├── How to Install PiKVM x86.pdf ├── README.MD ├── aiofiles.tar ├── apply-msd-patch.sh ├── armbian ├── armbian-motd ├── opt │ ├── armbian-sysinfo │ └── vcgencmd └── udev │ └── rules.d │ └── 99-kvmd.rules ├── create-flash-msd.sh ├── dtb └── 4.4 │ └── rk322x-box.dtb ├── fix-nfs-msd.sh ├── flash-img-creator.sh ├── install-x86.sh ├── install.sh ├── kvmd-3.291-1-any.pkg.tar.xz ├── libgpiod.sh ├── opikvm-logo.svg ├── patches ├── bullseye │ ├── 0001-Disable-GPIO-For-TV-Box.patch │ └── 0002-Support-for-old-version-python-3.patch └── custom │ └── old-kernel-msd │ ├── 0001-Revert-force-eject-feature-to-unlock-helper.patch │ ├── apply.sh │ └── kvmd-helper-otgmsd-unlock ├── pi-temp ├── pikvm-info ├── pistat ├── tshoot.sh ├── uninstall-pikvm.sh ├── update-rpikvm.sh ├── update-x86-pikvm.sh ├── v0-hdmiusb-zerow checklist.md └── web.css /.gitignore: -------------------------------------------------------------------------------- 1 | workspace.code-workspace 2 | tmp 3 | .vscode -------------------------------------------------------------------------------- /2targets-1pikvm.md: -------------------------------------------------------------------------------- 1 | QUICK HOW TO "ONE PIKVM - TWO TARGETS" by @srepac 2 | 3 | - Have you ever wanted to control multiple targets without a KVM switch using only one PiKVM? If so, this solution is exactly for you. In place of the x86 pikvm, you can also use RPi 3B with hdmi USB platform aka kvmd-platform-v0-hdmiusb-rpi3 https://148.135.104.55:8443/IMAGES/v0-hdmiusb-rpi3-20230423.img.xz 4 | 5 | REQUIREMENTS: 6 | - x86pikvm https://github.com/srepac/kvmd-armbian/blob/master/How%20to%20Install%20PiKVM%20x86.pdf 7 | - 2x uart + ch9329 (one per target) 8 | - 2x usb hdmi (one per target) 9 | 10 | VIDEO IN ACTION: 11 | - https://discord.com/channels/580094191938437144/580858755827367956/1194307516838977598 12 | 13 | Overview steps: 14 | 15 | 1. Create x86 pikvm per the doc above. Connect one pair of uart + ch9329 and usb hdmi per target. 16 | 17 | 2. Setup scripts in /usr/local/bin/ for use by GPIO menu. The ln -sf commands may need to be edited to reflect the correct devices used for your target 1 and target 2. In my case, I confirmed the first target used ttyUSB0 and video0, before connecting the next target uart + usb hdmi to the x86 PC. 18 | ``` 19 | [root@x86kvm bin]# pwd 20 | /usr/local/bin 21 | [root@x86kvm bin]# ls -l ch_reset.py target.sh 22 | -rwxr-xr-x 1 root root 471 Aug 25 16:45 ch_reset.py 23 | -rwxr-xr-x 1 root root 384 Jan 9 08:21 target.sh 24 | 25 | [root@x86kvm bin]# cat target.sh 26 | #!/bin/bash 27 | function usage() { 28 | echo "usage: $0 [#] where # is the target number to switch to." 29 | exit 1 30 | } 31 | 32 | function perform-change() { 33 | set -x 34 | echo "$USB,$VID,$TGT" > $CONFIG 35 | ln -sf $USB /dev/kvmd-hid 36 | ln -sf $VID /dev/kvmd-video 37 | systemctl restart kvmd # restart kvmd services after changing hid 38 | sleep 2 39 | ch_reset.py # reset ch9329 so it's fresh once we change targets 40 | ls -l /dev/kvmd* 41 | set +x 42 | } 43 | 44 | function show-config() { 45 | CONFIG="/etc/kvmd/current-target" 46 | if [ -e $CONFIG ]; then 47 | USB=$( cat $CONFIG | cut -d',' -f1 ) 48 | VID=$( cat $CONFIG | cut -d',' -f2 ) 49 | TGT=$( cat $CONFIG | cut -d',' -f3 ) 50 | NUM=$( echo $TGT | sed 's/target//g' ) 51 | echo "-> Current target info: $USB,$VID,$TGT" 52 | ls -l /dev/kvmd* 53 | else 54 | NUM=1 # CONFIG file does not exist so default is target 1 55 | fi 56 | } 57 | 58 | 59 | ### MAIN STARTS HERE ### 60 | show-config 61 | 62 | if [ $# -eq 0 ]; then 63 | echo "*** Missing target number. ***" 64 | usage 65 | fi 66 | 67 | case $1 in 68 | 1) if [ $1 -ne $NUM ] ; then 69 | echo "-> Control change to target $1. Please wait..." 70 | USB="ttyUSB0" 71 | VID="video0" 72 | TGT="target$1" 73 | perform-change 74 | else 75 | echo "-> Target you want is already set to $NUM" 76 | fi 77 | ;; 78 | 2) if [ $1 -ne $NUM ]; then 79 | echo "-> Control change to target $1. Please wait..." 80 | USB="ttyUSB1" 81 | VID="video2" 82 | TGT="target$1" 83 | perform-change 84 | else 85 | echo "-> Target you want is already set to $NUM" 86 | fi 87 | ;; 88 | 89 | -h|--help) 90 | usage 91 | ;; 92 | 93 | *) echo "*** Target not defined. Exiting. ***" 94 | exit 1 95 | ;; 96 | esac 97 | 98 | 99 | [root@x86kvm bin]# cat ch_reset.py 100 | #!/usr/bin/python3 101 | import serial 102 | import time 103 | 104 | device_path = "/dev/kvmd-hid" 105 | chip = serial.Serial(device_path, 9600, timeout=1) 106 | command = [87, 171, 0, 15, 0] 107 | sum = sum(command) % 256 108 | command.append(sum) 109 | 110 | print("Resetting CH9329") 111 | chip.write(serial.to_bytes(command)) 112 | time.sleep(2) 113 | data = list(chip.read(5)) 114 | print("Initial data:", data) 115 | 116 | if data[4] : 117 | more_data = list(chip.read(data[4])) 118 | data.extend(more_data) 119 | 120 | print("Output: ", data) 121 | chip.close() 122 | ``` 123 | 124 | **NOTE: Before moving on to the next step, make sure that you see two sets of prolific (uart+ch9329) and macrosilicon (usb hdmi) usb devices** 125 | ``` 126 | [root@x86kvm ~]# lsusb 127 | Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 128 | Bus 002 Device 005: ID 534d:2109 MacroSilicon 129 | Bus 002 Device 004: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port / Mobile Action MA-8910P 130 | Bus 002 Device 003: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port / Mobile Action MA-8910P 131 | Bus 002 Device 002: ID 534d:2109 MacroSilicon 132 | ``` 133 | 134 | 3. Setup sudoers to allow kvmd user to perform any commands 135 | ``` 136 | [root@x86kvm bin]# cat /etc/sudoers.d/custom_commands 137 | kvmd ALL=(ALL) NOPASSWD: ALL 138 | ``` 139 | 140 | 4. Setup override to show target buttons in GPIO menu 141 | ```[root@x86kvm bin]# cat /etc/kvmd/override.d/ch9329.yaml 142 | kvmd: 143 | gpio: 144 | drivers: 145 | ch_reset: 146 | type: cmd 147 | cmd: [/usr/local/bin/ch_reset.py] 148 | target1: 149 | type: cmd 150 | cmd: [/usr/bin/sudo, /usr/local/bin/target.sh, 1] 151 | target2: 152 | type: cmd 153 | cmd: [/usr/bin/sudo, /usr/local/bin/target.sh, 2] 154 | 155 | scheme: 156 | ch_reset_button: 157 | driver: ch_reset 158 | pin: 0 159 | mode: output 160 | switch: false 161 | target1_button: 162 | driver: target1 163 | pin: 1 164 | mode: output 165 | switch: false 166 | target2_button: 167 | driver: target2 168 | pin: 2 169 | mode: output 170 | switch: false 171 | 172 | view: 173 | table: 174 | - [] 175 | - ["#CUSTOM SCRIPTS"] 176 | - [] 177 | - ["#ch_reset.py", "ch_reset_button|Reset CH9329 HID"] 178 | - [] 179 | - ["#CHANGE TARGETS - requires webui restart"] 180 | - [] 181 | - ["#TARGET1", "target1_button|Target1"] 182 | - ["#TARGET2", "target2_button|Target2"] 183 | ``` 184 | 185 | 5. Add the default target 1 symlink changes to /usr/bin/kvmd-fix. **NOTE: Please make sure this matches your target 1 serial hid and video** 186 | ``` 187 | #!/bin/bash 188 | # Written by @srepac 189 | # 190 | ### These fixes are required in order for kvmd service to start properly 191 | # 192 | set -x 193 | 194 | ### setup the default target system 195 | CONFIG="/etc/kvmd/current-target" 196 | if [ -e $CONFIG ]; then 197 | USB=$( cat $CONFIG | cut -d',' -f1 ) 198 | VID=$( cat $CONFIG | cut -d',' -f2 ) 199 | TGT=$( cat $CONFIG | cut -d',' -f3 ) 200 | else 201 | TGT="target1" 202 | USB="ttyUSB0" 203 | VID="video0" 204 | echo "$USB,$VID,$TGT" > $CONFIG 205 | fi 206 | ln -sf $USB /dev/kvmd-hid 207 | ln -sf $VID /dev/kvmd-video 208 | echo "Controlling $TGT" 209 | systemctl restart kvmd 210 | ls -l /dev/kvmd* 211 | 212 | set +x 213 | ``` 214 | 215 | 6. Enjoy! 216 | -------------------------------------------------------------------------------- /How to Install PiKVM x86.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srepac/kvmd-armbian/a288b36815cff12a78fe1554cbcd4b229648bbd6/How to Install PiKVM x86.pdf -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # KVMD-ARMBIAN 2 | This project supports non-Raspberry Pi device to run pikvm on armbian or Raspberry Pi boards on Raspbian Bookworm 3 | - It also supports x86 pikvm with USB uart + ch9329 serial HID if you use the install-x86.sh script and follow the How to Install PiKVM x86.pdf 4 | - As of 04/21/2024, I have left the PiKVM Discord so if you need assistance, please email me: srepac@kvmnerds.com 5 | - You can also try and see if I'm on my own Discord at https://discord.gg/64EQQuwjsB 6 | 7 | # Install 8 | KVMD Install for armbian/raspbian 9 | 10 | It supports Allwinner, Amlogic and Rockchip based tv box, tested on phicomm n1, mxq pro 4k, tqc a01. 11 | Chipset needs to support USB OTG feature, lots of old amglogic chipset does not support otg feature, such as s805 and s905. 12 | You should install armbian with debian jammy as jammy has python 3.10 or python 3.11 (starting with kvmd 3.217) which the newer kvmd code is based on. 13 | Then run this script (2x) to install pikvm. 14 | 15 | This also works on rpi boards running Raspbian Bookworm. 16 | 17 | Install script is a fork from @srepac raspbian pikvm install script. 18 | 19 | **NOTE: all commands need to be run as root user.** 20 | 21 | Original Script [http://148.135.104.55/RPiKVM/install-pikvm-raspbian.sh] 22 | 23 | # Hardware for kvmd-armbian project 24 | * A tv box/arm board that supports otg feature: 25 | - Tests on phicomm n1(Amlogic s905d), mxq pro 4k (rk322x), tqc a01(Allwinner H6). 26 | - If you are using arm board you can remove gpio patch to enable gpio feature. 27 | * Video capture device: 28 | - HDMI to USB dongle (30 RMB On taobao, 10$ on aliexpress.) or USB HDMI loop capture dongle 29 | - cheap hdmi to usb dongle all use physics USB2.0 port, but fake USB3.0(USB 5GBPS, USB3.2GEN1) version supports 720P 60FPS, 30 | usb 2.0 version only supports 720P 30FPS. 31 | * USB-A to USB-A cable or micro-USB to USB-A cable: 32 | - Recommended to cut off usb cable's power line, otherwise it might cause otg disconnect. 33 | 34 | ## Step 1 35 | - Flash armbian debian [Recommended jammy] for your tv box (If kernel not support otg you should build a kernel enable otg features) 36 | - Flash armbian debian jammy for all others 37 | 38 | ## Step 2 (skip this step if running on Orange Pi zero and one boards) 39 | - Modify your dtb file to enable otg feature. Change dr_mode from host to peripheral for otg usb port. 40 | - If you use rk322x (rk3228A rk3228B rk3229) series chipset, you can use dtb/4.4/rk332x-box.dtb 41 | - Add the following to your /boot/armbianEnv.txt file 42 | ``` 43 | overlays=usbhost0 usbhost1 usbhost2 usbhost3 44 | ``` 45 | 46 | **NOTE: Skip steps 1 and 2 if you are running raspbian bookworm on rpi boards** 47 | ## Step 3 - Perform part 1 of install 48 | ``` 49 | apt update && apt upgrade -y 50 | apt install -y git vim make python3-dev gcc xz-utils wget sudo 51 | git clone https://github.com/srepac/kvmd-armbian.git 52 | cd kvmd-armbian 53 | ./install.sh 54 | ``` 55 | This will ask you to press ENTER to reboot after part 1 completes. 56 | 57 | ## Step 4 - Perform part 2 of install 58 | - run install.sh again after reboot os to perform part 2 of install. **NOTE: May require one more reboot if missing /dev/kvmd-hid-[keyboard|mouse]** 59 | ``` 60 | cd kvmd-armbian 61 | ./install.sh 62 | ``` 63 | - Enjoy 64 | 65 | 66 | # Updating RPiKVM 67 | Update armbian pikvm anytime in order to take advantage of new features/updates. 68 | 69 | - run the update-rpikvm.sh script to perform update 70 | ``` 71 | wget --no-check-certificate -O /usr/local/bin/update-rpikvm.sh http://148.135.104.55/RPiKVM/update-rpikvm.sh 72 | update-rpikvm.sh 73 | ``` 74 | 75 | 76 | # Tested device 77 | - Phicomm N1 78 | - TQC A01 (Ethernet port not working, only support wireless.) 79 | - RK322x based tvbox (MXQ, V88) 80 | - S905L2 based tvbox 81 | - Orange pi zero (tested by @MrSuicideParrot) 82 | - Orange Pi Zero and One (tested by @srepac) 83 | - Nano Pi Neo, Rock64, and Orange Pi Zero Plus (tested by @srepac) 84 | - Libre Computer Le Potato, La Frite 1GB, Renegade ROC-RK3328-CC and ALL-H3-CC H5 2GB (tested by @srepac) 85 | - Inovato Quadra tv box (tested by @srepac) 86 | - Big Tree Tech CB1+Rpi4 board (WIP by @srepac) 87 | - Orange Pi PC+ and orange pi 3 (tested by @ducs4rs) 88 | - RPi4B + PiKVM V3 HAT on Raspberry Pi OS bookworm (tested by @srepac on 10/19/23) 89 | - RPi4B + BliKVM V3 HAT on Raspberry Pi OS bookworm (tested by @srepac) 90 | - RPiCM4 + BliKVM V2 pcie HAT on Raspberry Pi OS bookworm (tested by @srepac) 91 | - RPiCM4 + Geekworm A8 pcie HAT on Raspberry Pi OS bookworm (tested by @srepac) 92 | - RPiCM4 + Geekworm X650 pcie HAT on Raspberry Pi OS bookworm (tested by @srepac) 93 | - RPiCM4 + Geekworm X635 HAT on Raspberry Pi OS bookworm (tested by @srepac) 94 | - BliKVM V4 with MangoPi MCore H616 on blikvm OS (tested by @srepac) 95 | - x86 VM and bare metal (tested by @srepac) 96 | - Orange pi 3b, OPi zero2w, and OPi zero 3 (tested by @thrice) 97 | -------------------------------------------------------------------------------- /aiofiles.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srepac/kvmd-armbian/a288b36815cff12a78fe1554cbcd4b229648bbd6/aiofiles.tar -------------------------------------------------------------------------------- /apply-msd-patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Written by @srepac 3 | # 4 | # This apply kvmd 3.291 patch so that MSD doesn't require the forced_eject patch 5 | # 6 | # Filename: apply-msd-patch.sh 7 | # 8 | # To ignore applying msd patch, run the following command as root: touch /root/disable-msd 9 | ### 10 | function usage() { 11 | echo "usage: $0 [-f] where -f will actually perform destructive tasks" 12 | exit 1 13 | } # end usage 14 | 15 | function patch-msd() { 16 | set -x 17 | 18 | if [ $( ls /sys/kernel/config/usb_gadget/kvmd/functions/mass_storage.usb0/lun.0/ | grep -c forced_eject ) -eq 1 ]; then 19 | echo "Found forced_eject patch already enabled. Nothing to do here." 20 | else 21 | PATCHNAME="3.291msd.patch" 22 | echo "Applying ${PATCHNAME} for use with kvmd $KVMDVERuse..." 23 | cd /usr/lib/python3/dist-packages/kvmd 24 | wget -q https://github.com/RainCat1998/Bli-PiKVM/raw/main/$PATCHNAME -O $PATCHNAME 25 | patch -p1 < $PATCHNAME 26 | 27 | # delete msd: and next line from override.yaml 28 | sed -i.msd -e '/ msd:/,+1d' /etc/kvmd/override.yaml 29 | fi 30 | 31 | set +x 32 | } # end patch-msd 33 | 34 | 35 | ### MAIN STARTS HERE ### 36 | WHOAMI=$( whoami ) 37 | if [[ "$WHOAMI" != "root" ]]; then 38 | echo "$WHOAMI, please run this as root." 39 | exit 1 40 | fi 41 | 42 | perform=0 43 | while getopts "fhv" opts; do 44 | case $opts in 45 | f) perform=1 ;; 46 | v) set -x; perform=0 ;; 47 | h) usage ;; 48 | *) perform=0 ;; 49 | esac 50 | done 51 | 52 | if [ $( mount | grep -c /kvmd/msd' ' ) -ge 1 ]; then 53 | KVMDVER=$( pikvm-info 2> /dev/null | grep kvmd-platform | awk '{print $1}' ) 54 | if [ "$KVMDVER" == "3.291" ]; then 55 | if [ -e /root/disable-msd ]; then 56 | echo "MSD has to be disabled. Skipping." 57 | else 58 | echo "-> Call to function patch-msd" ### ONLY apply patch if kvmd 3.291 and /root/disable-msd file not exist ### 59 | if [ $perform -eq 1 ]; then 60 | patch-msd 61 | else 62 | echo "Please re-run script with -f to perform destructive task: $0 -f" 63 | fi 64 | fi 65 | else 66 | echo "Found kvmd $KVMDVER running. Patch only applies to kvmd 3.291. Exiting." 67 | exit 1 68 | fi 69 | else 70 | echo "*** Make sure an /etc/fstab entry exists for /var/lib/kvmd/msd ***" 71 | echo "/var/lib/kvmd/msd is not mounted. Please create new partition for /var/lib/kvmd/msd and have it mounted before continuing." 72 | exit 1 73 | fi 74 | -------------------------------------------------------------------------------- /armbian/armbian-motd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -e /etc/update-motd.d/10-armbian-header ]; then /etc/update-motd.d/10-armbian-header; fi 3 | if [ -e /etc/update-motd.d/30-armbian-sysinfo ]; then /etc/update-motd.d/30-armbian-sysinfo; fi 4 | if [ -e /etc/update-motd.d/41-armbian-config ]; then /etc/update-motd.d/41-armbian-config; fi 5 | 6 | printf " Welcome to PiKVM - Open Source IP-KVM installed on SBC 7 | ____________________________________________________________________________ 8 | 9 | Useful commands: 10 | * Preventing kernel messages in the console: dmesg -n 1 11 | * Changing the Web UI password: kvmd-htpasswd set admin 12 | * Changing the root password: passwd 13 | 14 | Links: 15 | * Official website: https://pikvm.org 16 | * Documentation: https://docs.pikvm.org 17 | * Auth & 2FA: https://docs.pikvm.org/auth 18 | * Networking: https://wiki.archlinux.org/title/systemd-networkd 19 | * Kvmd-armbian: https://github.com/srepac/kvmd-armbian 20 | 21 | " 22 | 23 | if [ -e /etc/motd.custom ]; then cat /etc/motd.custom; fi 24 | -------------------------------------------------------------------------------- /armbian/opt/armbian-sysinfo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) Authors: http://www.armbian.com/authors 4 | # 5 | # This file is licensed under the terms of the GNU General Public 6 | # License version 2. This program is licensed "as is" without any 7 | # warranty of any kind, whether express or implied. 8 | # 9 | 10 | # DO NOT EDIT THIS FILE but add config options to /etc/default/armbian-motd 11 | # generate system information 12 | 13 | export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 14 | 15 | THIS_SCRIPT="sysinfo" 16 | MOTD_DISABLE="" 17 | STORAGE=/dev/sda1 18 | SHOW_IP_PATTERN="^bond.*|^[ewr].*|^br.*|^lt.*|^umts.*|^lan.*" 19 | 20 | CPU_TEMP_LIMIT=60 21 | HDD_TEMP_LIMIT=60 22 | AMB_TEMP_LIMIT=40 23 | 24 | [[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd 25 | 26 | for f in $MOTD_DISABLE; do 27 | [[ $f == $THIS_SCRIPT ]] && exit 0 28 | done 29 | 30 | # don't edit below here 31 | 32 | function display() { 33 | # $1=name $2=value $3=red_limit $4=minimal_show_limit $5=unit $6=after $7=acs/desc{ 34 | # battery red color is opposite, lower number 35 | if [[ "$1" == "Battery" ]]; then local great="<"; else local great=">"; fi 36 | if [[ -n "$2" && "$2" > "0" && (( "${2%.*}" -ge "$4" )) ]]; then 37 | printf "%-14s%s" "$1:" 38 | if awk "BEGIN{exit ! ($2 $great $3)}"; then echo -ne "\e[0;91m $2"; else echo -ne "\e[0;92m $2"; fi 39 | printf "%-1s%s\x1B[0m" "$5" 40 | printf "%-11s%s\t" "$6" 41 | return 1 42 | fi 43 | } # display 44 | 45 | function getboardtemp() { 46 | if [ -f /etc/armbianmonitor/datasources/soctemp ]; then 47 | read raw_temp /dev/null 48 | if [ ! -z $(echo "$raw_temp" | grep -o "^[1-9][0-9]*\.\?[0-9]*$") ] && (( $(echo "${raw_temp} < 200" |bc -l) )); then 49 | # Allwinner legacy kernels output degree C 50 | board_temp=${raw_temp} 51 | else 52 | board_temp=$(awk '{printf("%d",$1/1000)}' <<<${raw_temp}) 53 | fi 54 | elif [ -f /etc/armbianmonitor/datasources/pmictemp ]; then 55 | # fallback to PMIC temperature 56 | board_temp=$(awk '{printf("%d",$1/1000)}' /dev/null 66 | if [[ "$status_battery_connected" == "1" ]]; then 67 | read status_battery_charging < $mainline_dir/charger/charging 68 | read status_ac_connect < $mainline_dir/ac/connected 69 | read battery_percent< $mainline_dir/battery/capacity 70 | # dispay charging / percentage 71 | if [[ "$status_ac_connect" == "1" && "$battery_percent" -lt "100" ]]; then 72 | status_battery_text=" charging" 73 | elif [[ "$status_ac_connect" == "1" && "$battery_percent" -eq "100" ]]; then 74 | status_battery_text=" charged" 75 | else 76 | status_battery_text=" discharging" 77 | fi 78 | fi 79 | elif [[ -e "$legacy_dir/axp813-ac" ]]; then 80 | read status_battery_connected < $legacy_dir/axp20x-battery/present 81 | if [[ "$status_battery_connected" == "1" ]]; then 82 | status_battery_text=" "$(awk '{print tolower($0)}' < $legacy_dir/axp20x-battery/status) 83 | read status_ac_connect < $legacy_dir/axp813-ac/present 84 | read battery_percent< $legacy_dir/axp20x-battery/capacity 85 | fi 86 | elif [[ -e "$legacy_dir/battery" ]]; then 87 | if [[ (("$(cat $legacy_dir/battery/voltage_now)" -gt "5" )) ]]; then 88 | status_battery_text=" "$(awk '{print tolower($0)}' < $legacy_dir/battery/status) 89 | read battery_percent <$legacy_dir/battery/capacity 90 | fi 91 | fi 92 | } # batteryinfo 93 | 94 | function ambienttemp() { 95 | # define where w1 usually shows up 96 | W1_DIR="/sys/devices/w1_bus_master1/" 97 | if [ -f /etc/armbianmonitor/datasources/ambienttemp ]; then 98 | read raw_temp /dev/null 99 | amb_temp=$(awk '{printf("%d",$1/1000)}' <<<${raw_temp}) 100 | echo $amb_temp 101 | elif [[ -d $W1_DIR && $ONE_WIRE == yes ]]; then 102 | device=$(ls -1 $W1_DIR | grep -Eo '^[0-9]{1,4}' | head -1) 103 | if [[ -n $device ]]; then 104 | if [[ -d ${W1_DIR}${device}/hwmon/hwmon0 ]]; then hwmon=0; else hwmon=1; fi 105 | read raw_temp < ${W1_DIR}${device}/hwmon/hwmon${hwmon}/temp1_input 2>/dev/null 106 | amb_temp=$(awk '{printf("%d",$1/1000)}' <<<${raw_temp}) 107 | echo $amb_temp 108 | fi 109 | else 110 | # read ambient temperature from USB device if available 111 | if [[ ! -f /usr/bin/temper ]]; then 112 | echo "" 113 | return 114 | fi 115 | amb_temp=$(temper -c 2>/dev/null) 116 | case ${amb_temp} in 117 | *"find the USB device"*) 118 | echo "" 119 | ;; 120 | *) 121 | amb_temp=$(awk '{print $NF}' <<<$amb_temp | sed 's/C//g') 122 | echo -n "scale=1;${amb_temp}/1" | grep -oE "\-?[[:digit:]]+\.[[:digit:]]" 123 | esac 124 | fi 125 | } # ambienttemp 126 | 127 | function get_ip_addresses() { 128 | local ips=() 129 | for f in /sys/class/net/*; do 130 | local intf=$(basename $f) 131 | # match only interface names starting with e (Ethernet), br (bridge), w (wireless), r (some Ralink drivers use ra format) 132 | if [[ $intf =~ $SHOW_IP_PATTERN ]]; then 133 | local tmp=$(ip -4 addr show dev $intf | awk '/inet/ {print $2}' | cut -d'/' -f1) 134 | # add both name and IP - can be informative but becomes ugly with long persistent/predictable device names 135 | #[[ -n $tmp ]] && ips+=("$intf: $tmp") 136 | # add IP only 137 | [[ -n $tmp ]] && ips+=("$tmp") 138 | fi 139 | done 140 | echo "${ips[@]}" 141 | } # get_ip_addresses 142 | 143 | function storage_info() { 144 | # storage info 145 | RootInfo=$(df -h /) 146 | root_usage=$(awk '/\// {print $(NF-1)}' <<<${RootInfo} | sed 's/%//g') 147 | root_total=$(awk '/\// {print $(NF-4)}' <<<${RootInfo}) 148 | StorageInfo=$(df -h $STORAGE 2>/dev/null | grep $STORAGE) 149 | if [[ -n "${StorageInfo}" && ${RootInfo} != *$STORAGE* ]]; then 150 | storage_usage=$(awk '/\// {print $(NF-1)}' <<<${StorageInfo} | sed 's/%//g') 151 | storage_total=$(awk '/\// {print $(NF-4)}' <<<${StorageInfo}) 152 | if [[ -n "$(command -v smartctl)" ]]; then 153 | DISK="${STORAGE::-1}" 154 | storage_temp+=$(sudo smartctl -A $DISK 2> /dev/null | grep -i temperature | awk '{print $(NF-2)}') 155 | fi 156 | fi 157 | } # storage_info 158 | 159 | 160 | 161 | # query various systems and send some stuff to the background for overall faster execution. 162 | # Works only with ambienttemp and batteryinfo since A20 is slow enough :) 163 | amb_temp=$(ambienttemp &) 164 | ip_address=$(get_ip_addresses &) 165 | batteryinfo 166 | storage_info 167 | getboardtemp 168 | critical_load=80 169 | 170 | # get uptime, logged in users and load in one take 171 | UPTIME=$(LC_ALL=C uptime) 172 | UPT1=${UPTIME#*'up '} 173 | UPT2=${UPT1%'user'*} 174 | users=${UPT2//*','} 175 | users=${users//' '} 176 | time=${UPT2%','*} 177 | time=${time//','} 178 | time=$(echo $time | xargs) 179 | load=${UPTIME#*'load average: '} 180 | load=${load//','} 181 | load=$(echo $load | cut -d" " -f1) 182 | [[ $load == 0.0* ]] && load=0.10 183 | cpucount=$(grep -c processor /proc/cpuinfo) 184 | 185 | load=$(awk '{printf("%.0f",($1/$2) * 100)}' <<< "$load $cpucount") 186 | 187 | # memory and swap 188 | mem_info=$(LC_ALL=C free -w 2>/dev/null | grep "^Mem" || LC_ALL=C free | grep "^Mem") 189 | memory_usage=$(awk '{printf("%.0f",(($2-($4+$6+$7))/$2) * 100)}' <<<${mem_info}) 190 | mem_info=$(echo $mem_info | awk '{print $2}') 191 | memory_total=$(( mem_info / 1024 )) 192 | swap_info=$(LC_ALL=C free -m | grep "^Swap") 193 | swap_usage=$( (awk '/Swap/ { printf("%3.0f", $3/$2*100) }' <<<${swap_info} 2>/dev/null || echo 0) | tr -c -d '[:digit:]') 194 | swap_total=$(awk '{print $(2)}' <<<${swap_info}) 195 | if [[ ${memory_total} -gt 1000 ]]; then 196 | memory_total=$(awk '{printf("%.2f",$1/1024)}' <<<${memory_total})"G" 197 | else 198 | memory_total+="M" 199 | fi 200 | 201 | if [[ ${swap_total} -gt 500 ]]; then 202 | swap_total=$(awk '{printf("%.2f",$1/1024)}' <<<${swap_total})"G" 203 | else 204 | swap_total+="M" 205 | fi 206 | 207 | -------------------------------------------------------------------------------- /armbian/opt/vcgencmd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0` 3 | source armbian-sysinfo 4 | 5 | case $1 in 6 | get_throttled) echo "throttled=0x0";; 7 | # measure_temp) echo "temp=$board_temp'C";; 8 | get_config) 9 | case $2 in 10 | total_mem) 11 | NUM=$( free -m | grep Mem: | awk '{print $2}' ) 12 | if [ -e /var/log/dmesg ]; then 13 | KB=$( sudo grep 'Memory:' /var/log/dmesg | awk '{print $5}' | cut -d'/' -f2 | sed 's/K//g' | head -1 ) 14 | else 15 | KB="" 16 | fi 17 | 18 | if [[ "$KB" == "" ]]; then 19 | GB=`echo "( $NUM + 256 ) / 256" | bc` 20 | MB=$( echo "${GB} * 256" | bc ) 21 | else 22 | GB=$( echo "($KB + 2048) / 1024 / 256" | bc ) 23 | MB=$( echo "${GB} * 256" | bc ) 24 | fi 25 | echo "total_mem=$MB" 26 | ;; 27 | *) 28 | echo "invalid option";; 29 | esac 30 | ;; 31 | measure_clock) 32 | case ${2} in 33 | arm) 34 | # awk is probably overkill.... 35 | value=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq | awk '{print $1 * 1000}'` 36 | echo 'frequency(45)='${value} 37 | exit 38 | ;; 39 | core) 40 | value=0 # TODO / FIXME 41 | echo 'frequency(1)='${value} 42 | exit 43 | ;; 44 | # TODO anything else thrown an error/debug 45 | esac 46 | exit 47 | ;; 48 | measure_temp) 49 | # awk is probably overkill.... 50 | value=`cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1 / 1000}'` 51 | echo 'temp='${value}"'C" 52 | exit 53 | ;; 54 | measure_volts) 55 | case ${2} in 56 | core) 57 | value=1 # TODO / FIXME 58 | echo 'volt='${value}'.0000V' 59 | exit 60 | ;; 61 | # TODO anything else thrown an error/debug 62 | esac 63 | ;; 64 | version) 65 | echo 'Nov 4 2018 16:31:07' 66 | echo 'Copyright (c) 2012 rock64' 67 | echo 'version rock64_TODO (clean) (release)' 68 | exit 69 | ;; 70 | esac 71 | -------------------------------------------------------------------------------- /armbian/udev/rules.d/99-kvmd.rules: -------------------------------------------------------------------------------- 1 | # https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name 2 | # https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names 3 | KERNEL=="video[1-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check rpi4 1-1.4:1.0", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" 4 | KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" 5 | KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" 6 | KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" 7 | -------------------------------------------------------------------------------- /create-flash-msd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### 3 | # Written by @srepac 4 | # This script creates FLASH.img within your root fs in case you didnt' create an MSD partition 5 | # ... this will then add the proper /etc/fstab entry to mount the created FLASH.img 6 | # ... this is meant for non-arch pikvm builds (e.g. armbian/raspbian) 7 | ### 8 | 9 | # init variables 10 | validname=0; NAME="" 11 | validdir=0; FLASHDIR="/" 12 | validsize=0; SIZE=9999 13 | 14 | function usage() { 15 | echo "Usage: $(basename $0) [-n ] [ -s ] [ -d ]" 16 | echo 17 | echo "Example: $(basename $0) -n TEST -s 8 -d /home/admin creates 8GB /home/admin/TEST.img flash image" 18 | echo 19 | exit 1 20 | } 21 | 22 | VER=1.0 23 | echo 24 | echo "--- Flash image creator v${VER} by srepac ---" 25 | echo 26 | 27 | while getopts ':n:s:d:h' OPTION; do 28 | case $OPTION in 29 | n) 30 | NAME="$OPTARG" 31 | echo "flash drive name: $NAME.img" 32 | validname=1 33 | ;; 34 | d) 35 | FLASHDIR="$OPTARG" 36 | echo "flash drive location: $FLASHDIR" 37 | if [ ! -e $FLASHDIR ]; then 38 | echo "$FLASHDIR doesn't exist." 39 | validdir=0 40 | else 41 | validdir=1 42 | fi 43 | ;; 44 | s) 45 | SIZE="$OPTARG" 46 | echo "size: $SIZE GB" 47 | validsize=1 48 | ;; 49 | h) 50 | usage 51 | ;; 52 | *) 53 | echo "Invalid option $OPTION" 54 | usage 55 | ;; 56 | esac 57 | done 58 | 59 | if [[ "$FLASHDIR" != "/" ]]; then 60 | MAXSIZE=$( df -h $FLASHDIR | grep -v Size | awk '{print $4}' | cut -d'.' -f1 ) 61 | echo "MAXSIZE: $MAXSIZE" 62 | if [ $SIZE -gt $MAXSIZE ]; then 63 | echo "Size cannot be bigger than available size. Try again..." 64 | validsize=0 65 | fi 66 | fi 67 | 68 | while [ $validname -eq 0 ]; do 69 | read -p "Please enter flash drive name you want to use -> " NAME 70 | if [[ "$NAME" == "" ]]; then 71 | echo "Nothing was entered. Please try again..." 72 | validname=0 73 | else 74 | validname=1 75 | fi 76 | done 77 | 78 | while [ $validdir -eq 0 ]; do 79 | read -p "Please enter path to create the flash image -> " FLASHDIR 80 | if [[ "$FLASHDIR" == "" ]]; then 81 | echo "Nothing was entered. Please try again..." 82 | validdir=0 83 | elif [ ! -e $FLASHDIR ]; then 84 | echo "$FLASHDIR doesn't exist. Try again..." 85 | validdir=0 86 | else 87 | echo "-> $FLASHDIR exists. Continuing..." 88 | validdir=1 89 | fi 90 | done 91 | 92 | while [ $validsize -eq 0 ]; do 93 | MAXSIZE=$( df -h $FLASHDIR | grep -v Size | awk '{print $4}' | cut -d'.' -f1 ) 94 | 95 | read -p "Please enter size in GB (enter 1-$MAXSIZE) -> " SIZE 96 | SIZE=$( echo $SIZE | sed 's/[Gg][Bb]//g' ) # cleanup entry in case user entered 8GB instead of just 8 97 | 98 | if [[ "$SIZE" == "" ]]; then 99 | echo "Nothing was entered. Please try again..." 100 | validsize=0 101 | elif [ $SIZE -le 0 ]; then 102 | echo "Size cannot be zero or negative number. Try again..." 103 | validsize=0 104 | elif [ $SIZE -gt $MAXSIZE ]; then 105 | echo "Size cannot be bigger than available size. Try again..." 106 | validsize=0 107 | else 108 | validsize=1 109 | fi 110 | done 111 | 112 | echo "-> Creating empty ${SIZE}GB flash in $FLASHDIR/$NAME.img ..." 113 | truncate -s ${SIZE}G $FLASHDIR/$NAME.img # creates an 8GB flash image file (in seconds) 114 | 115 | echo 116 | echo "-> Formatting $FLASHDIR/$NAME.img as ext4 ..." 117 | mkfs.ext4 $FLASHDIR/$NAME.img # format ext4 flash image 118 | 119 | echo 120 | echo "-> Labeling $FLASHDIR/$NAME.img as PIMSD ..." 121 | e2label $FLASHDIR/$NAME.img PIMSD # label it as PIMSD 122 | 123 | systemctl daemon-reload 124 | 125 | TESTDIR="/mnt/TEST" 126 | echo 127 | echo "-> Mounting $FLASHDIR/$NAME.img into $TESTDIR and testing creating files..." 128 | mkdir -p $TESTDIR 129 | mount $FLASHDIR/$NAME.img $TESTDIR 130 | umount $TESTDIR 131 | sleep 3 132 | mount $FLASHDIR/$NAME.img $TESTDIR 133 | lsblk -f 134 | 135 | df -h | grep TEST 136 | cd $TESTDIR 137 | pwd 138 | chown kvmd:kvmd . 139 | touch newfile # test creating new file 140 | mkdir NFS_Primary # create NFS_Primary dir in case you use NFS https://docs.pikvm.org/msd/?h=nfs#nfs-storage 141 | ls -la 142 | cd 143 | 144 | echo 145 | echo "-> Unmounting $TESTDIR..." 146 | umount $TESTDIR 147 | 148 | # and then add into fstab 149 | echo 150 | if [ $( grep /var/lib/kvmd/msd' ' /etc/fstab | grep -c -v '#' ) -ge 1 ]; then 151 | echo "/etc/fstab entry already exists." 152 | else 153 | echo "-> Adding /etc/fstab entry..." 154 | echo $FLASHDIR/$NAME.img /var/lib/kvmd/msd ext4 nodev,nosuid,noexec,rw,errors=remount-ro,X-kvmd.otgmsd-root=/var/lib/kvmd/msd,X-kvmd.otgmsd-user=kvmd 0 0 >> /etc/fstab 155 | fi 156 | grep /var/lib/kvmd/msd' ' /etc/fstab | grep -v '#' 157 | -------------------------------------------------------------------------------- /dtb/4.4/rk322x-box.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srepac/kvmd-armbian/a288b36815cff12a78fe1554cbcd4b229648bbd6/dtb/4.4/rk322x-box.dtb -------------------------------------------------------------------------------- /fix-nfs-msd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Written by srepac (c) 2023 3 | # 4 | # This is meant for armbian PiKVM use -- required to make NFS MSD work properly (in effect since kvmd 3.206) 5 | # 6 | # In summary, this script will perform the following steps: 7 | : " 8 | 1. take the contents of /usr/lib/python3.11/site-packages/aiofiles from arch pikvm and extract it to armbian 9 | 2. rename aiofiles within /usr/lib/python3/dist-packages to aiofiles.YYYYMMDD.hhmm 10 | 3. create symlink to /usr/lib/python3.11/site-packages/aiofiles into /usr/lib/python3/dist-packages 11 | 4. restart kvmd 12 | " 13 | 14 | # first, check to make sure that NFS_Primary is mounted in /var/lib/kvmd/msd 15 | if [ $(mount | grep -c NFS_Primary) -le 0 ]; then 16 | echo "Missing /var/lib/kvmd/msd/NFS_Primary mount" 17 | echo "Please follow https://docs.pikvm.org/msd/#nfs-storage then re-run script." 18 | exit 1 19 | else 20 | echo "/var/lib/kvmd/msd/NFS_Primary is mounted." 21 | echo "-> Stopping kvmd" 22 | systemctl stop kvmd 23 | fi 24 | 25 | NAME="aiofiles.tar" 26 | AIOFILES="https://raw.githubusercontent.com/srepac/kvmd-armbian/master/$NAME" 27 | 28 | echo -n "-> Downloading $AIOFILES into /tmp ... " 29 | wget -O /tmp/$NAME $AIOFILES > /dev/null 2> /dev/null 30 | echo "done" 31 | 32 | LOCATION="/usr/lib/python3.11/site-packages" 33 | echo "-> Extracting /tmp/$NAME into $LOCATION" 34 | tar xvf /tmp/$NAME -C $LOCATION 35 | 36 | echo "-> Renaming original aiofiles and creating symlink to correct aiofiles" 37 | cd /usr/lib/python3/dist-packages 38 | mv aiofiles aiofiles.$(date +%Y%m%d.%H%M) 39 | ln -s $LOCATION/aiofiles . 40 | ls -ld aiofiles* 41 | 42 | echo "-> Restarting kvmd" 43 | systemctl restart kvmd 44 | 45 | echo "-> Please check webui to make sure that NFS MSD is working properly." 46 | -------------------------------------------------------------------------------- /flash-img-creator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Written by @srepac (c) 2024 3 | #### 4 | # Filename: flash-img-creator.sh 5 | #### 6 | 7 | # Remount internal storage to read-write mode manually: 8 | kvmd-helper-otgmsd-remount rw 9 | 10 | # Initialize variables 11 | FLASHIMG=/var/lib/kvmd/msd/flash.img # path/filename of img file 12 | LABEL="FLASHIMG" # partition label 13 | create=1 # default is to create img file 14 | 15 | if [ -e $FLASHIMG ]; then # check if img file already exists 16 | ls -l $FLASHIMG 17 | echo "Found existing $FLASHIMG" 18 | 19 | loop=1 20 | while [ $loop -eq 1 ]; do 21 | read -p "Do you want to remove it? Y/N -> " answer 22 | case $answer in 23 | y|Y) echo "Deleting $FLASHIMG"; rm -f $FLASHIMG; create=1; loop=0;; # remove and create new img file 24 | n|N) echo "Exiting."; loop=0; create=0;; # don't touch img file 25 | *) echo "Please try again."; loop=1;; 26 | esac 27 | done 28 | fi 29 | 30 | set -x 31 | if [ $create -eq 1 ]; then 32 | # Create empty image file in /var/lib/kvmd/msd (in internal storage of PiKVM images) of desired size 33 | # ... 8GB in this example and format it to FAT32 and add a label to it 34 | truncate -s 8G $FLASHIMG 35 | echo -e 'o\nn\np\n1\n\n\nt\nc\nw\n' | fdisk $FLASHIMG 36 | loop=$(losetup -f) 37 | losetup -P $loop $FLASHIMG 38 | mkfs.vfat ${loop}p1 39 | fatlabel ${loop}p1 $LABEL && sleep 5 40 | lsblk -f ${loop} 41 | losetup -d $loop 42 | chmod go+rw $FLASHIMG 43 | ls -l $FLASHIMG 44 | fi 45 | 46 | # Remount internal storage back to safe read-only mode: 47 | kvmd-helper-otgmsd-remount ro 48 | set +x 49 | -------------------------------------------------------------------------------- /install-x86.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # https://github.com/srepac/kvmd-armbian 3 | # 4 | # modified by xe5700 2021-11-04 xe5700@outlook.com 5 | # modified by NewbieOrange 2021-11-04 6 | # created by @srepac 08/09/2021 srepac@kvmnerds.com 7 | # Scripted Installer of Pi-KVM on x86 (as long as it's running python 3.10 or higher) 8 | # 9 | # *** MSD is disabled by default *** 10 | # 11 | # Mass Storage Device requires the use of a USB thumbdrive or SSD and will need to be added in /etc/fstab 12 | : ' 13 | # SAMPLE /etc/fstab entry for USB drive with only one partition formatted as ext4 for the entire drive: 14 | 15 | /dev/sda1 /var/lib/kvmd/msd ext4 nodev,nosuid,noexec,ro,errors=remount-ro,data=journal,X-kvmd.otgmsd-root=/var/lib/kvmd/msd,X-kvmd.otgmsd-user=kvmd 0 0 16 | 17 | ' 18 | # NOTE: This was tested on a new install of raspbian desktop and lite versions, but should also work on an existing install. 19 | # 20 | # Last change 20240526 2345 PDT 21 | VER=3.4 22 | set +x 23 | PIKVMREPO="https://files.pikvm.org/repos/arch/rpi4" 24 | KVMDFILE="kvmd-3.291-1-any.pkg.tar.xz" 25 | KVMDCACHE="/var/cache/kvmd"; mkdir -p $KVMDCACHE 26 | PKGINFO="${KVMDCACHE}/packages.txt" 27 | APP_PATH=$(readlink -f $(dirname $0)) 28 | LOGFILE="${KVMDCACHE}/installer.log"; touch $LOGFILE; echo "==== $( date ) ====" >> $LOGFILE 29 | CWD=`pwd` 30 | 31 | if [[ "$1" == "-h" || "$1" == "--help" ]]; then 32 | echo "usage: $0 [-f] where -f will force re-install new pikvm platform" 33 | exit 1 34 | fi 35 | 36 | WHOAMI=$( whoami ) 37 | if [ "$WHOAMI" != "root" ]; then 38 | echo "$WHOAMI, please run script as root." 39 | exit 1 40 | fi 41 | 42 | PYTHONVER=$( python3 -V | cut -d' ' -f2 | cut -d'.' -f1,2 ) 43 | case $PYTHONVER in 44 | 3.1[0-9]) # supports python 3.10+ 45 | echo "Python $PYTHONVER is supported." | tee -a $LOGFILE 46 | ;; 47 | *) 48 | echo "Python $PYTHONVER is NOT supported. Please make sure you have python3.10 or higher installed. Exiting." | tee -a $LOGFILE 49 | exit 1 50 | ;; 51 | esac 52 | 53 | ### added on 01/31/23 in case armbian is installed on rpi boards 54 | if [[ ! -e /boot/config.txt && -e /boot/firmware/config.txt ]]; then 55 | ln -sf /boot/firmware/config.txt /boot/config.txt 56 | fi 57 | 58 | MAKER="x86-pikvm" 59 | 60 | press-enter() { 61 | echo 62 | read -p "Press ENTER to continue or CTRL+C to break out of script." 63 | } # end press-enter 64 | 65 | gen-ssl-certs() { 66 | cd /etc/kvmd/nginx/ssl 67 | openssl ecparam -out server.key -name prime256v1 -genkey 68 | openssl req -new -x509 -sha256 -nodes -key server.key -out server.crt -days 3650 \ 69 | -subj "/C=US/ST=Denial/L=Denial/O=Pi-KVM/OU=Pi-KVM/CN=$(hostname)" 70 | cp server* /etc/kvmd/vnc/ssl/ 71 | cd ${APP_PATH} 72 | } # end gen-ssl-certs 73 | 74 | create-override() { 75 | if [ $( grep ^kvmd: /etc/kvmd/override.yaml | wc -l ) -eq 0 ]; then 76 | 77 | if [[ $( echo $platform | grep usb | wc -l ) -eq 1 ]]; then 78 | cat <> /etc/kvmd/override.yaml 79 | kvmd: 80 | hid: 81 | ### add entries for use with the ch9329 serial HID 82 | type: ch9329 83 | speed: 9600 # default speed after loading ch9329 plugin is 9600 84 | device: /dev/kvmd-hid 85 | msd: 86 | type: disabled 87 | atx: 88 | type: disabled 89 | streamer: 90 | forever: true 91 | cmd_append: 92 | - "--slowdown" # so target doesn't have to reboot 93 | resolution: 94 | default: 1280x720 95 | USBOVERRIDE 96 | 97 | else 98 | 99 | cat <> /etc/kvmd/override.yaml 100 | kvmd: 101 | hid: 102 | ### add entries for use with the ch9329 serial HID 103 | type: ch9329 104 | speed: 9600 # default speed after loading ch9329 plugin is 9600 105 | device: /dev/kvmd-hid 106 | msd: 107 | type: disabled 108 | streamer: 109 | forever: true 110 | cmd_append: 111 | - "--slowdown" # so target doesn't have to reboot 112 | CSIOVERRIDE 113 | 114 | fi 115 | 116 | fi 117 | } # end create-override 118 | 119 | install-python-packages() { 120 | for i in $( echo "aiofiles aiohttp appdirs asn1crypto async-timeout bottle cffi chardet click 121 | colorama cryptography dateutil dbus dev hidapi idna libgpiod mako marshmallow more-itertools multidict netifaces 122 | packaging passlib pillow ply psutil pycparser pyelftools pyghmi pygments pyparsing requests semantic-version 123 | setproctitle setuptools six spidev systemd tabulate urllib3 wrapt xlib yaml yarl pyotp qrcode serial serial-asyncio" ) 124 | do 125 | echo "apt-get install python3-$i -y" | tee -a $LOGFILE 126 | apt-get install python3-$i -y >> $LOGFILE 127 | done 128 | } # end install python-packages 129 | 130 | otg-devices() { 131 | modprobe libcomposite 132 | if [ ! -e /sys/kernel/config/usb_gadget/kvmd ]; then 133 | mkdir -p /sys/kernel/config/usb_gadget/kvmd/functions 134 | cd /sys/kernel/config/usb_gadget/kvmd/functions 135 | mkdir hid.usb0 hid.usb1 hid.usb2 mass_storage.usb0 136 | fi 137 | cd ${APP_PATH} 138 | } # end otg-device creation 139 | 140 | install-tc358743() { 141 | ### CSI Support for Raspbian ### 142 | curl https://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | apt-key add - 143 | echo "deb https://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main" | tee /etc/apt/sources.list.d/uv4l.list 144 | 145 | #apt-get update > /dev/null 146 | echo "apt-get install uv4l-tc358743-extras -y" 147 | apt-get install uv4l-tc358743-extras -y > /dev/null 148 | } # install package for tc358743 149 | 150 | boot-files() { 151 | if [[ -e /boot/config.txt && $( grep srepac /boot/config.txt | wc -l ) -eq 0 ]]; then 152 | 153 | if [[ $( echo $platform | grep usb | wc -l ) -eq 1 ]]; then # hdmiusb platforms 154 | 155 | cat <> /boot/config.txt 156 | # srepac custom configs 157 | ### 158 | hdmi_force_hotplug=1 159 | gpu_mem=${GPUMEM} 160 | enable_uart=1 161 | #dtoverlay=tc358743 162 | dtoverlay=disable-bt 163 | dtoverlay=dwc2,dr_mode=peripheral 164 | dtparam=act_led_gpio=13 165 | 166 | # HDMI audio capture 167 | #dtoverlay=tc358743-audio 168 | 169 | # SPI (AUM) 170 | #dtoverlay=spi0-1cs 171 | 172 | # I2C (display) 173 | dtparam=i2c_arm=on 174 | 175 | # Clock 176 | dtoverlay=i2c-rtc,pcf8563 177 | FIRMWARE 178 | 179 | else # CSI platforms 180 | 181 | cat <> /boot/config.txt 182 | # srepac custom configs 183 | ### 184 | hdmi_force_hotplug=1 185 | gpu_mem=${GPUMEM} 186 | enable_uart=1 187 | dtoverlay=tc358743 188 | dtoverlay=disable-bt 189 | dtoverlay=dwc2,dr_mode=peripheral 190 | dtparam=act_led_gpio=13 191 | 192 | # HDMI audio capture 193 | dtoverlay=tc358743-audio 194 | 195 | # SPI (AUM) 196 | dtoverlay=spi0-1cs 197 | 198 | # I2C (display) 199 | dtparam=i2c_arm=on 200 | 201 | # Clock 202 | dtoverlay=i2c-rtc,pcf8563 203 | CSIFIRMWARE 204 | 205 | # add the tc358743 module to be loaded at boot for CSI 206 | if [[ $( grep -w tc358743 /etc/modules | wc -l ) -eq 0 ]]; then 207 | echo "tc358743" >> /etc/modules 208 | fi 209 | 210 | fi 211 | 212 | fi # end of check if entries are already in /boot/config.txt 213 | 214 | #install-tc358743 215 | 216 | # Remove OTG serial (Orange pi zero's kernel not support it) 217 | sed -i '/^g_serial/d' /etc/modules 218 | 219 | # /etc/modules required entries for DWC2, HID and I2C 220 | if [[ $( grep -w dwc2 /etc/modules | wc -l ) -eq 0 ]]; then 221 | echo "dwc2" >> /etc/modules 222 | fi 223 | if [[ $( grep -w libcomposite /etc/modules | wc -l ) -eq 0 ]]; then 224 | echo "libcomposite" >> /etc/modules 225 | fi 226 | if [[ $( grep -w i2c-dev /etc/modules | wc -l ) -eq 0 ]]; then 227 | echo "i2c-dev" >> /etc/modules 228 | fi 229 | 230 | if [ -e /boot/config.txt ]; then 231 | printf "\n/boot/config.txt\n\n" | tee -a $LOGFILE 232 | cat /boot/config.txt | tee -a $LOGFILE 233 | fi 234 | 235 | printf "\n/etc/modules\n\n" | tee -a $LOGFILE 236 | cat /etc/modules | tee -a $LOGFILE 237 | } # end of necessary boot files 238 | 239 | get-packages() { 240 | printf "\n\n-> Getting Pi-KVM packages from ${PIKVMREPO}\n\n" | tee -a $LOGFILE 241 | mkdir -p ${KVMDCACHE}/ARCHIVE 242 | if [ $( ls ${KVMDCACHE}/kvmd* > /dev/null 2>&1 | wc -l ) -gt 0 ]; then 243 | mv ${KVMDCACHE}/kvmd* ${KVMDCACHE}/ARCHIVE ### move previous kvmd* packages into ARCHIVE 244 | fi 245 | 246 | echo "wget --no-check-certificate ${PIKVMREPO} -O ${PKGINFO}" | tee -a $LOGFILE 247 | wget --no-check-certificate ${PIKVMREPO} -O ${PKGINFO} 2> /dev/null 248 | echo 249 | 250 | # only get the latest kvmd version 251 | LATESTKVMD=$( grep kvmd-[0-9] $PKGINFO | grep -v sig | tail -1 ) 252 | VERSION=$( echo $LATESTKVMD | cut -d'-' -f2 ) 253 | 254 | # Download each of the pertinent packages for Rpi4, webterm, and the main service 255 | for pkg in `egrep "janus|$LATESTKVMD|$platform-$VERSION|webterm" ${PKGINFO} | grep -v sig | cut -d'>' -f1 | cut -d'"' -f2` 256 | do 257 | rm -f ${KVMDCACHE}/$pkg* 258 | echo "wget --no-check-certificate ${PIKVMREPO}/$pkg -O ${KVMDCACHE}/$pkg" | tee -a $LOGFILE 259 | wget --no-check-certificate ${PIKVMREPO}/$pkg -O ${KVMDCACHE}/$pkg 2> /dev/null 260 | done 261 | 262 | echo 263 | echo "ls -l ${KVMDCACHE}" | tee -a $LOGFILE 264 | ls -l ${KVMDCACHE} | tee -a $LOGFILE 265 | echo 266 | } # end get-packages function 267 | 268 | get-platform() { 269 | tryagain=1 270 | while [ $tryagain -eq 1 ]; do 271 | case $MAKER in 272 | Raspberry) ### get which capture device for use with RPi boards 273 | # amglogic tv box only has usb port, use usb dongle. 274 | printf "Choose which capture device you will use:\n\n 1 - USB dongle\n 2 - v2 CSI\n 3 - V3 HAT\n" 275 | read -p "Please type [1-3]: " capture 276 | ;; 277 | 278 | *) capture=1;; ### force all other sbcs to use hdmiusb platform 279 | esac 280 | 281 | case $capture in 282 | 1) platform="kvmd-platform-v0-hdmiusb-rpi3"; tryagain=0;; ## force x86 install to use rpi3 + serial hid 283 | 2) platform="kvmd-platform-v2-hdmi-rpi4"; tryagain=0;; 284 | 3) platform="kvmd-platform-v3-hdmi-rpi4"; tryagain=0;; 285 | *) printf "\nTry again.\n"; tryagain=1;; 286 | esac 287 | 288 | echo 289 | echo "Platform selected -> $platform" | tee -a $LOGFILE 290 | echo 291 | done 292 | } # end get-platform 293 | 294 | install-kvmd-pkgs() { 295 | cd / 296 | 297 | INSTLOG="${KVMDCACHE}/installed_ver.txt"; rm -f $INSTLOG 298 | date > $INSTLOG 299 | 300 | # uncompress platform package first 301 | i=$( ls ${KVMDCACHE}/${platform}*.tar.xz ) 302 | _platformver=$( echo $i | sed -e 's/3\.29[2-9]*/3.291/g' -e 's/3\.3[0-9]*/3.291/g' ) 303 | echo "-> Extracting package $_platformver into /" | tee -a $INSTLOG 304 | tar xfJ $i 305 | 306 | # then uncompress, kvmd-{version}, kvmd-webterm, and janus packages 307 | for i in $( ls ${KVMDCACHE}/*.tar.xz | egrep 'kvmd-[0-9]|webterm' ) 308 | do 309 | case $i in 310 | *kvmd-3.29[2-9]*|*kvmd-3.[3-9]*|*kvmd-[45].[1-9]*) # if latest/greatest is 3.292 and higher, then force 3.291 install 311 | echo "*** Force install kvmd 3.291 ***" | tee -a $LOGFILE 312 | # copy kvmd-3.291 package 313 | cp $CWD/$KVMDFILE $KVMDCACHE/ 314 | i=$KVMDCACHE/$KVMDFILE 315 | ;; 316 | *) 317 | ;; 318 | esac 319 | 320 | echo "-> Extracting package $i into /" | tee -a $INSTLOG 321 | tar xfJ $i 322 | done 323 | 324 | # uncompress janus package if /usr/bin/janus doesn't exist 325 | if [ ! -e /usr/bin/janus ]; then 326 | i=$( ls ${KVMDCACHE}/*.tar.xz | egrep janus | grep -v 1x ) 327 | echo "-> Extracting package $i into /" | tee -a $INSTLOG 328 | tar xfJ $i 329 | 330 | else # confirm that /usr/bin/janus actually runs properly 331 | /usr/bin/janus --version > /dev/null 2> /dev/null 332 | if [ $? -eq 0 ]; then 333 | echo "You have a working valid janus binary." | tee -a $LOGFILE 334 | else # error status code, so uncompress from REPO package 335 | i=$( ls ${KVMDCACHE}/*.tar.xz | egrep janus ) 336 | echo "-> Extracting package $i into /" | tee -a $INSTLOG 337 | tar xfJ $i 338 | fi 339 | fi 340 | 341 | cd ${APP_PATH} 342 | } # end install-kvmd-pkgs 343 | 344 | fix-udevrules() { 345 | # for hdmiusb, replace %b with 1-1.4:1.0 in /etc/udev/rules.d/99-kvmd.rules 346 | sed -i -e 's+\%b+1-1.4:1.0+g' -e 's+ttyAMA0+ttyUSB[0-2]+g' /etc/udev/rules.d/99-kvmd.rules | tee -a $LOGFILE 347 | echo 348 | cat /etc/udev/rules.d/99-kvmd.rules | tee -a $LOGFILE 349 | } # end fix-udevrules 350 | 351 | enable-kvmd-svcs() { 352 | # enable KVMD services but don't start them 353 | echo "-> Enabling $SERVICES services, but do not start them." | tee -a $LOGFILE 354 | systemctl enable $SERVICES 355 | } # end enable-kvmd-svcs 356 | 357 | build-ustreamer() { 358 | printf "\n\n-> Building ustreamer\n\n" | tee -a $LOGFILE 359 | # Install packages needed for building ustreamer source 360 | echo "apt install -y make libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev libmd-dev libdrm-dev janus-dev janus" | tee -a $LOGFILE 361 | apt install -y make libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev libmd-dev libdrm-dev janus-dev janus >> $LOGFILE 362 | 363 | # fix refcount.h 364 | sed -i -e 's|^#include "refcount.h"$|#include "../refcount.h"|g' /usr/include/janus/plugins/plugin.h 365 | 366 | # Download ustreamer source and build it 367 | cd /tmp 368 | git clone --depth=1 https://github.com/pikvm/ustreamer 369 | cd ustreamer/ 370 | make WITH_GPIO=1 WITH_SYSTEMD=1 WITH_JANUS=1 WITH_V4P=1 -j 371 | make install 372 | # kvmd service is looking for /usr/bin/ustreamer 373 | ln -sf /usr/local/bin/ustreamer* /usr/bin/ 374 | } # end build-ustreamer 375 | 376 | install-dependencies() { 377 | echo 378 | echo "-> Installing dependencies for pikvm" | tee -a $LOGFILE 379 | 380 | echo "apt install -y nginx python3 net-tools bc expect v4l-utils iptables vim dos2unix screen tmate nfs-common gpiod ffmpeg dialog iptables dnsmasq git python3-pip tesseract-ocr tesseract-ocr-eng libasound2-dev libsndfile-dev libspeexdsp-dev lm-sensors libdrm-dev" | tee -a $LOGFILE 381 | apt install -y nginx python3 net-tools bc expect v4l-utils iptables vim dos2unix screen tmate nfs-common gpiod ffmpeg dialog iptables dnsmasq git python3-pip tesseract-ocr tesseract-ocr-eng libasound2-dev libsndfile-dev libspeexdsp-dev lm-sensors libdrm-dev >> $LOGFILE 382 | 383 | sed -i -e 's/#port=5353/port=5353/g' /etc/dnsmasq.conf 384 | 385 | install-python-packages 386 | 387 | echo "-> Install python3 modules dbus_next and zstandard" | tee -a $LOGFILE 388 | if [[ "$PYTHONVER" == "3.11" ]]; then 389 | apt install -y python3-dbus-next python3-zstandard 390 | else 391 | pip3 install dbus_next zstandard 392 | fi 393 | 394 | echo "-> Make tesseract data link" | tee -a $LOGFILE 395 | ln -sf /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata 396 | 397 | echo "-> Install TTYD" | tee -a $LOGFILE 398 | apt install -y ttyd | tee -a $LOGFILE 399 | if [ ! -e /usr/bin/ttyd ]; then 400 | # Build and install ttyd 401 | # cd /tmp 402 | apt-get install -y build-essential cmake git libjson-c-dev libwebsockets-dev 403 | git clone https://github.com/tsl0922/ttyd.git 404 | cd ttyd && mkdir build && cd build 405 | cmake .. 406 | make -j && make install 407 | cp ttyd /usr/bin/ttyd 408 | # Install binary from GitHub 409 | #arch=$(dpkg --print-architecture) 410 | #latest=$(curl -sL https://api.github.com/repos/tsl0922/ttyd/releases/latest | jq -r ".tag_name") 411 | #if [ $arch = arm64 ]; then 412 | # arch='aarch64' 413 | #fi 414 | #wget --no-check-certificate "https://github.com/tsl0922/ttyd/releases/download/$latest/ttyd.$arch" -O /usr/bin/ttyd 415 | chmod +x /usr/bin/ttyd 416 | fi 417 | 418 | printf "\n\n-> Building wiringpi from source\n\n" | tee -a $LOGFILE 419 | cd /tmp; rm -rf WiringPi 420 | git clone https://github.com/WiringPi/WiringPi.git 421 | cd WiringPi 422 | ./build 423 | gpio -v 424 | 425 | echo "-> Install ustreamer" | tee -a $LOGFILE 426 | if [ ! -e /usr/bin/ustreamer ]; then 427 | cd /tmp 428 | apt-get install -y libevent-2.1-7 libevent-core-2.1-7 libevent-pthreads-2.1-7 build-essential 429 | ### required dependent packages for ustreamer ### 430 | build-ustreamer 431 | cd ${APP_PATH} 432 | fi 433 | } # end install-dependencies 434 | 435 | python-pkg-dir() { 436 | # debian system python3 no alias 437 | # create quick python script to show where python packages need to go 438 | cat << MYSCRIPT > /tmp/syspath.py 439 | #!$(which python3) 440 | import sys 441 | print (sys.path) 442 | MYSCRIPT 443 | 444 | chmod +x /tmp/syspath.py 445 | 446 | #PYTHONDIR=$( /tmp/syspath.py | awk -F, '{print $NF}' | cut -d"'" -f2 ) 447 | ### hardcode path for armbian/raspbian 448 | PYTHONDIR="/usr/lib/python3/dist-packages" 449 | } # end python-pkg-dir 450 | 451 | fix-nginx-symlinks() { 452 | # disable default nginx service since we will use kvmd-nginx instead 453 | echo 454 | echo "-> Disabling nginx service, so that we can use kvmd-nginx instead" | tee -a $LOGFILE 455 | systemctl disable --now nginx 456 | 457 | # setup symlinks 458 | echo 459 | echo "-> Creating symlinks for use with kvmd python scripts" | tee -a $LOGFILE 460 | if [ ! -e /usr/bin/nginx ]; then ln -sf /usr/sbin/nginx /usr/bin/; fi 461 | if [ ! -e /usr/sbin/python ]; then ln -sf /usr/bin/python3 /usr/sbin/python; fi 462 | if [ ! -e /usr/bin/iptables ]; then ln -sf /usr/sbin/iptables /usr/bin/iptables; fi 463 | if [ ! -e /usr/bin/vcgencmd ]; then ln -sf /opt/vc/bin/* /usr/bin/; fi 464 | 465 | python-pkg-dir 466 | 467 | if [ ! -e $PYTHONDIR/kvmd ]; then 468 | # Debian python版本比 pikvm官方的低一些 469 | # in case new kvmd packages are now using python 3.11 470 | ln -sf /usr/lib/python3.1*/site-packages/kvmd* ${PYTHONDIR} 471 | fi 472 | } # end fix-nginx-symlinks 473 | 474 | fix-python-symlinks(){ 475 | python-pkg-dir 476 | 477 | if [ ! -e $PYTHONDIR/kvmd ]; then 478 | # Debian python版本比 pikvm官方的低一些 479 | ln -sf /usr/lib/python3.1*/site-packages/kvmd* ${PYTHONDIR} 480 | fi 481 | } 482 | 483 | apply-custom-patch(){ 484 | read -p "Do you want apply old kernel msd patch? [y/n]" answer 485 | case $answer in 486 | n|N|no|No) 487 | echo 'You skipped this patch.' 488 | ;; 489 | y|Y|Yes|yes) 490 | ./patches/custom/old-kernel-msd/apply.sh 491 | ;; 492 | *) 493 | echo "Try again.";; 494 | esac 495 | } 496 | 497 | fix-kvmd-for-tvbox-armbian(){ 498 | # 打补丁来移除一些对armbian和电视盒子不太支持的特性 499 | cd /usr/lib/python3.10/site-packages/ 500 | git apply ${APP_PATH}/patches/bullseye/*.patch 501 | cd ${APP_PATH} 502 | read -p "Do you want to apply custom patches? [y/n] " answer 503 | case $answer in 504 | n|N|no|No) 505 | return; 506 | ;; 507 | y|Y|Yes|yes) 508 | apply-custom-patch; 509 | return; 510 | ;; 511 | *) 512 | echo "Try again.";; 513 | esac 514 | } 515 | 516 | fix-webterm() { 517 | echo 518 | echo "-> Creating kvmd-webterm homedir" | tee -a $LOGFILE 519 | mkdir -p /home/kvmd-webterm 520 | chown kvmd-webterm /home/kvmd-webterm 521 | ls -ld /home/kvmd-webterm | tee -a $LOGFILE 522 | 523 | # remove -W option since ttyd installed on raspbian/armbian is 1.6.3 (-W option only works with ttyd 1.7.x) 524 | _ttydver=$( ttyd -v | awk '{print $NF}' ) 525 | case $_ttydver in 526 | 1.6*) 527 | echo "ttyd $_ttydver found. Removing -W from /lib/systemd/system/kvmd-webterm.service" 528 | sed -i -e '/-W \\/d' /lib/systemd/system/kvmd-webterm.service 529 | ;; 530 | 1.7*) 531 | echo "ttyd $_ttydver found. Nothing to do." 532 | ;; 533 | esac 534 | 535 | # add sudoers entry for kvmd-webterm user to be able to run sudo 536 | echo "kvmd-webterm ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/kvmd-webterm; chmod 440 /etc/sudoers.d/kvmd-webterm 537 | } # end fix-webterm 538 | 539 | create-kvmdfix() { 540 | # Create kvmd-fix service and script 541 | cat < /lib/systemd/system/kvmd-fix.service 542 | [Unit] 543 | Description=KVMD Fixes 544 | After=network.target network-online.target nss-lookup.target 545 | Before=kvmd.service 546 | 547 | [Service] 548 | User=root 549 | Type=simple 550 | ExecStart=/usr/bin/kvmd-fix 551 | 552 | [Install] 553 | WantedBy=multi-user.target 554 | ENDSERVICE 555 | 556 | cat < /usr/bin/kvmd-fix 557 | #!/bin/bash 558 | # Written by @srepac 559 | # 1. Properly set group ownership of /dev/gpio* 560 | # 2. fix /dev/kvmd-video symlink to point to /dev/video1 (Amglogic Device video0 is not usb device) 561 | # 562 | ### These fixes are required in order for kvmd service to start properly 563 | # 564 | set -x 565 | 566 | # create this dir so that running kvmd-otgconf doesn't give errors 567 | mkdir -p /run/kvmd/otg 568 | /root/ch_reset.py 569 | 570 | if [ \$( ls /dev/ | grep -c gpio ) -gt 0 ]; then 571 | chgrp gpio /dev/gpio* 572 | chmod 660 /dev/gpio* 573 | ls -l /dev/gpio* 574 | fi 575 | 576 | udevadm trigger 577 | sleep 3 578 | ls -l /dev/kvmd* 579 | 580 | if [ \$( systemctl | grep kvmd-oled | grep -c activ ) -eq 0 ]; then 581 | echo "kvmd-oled service is not enabled." 582 | exit 0 583 | else 584 | echo "kvmd-oled service is enabled and activated." 585 | fi 586 | 587 | ### kvmd-oled fix: swap i2c-0 <-> i2c-1 (code is looking for I2C oled on i2c-1) 588 | # pins #1 - 3.3v, #3 - SDA, #5 - SCL, and #9 - GND 589 | i2cget -y 0 0x3c 590 | if [ \$? -eq 0 ]; then 591 | echo "-> Found valid I2C OLED at i2c-0. Applying I2C OLED fix." 592 | cd /dev 593 | 594 | # rename i2c-0 -> i2c-9, move i2c-1 to i2c-0, and rename the good i2c-9 to i2c-1 595 | mv i2c-0 i2c-9 596 | mv i2c-1 i2c-0 597 | mv i2c-9 i2c-1 598 | 599 | # restart kvmd-oled service 600 | systemctl restart kvmd-oled 601 | else 602 | echo "-> I2C OLED fix already applied and OLED should be showing info." 603 | fi 604 | SCRIPTEND 605 | 606 | chmod +x /usr/bin/kvmd-fix 607 | 608 | cat << CHRESET > /root/ch_reset.py 609 | #!/usr/bin/python3 610 | import serial 611 | import time 612 | 613 | device_path = "/dev/kvmd-hid" 614 | 615 | chip = serial.Serial(device_path, 9600, timeout=1) 616 | 617 | command = [87, 171, 0, 15, 0] 618 | sum = sum(command) % 256 619 | command.append(sum) 620 | 621 | print("Resetting CH9329") 622 | 623 | chip.write(serial.to_bytes(command)) 624 | 625 | time.sleep(2) 626 | 627 | data = list(chip.read(5)) 628 | 629 | print("Initial data:", data) 630 | 631 | if data[4] : 632 | more_data = list(chip.read(data[4])) 633 | data.extend(more_data) 634 | 635 | print("Output: ", data) 636 | 637 | 638 | chip.close() 639 | CHRESET 640 | 641 | chmod +x /root/ch_reset.py 642 | } # end create-kvmdfix 643 | 644 | set-ownership() { 645 | # set proper ownership of password files and kvmd-webterm homedir 646 | cd /etc/kvmd 647 | chown kvmd:kvmd htpasswd 648 | chown kvmd-ipmi:kvmd-ipmi ipmipasswd 649 | chown kvmd-vnc:kvmd-vnc vncpasswd 650 | chown kvmd-webterm /home/kvmd-webterm 651 | 652 | # add kvmd user to video group (this is required in order to use CSI bridge with OMX and h264 support) 653 | usermod -a -G video kvmd 654 | 655 | # add kvmd user to dialout group (required for xh_hk4401 kvm switch support) 656 | usermod -a -G dialout kvmd 657 | } # end set-ownership 658 | 659 | check-kvmd-works() { 660 | echo "-> Checking kvmd -m works before continuing" | tee -a $LOGFILE 661 | invalid=1 662 | while [ $invalid -eq 1 ]; do 663 | kvmd -m 664 | read -p "Did kvmd -m run properly? [y/n] " answer 665 | case $answer in 666 | n|N|no|No) 667 | echo "Please install missing packages as per the kvmd -m output in another ssh/terminal." 668 | ;; 669 | y|Y|Yes|yes) 670 | invalid=0 671 | ;; 672 | *) 673 | echo "Try again.";; 674 | esac 675 | done 676 | } # end check-kvmd-works 677 | 678 | start-kvmd-svcs() { 679 | #### start the main KVM services in order #### 680 | # 1. nginx is the webserver 681 | # 2. kvmd-otg is for OTG devices (keyboard/mouse, etc..) 682 | # 3. kvmd is the main daemon 683 | systemctl daemon-reload 684 | systemctl restart $SERVICES 685 | } # end start-kvmd-svcs 686 | 687 | fix-motd() { 688 | if [ -e /etc/motd ]; then rm /etc/motd; fi 689 | cp armbian/armbian-motd /usr/bin/ 690 | sed -i -e 's/Orange Pi/x86 PC/g' -e 's|^/etc|#/etc|g' /usr/bin/armbian-motd 691 | sed -i 's/cat \/etc\/motd/armbian-motd/g' /lib/systemd/system/kvmd-webterm.service 692 | systemctl daemon-reload 693 | # systemctl restart kvmd-webterm 694 | } # end fix-motd 695 | 696 | # 安装armbian的包 697 | armbian-packages() { 698 | mkdir -p /opt/vc/bin/ 699 | #cd /opt/vc/bin 700 | if [ ! -e /usr/bin/vcgencmd ]; then 701 | # Install vcgencmd for armbian platform 702 | cp -rf armbian/opt/* /opt/vc/bin 703 | else 704 | ln -s /usr/bin/vcgencmd /opt/vc/bin/ 705 | fi 706 | #cp -rf armbian/udev /etc/ 707 | 708 | cd ${APP_PATH} 709 | } # end armbian-packages 710 | 711 | fix-nfs-msd() { 712 | NAME="aiofiles.tar" 713 | 714 | LOCATION="/usr/lib/python3.11/site-packages" 715 | echo "-> Extracting $NAME into $LOCATION" | tee -a $LOGFILE 716 | tar xvf $NAME -C $LOCATION 717 | 718 | echo "-> Renaming original aiofiles and creating symlink to correct aiofiles" | tee -a $LOGFILE 719 | cd /usr/lib/python3/dist-packages 720 | mv aiofiles aiofiles.$(date +%Y%m%d.%H%M) 721 | ln -s $LOCATION/aiofiles . 722 | ls -ld aiofiles* | tail -5 | tee -a $LOGFILE 723 | } 724 | 725 | add-ch9329-support() { 726 | wget --no-check-certificate -O install-ch9329.sh https://148.135.104.55/PiKVM/install-ch9329.sh 2> /dev/null 727 | chmod +x install-ch9329.sh 728 | ./install-ch9329.sh 729 | } 730 | 731 | apply-x86-mods() { 732 | TARBALL="x86-mods.tar" 733 | wget --no-check-certificate -O $TARBALL https://148.135.104.55/RPiKVM/$TARBALL 2> /dev/null 734 | 735 | if [ -e $TARBALL ]; then 736 | echo "-> Making backup of files that require modification" | tee -a $LOGFILE 737 | for i in $( tar tf $TARBALL ); do 738 | echo "cp $PYTHONDIR/$i $PYTHONDIR/$i.orig" | tee -a $LOGFILE 739 | cp $PYTHONDIR/$i $PYTHONDIR/$i.orig 740 | done 741 | tar tvf $TARBALL 742 | 743 | echo "tar xvf $TARBALL -C $PYTHONDIR" | tee -a $LOGFILE 744 | tar xvf $TARBALL -C $PYTHONDIR 745 | 746 | for i in $( tar tf $TARBALL ); do 747 | ls -l $PYTHONDIR/$i 748 | done 749 | else 750 | echo "Missing $TARBALL. Please obtain the tar file from @srepac and try again." | tee -a $LOGFILE 751 | fi 752 | } 753 | 754 | fix-nginx() { 755 | #set -x 756 | KERNEL=$( uname -r | awk -F\- '{print $1}' ) 757 | ARCH=$( uname -r | awk -F\- '{print $NF}' ) 758 | echo "KERNEL: $KERNEL ARCH: $ARCH" | tee -a $LOGFILE 759 | case $ARCH in 760 | ARCH) SEARCHKEY=nginx-mainline;; 761 | *) SEARCHKEY="nginx/";; 762 | esac 763 | 764 | HTTPSCONF="/etc/kvmd/nginx/listen-https.conf" 765 | echo "HTTPSCONF BEFORE: $HTTPSCONF" | tee -a $LOGFILE 766 | cat $HTTPSCONF | tee -a $LOGFILE 767 | 768 | if [[ ! -e /usr/local/bin/pikvm-info || ! -e /tmp/pacmanquery ]]; then 769 | wget --no-check-certificate -O /usr/local/bin/pikvm-info https://148.135.104.55/PiKVM/pikvm-info 2> /dev/null 770 | chmod +x /usr/local/bin/pikvm-info 771 | echo "Getting list of packages installed..." | tee -a $LOGFILE 772 | pikvm-info > /dev/null ### this generates /tmp/pacmanquery with list of installed pkgs 773 | fi 774 | 775 | NGINXVER=$( grep $SEARCHKEY /tmp/pacmanquery | awk '{print $1}' | cut -d'.' -f1,2 ) 776 | echo 777 | echo "NGINX version installed: $NGINXVER" | tee -a $LOGFILE 778 | 779 | case $NGINXVER in 780 | 1.2[56789]|1.3*|1.4*|1.5*) # nginx version 1.25 and higher 781 | cat << NEW_CONF > $HTTPSCONF 782 | listen 443 ssl; 783 | listen [::]:443 ssl; 784 | http2 on; 785 | NEW_CONF 786 | ;; 787 | 788 | 1.18|*) # nginx version 1.18 and lower 789 | cat << ORIG_CONF > $HTTPSCONF 790 | listen 443 ssl http2; 791 | listen [::]:443 ssl; 792 | ORIG_CONF 793 | ;; 794 | 795 | esac 796 | 797 | echo "HTTPSCONF AFTER: $HTTPSCONF" | tee -a $LOGFILE 798 | cat $HTTPSCONF | tee -a $LOGFILE 799 | set +x 800 | } # end fix-nginx 801 | 802 | ocr-fix() { # create function 803 | echo 804 | echo "-> Apply OCR fix..." | tee -a $LOGFILE 805 | 806 | # 1. verify that Pillow module is currently running 9.0.x 807 | PILLOWVER=$( pip3 list | grep -i pillow | awk '{print $NF}' ) 808 | 809 | case $PILLOWVER in 810 | 9.*|8.*|7.*) # Pillow running at 9.x and lower 811 | # 2. update Pillow to 10.0.0 812 | pip3 install -U Pillow 2>> $LOGFILE 813 | 814 | # 3. check that Pillow module is now running 10.0.0 815 | pip3 list | grep -i pillow | tee -a $LOGFILE 816 | 817 | #4. restart kvmd and confirm OCR now works. 818 | systemctl restart kvmd 819 | ;; 820 | 821 | 10.*|11.*|12.*) # Pillow running at 10.x and higher 822 | echo "Already running Pillow $PILLOWVER. Nothing to do." | tee -a $LOGFILE 823 | ;; 824 | 825 | esac 826 | 827 | echo 828 | } # end ocr-fix 829 | 830 | x86-fix-3.256() { 831 | echo "-> Apply x86-fix for 3.256 and higher..." | tee -a $LOGFILE 832 | cd /usr/lib/python3/dist-packages/kvmd/apps/ 833 | cp __init__.py __init__.py.$( date +%Y%m%d ) 834 | wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/kvmd/apps/__init__.py 2> /dev/null 835 | mv __init__.py.1 __init__.py 836 | 837 | cd /usr/share/kvmd/web/share/js 838 | if [ -e session.js ]; then 839 | cp session.js session.js.$( date +%Y%m%d ) 840 | fi 841 | wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/web/share/js/kvm/session.js 2> /dev/null 842 | if [ -e session.js.1 ]; then 843 | mv session.js.1 session.js 844 | fi 845 | 846 | cd /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/ 847 | cp hw.py hw.py.$( date +%Y%m%d ) 848 | #wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/kvmd/apps/kvmd/info/hw.py 2> /dev/null 849 | #mv hw.py.1 hw.py 850 | wget --no-check-certificate -O hw.py https://148.135.104.55/PiKVM/TESTING/hw.py 2> /dev/null 851 | } # end x86-fix-3.256 852 | 853 | x86-fix-3.281() { 854 | echo "-> Apply x86-fix for 3.281 and higher..." | tee -a $LOGFILE 855 | cd /usr/lib/python3/dist-packages/kvmd/apps/ 856 | cp __init__.py __init__.py.$( date +%Y%m%d ) 857 | wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/kvmd/apps/__init__.py 2> /dev/null 858 | #wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/kvmd/apps/__init__.py 2> /dev/null 859 | 860 | ### x86 pikvm fix for 3.281 and higher 861 | wget -O __init__.py.1 --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/a1b8a077ee1ae829e01aa5224196ce687adc9deb/kvmd/apps/__init__.py 2> /dev/null 862 | mv __init__.py.1 __init__.py 863 | 864 | cd /usr/lib/python3/dist-packages/kvmd/apps/kvmd 865 | wget -O streamer.py.1 --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/a1b8a077ee1ae829e01aa5224196ce687adc9deb/kvmd/apps/kvmd/streamer.py 2> /dev/null 866 | mv streamer.py.1 streamer.py 867 | } # end x86-fix-3.281 868 | 869 | update-logo() { 870 | sed -i -e 's|class="svg-gray"|class="svg-color"|g' /usr/share/kvmd/web/index.html 871 | sed -i -e 's|target="_blank"> /dev/null 2> /dev/null 875 | cd /usr/share/kvmd/web/share/svg 876 | cp logo.svg logo.svg.old 877 | cp opikvm-logo.svg logo.svg 878 | 879 | # change some text in the main html page 880 | sed -i.bak -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' /usr/share/kvmd/web/index.html 881 | sed -i.bak -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' /usr/share/kvmd/web/kvm/index.html 882 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/kvm/index.html 883 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/index.html 884 | cd 885 | } 886 | 887 | function fix-hk4401() { 888 | # https://github.com/ThomasVon2021/blikvm/issues/168 889 | 890 | # Download kvmd-4.2 package from kvmnerds.com to /tmp and extract only the xh_hk4401.py script 891 | cd /tmp 892 | wget --no-check-certificate -O kvmd-4.2-1-any.pkg.tar.xz https://148.135.104.55/REPO/NEW/kvmd-4.2-1-any.pkg.tar.xz 2> /dev/null 893 | tar xvfJ kvmd-4.2-1-any.pkg.tar.xz --wildcards --no-anchored 'xh_hk4401.py' 894 | 895 | # Show diff of 4.2 version of xh_hk4401.py vs. current installed version 896 | cd usr/lib/python3.12/site-packages/kvmd/plugins/ugpio/ 897 | diff xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 898 | 899 | # make a backup of current xh_hk4401.py script 900 | cp /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py.3.291 901 | 902 | # replace it with the kvmd 4.2 version of script which allows use of protocol: 2 903 | cp xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 904 | cd 905 | } # end fix-hk4401 906 | 907 | 908 | ### MAIN STARTS HERE ### 909 | # Install is done in two parts 910 | # First part requires a reboot in order to create kvmd users and groups 911 | # Second part will start the necessary kvmd services 912 | 913 | # if /etc/kvmd/htpasswd exists, then make a backup 914 | if [ -e /etc/kvmd/htpasswd ]; then cp /etc/kvmd/htpasswd /etc/kvmd/htpasswd.save; fi 915 | 916 | ### I uploaded all these into github on 05/22/23 -- so just copy them into correct location 917 | cd ${APP_PATH} 918 | cp -rf pistat /usr/local/bin/pistat 919 | cp -rf pi-temp /usr/local/bin/pi-temp 920 | cp -rf pikvm-info /usr/local/bin/pikvm-info 921 | cp -rf update-x86-pikvm.sh /usr/local/bin/update-rpikvm.sh 922 | cp -rf tshoot.sh /usr/local/bin/tshoot.sh 923 | chmod +x /usr/local/bin/pi* /usr/local/bin/update-rpikvm.sh /usr/local/bin/tshoot.sh 924 | 925 | ### fix for kvmd 3.230 and higher 926 | ln -sf python3 /usr/bin/python 927 | GPUMEM=256 928 | 929 | SERVICES="kvmd-nginx kvmd-webterm kvmd kvmd-fix" 930 | 931 | # added option to re-install by adding -f parameter (for use as platform switcher) 932 | PYTHON_VERSION=$( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) 933 | if [[ $( grep kvmd /etc/passwd | wc -l ) -eq 0 || "$1" == "-f" ]]; then 934 | printf "\nRunning part 1 of PiKVM installer script v$VER for x86 by @srepac\n" | tee -a $LOGFILE 935 | get-platform 936 | get-packages 937 | install-kvmd-pkgs 938 | boot-files 939 | create-override 940 | gen-ssl-certs 941 | fix-udevrules 942 | install-dependencies 943 | otg-devices 944 | armbian-packages 945 | systemctl disable --now janus ttyd 946 | 947 | printf "\n\nReboot is required to create kvmd users and groups.\nPlease re-run this script after reboot to complete the install.\n" | tee -a $LOGFILE 948 | 949 | # fix-kvmd-for-tvbox-armbian 950 | 951 | # Fix paste-as-keys if running python 3.7 952 | if [[ $( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) == "3.7" ]]; then 953 | sed -i -e 's/reversed//g' /usr/lib/python3.1*/site-packages/kvmd/keyboard/printer.py 954 | fi 955 | 956 | ### run these to make sure kvmd users are created ### 957 | echo "-> Ensuring KVMD users and groups ..." | tee -a $LOGFILE 958 | systemd-sysusers /usr/lib/sysusers.d/kvmd.conf 959 | systemd-sysusers /usr/lib/sysusers.d/kvmd-webterm.conf 960 | 961 | # Ask user to press CTRL+C before reboot or ENTER to proceed with reboot 962 | press-enter 963 | reboot 964 | else 965 | printf "\nRunning part 2 of PiKVM installer script v$VER for x86 by @srepac\n" | tee -a $LOGFILE 966 | 967 | apt reinstall -y janus > /dev/null 2>&1 968 | 969 | ### run these to make sure kvmd users are created ### 970 | echo "-> Ensuring KVMD users and groups ..." | tee -a $LOGFILE 971 | systemd-sysusers /usr/lib/sysusers.d/kvmd.conf 972 | systemd-sysusers /usr/lib/sysusers.d/kvmd-webterm.conf 973 | 974 | ### additional python pip dependencies for kvmd 3.238 and higher 975 | case $PYTHONVER in 976 | *3.10*|*3.[987]*) 977 | pip3 install async-lru 2> /dev/null 978 | ### Fixes issues with kvmd 3.291 on x86 -- only applies to python 3.10 or older ### 979 | #sed -i -e 's|gpiod.LineEvent|gpiod.EdgeEvent|g' /usr/lib/python3/dist-packages/kvmd/aiogp.py 980 | #sed -i -e 's|gpiod.Line,|gpiod.line,|g' /usr/lib/python3/dist-packages/kvmd/aiogp.py 981 | #sed -i -e 's|gpiod.EdgeEvent|gpiod.LineEvent|g' /usr/lib/python3/dist-packages/kvmd/aiogp.py 982 | #sed -i -e 's|gpiod.line,|gpiod.Line,|g' /usr/lib/python3/dist-packages/kvmd/aiogp.py 983 | ;; 984 | *3.11*) 985 | pip3 install async-lru --break-system-packages 2> /dev/null 986 | ;; 987 | esac 988 | 989 | fix-nginx-symlinks 990 | fix-python-symlinks 991 | fix-webterm 992 | fix-motd 993 | fix-nfs-msd 994 | fix-nginx 995 | ocr-fix 996 | fix-hk4401 997 | 998 | set-ownership 999 | create-kvmdfix 1000 | if [ ! -e ${LOCATION}/kvmd/plugins/hid/ch9329 ]; then add-ch9329-support; fi # starting with kvmd 3.239, ch9329 has been merged with kvmd master 1001 | apply-x86-mods 1002 | x86-fix-3.256 1003 | x86-fix-3.281 1004 | check-kvmd-works 1005 | enable-kvmd-svcs 1006 | update-logo 1007 | start-kvmd-svcs 1008 | 1009 | printf "\nCheck kvmd devices\n\n" | tee -a $LOGFILE 1010 | ls -l /dev/kvmd* | tee -a $LOGFILE 1011 | printf "\nYou should see devices for keyboard, mouse, and video.\n" | tee -a $LOGFILE 1012 | 1013 | printf "\nPoint a browser to https://$(hostname)\nIf it doesn't work, then reboot one last time.\nPlease make sure kvmd services are running after reboot.\n" | tee -a $LOGFILE 1014 | fi 1015 | 1016 | cd $CWD 1017 | cp -rf web.css /etc/kvmd/web.css 1018 | 1019 | systemctl status $SERVICES | grep Loaded | tee -a $LOGFILE 1020 | 1021 | ### fix totp.secret file permissions for use with 2FA 1022 | chmod go+r /etc/kvmd/totp.secret 1023 | chown kvmd:kvmd /etc/kvmd/totp.secret 1024 | 1025 | ### create rw and ro so that /usr/bin/kvmd-bootconfig doesn't fail 1026 | touch /usr/local/bin/rw /usr/local/bin/ro 1027 | chmod +x /usr/local/bin/rw /usr/local/bin/ro 1028 | 1029 | ### update default hostname info in webui to reflect current hostname 1030 | sed -i -e "s/localhost.localdomain/`hostname`/g" /etc/kvmd/meta.yaml 1031 | 1032 | ### restore htpasswd from previous install, if applies 1033 | if [ -e /etc/kvmd/htpasswd.save ]; then cp /etc/kvmd/htpasswd.save /etc/kvmd/htpasswd; fi 1034 | 1035 | ### instead of showing # fps dynamic, show REDACTED fps dynamic instead; USELESS fps meter fix 1036 | #sed -i -e 's|${__fps}|REDACTED|g' /usr/share/kvmd/web/share/js/kvm/stream_mjpeg.js 1037 | 1038 | ### fix kvmd-webterm 0.49 change that changed ttyd to kvmd-ttyd which broke webterm 1039 | sed -i -e 's/kvmd-ttyd/ttyd/g' /lib/systemd/system/kvmd-webterm.service 1040 | 1041 | # get rid of this line, otherwise kvmd-nginx won't start properly since the nginx version is not 1.25 and higher 1042 | if [ -e /etc/kvmd/nginx/nginx.conf.mako ]; then 1043 | sed -i -e '/http2 on;/d' /etc/kvmd/nginx/nginx.conf.mako 1044 | fi 1045 | 1046 | systemctl restart kvmd-nginx kvmd-webterm kvmd 1047 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # https://github.com/srepac/kvmd-armbian 3 | # 4 | # modified by xe5700 2021-11-04 xe5700@outlook.com 5 | # modified by NewbieOrange 2021-11-04 6 | # created by @srepac 08/09/2021 srepac@kvmnerds.com 7 | # Scripted Installer of Pi-KVM on Armbian 32-bit and 64-bit (as long as it's running python 3.10 or higher) 8 | # 9 | # *** MSD is disabled by default *** 10 | # 11 | # Mass Storage Device requires the use of a USB thumbdrive or SSD and will need to be added in /etc/fstab 12 | : ' 13 | # SAMPLE /etc/fstab entry for USB drive with only one partition formatted as ext4 for the entire drive: 14 | 15 | /dev/sda1 /var/lib/kvmd/msd ext4 nodev,nosuid,noexec,ro,errors=remount-ro,data=journal,X-kvmd.otgmsd-root=/var/lib/kvmd/msd,X-kvmd.otgmsd-user=kvmd 0 0 16 | 17 | ' 18 | # NOTE: This was tested on a new install of raspbian desktop and lite versions, but should also work on an existing install. 19 | # 20 | # Last change 20240526 2345 PDT 21 | VER=3.4 22 | set +x 23 | PIKVMREPO="https://files.pikvm.org/repos/arch/rpi4" 24 | KVMDFILE="kvmd-3.291-1-any.pkg.tar.xz" 25 | KVMDCACHE="/var/cache/kvmd"; mkdir -p $KVMDCACHE 26 | PKGINFO="${KVMDCACHE}/packages.txt" 27 | APP_PATH=$(readlink -f $(dirname $0)) 28 | LOGFILE="${KVMDCACHE}/installer.log"; touch $LOGFILE; echo "==== $( date ) ====" >> $LOGFILE 29 | 30 | cm4=0 # variable to take care of CM4 specific changes 31 | csisvc=0 # variable to take care of starting CSI specific services 32 | 33 | if [[ "$1" == "-h" || "$1" == "--help" ]]; then 34 | echo "usage: $0 [-f] where -f will force re-install new pikvm platform" 35 | exit 1 36 | fi 37 | 38 | CWD=`pwd` 39 | 40 | WHOAMI=$( whoami ) 41 | if [ "$WHOAMI" != "root" ]; then 42 | echo "$WHOAMI, please run script as root." 43 | exit 1 44 | fi 45 | 46 | PYTHONVER=$( python3 -V | cut -d' ' -f2 | cut -d'.' -f1,2 ) 47 | case $PYTHONVER in 48 | 3.1[0-9]) 49 | echo "Python $PYTHONVER is supported." | tee -a $LOGFILE 50 | ;; 51 | *) 52 | echo "Python $PYTHONVER is NOT supported. Please make sure you have python3.10 or higher installed. Exiting." | tee -a $LOGFILE 53 | exit 1 54 | ;; 55 | esac 56 | 57 | ### added on 01/31/23 in case armbian is installed on rpi boards 58 | if [ -e /boot/firmware/config.txt ]; then 59 | _BOOTCONF="/boot/firmware/config.txt" 60 | else 61 | _BOOTCONF="/boot/config.txt" 62 | fi 63 | 64 | MAKER=$(tr -d '\0' < /proc/device-tree/model | awk '{print $1}') 65 | 66 | press-enter() { 67 | echo 68 | read -p "Press ENTER to continue or CTRL+C to break out of script." 69 | } # end press-enter 70 | 71 | gen-ssl-certs() { 72 | cd /etc/kvmd/nginx/ssl 73 | openssl ecparam -out server.key -name prime256v1 -genkey 74 | openssl req -new -x509 -sha256 -nodes -key server.key -out server.crt -days 3650 \ 75 | -subj "/C=US/ST=Denial/L=Denial/O=Pi-KVM/OU=Pi-KVM/CN=$(hostname)" 76 | cp server* /etc/kvmd/vnc/ssl/ 77 | cd ${APP_PATH} 78 | } # end gen-ssl-certs 79 | 80 | enable-csi-svcs() { 81 | if [ ${csisvc} -eq 1 ]; then 82 | systemctl enable kvmd-tc358743 kvmd-janus-static 83 | fi 84 | } 85 | 86 | create-override() { 87 | if [ $( grep ^kvmd: /etc/kvmd/override.yaml | wc -l ) -eq 0 ]; then 88 | 89 | if [[ $( echo $platform | grep usb | wc -l ) -eq 1 ]]; then 90 | cat <> /etc/kvmd/override.yaml 91 | kvmd: 92 | hid: 93 | mouse_alt: 94 | device: /dev/kvmd-hid-mouse-alt # allow relative mouse mode 95 | msd: 96 | type: disabled 97 | atx: 98 | type: disabled 99 | streamer: 100 | forever: true 101 | cmd_append: 102 | - "--slowdown" # so target doesn't have to reboot 103 | resolution: 104 | default: 1280x720 105 | USBOVERRIDE 106 | 107 | else 108 | 109 | cat <> /etc/kvmd/override.yaml 110 | kvmd: 111 | ### disable fan socket check ### 112 | info: 113 | fan: 114 | unix: '' 115 | hid: 116 | mouse_alt: 117 | device: /dev/kvmd-hid-mouse-alt 118 | msd: 119 | type: disabled 120 | streamer: 121 | forever: true 122 | cmd_append: 123 | - "--slowdown" # so target doesn't have to reboot 124 | CSIOVERRIDE 125 | 126 | fi 127 | 128 | fi 129 | } # end create-override 130 | 131 | install-python-packages() { 132 | for i in $( echo "aiofiles aiohttp appdirs asn1crypto async-timeout bottle cffi chardet click 133 | colorama cryptography dateutil dbus dev hidapi idna libgpiod mako marshmallow more-itertools multidict netifaces 134 | packaging passlib pillow ply psutil pycparser pyelftools pyghmi pygments pyparsing requests semantic-version 135 | setproctitle setuptools six spidev systemd tabulate urllib3 wrapt xlib yaml yarl pyotp qrcode serial serial-asyncio" ) 136 | do 137 | echo "apt-get install python3-$i -y" | tee -a $LOGFILE 138 | apt-get install python3-$i -y >> $LOGFILE 139 | done 140 | } # end install python-packages 141 | 142 | otg-devices() { 143 | modprobe libcomposite 144 | if [ ! -e /sys/kernel/config/usb_gadget/kvmd ]; then 145 | mkdir -p /sys/kernel/config/usb_gadget/kvmd/functions 146 | cd /sys/kernel/config/usb_gadget/kvmd/functions 147 | mkdir hid.usb0 hid.usb1 hid.usb2 mass_storage.usb0 148 | fi 149 | cd ${APP_PATH} 150 | } # end otg-device creation 151 | 152 | install-tc358743() { 153 | ### CSI Support for Raspbian ### 154 | #curl https://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | apt-key add - 155 | #echo "deb https://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main" | tee /etc/apt/sources.list.d/uv4l.list 156 | 157 | #apt-get update >> $LOGFILE 158 | #echo "apt-get install uv4l-tc358743-extras -y" | tee -a $LOGFILE 159 | #apt-get install uv4l-tc358743-extras -y >> $LOGFILE 160 | 161 | systemctl enable kvmd-tc358743 kvmd-janus-static 162 | } # install package for tc358743 163 | 164 | boot-files() { 165 | if [[ -e ${_BOOTCONF} && $( grep srepac ${_BOOTCONF} | wc -l ) -eq 0 ]]; then 166 | 167 | if [[ $( echo $platform | grep usb | wc -l ) -eq 1 ]]; then # hdmiusb platforms 168 | 169 | cat <> ${_BOOTCONF} 170 | # srepac custom configs 171 | ### 172 | hdmi_force_hotplug=1 173 | gpu_mem=${GPUMEM} 174 | enable_uart=1 175 | 176 | #dtoverlay=tc358743 177 | 178 | dtoverlay=disable-bt 179 | dtoverlay=dwc2,dr_mode=peripheral 180 | dtparam=act_led_gpio=13 181 | 182 | # HDMI audio capture 183 | #dtoverlay=tc358743-audio 184 | 185 | # SPI (AUM) 186 | #dtoverlay=spi0-1cs 187 | 188 | # I2C (display) 189 | dtparam=i2c_arm=on 190 | 191 | # Clock 192 | dtoverlay=i2c-rtc,pcf8563 193 | FIRMWARE 194 | 195 | else # CSI platforms 196 | 197 | cat <> ${_BOOTCONF} 198 | # srepac custom configs 199 | ### 200 | hdmi_force_hotplug=1 201 | gpu_mem=${GPUMEM} 202 | enable_uart=1 203 | 204 | dtoverlay=tc358743 205 | 206 | dtoverlay=disable-bt 207 | dtoverlay=dwc2,dr_mode=peripheral 208 | dtparam=act_led_gpio=13 209 | 210 | # HDMI audio capture 211 | dtoverlay=tc358743-audio 212 | 213 | # SPI (AUM) 214 | dtoverlay=spi0-1cs 215 | 216 | # I2C (display) 217 | dtparam=i2c_arm=on 218 | 219 | # Clock 220 | dtoverlay=i2c-rtc,pcf8563 221 | CSIFIRMWARE 222 | 223 | # add the tc358743 module to be loaded at boot for CSI 224 | if [[ $( grep -w tc358743 /etc/modules | wc -l ) -eq 0 ]]; then 225 | echo "tc358743" >> /etc/modules 226 | fi 227 | 228 | install-tc358743 229 | 230 | fi 231 | 232 | fi # end of check if entries are already in /boot/config.txt 233 | 234 | # Remove OTG serial (Orange pi zero's kernel not support it) 235 | sed -i '/^g_serial/d' /etc/modules 236 | 237 | # /etc/modules required entries for DWC2, HID and I2C 238 | if [[ $( grep -w dwc2 /etc/modules | wc -l ) -eq 0 ]]; then 239 | echo "dwc2" >> /etc/modules 240 | fi 241 | if [[ $( grep -w libcomposite /etc/modules | wc -l ) -eq 0 ]]; then 242 | echo "libcomposite" >> /etc/modules 243 | fi 244 | if [[ $( grep -w i2c-dev /etc/modules | wc -l ) -eq 0 ]]; then 245 | echo "i2c-dev" >> /etc/modules 246 | fi 247 | 248 | if [ -e ${_BOOTCONF} ]; then 249 | printf "\n${_BOOTCONF}\n\n" | tee -a $LOGFILE 250 | cat ${_BOOTCONF} | tee -a $LOGFILE 251 | fi 252 | 253 | printf "\n/etc/modules\n\n" | tee -a $LOGFILE 254 | cat /etc/modules | tee -a $LOGFILE 255 | } # end of necessary boot files 256 | 257 | get-packages() { 258 | printf "\n\n-> Getting Pi-KVM packages from ${PIKVMREPO}\n\n" | tee -a $LOGFILE 259 | mkdir -p ${KVMDCACHE}/ARCHIVE 260 | if [ $( ls ${KVMDCACHE}/kvmd* > /dev/null 2>&1 | wc -l ) -gt 0 ]; then 261 | mv ${KVMDCACHE}/kvmd* ${KVMDCACHE}/ARCHIVE ### move previous kvmd* packages into ARCHIVE 262 | fi 263 | 264 | echo "wget --no-check-certificate ${PIKVMREPO} -O ${PKGINFO}" | tee -a $LOGFILE 265 | wget --no-check-certificate ${PIKVMREPO} -O ${PKGINFO} 2> /dev/null 266 | echo 267 | 268 | # only get the latest kvmd version 269 | LATESTKVMD=$( grep kvmd-[0-9] $PKGINFO | grep -v sig | tail -1 ) 270 | VERSION=$( echo $LATESTKVMD | cut -d'-' -f2 ) 271 | 272 | # Download each of the pertinent packages for Rpi4, webterm, and the main service 273 | for pkg in `egrep "janus|$LATESTKVMD|$platform-$VERSION|webterm" ${PKGINFO} | grep -v sig | cut -d'>' -f1 | cut -d'"' -f2` 274 | do 275 | rm -f ${KVMDCACHE}/$pkg* 276 | echo "wget --no-check-certificate ${PIKVMREPO}/$pkg -O ${KVMDCACHE}/$pkg" | tee -a $LOGFILE 277 | wget --no-check-certificate ${PIKVMREPO}/$pkg -O ${KVMDCACHE}/$pkg 2> /dev/null 278 | done 279 | 280 | echo 281 | echo "ls -l ${KVMDCACHE}" | tee -a $LOGFILE 282 | ls -l ${KVMDCACHE} | tee -a $LOGFILE 283 | echo 284 | } # end get-packages function 285 | 286 | get-platform() { 287 | tryagain=1 288 | while [ $tryagain -eq 1 ]; do 289 | echo -n "Single Board Computer: $MAKER " | tee -a $LOGFILE 290 | case $MAKER in 291 | Raspberry) ### get which capture device for use with RPi boards 292 | model=$( tr -d '\0' < /proc/device-tree/model | cut -d' ' -f3,4,5 | sed -e 's/ //g' -e 's/Z/z/g' -e 's/Model//' -e 's/Rev//g' -e 's/1.[0-9]//g' ) 293 | 294 | echo "Pi Model $model" | tee -a $LOGFILE 295 | case $model in 296 | 297 | zero2*) 298 | # force platform to only use v2-hdmi for zero2w 299 | platform="kvmd-platform-v2-hdmi-zero2w" 300 | export GPUMEM=96 301 | tryagain=0 302 | ;; 303 | 304 | zero[Ww]) 305 | ### added on 02/18/2022 306 | # force platform to only use v2-hdmi for zerow 307 | platform="kvmd-platform-v2-hdmi-zerow" 308 | ZEROWREPO="https://148.135.104.55/REPO/NEW" 309 | wget --no-check-certificate -O kvmnerds-packages.txt $ZEROWREPO 2> /dev/null 310 | ZEROWPLATFILE=$( grep kvmd-platform kvmnerds-packages.txt | grep -v sig | cut -d'"' -f4 | grep zerow | tail -1 | cut -d/ -f1 ) 311 | 312 | # download the zerow platform file from custom repo 313 | echo "wget --no-check-certificate -O $KVMDCACHE/$ZEROWPLATFILE $ZEROWREPO/$ZEROWPLATFILE" | tee -a $LOGFILE 314 | wget --no-check-certificate -O $KVMDCACHE/$ZEROWPLATFILE $ZEROWREPO/$ZEROWPLATFILE 2> /dev/null 315 | export GPUMEM=64 316 | tryagain=0 317 | ;; 318 | 319 | 3A*) 320 | ### added on 02/18/2022 321 | # force platform to only use v2-hdmi for rpi3 A+ ONLY 322 | # this script doesn't make distinction between rpi3 A, A+ or B 323 | ### it assumes you are using an rpi3 A+ that has the OTG support 324 | ### if your pikvm doesn't work (e.g. kb/mouse won't work), then 325 | ### ... rpi3 does NOT have an OTG port and will require arduino HID 326 | #platform="kvmd-platform-v2-hdmi-rpi3" # this platform package doesn't support webrtc 327 | platform="kvmd-platform-v2-hdmi-rpi4" # use rpi4 platform which supports webrtc 328 | export GPUMEM=96 329 | tryagain=0 330 | ;; 331 | 332 | "3B"|"2B"|"2A"|"B"|"A") 333 | ### added on 02/25/2022 but updated on 03/01/2022 (GPUMEM hardcoded to 16MB) 334 | echo "Pi ${model} board does not have OTG support. You will need to use serial HID via Arduino." 335 | SERIAL=1 # set flag to indicate Serial HID (default is 0 for all other boards) 336 | number=$( echo $model | sed 's/[A-Z]//g' ) 337 | 338 | tryagain=1 339 | while [ $tryagain -eq 1 ]; do 340 | printf "Choose which capture device you will use:\n\n 341 | 1 - v0 USB dongle (ch9329 or arduino) 342 | 2 - v0 CSI (ch9329 or arduino) 343 | 3 - v1 USB dongle (pico hid) 344 | 4 - v1 CSI (pico hid) 345 | 346 | " 347 | read -p "Please type [1-4]: " capture 348 | case $capture in 349 | 1) platform="kvmd-platform-v0-hdmiusb-rpi${number}"; tryagain=0;; 350 | 2) platform="kvmd-platform-v0-hdmi-rpi${number}"; tryagain=0;; 351 | 3) platform="kvmd-platform-v1-hdmiusb-rpi${number}"; tryagain=0;; 352 | 4) platform="kvmd-platform-v1-hdmi-rpi${number}"; tryagain=0;; 353 | *) printf "\nTry again.\n"; tryagain=1;; 354 | esac 355 | done 356 | ;; 357 | 358 | "400") 359 | ### added on 02/22/2022 -- force pi400 to use usb dongle as there's no CSI connector on it 360 | platform="kvmd-platform-v2-hdmiusb-rpi4" 361 | export GPUMEM=256 362 | tryagain=0 363 | ;; 364 | 365 | *) ### default to use rpi4 platform image for CM4 and Pi4 366 | tryagain=1 367 | while [ $tryagain -eq 1 ]; do 368 | printf "Choose which capture device you will use:\n 369 | 1 - USB dongle 370 | 2 - v2 CSI 371 | 3 - V3 HAT 372 | 4 - V4mini (use this for any CM4 boards with CSI capture) 373 | 5 - V4plus 374 | 375 | " 376 | read -p "Please type [1-5]: " capture 377 | case $capture in 378 | 1) platform="kvmd-platform-v2-hdmiusb-rpi4"; export GPUMEM=256; tryagain=0;; 379 | 2) platform="kvmd-platform-v2-hdmi-rpi4"; export GPUMEM=128; csisvc=1; tryagain=0;; 380 | 3) platform="kvmd-platform-v3-hdmi-rpi4"; export GPUMEM=128; csisvc=1; tryagain=0;; 381 | 4) platform="kvmd-platform-v4mini-hdmi-rpi4"; export GPUMEM=128; csisvc=1; cm4=1; tryagain=0;; 382 | 5) platform="kvmd-platform-v4plus-hdmi-rpi4"; export GPUMEM=128; csisvc=1; cm4=1; tryagain=0;; 383 | *) printf "\nTry again.\n"; tryagain=1;; 384 | esac 385 | done 386 | ;; # end case $model in *) 387 | 388 | esac # end case $model 389 | ;; # end case $MAKER in Raspberry) 390 | 391 | *) # other SBC makers can only support hdmi dongle 392 | model=$( tr -d '\0' < /proc/device-tree/model | cut -d' ' -f2,3,4,5,6 ) 393 | echo "$model" | tee -a $LOGFILE 394 | platform="kvmd-platform-v2-hdmiusb-rpi4"; tryagain=0 395 | ;; 396 | 397 | esac # end case $MAKER 398 | 399 | echo 400 | echo "Platform selected -> $platform" | tee -a $LOGFILE 401 | echo 402 | done 403 | } # end get-platform 404 | 405 | install-kvmd-pkgs() { 406 | cd / 407 | 408 | INSTLOG="${KVMDCACHE}/installed_ver.txt"; rm -f $INSTLOG 409 | date > $INSTLOG 410 | 411 | # uncompress platform package first 412 | i=$( ls ${KVMDCACHE}/${platform}*.tar.xz ) ### install the most up to date kvmd-platform package 413 | 414 | # change the log entry to show 3.291 platform installed as we'll be forcing kvmd-3.291 instead of latest/greatest kvmd 415 | _platformver=$( echo $i | sed -e 's/3\.29[2-9]*/3.291/g' -e 's/3\.3[0-9]*/3.291/g' -e 's/3.2911/3.291/g' -e 's/4\.[0-9].*-/3.291-/g' ) 416 | echo "-> Extracting package $_platformver into /" | tee -a $INSTLOG 417 | tar xfJ $i 418 | 419 | # then uncompress, kvmd-{version}, kvmd-webterm, and janus packages 420 | for i in $( ls ${KVMDCACHE}/*.tar.xz | egrep 'kvmd-[0-9]|webterm' ) 421 | do 422 | case $i in 423 | *kvmd-3.29[2-9]*|*kvmd-3.[3-9]*|*kvmd-[45].[1-9]*) # if latest/greatest is 3.292 and higher, then force 3.291 install 424 | echo "*** Force install kvmd 3.291 ***" | tee -a $LOGFILE 425 | # copy kvmd-3.291 package 426 | cp $CWD/$KVMDFILE $KVMDCACHE/ 427 | i=$KVMDCACHE/$KVMDFILE 428 | ;; 429 | *) 430 | ;; 431 | esac 432 | 433 | echo "-> Extracting package $i into /" >> $INSTLOG 434 | tar xfJ $i 435 | done 436 | 437 | # uncompress janus package if /usr/bin/janus doesn't exist 438 | if [ ! -e /usr/bin/janus ]; then 439 | i=$( ls ${KVMDCACHE}/*.tar.xz | egrep janus | grep -v 1x ) 440 | echo "-> Extracting package $i into /" >> $INSTLOG 441 | tar xfJ $i 442 | 443 | else # confirm that /usr/bin/janus actually runs properly 444 | /usr/bin/janus --version > /dev/null 2>> $LOGFILE 445 | if [ $? -eq 0 ]; then 446 | echo "You have a working valid janus binary." | tee -a $LOGFILE 447 | else # error status code, so uncompress from REPO package 448 | #i=$( ls ${KVMDCACHE}/*.tar.xz | egrep janus ) 449 | #echo "-> Extracting package $i into /" >> $INSTLOG 450 | #tar xfJ $i 451 | apt-get remove janus janus-dev -y >> $LOGFILE 452 | apt-get install janus janus-dev -y >> $LOGFILE 453 | fi 454 | fi 455 | 456 | cd ${APP_PATH} 457 | } # end install-kvmd-pkgs 458 | 459 | fix-udevrules() { 460 | # for hdmiusb, replace %b with 1-1.4:1.0 in /etc/udev/rules.d/99-kvmd.rules 461 | sed -i -e 's+\%b+1-1.4:1.0+g' /etc/udev/rules.d/99-kvmd.rules | tee -a $LOGFILE 462 | echo 463 | cat /etc/udev/rules.d/99-kvmd.rules | tee -a $LOGFILE 464 | } # end fix-udevrules 465 | 466 | enable-kvmd-svcs() { 467 | # enable KVMD services but don't start them 468 | echo "-> Enabling $SERVICES services, but do not start them." | tee -a $LOGFILE 469 | systemctl enable $SERVICES 470 | 471 | case $( pikvm-info | grep kvmd-platform | cut -d'-' -f4 ) in 472 | hdmi) 473 | echo "Starting kvmd-tc358743 and kvmd-janus-static services for CSI 2 HDMI capture" 474 | systemctl restart kvmd-tc358743 kvmd-janus-static 475 | systemctl status kvmd-tc358743 kvmd-janus-static | grep Loaded 476 | ;; 477 | hdmiusb) 478 | echo "USB-HDMI capture" 479 | ;; 480 | esac | tee -a $LOGFILE 481 | } # end enable-kvmd-svcs 482 | 483 | build-ustreamer() { 484 | printf "\n\n-> Building ustreamer\n\n" | tee -a $LOGFILE 485 | # Install packages needed for building ustreamer source 486 | echo "apt install -y make libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev libmd-dev libdrm-dev janus-dev janus" | tee -a $LOGFILE 487 | apt install -y make libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev libmd-dev libdrm-dev janus-dev janus >> $LOGFILE 488 | 489 | # fix refcount.h 490 | sed -i -e 's|^#include "refcount.h"$|#include "../refcount.h"|g' /usr/include/janus/plugins/plugin.h 491 | 492 | # Download ustreamer source and build it 493 | cd /tmp 494 | git clone --depth=1 https://github.com/pikvm/ustreamer 495 | cd ustreamer/ 496 | make WITH_GPIO=1 WITH_SYSTEMD=1 WITH_JANUS=1 WITH_V4P=1 -j 497 | make install 498 | # kvmd service is looking for /usr/bin/ustreamer 499 | ln -sf /usr/local/bin/ustreamer* /usr/bin/ 500 | 501 | # add janus support 502 | mkdir -p /usr/lib/ustreamer/janus 503 | cp /tmp/ustreamer/janus/libjanus_ustreamer.so /usr/lib/ustreamer/janus 504 | } # end build-ustreamer 505 | 506 | install-dependencies() { 507 | echo 508 | echo "-> Installing dependencies for pikvm" | tee -a $LOGFILE 509 | 510 | echo "apt install -y nginx python3 net-tools bc expect v4l-utils iptables vim dos2unix screen tmate nfs-common gpiod ffmpeg dialog iptables dnsmasq git python3-pip tesseract-ocr tesseract-ocr-eng libasound2-dev libsndfile-dev libspeexdsp-dev libdrm-dev" | tee -a $LOGFILE 511 | apt install -y nginx python3 net-tools bc expect v4l-utils iptables vim dos2unix screen tmate nfs-common gpiod ffmpeg dialog iptables dnsmasq git python3-pip tesseract-ocr tesseract-ocr-eng libasound2-dev libsndfile-dev libspeexdsp-dev libdrm-dev >> $LOGFILE 512 | 513 | sed -i -e 's/#port=5353/port=5353/g' /etc/dnsmasq.conf 514 | 515 | install-python-packages 516 | 517 | echo "-> Install python3 modules dbus_next and zstandard" | tee -a $LOGFILE 518 | if [[ "$PYTHONVER" == "3.11" || "$PYTHONVER" == "3.12" ]]; then 519 | apt install -y python3-dbus-next python3-zstandard 520 | else 521 | pip3 install dbus_next zstandard 522 | fi 523 | 524 | echo "-> Make tesseract data link" | tee -a $LOGFILE 525 | ln -sf /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata 526 | 527 | echo "-> Install TTYD" | tee -a $LOGFILE 528 | apt install -y ttyd | tee -a $LOGFILE 529 | if [ ! -e /usr/bin/ttyd ]; then 530 | # Build and install ttyd 531 | cd /tmp 532 | apt-get install -y build-essential cmake git libjson-c-dev libwebsockets-dev 533 | git clone https://github.com/tsl0922/ttyd.git 534 | cd ttyd && mkdir build && cd build 535 | cmake .. 536 | make -j && make install 537 | cp ttyd /usr/bin/ttyd 538 | # Install binary from GitHub 539 | #arch=$(dpkg --print-architecture) 540 | #latest=$(curl -sL https://api.github.com/repos/tsl0922/ttyd/releases/latest | jq -r ".tag_name") 541 | #latest=1.6.3 # confirmed works with pikvm, latest from github (1.7.4) did not allow typing in webterm 542 | #if [ $arch = arm64 ]; then 543 | # arch='aarch64' 544 | #fi 545 | #wget --no-check-certificate "https://github.com/tsl0922/ttyd/releases/download/$latest/ttyd.$arch" -O /usr/bin/ttyd 546 | chmod +x /usr/bin/ttyd 547 | fi 548 | /usr/bin/ttyd -v | tee -a $LOGFILE 549 | 550 | if [ ! -e /usr/local/bin/gpio ]; then 551 | printf "\n\n-> Building wiringpi from source\n\n" | tee -a $LOGFILE 552 | cd /tmp; rm -rf WiringPi 553 | git clone https://github.com/WiringPi/WiringPi.git 554 | cd WiringPi 555 | ./build 556 | else 557 | printf "\n\n-> Wiringpi (gpio) is already installed.\n\n" | tee -a $LOGFILE 558 | fi 559 | gpio -v | tee -a $LOGFILE 560 | 561 | echo "-> Install ustreamer" | tee -a $LOGFILE 562 | if [ ! -e /usr/bin/ustreamer ]; then 563 | cd /tmp 564 | apt-get install -y libevent-2.1-7 libevent-core-2.1-7 libevent-pthreads-2.1-7 build-essential 565 | ### required dependent packages for ustreamer ### 566 | build-ustreamer 567 | cd ${APP_PATH} 568 | fi 569 | echo -n "ustreamer version: " | tee -a $LOGFILE 570 | ustreamer -v | tee -a $LOGFILE 571 | ustreamer --features | tee -a $LOGFILE 572 | } # end install-dependencies 573 | 574 | python-pkg-dir() { 575 | # debian system python3 no alias 576 | # create quick python script to show where python packages need to go 577 | cat << MYSCRIPT > /tmp/syspath.py 578 | #!$(which python3) 579 | import sys 580 | print (sys.path) 581 | MYSCRIPT 582 | 583 | chmod +x /tmp/syspath.py 584 | 585 | #PYTHONDIR=$( /tmp/syspath.py | awk -F, '{print $NF}' | cut -d"'" -f2 ) 586 | ### hardcode path for armbian/raspbian 587 | PYTHONDIR="/usr/lib/python3/dist-packages" 588 | } # end python-pkg-dir 589 | 590 | fix-nginx-symlinks() { 591 | # disable default nginx service since we will use kvmd-nginx instead 592 | echo 593 | echo "-> Disabling nginx service, so that we can use kvmd-nginx instead" | tee -a $LOGFILE 594 | systemctl disable --now nginx 595 | 596 | # setup symlinks 597 | echo 598 | echo "-> Creating symlinks for use with kvmd python scripts" | tee -a $LOGFILE 599 | if [ ! -e /usr/bin/nginx ]; then ln -sf /usr/sbin/nginx /usr/bin/; fi 600 | if [ ! -e /usr/sbin/python ]; then ln -sf /usr/bin/python3 /usr/sbin/python; fi 601 | if [ ! -e /usr/bin/iptables ]; then ln -sf /usr/sbin/iptables /usr/bin/iptables; fi 602 | if [ ! -e /usr/bin/vcgencmd ]; then ln -sf /opt/vc/bin/* /usr/bin/; fi 603 | 604 | python-pkg-dir 605 | 606 | if [ ! -e $PYTHONDIR/kvmd ]; then 607 | # Debian python版本比 pikvm官方的低一些 608 | # in case new kvmd packages are now using python 3.11 609 | ln -sf /usr/lib/python3.1*/site-packages/kvmd* ${PYTHONDIR} 610 | fi 611 | } # end fix-nginx-symlinks 612 | 613 | fix-python-symlinks(){ 614 | python-pkg-dir 615 | 616 | if [ ! -e $PYTHONDIR/kvmd ]; then 617 | # Debian python版本比 pikvm官方的低一些 618 | ln -sf /usr/lib/python3.1*/site-packages/kvmd* ${PYTHONDIR} 619 | fi 620 | } 621 | 622 | apply-custom-patch(){ 623 | read -p "Do you want apply old kernel msd patch? [y/n]" answer 624 | case $answer in 625 | n|N|no|No) 626 | echo 'You skipped this patch.' 627 | ;; 628 | y|Y|Yes|yes) 629 | ./patches/custom/old-kernel-msd/apply.sh 630 | ;; 631 | *) 632 | echo "Try again.";; 633 | esac 634 | } 635 | 636 | fix-kvmd-for-tvbox-armbian(){ 637 | # 打补丁来移除一些对armbian和电视盒子不太支持的特性 638 | cd /usr/lib/python3.10/site-packages/ 639 | git apply ${APP_PATH}/patches/bullseye/*.patch 640 | cd ${APP_PATH} 641 | read -p "Do you want to apply custom patches? [y/n] " answer 642 | case $answer in 643 | n|N|no|No) 644 | return; 645 | ;; 646 | y|Y|Yes|yes) 647 | apply-custom-patch; 648 | return; 649 | ;; 650 | *) 651 | echo "Try again.";; 652 | esac 653 | } 654 | 655 | fix-webterm() { 656 | echo 657 | echo "-> Creating kvmd-webterm homedir" | tee -a $LOGFILE 658 | mkdir -p /home/kvmd-webterm 659 | chown kvmd-webterm /home/kvmd-webterm 660 | ls -ld /home/kvmd-webterm | tee -a $LOGFILE 661 | 662 | # remove -W option since ttyd installed on raspbian/armbian is 1.6.3 (-W option only works with ttyd 1.7.x) 663 | _ttydver=$( /usr/bin/ttyd -v | awk '{print $NF}' ) 664 | case $_ttydver in 665 | 1.6*) 666 | echo "ttyd $_ttydver found. Removing -W from /lib/systemd/system/kvmd-webterm.service" 667 | sed -i -e '/-W \\/d' /lib/systemd/system/kvmd-webterm.service 668 | ;; 669 | 1.7*) 670 | echo "ttyd $_ttydver found. Nothing to do." 671 | ;; 672 | esac 673 | 674 | # add sudoers entry for kvmd-webterm user to be able to run sudo 675 | echo "kvmd-webterm ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/kvmd-webterm; chmod 440 /etc/sudoers.d/kvmd-webterm 676 | } # end fix-webterm 677 | 678 | create-kvmdfix() { 679 | # Create kvmd-fix service and script 680 | cat < /lib/systemd/system/kvmd-fix.service 681 | [Unit] 682 | Description=KVMD Fixes 683 | After=network.target network-online.target nss-lookup.target 684 | Before=kvmd.service 685 | 686 | [Service] 687 | User=root 688 | Type=simple 689 | ExecStart=/usr/bin/kvmd-fix 690 | 691 | [Install] 692 | WantedBy=multi-user.target 693 | ENDSERVICE 694 | 695 | cat < /usr/bin/kvmd-fix 696 | #!/bin/bash 697 | # Written by @srepac 698 | # 1. Properly set group ownership of /dev/gpio* 699 | # 2. fix /dev/kvmd-video symlink to point to /dev/video1 (Amglogic Device video0 is not usb device) 700 | # 701 | ### These fixes are required in order for kvmd service to start properly 702 | # 703 | set -x 704 | chgrp gpio /dev/gpio* 705 | chmod 660 /dev/gpio* 706 | ls -l /dev/gpio* 707 | 708 | udevadm trigger 709 | ls -l /dev/kvmd-video 710 | 711 | if [ \$( systemctl | grep kvmd-oled | grep -c activ ) -eq 0 ]; then 712 | echo "kvmd-oled service is not enabled." 713 | exit 0 714 | else 715 | echo "kvmd-oled service is enabled and activated." 716 | fi 717 | 718 | ### kvmd-oled fix: swap i2c-0 <-> i2c-1 (code is looking for I2C oled on i2c-1) 719 | # pins #1 - 3.3v, #3 - SDA, #5 - SCL, and #9 - GND 720 | i2cget -y 0 0x3c 721 | if [ \$? -eq 0 ]; then 722 | echo "-> Found valid I2C OLED at i2c-0. Applying I2C OLED fix." 723 | cd /dev 724 | 725 | # rename i2c-0 -> i2c-9, move i2c-1 to i2c-0, and rename the good i2c-9 to i2c-1 726 | mv i2c-0 i2c-9 727 | mv i2c-1 i2c-0 728 | mv i2c-9 i2c-1 729 | 730 | # restart kvmd-oled service 731 | systemctl restart kvmd-oled 732 | else 733 | echo "-> I2C OLED fix already applied and OLED should be showing info." 734 | fi 735 | SCRIPTEND 736 | 737 | chmod +x /usr/bin/kvmd-fix 738 | } # end create-kvmdfix 739 | 740 | set-ownership() { 741 | # set proper ownership of password files and kvmd-webterm homedir 742 | cd /etc/kvmd 743 | chown kvmd:kvmd htpasswd 744 | chown kvmd-ipmi:kvmd-ipmi ipmipasswd 745 | chown kvmd-vnc:kvmd-vnc vncpasswd 746 | chown kvmd-webterm /home/kvmd-webterm 747 | 748 | # add kvmd user to video group (this is required in order to use CSI bridge with OMX and h264 support) 749 | usermod -a -G video kvmd 750 | 751 | # add kvmd user to dialout group (required for xh_hk4401 kvm switch support) 752 | usermod -a -G dialout kvmd 753 | 754 | ### fix totp.secret file permissions for use with 2FA 755 | chmod go+r /etc/kvmd/totp.secret 756 | chown kvmd:kvmd /etc/kvmd/totp.secret 757 | } # end set-ownership 758 | 759 | check-kvmd-works() { 760 | echo "-> Checking kvmd -m works before continuing" | tee -a $LOGFILE 761 | invalid=1 762 | while [ $invalid -eq 1 ]; do 763 | kvmd -m | tee -a $LOGFILE 764 | read -p "Did kvmd -m run properly? [y/n] " answer 765 | case $answer in 766 | n|N|no|No) 767 | echo "Please install missing packages as per the kvmd -m output in another ssh/terminal." 768 | ;; 769 | y|Y|Yes|yes) 770 | invalid=0 771 | ;; 772 | *) 773 | echo "Try again.";; 774 | esac 775 | done 776 | } # end check-kvmd-works 777 | 778 | start-kvmd-svcs() { 779 | #### start the main KVM services in order #### 780 | # 1. nginx is the webserver 781 | # 2. kvmd-otg is for OTG devices (keyboard/mouse, etc..) 782 | # 3. kvmd is the main daemon 783 | systemctl daemon-reload 784 | systemctl restart $SERVICES 785 | } # end start-kvmd-svcs 786 | 787 | fix-motd() { 788 | if [ -e /etc/motd ]; then rm /etc/motd; fi 789 | cp armbian/armbian-motd /usr/bin/ 790 | sed -i 's/cat \/etc\/motd/armbian-motd/g' /lib/systemd/system/kvmd-webterm.service 791 | systemctl daemon-reload 792 | # systemctl restart kvmd-webterm 793 | } # end fix-motd 794 | 795 | # 安装armbian的包 796 | armbian-packages() { 797 | mkdir -p /opt/vc/bin/ 798 | #cd /opt/vc/bin 799 | if [ ! -e /usr/bin/vcgencmd ]; then 800 | # Install vcgencmd for armbian platform 801 | cp -rf armbian/opt/* /opt/vc/bin 802 | else 803 | ln -s /usr/bin/vcgencmd /opt/vc/bin/ 804 | fi 805 | #cp -rf armbian/udev /etc/ 806 | 807 | cd ${APP_PATH} 808 | } # end armbian-packages 809 | 810 | fix-nfs-msd() { 811 | NAME="aiofiles.tar" 812 | 813 | for i in 3.11 3.12; do 814 | LOCATION="/usr/lib/python$i/site-packages" 815 | if [ -e $LOCATION ]; then 816 | echo "-> Extracting $NAME into $LOCATION" | tee -a $LOGFILE 817 | tar xvf $NAME -C $LOCATION 818 | 819 | echo "-> Renaming original aiofiles and creating symlink to correct aiofiles" | tee -a $LOGFILE 820 | cd /usr/lib/python3/dist-packages 821 | mv aiofiles aiofiles.$(date +%Y%m%d.%H%M) 822 | ln -s $LOCATION/aiofiles . 823 | ls -ld aiofiles* | tail -5 824 | fi 825 | done 826 | } 827 | 828 | fix-nginx() { 829 | #set -x 830 | KERNEL=$( uname -r | awk -F\- '{print $1}' ) 831 | ARCH=$( uname -r | awk -F\- '{print $NF}' ) 832 | echo "KERNEL: $KERNEL ARCH: $ARCH" | tee -a $LOGFILE 833 | case $ARCH in 834 | ARCH) SEARCHKEY=nginx-mainline;; 835 | *) SEARCHKEY="nginx/";; 836 | esac 837 | 838 | HTTPSCONF="/etc/kvmd/nginx/listen-https.conf" 839 | echo "HTTPSCONF BEFORE: $HTTPSCONF" | tee -a $LOGFILE 840 | cat $HTTPSCONF | tee -a $LOGFILE 841 | 842 | if [[ ! -e /usr/local/bin/pikvm-info || ! -e /tmp/pacmanquery ]]; then 843 | wget --no-check-certificate -O /usr/local/bin/pikvm-info https://148.135.104.55/PiKVM/pikvm-info 2> /dev/null 844 | chmod +x /usr/local/bin/pikvm-info 845 | echo "Getting list of packages installed..." | tee -a $LOGFILE 846 | pikvm-info > /dev/null ### this generates /tmp/pacmanquery with list of installed pkgs 847 | fi 848 | 849 | NGINXVER=$( grep $SEARCHKEY /tmp/pacmanquery | awk '{print $1}' | cut -d'.' -f1,2 ) 850 | echo 851 | echo "NGINX version installed: $NGINXVER" | tee -a $LOGFILE 852 | 853 | case $NGINXVER in 854 | 1.2[56789]|1.3*|1.4*|1.5*) # nginx version 1.25 and higher 855 | cat << NEW_CONF > $HTTPSCONF 856 | listen 443 ssl; 857 | listen [::]:443 ssl; 858 | http2 on; 859 | NEW_CONF 860 | ;; 861 | 862 | 1.18|*) # nginx version 1.18 and lower 863 | cat << ORIG_CONF > $HTTPSCONF 864 | listen 443 ssl http2; 865 | listen [::]:443 ssl; 866 | ORIG_CONF 867 | ;; 868 | 869 | esac 870 | 871 | echo "HTTPSCONF AFTER: $HTTPSCONF" | tee -a $LOGFILE 872 | cat $HTTPSCONF | tee -a $LOGFILE 873 | set +x 874 | } # end fix-nginx 875 | 876 | ocr-fix() { # create function 877 | echo 878 | echo "-> Apply OCR fix..." | tee -a $LOGFILE 879 | 880 | set -x 881 | # 1. verify that Pillow module is currently running 9.0.x 882 | PILLOWVER=$( grep -i pillow $PIP3LIST | awk '{print $NF}' ) 883 | 884 | case $PILLOWVER in 885 | 9.*|8.*|7.*) # Pillow running at 9.x and lower 886 | # 2. update Pillow to 10.0.0 887 | pip3 install -U Pillow 2>> $LOGFILE 888 | 889 | # 3. check that Pillow module is now running 10.0.0 890 | pip3 list | grep -i pillow | tee -a $LOGFILE 891 | 892 | #4. restart kvmd and confirm OCR now works. 893 | systemctl restart kvmd 894 | ;; 895 | 896 | 10.*|11.*|12.*) # Pillow running at 10.x and higher 897 | echo "Already running Pillow $PILLOWVER. Nothing to do." | tee -a $LOGFILE 898 | ;; 899 | 900 | esac 901 | 902 | set +x 903 | echo 904 | } # end ocr-fix 905 | 906 | async-lru-fix() { 907 | echo 908 | echo "-> Ensuring async-lru is installed with version 2.x ..." | tee -a $LOGFILE 909 | pip3 install async-lru 2> /dev/null 910 | PIP3LIST="/tmp/pip3.list"; /bin/rm -f $PIP3LIST 911 | pip3 list 2> /dev/null > $PIP3LIST 912 | 913 | ASYNCLRUVER=$( grep -i 'async[-_]lru' $PIP3LIST | awk '{print $NF}' ) 914 | echo "ASYNC-LRU version: $ASYNCLRUVER" 915 | case $ASYNCLRUVER in 916 | 2.*) echo "Nothing to do. aync-lru is already running $ASYNCLRUVER" | tee -a $LOFILE;; 917 | 1.*|*) pip3 install -U async_lru --break-system-packages | tee -a $LOGFILE;; # raspbian bookworm only installs 1.0.x, this forces 2.0.x 918 | esac 919 | } # end async-lru-fix 920 | 921 | cm4-mods() { # apply CM4 specific mods 922 | if [ $cm4 -eq 1 ]; then 923 | echo "-> Applying CM4 specific changes" | tee -a $LOGFILE 924 | 925 | # Add CM4 otg fix 926 | sed -i --follow-symlinks -e 's/^otg_mode=1/#otg_mode=1/g' ${_BOOTCONF} 927 | 928 | # add 4lane CSI support 929 | sed -i --follow-symlinks -e 's|^dtoverlay=tc358743$|\n# Video (CM4)\ndtoverlay=tc358743,4lane=1\n|g' ${_BOOTCONF} 930 | 931 | # v4mini and v4plus yaml file are the same 932 | cp /etc/kvmd/main.yaml /etc/kvmd/main.yaml.orig 933 | cp /usr/share/kvmd/configs.default/kvmd/main/v4mini-hdmi-rpi4.yaml /etc/kvmd/main.yaml 934 | 935 | # update EDID to support 1920x1080p 60hz and 1920x1200 60hz 936 | cp /etc/kvmd/tc358743-edid.hex /etc/kvmd/tc358743-edid.hex.orig 937 | cp /usr/share/kvmd/configs.default/kvmd/edid/v4mini-hdmi.hex /etc/kvmd/tc358743-edid.hex 938 | fi 939 | } # end cm4-mods 940 | 941 | update-logo() { 942 | sed -i -e 's|class="svg-gray"|class="svg-color"|g' /usr/share/kvmd/web/index.html 943 | sed -i -e 's|target="_blank"> /dev/null 2> /dev/null 947 | cd /usr/share/kvmd/web/share/svg 948 | cp logo.svg logo.svg.old 949 | cp opikvm-logo.svg logo.svg 950 | 951 | # change some text in the main html page 952 | sed -i.bak -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' /usr/share/kvmd/web/index.html 953 | sed -i.bak -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' /usr/share/kvmd/web/kvm/index.html 954 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/kvm/index.html 955 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/index.html 956 | cd 957 | } 958 | 959 | function fix-hk4401() { 960 | # https://github.com/ThomasVon2021/blikvm/issues/168 961 | 962 | # Download kvmd-4.2 package from kvmnerds.com to /tmp and extract only the xh_hk4401.py script 963 | cd /tmp 964 | wget --no-check-certificate -O kvmd-4.2-1-any.pkg.tar.xz https://148.135.104.55/REPO/NEW/kvmd-4.2-1-any.pkg.tar.xz 2> /dev/null 965 | tar xvfJ kvmd-4.2-1-any.pkg.tar.xz --wildcards --no-anchored 'xh_hk4401.py' 966 | 967 | # Show diff of 4.2 version of xh_hk4401.py vs. current installed version 968 | cd usr/lib/python3.12/site-packages/kvmd/plugins/ugpio/ 969 | diff xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 970 | 971 | # make a backup of current xh_hk4401.py script 972 | cp /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py.3.291 973 | 974 | # replace it with the kvmd 4.2 version of script which allows use of protocol: 2 975 | cp xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 976 | cd 977 | } # end fix-hk4401 978 | 979 | 980 | ### MAIN STARTS HERE ### 981 | # Install is done in two parts 982 | # First part requires a reboot in order to create kvmd users and groups 983 | # Second part will start the necessary kvmd services 984 | 985 | # if /etc/kvmd/htpasswd exists, then make a backup 986 | if [ -e /etc/kvmd/htpasswd ]; then cp /etc/kvmd/htpasswd /etc/kvmd/htpasswd.save; fi 987 | 988 | ### I uploaded all these into github on 05/22/23 -- so just copy them into correct location 989 | cd ${APP_PATH} 990 | cp -rf pistat /usr/local/bin/pistat 991 | cp -rf pi-temp /usr/local/bin/pi-temp 992 | cp -rf pikvm-info /usr/local/bin/pikvm-info 993 | cp -rf update-rpikvm.sh /usr/local/bin/update-rpikvm.sh 994 | cp -rf tshoot.sh /usr/local/bin/tshoot.sh 995 | chmod +x /usr/local/bin/pi* /usr/local/bin/update-rpikvm.sh /usr/local/bin/tshoot.sh 996 | 997 | ### fix for kvmd 3.230 and higher 998 | ln -sf python3 /usr/bin/python 999 | 1000 | SERVICES="kvmd-nginx kvmd-webterm kvmd-otg kvmd kvmd-fix" 1001 | 1002 | # added option to re-install by adding -f parameter (for use as platform switcher) 1003 | PYTHON_VERSION=$( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) 1004 | if [[ $( grep kvmd /etc/passwd | wc -l ) -eq 0 || "$1" == "-f" ]]; then 1005 | printf "\nRunning part 1 of PiKVM installer script v$VER by @srepac\n" | tee -a $LOGFILE 1006 | get-platform 1007 | get-packages 1008 | install-kvmd-pkgs 1009 | enable-csi-svcs 1010 | cm4-mods 1011 | boot-files 1012 | create-override 1013 | gen-ssl-certs 1014 | fix-udevrules 1015 | install-dependencies 1016 | otg-devices 1017 | armbian-packages 1018 | systemctl disable --now janus ttyd 1019 | 1020 | printf "\nEnd part 1 of PiKVM installer script v$VER by @srepac\n" >> $LOGFILE 1021 | printf "\nReboot is required to create kvmd users and groups.\nPlease re-run this script after reboot to complete the install.\n" | tee -a $LOGFILE 1022 | 1023 | # Fix paste-as-keys if running python 3.7 1024 | if [[ $( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) == "3.7" ]]; then 1025 | sed -i -e 's/reversed//g' /usr/lib/python3.1*/site-packages/kvmd/keyboard/printer.py 1026 | fi 1027 | 1028 | ### run these to make sure kvmd users are created ### 1029 | echo "-> Ensuring KVMD users and groups ..." | tee -a $LOGFILE 1030 | systemd-sysusers /usr/lib/sysusers.d/kvmd.conf 1031 | systemd-sysusers /usr/lib/sysusers.d/kvmd-webterm.conf 1032 | 1033 | # Ask user to press CTRL+C before reboot or ENTER to proceed with reboot 1034 | press-enter 1035 | reboot 1036 | else 1037 | printf "\nRunning part 2 of PiKVM installer script v$VER by @srepac\n" | tee -a $LOGFILE 1038 | 1039 | echo "-> Re-installing janus ..." | tee -a $LOGFILE 1040 | apt reinstall -y janus > /dev/null 2>&1 1041 | 1042 | ### run these to make sure kvmd users are created ### 1043 | echo "-> Ensuring KVMD users and groups ..." | tee -a $LOGFILE 1044 | systemd-sysusers /usr/lib/sysusers.d/kvmd.conf 1045 | systemd-sysusers /usr/lib/sysusers.d/kvmd-webterm.conf 1046 | 1047 | fix-nginx-symlinks 1048 | fix-python-symlinks 1049 | fix-webterm 1050 | fix-motd 1051 | fix-nfs-msd 1052 | fix-nginx 1053 | async-lru-fix 1054 | ocr-fix 1055 | fix-hk4401 1056 | 1057 | set-ownership 1058 | create-kvmdfix 1059 | 1060 | ### additional python pip dependencies for kvmd 3.238 and higher 1061 | case $PYTHONVER in 1062 | 3.10*|3.[987]*) 1063 | pip3 install async-lru 2> /dev/null 1064 | ### Fix for kvmd 3.291 -- only applies to python 3.10 ### 1065 | sed -i -e 's|gpiod.EdgeEvent|gpiod.LineEvent|g' /usr/lib/python3/dist-packages/kvmd/aiogp.py 1066 | sed -i -e 's|gpiod.line,|gpiod.Line,|g' /usr/lib/python3/dist-packages/kvmd/aiogp.py 1067 | ;; 1068 | 3.1[1-9]*) 1069 | pip3 install async-lru --break-system-packages 2> /dev/null 1070 | ;; 1071 | esac 1072 | check-kvmd-works 1073 | 1074 | enable-kvmd-svcs 1075 | update-logo 1076 | start-kvmd-svcs 1077 | 1078 | printf "\nCheck kvmd devices\n\n" | tee -a $LOGFILE 1079 | ls -l /dev/kvmd* | tee -a $LOGFILE 1080 | printf "\nYou should see devices for keyboard, mouse, and video.\n" | tee -a $LOGFILE 1081 | 1082 | printf "\nPoint a browser to https://$(hostname)\nIf it doesn't work, then reboot one last time.\nPlease make sure kvmd services are running after reboot.\n" | tee -a $LOGFILE 1083 | fi 1084 | 1085 | cd $CWD 1086 | cp -rf web.css /etc/kvmd/web.css 1087 | 1088 | systemctl status $SERVICES | grep Loaded | tee -a $LOGFILE 1089 | 1090 | ### create rw and ro so that /usr/bin/kvmd-bootconfig doesn't fail 1091 | touch /usr/local/bin/rw /usr/local/bin/ro 1092 | chmod +x /usr/local/bin/rw /usr/local/bin/ro 1093 | 1094 | ### update default hostname info in webui to reflect current hostname 1095 | sed -i -e "s/localhost.localdomain/`hostname`/g" /etc/kvmd/meta.yaml 1096 | 1097 | ### restore htpasswd from previous install, if applies 1098 | if [ -e /etc/kvmd/htpasswd.save ]; then cp /etc/kvmd/htpasswd.save /etc/kvmd/htpasswd; fi 1099 | 1100 | ### instead of showing # fps dynamic, show REDACTED fps dynamic instead; USELESS fps meter fix 1101 | #sed -i -e 's|${__fps}|REDACTED|g' /usr/share/kvmd/web/share/js/kvm/stream_mjpeg.js 1102 | 1103 | ### fix kvmd-webterm 0.49 change that changed ttyd to kvmd-ttyd which broke webterm 1104 | sed -i -e 's/kvmd-ttyd/ttyd/g' /lib/systemd/system/kvmd-webterm.service 1105 | 1106 | # get rid of this line, otherwise kvmd-nginx won't start properly since the nginx version is not 1.25 and higher 1107 | if [ -e /etc/kvmd/nginx/nginx.conf.mako ]; then 1108 | sed -i -e '/http2 on;/d' /etc/kvmd/nginx/nginx.conf.mako 1109 | fi 1110 | 1111 | systemctl restart kvmd-nginx kvmd-webterm kvmd 1112 | -------------------------------------------------------------------------------- /kvmd-3.291-1-any.pkg.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srepac/kvmd-armbian/a288b36815cff12a78fe1554cbcd4b229648bbd6/kvmd-3.291-1-any.pkg.tar.xz -------------------------------------------------------------------------------- /libgpiod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Written by @srepac (c) 2024 3 | # This script will compile the correct libgpiod version based on python version installed 4 | # 5 | function downgrade() { 6 | # ---------------------- 7 | # libgpiod to run v1.6.3 8 | # ---------------------- 9 | set -x 10 | apt install -y autoconf-archive libtool dh-autoreconf 11 | 12 | cd /tmp 13 | FILE="libgpiod-1.6.3.tar.gz" 14 | GPIODVER=$( echo $FILE | cut -d'-' -f2 | sed 's/.tar.gz//g' ) 15 | wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/$FILE -O $FILE 2> /dev/null 16 | tar xfz $FILE 17 | cd libgpiod-$GPIODVER 18 | 19 | ### this works with python3.10 20 | ./autogen.sh --enable-tools=yes --prefix=/usr 21 | 22 | make 23 | make install 24 | set +x 25 | } # end function downgrade 26 | 27 | 28 | function upgrade() { 29 | # -------------------- 30 | # libgpiod to run v2.1 31 | # -------------------- 32 | set -x 33 | apt install -y autoconf-archive libtool dh-autoreconf 34 | 35 | cd /tmp 36 | FILE="libgpiod-2.1.2.tar.gz" 37 | GPIODVER=$( echo $FILE | cut -d'-' -f2 | sed 's/.tar.gz//g' ) 38 | wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/$FILE -O $FILE 2> /dev/null 39 | tar xfz $FILE 40 | cd libgpiod-$GPIODVER 41 | 42 | ./autogen.sh --enable-tools=yes --enable-bindings-python=yes --prefix=/usr 43 | make -j$( nproc --all ) 44 | make install 45 | set +x 46 | 47 | gpioinfo -v | head -1 48 | } # end function upgrade 49 | 50 | 51 | ### MAIN STARTS HERE 52 | _PYTHONVER=$( python -V | awk '{print $NF}' ) 53 | echo "Python $_PYTHONVER" 54 | 55 | _libgpiodver=$( gpioinfo -v | head -1 | awk '{print $NF}' ) 56 | case $_libgpiodver in 57 | v1.*) 58 | ### if python version is 3.11, then upgrade to 2.1 else leave it alone 59 | case $_PYTHONVER in 60 | 61 | 3.1[1-9]*) # 3.11 and higher 62 | echo "Found libgpiod $_libgpiodver. Upgrading to v2.1 Please wait..." 63 | upgrade 64 | ;; 65 | 66 | 3.10*|3.[987]*) 67 | echo "Found libgpiod $_libgpiodver. Nothing to do" 68 | ;; 69 | 70 | esac 71 | ;; 72 | 73 | v2.*) 74 | ### if python version is 3.10 or older, then downgrade to 1.6.3 else leave it at 2.1 for python 3.11 75 | case $_PYTHONVER in 76 | 77 | 3.10*|*3.[987]*) 78 | echo "Found libgpiod $_libgpiodver. Downgrading to v1.6.3 Please wait..." 79 | downgrade 80 | ;; 81 | 82 | 3.1[1-9]*) # 3.11 and higher 83 | echo "Found libgpiod $_libgpiodver. Nothing to do" 84 | ;; 85 | 86 | esac 87 | ;; 88 | 89 | *) 90 | echo "Undefined function."; exit 1 91 | ;; 92 | esac 93 | -------------------------------------------------------------------------------- /opikvm-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 15 | 16 | 17 | Page-1 18 | 19 | 20 | path7 21 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /patches/bullseye/0001-Disable-GPIO-For-TV-Box.patch: -------------------------------------------------------------------------------- 1 | From 960b30ec20b370269ee43282f1867861e714e33f Mon Sep 17 00:00:00 2001 2 | From: xe5700 <9338143+xe5700@users.noreply.github.com> 3 | Date: Thu, 19 May 2022 23:01:20 +0800 4 | Subject: [PATCH] Disable-GPIO-For-TV-Box 5 | 6 | --- 7 | kvmd/apps/kvmd/ugpio.py | 12 ++++++------ 8 | 1 file changed, 6 insertions(+), 6 deletions(-) 9 | 10 | diff --git a/kvmd/apps/kvmd/ugpio.py b/kvmd/apps/kvmd/ugpio.py 11 | index cea60bb9..9328496a 100644 12 | --- a/kvmd/apps/kvmd/ugpio.py 13 | +++ b/kvmd/apps/kvmd/ugpio.py 14 | @@ -282,15 +282,16 @@ class UserGpio: 15 | 16 | def sysprep(self) -> None: 17 | get_logger(0).info("Preparing User-GPIO drivers ...") 18 | - for (_, driver) in tools.sorted_kvs(self.__drivers): 19 | - driver.prepare() 20 | +# for (_, driver) in tools.sorted_kvs(self.__drivers): 21 | +# driver.prepare() 22 | 23 | async def systask(self) -> None: 24 | get_logger(0).info("Running User-GPIO drivers ...") 25 | - await asyncio.gather(*[ 26 | - driver.run() 27 | - for (_, driver) in tools.sorted_kvs(self.__drivers) 28 | - ]) 29 | +# await asyncio.gather(*[ 30 | +# driver.run() 31 | +# for (_, driver) in tools.sorted_kvs(self.__drivers) 32 | +# ]) 33 | + await asyncio.Event().wait() 34 | 35 | async def cleanup(self) -> None: 36 | for driver in self.__drivers.values(): 37 | -- 38 | 2.34.1.windows.1 39 | 40 | -------------------------------------------------------------------------------- /patches/bullseye/0002-Support-for-old-version-python-3.patch: -------------------------------------------------------------------------------- 1 | From da823e27ec906cc4c6bed4ace7913fd37bec72cc Mon Sep 17 00:00:00 2001 2 | From: xe5700 <9338143+xe5700@users.noreply.github.com> 3 | Date: Thu, 19 May 2022 18:56:34 +0800 4 | Subject: [PATCH 1/1] Support for old version python 3 5 | 6 | --- 7 | htserver.py | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/kvmd/htserver.py b/kvmd/htserver.py 11 | index c24837d..4a3c306 100644 12 | --- a/kvmd/htserver.py 13 | +++ b/kvmd/htserver.py 14 | @@ -297,7 +297,7 @@ class HttpServer: 15 | shutdown_timeout=1, 16 | access_log_format=access_log_format, 17 | print=self.__run_app_print, 18 | - loop=asyncio.get_event_loop(), 19 | + #loop=asyncio.get_event_loop(), 20 | ) 21 | 22 | # ===== 23 | -- 24 | 2.34.1.windows.1 25 | 26 | -------------------------------------------------------------------------------- /patches/custom/old-kernel-msd/0001-Revert-force-eject-feature-to-unlock-helper.patch: -------------------------------------------------------------------------------- 1 | From 3d137882ac38ac046b7d09cada1883b304b04319 Mon Sep 17 00:00:00 2001 2 | From: xe5700 <9338143+xe5700@users.noreply.github.com> 3 | Date: Fri, 20 May 2022 18:34:21 +0800 4 | Subject: [PATCH] Revert force eject feature to unlock helper 5 | 6 | --- 7 | kvmd/aiohelpers.py | 31 ++++++++++++----- 8 | kvmd/apps/otg/__init__.py | 3 +- 9 | kvmd/apps/otgmsd/__init__.py | 25 +++++++++++++- 10 | kvmd/helpers/unlock/__init__.py | 58 ++++++++++++++++++++++++++++++++ 11 | kvmd/helpers/unlock/__main__.py | 24 +++++++++++++ 12 | kvmd/plugins/msd/otg/__init__.py | 19 ++++++++--- 13 | kvmd/plugins/msd/otg/drive.py | 5 +-- 14 | 7 files changed, 145 insertions(+), 20 deletions(-) 15 | create mode 100644 kvmd/helpers/unlock/__init__.py 16 | create mode 100644 kvmd/helpers/unlock/__main__.py 17 | 18 | diff --git a/kvmd/aiohelpers.py b/kvmd/aiohelpers.py 19 | index 6357764c..37a5d4b9 100644 20 | --- a/kvmd/aiohelpers.py 21 | +++ b/kvmd/aiohelpers.py 22 | @@ -40,11 +40,26 @@ async def remount(name: str, base_cmd: List[str], rw: bool) -> bool: 23 | ] 24 | logger.info("Remounting %s storage to %s: %s ...", name, mode.upper(), cmd) 25 | try: 26 | - proc = await aioproc.log_process(cmd, logger) 27 | - if proc.returncode != 0: 28 | - assert proc.returncode is not None 29 | - raise subprocess.CalledProcessError(proc.returncode, cmd) 30 | - except Exception as err: 31 | - logger.error("Can't remount %s storage: %s", name, tools.efmt(err)) 32 | - return False 33 | - return True 34 | + await _run_helper(cmd) 35 | + except Exception: 36 | + logger.error("Can't remount internal storage") 37 | + raise 38 | + 39 | + 40 | +async def unlock_drive(base_cmd: List[str]) -> None: 41 | + logger = get_logger(0) 42 | + logger.info("Unlocking the drive ...") 43 | + try: 44 | + await _run_helper(base_cmd) 45 | + except Exception: 46 | + logger.error("Can't unlock the drive") 47 | + raise 48 | + 49 | + 50 | +# ===== 51 | +async def _run_helper(cmd: List[str]) -> None: 52 | + logger = get_logger(0) 53 | + logger.info("Executing helper %s ...", cmd) 54 | + proc = await aioproc.log_process(cmd, logger) 55 | + if proc.returncode != 0: 56 | + raise MsdError(f"Error while helper execution: pid={proc.pid}; retcode={proc.returncode}") 57 | diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py 58 | index cbf7a197..d0ed0554 100644 59 | --- a/kvmd/apps/otg/__init__.py 60 | +++ b/kvmd/apps/otg/__init__.py 61 | @@ -182,7 +182,6 @@ class _GadgetConfig: 62 | _chown(join(func_path, "lun.0/cdrom"), user) 63 | _chown(join(func_path, "lun.0/ro"), user) 64 | _chown(join(func_path, "lun.0/file"), user) 65 | - _chown(join(func_path, "lun.0/forced_eject"), user) 66 | _symlink(func_path, join(self.__profile_path, func)) 67 | name = ("Mass Storage Drive" if self.__msd_instance == 0 else f"Extra Drive #{self.__msd_instance}") 68 | self.__create_meta(func, name) 69 | @@ -291,7 +290,7 @@ def _cmd_stop(config: Section) -> None: 70 | logger.info("Disabling gadget %r ...", config.otg.gadget) 71 | _write(join(gadget_path, "UDC"), "\n") 72 | 73 | - _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), optional=True) 74 | + _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), True) 75 | 76 | profile_path = join(gadget_path, usb.G_PROFILE) 77 | for func in os.listdir(profile_path): 78 | diff --git a/kvmd/apps/otgmsd/__init__.py b/kvmd/apps/otgmsd/__init__.py 79 | index f57b3107..78f8e3c7 100644 80 | --- a/kvmd/apps/otgmsd/__init__.py 81 | +++ b/kvmd/apps/otgmsd/__init__.py 82 | @@ -21,12 +21,15 @@ 83 | 84 | 85 | import os 86 | +import signal 87 | import errno 88 | import argparse 89 | 90 | from typing import List 91 | from typing import Optional 92 | 93 | +import psutil 94 | + 95 | from ...validators.basic import valid_bool 96 | from ...validators.basic import valid_int_f0 97 | from ...validators.os import valid_abs_file 98 | @@ -56,6 +59,21 @@ def _set_param(gadget: str, instance: int, param: str, value: str) -> None: 99 | raise 100 | 101 | 102 | +def _unlock() -> None: 103 | + # https://github.com/torvalds/linux/blob/3039fad/drivers/usb/gadget/function/f_mass_storage.c#L2924 104 | + found = False 105 | + for proc in psutil.process_iter(): 106 | + attrs = proc.as_dict(attrs=["name", "exe", "pid"]) 107 | + if attrs.get("name") == "file-storage" and not attrs.get("exe"): 108 | + try: 109 | + proc.send_signal(signal.SIGUSR1) 110 | + found = True 111 | + except Exception as err: 112 | + raise SystemExit(f"Can't send SIGUSR1 to MSD kernel thread with pid={attrs['pid']}: {err}") 113 | + if not found: 114 | + raise SystemExit("Can't find MSD kernel thread") 115 | + 116 | + 117 | # ===== 118 | def main(argv: Optional[List[str]]=None) -> None: 119 | (parent_parser, argv, config) = init( 120 | @@ -70,6 +88,8 @@ def main(argv: Optional[List[str]]=None) -> None: 121 | ) 122 | parser.add_argument("-i", "--instance", default=0, type=valid_int_f0, 123 | metavar="", help="Drive instance (0 for KVMD drive)") 124 | + parser.add_argument("--unlock", action="store_true", 125 | + help="Send SIGUSR1 to MSD kernel thread") 126 | parser.add_argument("--set-cdrom", default=None, type=valid_bool, 127 | metavar="<1|0|yes|no>", help="Set CD-ROM flag") 128 | parser.add_argument("--set-rw", default=None, type=valid_bool, 129 | @@ -89,8 +109,11 @@ def main(argv: Optional[List[str]]=None) -> None: 130 | set_param = (lambda param, value: _set_param(config.otg.gadget, options.instance, param, value)) 131 | get_param = (lambda param: _get_param(config.otg.gadget, options.instance, param)) 132 | 133 | + if options.unlock: 134 | + _unlock() 135 | + 136 | if options.eject: 137 | - set_param("forced_eject", "") 138 | + set_param("file", "") 139 | 140 | if options.set_cdrom is not None: 141 | set_param("cdrom", str(int(options.set_cdrom))) 142 | diff --git a/kvmd/helpers/unlock/__init__.py b/kvmd/helpers/unlock/__init__.py 143 | new file mode 100644 144 | index 00000000..140e0e7c 145 | --- /dev/null 146 | +++ b/kvmd/helpers/unlock/__init__.py 147 | @@ -0,0 +1,58 @@ 148 | +# ========================================================================== # 149 | +# # 150 | +# KVMD - The main PiKVM daemon. # 151 | +# # 152 | +# Copyright (C) 2018-2022 Maxim Devaev # 153 | +# # 154 | +# This program is free software: you can redistribute it and/or modify # 155 | +# it under the terms of the GNU General Public License as published by # 156 | +# the Free Software Foundation, either version 3 of the License, or # 157 | +# (at your option) any later version. # 158 | +# # 159 | +# This program is distributed in the hope that it will be useful, # 160 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of # 161 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 162 | +# GNU General Public License for more details. # 163 | +# # 164 | +# You should have received a copy of the GNU General Public License # 165 | +# along with this program. If not, see . # 166 | +# # 167 | +# ========================================================================== # 168 | + 169 | + 170 | +import sys 171 | +import signal 172 | + 173 | +import psutil 174 | + 175 | + 176 | +# ===== 177 | +_PROCESS_NAME = "file-storage" 178 | + 179 | + 180 | +# ===== 181 | +def _log(msg: str) -> None: 182 | + print(msg, file=sys.stderr) 183 | + 184 | + 185 | +def _unlock() -> None: 186 | + # https://github.com/torvalds/linux/blob/3039fad/drivers/usb/gadget/function/f_mass_storage.c#L2924 187 | + found = False 188 | + for proc in psutil.process_iter(): 189 | + attrs = proc.as_dict(attrs=["name", "exe", "pid"]) 190 | + if attrs.get("name") == _PROCESS_NAME and not attrs.get("exe"): 191 | + _log(f"Sending SIGUSR1 to MSD {_PROCESS_NAME!r} kernel thread with pid={attrs['pid']} ...") 192 | + try: 193 | + proc.send_signal(signal.SIGUSR1) 194 | + found = True 195 | + except Exception as err: 196 | + raise SystemExit(f"Can't send SIGUSR1 to MSD kernel thread with pid={attrs['pid']}: {err}") 197 | + if not found: 198 | + raise SystemExit(f"Can't find MSD kernel thread {_PROCESS_NAME!r}") 199 | + 200 | + 201 | +# ===== 202 | +def main() -> None: 203 | + if len(sys.argv) != 2 or sys.argv[1] != "unlock": 204 | + raise SystemExit(f"Usage: {sys.argv[0]} [unlock]") 205 | + _unlock() 206 | diff --git a/kvmd/helpers/unlock/__main__.py b/kvmd/helpers/unlock/__main__.py 207 | new file mode 100644 208 | index 00000000..3849d1b9 209 | --- /dev/null 210 | +++ b/kvmd/helpers/unlock/__main__.py 211 | @@ -0,0 +1,24 @@ 212 | +# ========================================================================== # 213 | +# # 214 | +# KVMD - The main PiKVM daemon. # 215 | +# # 216 | +# Copyright (C) 2018-2022 Maxim Devaev # 217 | +# # 218 | +# This program is free software: you can redistribute it and/or modify # 219 | +# it under the terms of the GNU General Public License as published by # 220 | +# the Free Software Foundation, either version 3 of the License, or # 221 | +# (at your option) any later version. # 222 | +# # 223 | +# This program is distributed in the hope that it will be useful, # 224 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of # 225 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 226 | +# GNU General Public License for more details. # 227 | +# # 228 | +# You should have received a copy of the GNU General Public License # 229 | +# along with this program. If not, see . # 230 | +# # 231 | +# ========================================================================== # 232 | + 233 | + 234 | +from . import main 235 | +main() 236 | diff --git a/kvmd/plugins/msd/otg/__init__.py b/kvmd/plugins/msd/otg/__init__.py 237 | index 409b899a..1342c6b4 100644 238 | --- a/kvmd/plugins/msd/otg/__init__.py 239 | +++ b/kvmd/plugins/msd/otg/__init__.py 240 | @@ -140,6 +140,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 241 | storage_path: str, 242 | 243 | remount_cmd: List[str], 244 | + unlock_cmd: List[str], 245 | 246 | initial: Dict, 247 | 248 | @@ -154,6 +155,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 249 | self.__meta_path = os.path.join(self.__storage_path, "meta") 250 | 251 | self.__remount_cmd = remount_cmd 252 | + self.__unlock_cmd = unlock_cmd 253 | 254 | self.__initial_image: str = initial["image"] 255 | self.__initial_cdrom: bool = initial["cdrom"] 256 | @@ -178,10 +180,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 257 | 258 | "storage": Option("/var/lib/kvmd/msd", type=valid_abs_dir, unpack_as="storage_path"), 259 | 260 | - "remount_cmd": Option([ 261 | - "/usr/bin/sudo", "--non-interactive", 262 | - "/usr/bin/kvmd-helper-otgmsd-remount", "{mode}", 263 | - ], type=valid_command), 264 | + "remount_cmd": Option([*sudo, "/usr/bin/kvmd-helper-otgmsd-remount", "{mode}"], type=valid_command), 265 | + "unlock_cmd": Option([*sudo, "/usr/bin/kvmd-helper-otgmsd-unlock", "unlock"], type=valid_command), 266 | 267 | "initial": { 268 | "image": Option("", type=valid_printable_filename, if_empty=""), 269 | @@ -241,6 +241,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 270 | async def reset(self) -> None: 271 | async with self.__state.busy(check_online=False): 272 | try: 273 | + await self.__unlock_drive() 274 | self.__drive.set_image_path("") 275 | self.__drive.set_rw_flag(False) 276 | self.__drive.set_cdrom_flag(False) 277 | @@ -290,12 +291,15 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 278 | if not os.path.exists(self.__state.vd.image.path): 279 | raise MsdUnknownImageError() 280 | 281 | + await self.__unlock_drive() 282 | self.__drive.set_cdrom_flag(self.__state.vd.cdrom) 283 | self.__drive.set_image_path(self.__state.vd.image.path) 284 | 285 | else: 286 | if not (self.__state.vd.connected or self.__drive.get_image_path()): 287 | raise MsdDisconnectedError() 288 | + 289 | + await self.__unlock_drive() 290 | self.__drive.set_image_path("") 291 | 292 | self.__state.vd.connected = connected 293 | @@ -474,6 +478,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 294 | if os.path.exists(path): 295 | logger.info("Setting up initial image %r ...", self.__initial_image) 296 | try: 297 | + await self.__unlock_drive() 298 | self.__drive.set_cdrom_flag(self.__initial_cdrom) 299 | self.__drive.set_image_path(path) 300 | except Exception: 301 | @@ -541,4 +546,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes 302 | 303 | async def __remount_storage(self, rw: bool) -> None: 304 | if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)): 305 | - raise MsdError("Can't execute remount helper") 306 | + pass 307 | + #raise MsdError("Can't execute remount helper") 308 | + 309 | + async def __unlock_drive(self) -> None: 310 | + await helpers.unlock_drive(self.__unlock_cmd) 311 | \ No newline at end of file 312 | diff --git a/kvmd/plugins/msd/otg/drive.py b/kvmd/plugins/msd/otg/drive.py 313 | index 11af7f81..ee54e5e9 100644 314 | --- a/kvmd/plugins/msd/otg/drive.py 315 | +++ b/kvmd/plugins/msd/otg/drive.py 316 | @@ -53,10 +53,7 @@ class Drive: 317 | # ===== 318 | 319 | def set_image_path(self, path: str) -> None: 320 | - if path: 321 | - self.__set_param("file", path) 322 | - else: 323 | - self.__set_param("forced_eject", "") 324 | + self.__set_param("file", path) 325 | 326 | def get_image_path(self) -> str: 327 | return self.__get_param("file") 328 | -- 329 | 2.34.1.windows.1 330 | 331 | -------------------------------------------------------------------------------- /patches/custom/old-kernel-msd/apply.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | APP_PATH=$(readlink -f $(dirname $0)) 3 | echo "-> Apply patches" 4 | cd /usr/lib/python3.10/site-packages/ 5 | git apply ${APP_PATH}/*.patch 6 | cd ${APP_PATH} 7 | echo "-> Add otgmsd unlock link" 8 | cp kvmd-helper-otgmsd-unlock /usr/bin/ 9 | echo "-> Add sudoer" 10 | echo "kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-unlock" >> /etc/sudoers.d/99_kvmd 11 | echo "-> Apply old kernel msd patch done." -------------------------------------------------------------------------------- /patches/custom/old-kernel-msd/kvmd-helper-otgmsd-unlock: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/python 2 | # KVMD-ARMBIAN 3 | 4 | from kvmd.helpers.unlock import main 5 | 6 | if __name__ == "__main__": 7 | main() 8 | -------------------------------------------------------------------------------- /pi-temp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script: pi-temp.sh 3 | # This should work on both arch and ubuntu/debian 4 | # Purpose: Display the ARM CPU and GPU temperatures of Raspberry Pi 2/3/4/Zero 5 | # Edited by: srepac 6 | # convert C to F and show both temps in output 7 | # ------------------------------------------------------- 8 | # install basic calculator for conversions 9 | if [ `which bc | wc -l` -le 0 ]; then 10 | sudo apt-get install bc -y 11 | fi 12 | 13 | MODEL=$(tr -d '\0' ${cpuC}'C\t${cpuF}'F\n" 33 | printf "GPU => ${GPUTEMP}'C\t${gpuF}'F\n" 34 | 35 | MHZ=`sudo cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq` 36 | #echo "CPU0 MHZ: $((MHZ/1000))" 37 | 38 | #echo "CPU (All) MHz: " 39 | count=0 40 | for i in `sudo cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq`; do 41 | cpu=`echo "scale=2; $i / 1000" | bc` 42 | echo "CPU${count} MHz: $cpu" 43 | count=`expr $count + 1` 44 | done 45 | 46 | sudo vcgencmd measure_volts | sed 's/volt=/vCore /g' 47 | sudo vcgencmd get_throttled 48 | -------------------------------------------------------------------------------- /pikvm-info: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Put this file into /usr/local/bin then you can run pikvm-info anywhere while logged in to Pi-KVM terminal/ssh 3 | # 4 | uptime 5 | NAME=$( grep -E ^NAME= /etc/os-release | cut -d'"' -f2 ) 6 | echo "Host OS: $NAME `uname -r` `uname -m`" 7 | 8 | # pistat is built-in to pi-kvm (/usr/local/bin) 9 | pistat && echo 10 | 11 | printf "%-32s\t%s\n" "Version" "Package-Name" "---------------------------------" "-----------------------------" 12 | 13 | get-packages() { 14 | printf "%-32s\t%s\n" $( python3 -V | awk '{print $2}' ) "python3" 15 | GPIODVER=$( gpioinfo -v | grep libgpiod | awk '{print $NF}' | sed 's/v//g' ) 16 | printf "%-32s\t%s\n" $GPIODVER "libgpiod" 17 | 18 | if [[ $( echo $NAME | cut -d' ' -f1 ) == "Arch" ]]; then 19 | if [ ! -e $TMPFILE ]; then 20 | pacman -Q | awk '{print $2, $1}' > $TMPFILE 21 | fi 22 | KVMDVER=$( grep kvmd-platform $TMPFILE | awk '{print $1}' ) 23 | PLATFORM=$( grep kvmd-platform $TMPFILE | awk '{print $2}' | cut -d'-' -f1,2,3,4,5 ) 24 | printf "%-32s\t%s\n" $KVMDVER $PLATFORM 25 | OPENSSH="openssh" 26 | 27 | else 28 | KVMDPLAT=$( grep kvmd-platform /var/cache/kvmd/installed_ver.txt | awk '{print $4}' | awk -F\/ '{print $NF}' | tail -1 ) 29 | if [[ "$KVMDPLAT" != "" ]]; then 30 | KVMDVER=$( echo $KVMDPLAT | cut -d'-' -f6 ) 31 | PLATFORM=$( echo $KVMDPLAT | cut -d'-' -f1,2,3,4,5 ) 32 | printf "%-32s\t%s\n" $KVMDVER $PLATFORM 33 | fi 34 | 35 | OPENSSH="openssh-server" 36 | 37 | if [ ! -e $TMPFILE ]; then 38 | apt list 2> /dev/null | grep -E 'upgradable|installed' | grep -v ^$ | awk '{print $2, $1}' > $TMPFILE 39 | fi 40 | 41 | fi 42 | USTREAMVER=$( /usr/bin/ustreamer -v ) 43 | printf "%-32s\t%s\n" $USTREAMVER "ustreamer" 44 | #JANUSVER=$( /usr/bin/janus -V | tail -1 | awk '{print $NF}' ) 45 | JANUSVER=$( /usr/bin/janus -V | grep -E 'version|janus' | awk '{print $NF}' | sed -e 's|(||g' -e 's|)||g' ) 46 | printf "%-32s\t%s\n" $JANUSVER "janus" 47 | TTYDVER=$( ttyd -v | awk '{print $NF}' ) 48 | printf "%-32s\t%s\n" $TTYDVER "ttyd" 49 | } # end get-packages 50 | 51 | 52 | TMPFILE="/tmp/pacmanquery" 53 | 54 | get-packages 55 | PACKAGES="v4l janus pikvm kvmd ustreamer nginx wpa wireless python3/stable python$ firmware raspberrypi tailscale ttyd $OPENSSH" 56 | for PKG in $( echo $PACKAGES ); do 57 | if [ $(grep -c $PKG $TMPFILE) -gt 0 ]; then 58 | printf "%-32s\t%s\n" $(grep $PKG $TMPFILE | sed 's/-[1-9]+//g' | awk -F\/ '{print $1}' ) 59 | fi 60 | done | sort -u | sort -k2 61 | -------------------------------------------------------------------------------- /pistat: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copy this into /usr/local/bin/pistat 3 | # This version will show Pi RAM size and will work in Raspbian also 4 | # sample output: Raspberry Pi 4 Model B 1.2 4GB 5 | if [ ! -e /usr/bin/vcgencmd ]; then 6 | if [ -e /opt/vc/bin/vcgencmd ]; then 7 | ln -sf /opt/vc/bin/vcgencmd /usr/bin/vcgencmd 8 | fi 9 | fi 10 | 11 | if [[ $( whoami ) == "root" ]]; then 12 | VCGENCMD="/usr/bin/vcgencmd" 13 | else 14 | VCGENCMD="sudo /usr/bin/vcgencmd" 15 | fi 16 | 17 | if [ -t 1 ]; then 18 | color_comment=$(echo -e '\e[1;30m') 19 | color_value=$(echo -e '\e[1;35m') 20 | color_ok=$(echo -e '\e[1;32m') 21 | color_fail=$(echo -e '\e[1;31m') 22 | color_reset=$(echo -e '\e[0m') 23 | else 24 | color_comment="" 25 | color_value="" 26 | color_ok="" 27 | color_fail="" 28 | color_reset="" 29 | fi 30 | no="${color_ok}no${color_reset}" 31 | yes="${color_fail}yes${color_reset}" 32 | 33 | # code section to get Pi RAM size 34 | RAMGB=$( $VCGENCMD get_config total_mem | awk -F= '{NUM = $2} END { if ( NUM < 1024 ) print NUM "MB"; else print (NUM / 1024) "GB"; }' ) 35 | if [ -f /proc/device-tree/model ]; then 36 | echo "${color_reset}# $(tr -d '\0' < /proc/device-tree/model) ${RAMGB}${color_reset}" 37 | else 38 | RAM=$( echo "( $( free -m | grep Mem: | awk '{print $2}' ) + 340 ) / 1024" | bc ) 39 | TMPFILE="/tmp/dmidecode.system"; rm -f $TMPFILE 40 | dmidecode -t system > $TMPFILE 41 | echo "${color_reset}# $(grep -i version $TMPFILE | cut -d':' -f2 | sed 's/^ //g') $(grep Product $TMPFILE | cut -d: -f2 | cut -d\( -f1 | sed 's/^ //g') ${RAM}GB${color_reset}" 42 | fi 43 | 44 | if [ -e /proc/device-tree/serial-number ]; then 45 | echo "${color_reset}Serial number: $(tr -d '\0' < /proc/device-tree/serial-number) ${color_reset}" 46 | fi 47 | 48 | echo 49 | if [ -e /sys/class/thermal/thermal_zone0/temp ]; then 50 | echo "CPU temp: ${color_value}$(echo "scale=2; $( /dev/null | egrep -A 2 'coretemp|k10temp' | egrep '^temp|^Core' | awk '{print $2}' | sed -e 's/°C//g' -e 's/+//g') 54 | echo "CPU temp: ${color_value}${CPUTEMP}'C${color_reset}" 55 | gpu_temp=$(sensors 2> /dev/null | grep -A 2 radeon | grep ^temp | awk '{print $2}' | sed -e 's/°C//g' -e 's/+//g') 56 | fi 57 | echo "GPU temp: ${color_value}${gpu_temp}'C${color_reset}" 58 | 59 | flags=$($VCGENCMD get_throttled) 60 | flags="${flags#*=}" 61 | echo 62 | echo "Throttled flags: ${color_value}${flags}${color_reset}" 63 | 64 | echo 65 | echo -n "Throttled now: " 66 | ((($flags&0x4)!=0)) && echo "${yes}" || echo "${no}" 67 | 68 | echo -n "Throttled past: " 69 | ((($flags&0x40000)!=0)) && echo "${yes}" || echo "${no}" 70 | 71 | echo 72 | echo -n "Undervoltage now: " 73 | ((($flags&0x1)!=0)) && echo "${yes}" || echo "${no}" 74 | 75 | echo -n "Undervoltage past: " 76 | ((($flags&0x10000)!=0)) && echo "${yes}" || echo "${no}" 77 | 78 | echo 79 | echo -n "Frequency capped now: " 80 | ((($flags&0x2)!=0)) && echo "${yes}" || echo "${no}" 81 | 82 | echo -n "Frequency capped past: " 83 | ((($flags&0x20000)!=0)) && echo "${yes}" || echo "${no}" 84 | 85 | # ===== 86 | # https://stackoverflow.com/questions/13889659/read-a-file-by-bytes-in-bash 87 | read8() { 88 | local _r8_var=${1:-OUTBIN} _r8_car LANG=C IFS= 89 | read -r -d '' -n 1 _r8_car 90 | printf -v $_r8_var %d "'"$_r8_car 91 | } 92 | read16() { 93 | local _r16_var=${1:-OUTBIN} _r16_lb _r16_hb 94 | read8 _r16_hb && read8 _r16_lb 95 | printf -v $_r16_var %d $(( _r16_hb<<8 | _r16_lb )) 96 | } 97 | read32() { 98 | local _r32_var=${1:-OUTBIN} _r32_lw _r32_hw 99 | read16 _r32_hw && read16 _r32_lw 100 | printf -v $_r32_var %d $(( _r32_hw<<16| _r32_lw )) 101 | } 102 | 103 | power=/proc/device-tree/chosen/power 104 | if [ -f $power/max_current ]; then 105 | read32 value < $power/max_current 106 | echo 107 | echo "Max power supply current:" ${color_value}$(bc <<< "scale=1; $value / 1000")A${color_reset} 108 | fi 109 | if [ -f $power/usb_max_current_enable ]; then 110 | read32 value < $power/usb_max_current_enable 111 | echo "USB max current enabled: " ${color_value}$(test $value -ne 0 && echo yes || echo no)${color_reset} 112 | fi 113 | if [ -f $power/usb_over_current_detected ]; then 114 | read32 value < $power/usb_over_current_detected 115 | echo "USB overcurrent detected:" $(test $value -ne 0 && echo $yes || echo $no) 116 | fi 117 | -------------------------------------------------------------------------------- /tshoot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Written by srepac for PiKVM project 3 | # 4 | # This script checks what platform is installed on PiKVM (lots of users install wrong platform) 5 | # Also, it will then check to make sure capture card is connected properly to the correct port 6 | # ... and report any issues and possible resolutions. 7 | # 8 | WHOAMI=`whoami` 9 | if [[ "$WHOAMI" != "root" ]]; then 10 | echo "$WHOAMI, you must be root to run this." 11 | exit 1 12 | fi 13 | 14 | errors=0 15 | ARCHLINUX=$( grep PRETTY /etc/os-release | grep -c Arch ) 16 | if [ $ARCHLINUX -eq 1 ]; then 17 | SEARCH="1-1.4" # use only port 1-1.4 on arch linux pikvm 18 | PLATFORM=$( pacman -Q | grep kvmd-platform | cut -d'-' -f3,4,5 | sed 's/ / /g' ) 19 | else 20 | #SEARCH="MACRO" # any port on anything other than arch pikvm 21 | SEARCH="UVC" # any port on anything other than arch pikvm 22 | # Show kvmd-platform version for raspbian pikvm on rpi4 23 | v2v3=$( grep platform /var/cache/kvmd/installed_ver.txt | cut -d'-' -f3 | tail -1 ) 24 | if [[ $( grep video /etc/udev/rules.d/99-kvmd.rules | grep -c hdmiusb ) -gt 0 ]]; then 25 | platform="v2-hdmiusb-rpi4" 26 | else 27 | platform="${v2v3}-hdmi-rpi4" 28 | fi 29 | PLATFORM="$platform $( grep kvmd-platform /var/cache/kvmd/installed_ver.txt | tail -1 | awk '{print $4}' | awk -F\- '{print $NF}')" 30 | fi 31 | 32 | echo "kvmd-platform/installed version: ${PLATFORM}" 33 | echo 34 | 35 | echo "+ Checking for KB, VID, and MOUSE symlinks..." 36 | KVMDEVS="/tmp/kvmdevs.txt"; rm -f $KVMDEVS 37 | ls -l /dev/kvmd* > $KVMDEVS # check for kb/mouse/video 38 | STATUS="Found" 39 | if [ $( grep -c hid' ' $KVMDEVS ) -ge 1 ]; then 40 | STATUS="$STATUS Serial keyboard mouse" 41 | else 42 | if [ $( grep -c keyboard $KVMDEVS ) -ge 1 ]; then 43 | STATUS="$STATUS OTG keyboard" 44 | else 45 | STATUS="$STATUS " 46 | let errors=errors+1 47 | fi 48 | if [ $( grep -c mouse $KVMDEVS ) -ge 1 ]; then 49 | STATUS="$STATUS mouse" 50 | else 51 | STATUS="$STATUS " 52 | let errors=errors+1 53 | fi 54 | fi 55 | if [ $( grep -c video $KVMDEVS ) -ge 1 ]; then 56 | STATUS="$STATUS + video" 57 | else 58 | STATUS="$STATUS + " 59 | let errors=errors+1 60 | fi 61 | echo "+ $STATUS"; cat $KVMDEVS 62 | echo 63 | 64 | echo "+ Checking for capture device..." 65 | HDMIUSB=$( grep video /etc/udev/rules.d/99-kvmd.rules | grep -c hdmiusb ) 66 | if [ $HDMIUSB -ge 1 ]; then 67 | # Make sure MACROSILICON controller on USB-HDMI is plugged in to 1-1.4 68 | # show the last MACROSILICON entry in case user moved the usb dongle 69 | # ... this way, script will always give the most recent check 70 | # ... NOTE: You can move usb dongle anytime without powering down Pi 71 | if [ ! -e /var/log/dmesg ]; then 72 | USBHDMI=$( dmesg | grep -i $SEARCH | grep 'usb ' | tail -1 ) 73 | else 74 | USBHDMI=$( grep -i $SEARCH /var/log/dmesg | grep 'usb ' | tail -1 ) 75 | fi 76 | if [[ $( echo $USBHDMI | grep -c $SEARCH ) -gt 0 ]]; then 77 | printf "+ USB HDMI dongle is connected properly\n" 78 | echo "$USBHDMI" 79 | echo 80 | echo "*NOTE: USB HDMI max supported resolution is 4k 30Hz downsampled to 1080p for PiKVM" 81 | else 82 | printf "x Pls put USB HDMI dongle to bottom USB2.0 port on Pi4 OR any USB port on non RPi SBC.\n" 83 | let errors=errors+1 84 | fi 85 | else 86 | # PiKVM uses CSI bridge capture so check to make sure it's in the CAMERA port 87 | # ... NOTE: Poweroff Pi before moving CSI cable 88 | CSI=$( dmesg | grep tc35 | grep -E -v -i 'Dependency|Modules' ) 89 | if [[ $( echo $CSI | grep -c not ) -gt 0 ]]; then 90 | printf "x Check CSI cable is properly connected to Pi Camera port.\n" 91 | let errors=errors+1 92 | else 93 | printf "+ CSI 2 HDMI bridge is connected properly\n" 94 | fi 95 | echo "$CSI"; echo 96 | 97 | echo "+ Checking for what resolution the target is sending..." 98 | DVTIMINGS="/tmp/dvtimings.txt" 99 | v4l2-ctl --query-dv-timings > $DVTIMINGS # look for active width/height and pixelclock 100 | HEIGHT=$( grep Active $DVTIMINGS | grep height | awk '{print $NF}') 101 | WIDTH=$( grep Active $DVTIMINGS | grep width | awk '{print $NF}') 102 | if [[ $HEIGHT -eq 0 && $WIDTH -eq 0 ]]; then 103 | echo "+ < NO SIGNAL > from target. Check/replace HDMI cable or power ON target." 104 | let errors=errors+1 105 | else 106 | HZ=$( grep Pixelclock $DVTIMINGS | awk -F\( '{print $2}' | cut -d' ' -f1 ) 107 | echo "+ Active Target resolution: ${WIDTH}x${HEIGHT} ${HZ}Hz" 108 | cat $DVTIMINGS | grep -E 'width|height|Pixel' | grep -v Total 109 | fi 110 | 111 | echo 112 | echo "*NOTE1: PiKVM V2/V3 CSI builds max supported resolution: 720p 60Hz + 1080p 50Hz" 113 | echo "*NOTE2: PiKVM V4 max supported resolution: 1080p 60Hz + 1920x1200 60Hz" 114 | echo "*NOTE3: BliKVM v1/v2 max supported resolution: 1080p 60Hz + 1920x1200 60Hz" 115 | echo "*NOTE4: BliKVM v3 HAT max supported resolution: 720p 60Hz + 1080p 50Hz" 116 | echo "*NOTE5: Geekworm x652/x680 (v1.5) max supported resolution: 1080p 60Hz + 1920x1200 60Hz" 117 | echo "*NOTE6: Geekworm x650, old x680, and A3/A4/A8 max supported resolution: 720p 60Hz + 1080p 50Hz" 118 | echo "*NOTE7: Geekworm x635 max supported resolution: 720p 30Hz + 1080p 30Hz" 119 | fi 120 | printf "\n--- KNOW THE LIMITS AND MAKE SURE TARGET RESOLUTIONS STAY WITHIN THOSE LIMITS ---\n" 121 | 122 | echo 123 | if [ $errors -gt 0 ]; then 124 | echo "-> Found $errors error(s). Please fix then try again." 125 | else 126 | echo "Congratulations, $errors errors found. If you are having issues with K V or M, then check hardware/cables." 127 | fi 128 | -------------------------------------------------------------------------------- /uninstall-pikvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script written by @srepac as requested by @Mark Jim 3 | # This script perform uninstall of pikvm from raspbian. This performs the following: 4 | # 5 | # 1. Stop/disable kvmd services 6 | # 2. Remove the main kvmd package files based on what was installed (see /var/cache/kvmd/installed_ver.txt file) 7 | # 8 | # CAVEATS: 9 | # 1. Script does not remove /usr/bin/ttyd (webterm), /usr/bin/ustreamer, /usr/bin/ustreamer-dump 10 | # 2. Script does not remove /usr/bin/janus (webrtc) and all its dependent files also 11 | # 3. Script does not remove any directories; you may end up with empty directories 12 | ### 13 | # CHANGELOG: 14 | # 1.0 20220218 created script 15 | # 1.1 20220220 confirm uninstall and add -f option to perform destructive commands 16 | # 1.2 20220225 restore original /etc/motd 17 | # 2.0 20220225 save custom configs for possible restores during re-install later 18 | VER=2.0 19 | 20 | save-configs() { ### save config files inside /etc/kvmd in case user re-installs pikvm later 21 | if [[ $f_flag -eq 1 ]]; then 22 | printf "\n-> Saving config files\n" 23 | 24 | # Save passwd files used by PiKVM 25 | cp /etc/kvmd/htpasswd /etc/kvmd/htpasswd.save 26 | cp /etc/kvmd/ipmipasswd /etc/kvmd/ipmipasswd.save 27 | cp /etc/kvmd/vncpasswd /etc/kvmd/vncpasswd.save 28 | 29 | # Save webUI name and overrides 30 | cp /etc/kvmd/meta.yaml /etc/kvmd/meta.yaml.save 31 | cp /etc/kvmd/override.yaml /etc/kvmd/override.yaml.save 32 | cp /etc/kvmd/web.css /etc/kvmd/web.css.save 33 | 34 | # Save Janus configs 35 | #cp /etc/kvmd/janus/janus.cfg /etc/kvmd/janus/janus.cfg.save 36 | 37 | # Save sudoers.d/99_kvmd 38 | cp /etc/sudoers.d/99_kvmd /etc/sudoers.d/99_kvmd.save 39 | cp /etc/sudoers.d/custom_commands /etc/sudoers.d/custom_commands.save 40 | fi 41 | } 42 | 43 | stop-disable-kvmd() { 44 | #for i in $( systemctl | grep kvmd | grep -v var | awk '{print $1}') 45 | 46 | for i in $( systemctl | grep kvmd | grep -v var | awk '$1||$2 ~ /kvmd/ {print $2, $1}' | sed 's/loaded //g' | cut -d' ' -f1 ) 47 | do 48 | echo "-> Stopping/disabling ${i} ..." 49 | if [[ $f_flag -eq 1 ]]; then systemctl disable --now $i; fi 50 | done 51 | } # end stop-disable-kvmd 52 | 53 | # Determine what kvmd version was installed last 54 | remove-kvmd-package() { 55 | printf "\nProceeding to remove kvmd package files\n" | tee -a $LOGFILE 56 | #KVMDVER=$( egrep 'kvmd-[0-9]' $INSTLOG | awk '{print $4}' | cut -d'-' -f2 | tail -1 ) 57 | KVMDVER=$( pikvm-info | grep kvmd-platform | awk '{print $1}' ) 58 | KVMDPKG="kvmd-${KVMDVER}" 59 | 60 | echo "Uninstalling ${KVMDPKG} from this system." 61 | for file in $( tar tvfJ /var/cache/kvmd/${KVMDPKG}* | awk '{print $NF}' | grep -v '/$' ) 62 | do 63 | echo "-> Deleting /$file ..." 64 | if [[ $f_flag -eq 1 ]]; then rm /$file; fi 65 | done 66 | } # end remove-kvmd-package 67 | 68 | restore-motd() { 69 | if [[ $f_flag -eq 1 ]]; then 70 | if [ -e /etc/motd.orig ]; then cp -f /etc/motd.orig /etc/motd; fi 71 | fi 72 | cat /etc/motd 73 | } # end restore-motd 74 | 75 | are-you-sure() { 76 | invalidinput=1 77 | while [ $invalidinput -eq 1 ]; do 78 | read -p "Uninstall PiKVM from this system. Are you sure? [y/n] " SURE 79 | case $SURE in 80 | Y|y) invalidinput=0 ;; 81 | N|n) echo "Exiting."; exit 0 ;; 82 | *) echo "Invalid input. try again."; invalidinput=1 ;; 83 | esac 84 | done 85 | } # end are-you-sure fn 86 | 87 | 88 | 89 | ### MAIN STARTS HERE ### 90 | if [ -e /usr/local/bin/rw ]; then rw; fi 91 | mkdir -p /var/cache/kvmd # create directory in case it hasn't been created yet (e.g. installer hasn't been run) 92 | export INSTLOG="/var/cache/kvmd/installed_ver.txt" 93 | export LOGFILE="/var/cache/kvmd/uninstall.log"; rm -f $LOGFILE 94 | if [ ! -e $INSTLOG ]; then 95 | echo "Install log missing. Nothing to do." | tee -a $LOGFILE 96 | exit 1 97 | fi 98 | 99 | if [[ "$1" == "-f" ]]; then 100 | printf "\n*** Actually perform destructive commands option set.\n\n" 101 | f_flag=1 102 | else 103 | printf "\n*** Only SHOWING what will be performed. Re-run with -f to actually perform destructive commands.\n\n" 104 | f_flag=0 105 | fi 106 | 107 | are-you-sure 108 | save-configs | tee -a $LOGFILE 109 | stop-disable-kvmd | tee -a $LOGFILE 110 | restore-motd | tee -a $LOGFILE 111 | remove-kvmd-package | tee -a $LOGFILE 112 | if [ -e /usr/local/bin/ro ]; then ro; fi 113 | -------------------------------------------------------------------------------- /update-rpikvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ## Update script for Raspbian/Armbian 4 | # 5 | # NOTES: 6 | # 7 | # kvmd 3.291 and earlier requires libgpiod v1.6 and python 3.11 (default installer uses kvmd 3.291) 8 | # python 3.11 is required starting with kvmd 3.217 (prior to that, you could run kvmd on python 3.10) 9 | # kvmd 3.292 to 3.333 requires libgpiod v2.1 and python 3.11 10 | # kvmd 4.1 and higher requires python 3.12 11 | # kvmd 4.21 and higher requires python 3.13 12 | # 13 | ### 14 | # Updated on 20240415 1510PDT 15 | ### 16 | PIKVMREPO="https://pikvm.org/repos/rpi4" 17 | PIKVMREPO="https://files.pikvm.org/repos/arch/rpi4/" # as of 11/05/2021 18 | KVMDCACHE="/var/cache/kvmd" 19 | PKGINFO="${KVMDCACHE}/packages.txt" 20 | REPOFILE="/tmp/pikvmrepo.html"; /bin/rm -f $REPOFILE 21 | ln -sf python3 /usr/bin/python 22 | 23 | WHOAMI=$( whoami ) 24 | if [[ "$WHOAMI" != "root" ]]; then 25 | echo "$WHOAMI, you must run this as root." 26 | exit 1 27 | fi 28 | 29 | get-packages() { 30 | printf "\n-> Getting newest Pi-KVM packages from ${PIKVMREPO}\n\n" 31 | mkdir -p ${KVMDCACHE}; cd ${KVMDCACHE} 32 | wget --no-check-certificate ${PIKVMREPO} -O ${PKGINFO} 2> /dev/null 33 | 34 | # Download each of the pertinent packages for Rpi4, webterm, and the main service 35 | for pkg in `egrep 'kvmd|ustreamer' ${PKGINFO} | cut -d'"' -f2 | grep -v sig | egrep -i "kvmd-[0-9]\.|${INSTALLED_PLATFORM}|ustreamer"` 36 | do 37 | rm -f ${KVMDCACHE}/$pkg* 38 | wget --no-check-certificate ${PIKVMREPO}/$pkg -O ${KVMDCACHE}/$pkg 2> /dev/null 39 | ls -l $pkg 40 | done 41 | } # end get-packages function 42 | 43 | save-configs() { 44 | printf "\n-> Saving config files\n" 45 | cp /etc/udev/rules.d/99-kvmd.rules /etc/udev/rules.d/99-kvmd.rules.save 46 | 47 | # Save passwd files used by PiKVM 48 | cp /etc/kvmd/htpasswd /etc/kvmd/htpasswd.save 49 | cp /etc/kvmd/ipmipasswd /etc/kvmd/ipmipasswd.save 50 | cp /etc/kvmd/vncpasswd /etc/kvmd/vncpasswd.save 51 | 52 | # Save webUI name and overrides 53 | cp /etc/kvmd/meta.yaml /etc/kvmd/meta.yaml.save 54 | cp /etc/kvmd/override.yaml /etc/kvmd/override.yaml.save 55 | cp /etc/kvmd/web.css /etc/kvmd/web.css.save 56 | 57 | # Save Janus configs 58 | #cp /etc/kvmd/janus/janus.cfg /etc/kvmd/janus/janus.cfg.save 59 | 60 | # Save sudoers.d/99_kvmd 61 | cp /etc/sudoers.d/99_kvmd /etc/sudoers.d/99_kvmd.save 62 | 63 | # Save mouse settings (in case you changed move freq to 10ms from 100ms) 64 | cp /usr/share/kvmd/web/share/js/kvm/mouse.js /usr/share/kvmd/web/share/js/kvm/mouse.js.save 65 | 66 | cp /etc/kvmd/nginx/listen-https.conf /etc/kvmd/nginx/listen-https.conf.save 67 | } # end save-configs 68 | 69 | restore-configs() { 70 | printf "\n-> Restoring config files\n" 71 | cp /etc/udev/rules.d/99-kvmd.rules.save /etc/udev/rules.d/99-kvmd.rules 72 | 73 | # Restore passwd files used by PiKVM 74 | cp /etc/kvmd/htpasswd.save /etc/kvmd/htpasswd 75 | cp /etc/kvmd/ipmipasswd.save /etc/kvmd/ipmipasswd 76 | cp /etc/kvmd/vncpasswd.save /etc/kvmd/vncpasswd 77 | 78 | # Restore webUI name and overrides 79 | cp /etc/kvmd/meta.yaml.save /etc/kvmd/meta.yaml 80 | cp /etc/kvmd/override.yaml.save /etc/kvmd/override.yaml 81 | cp /etc/kvmd/web.css.save /etc/kvmd/web.css 82 | 83 | # Restore Janus configs 84 | #cp /etc/kvmd/janus/janus.cfg.save /etc/kvmd/janus/janus.cfg 85 | 86 | # Restore sudoers.d/99_kvmd 87 | cp /etc/sudoers.d/99_kvmd.save /etc/sudoers.d/99_kvmd 88 | 89 | # Restore mouse settings (in case you changed move freq to 10ms from 100ms) 90 | cp /usr/share/kvmd/web/share/js/kvm/mouse.js.save /usr/share/kvmd/web/share/js/kvm/mouse.js 91 | 92 | cp /etc/kvmd/nginx/listen-https.conf.save /etc/kvmd/nginx/listen-https.conf 93 | } # end restore-configs 94 | 95 | set-ownership() { 96 | printf "\n-> Setting ownership of /etc/kvmd/*passwd files\n" 97 | # set proper ownership of password files 98 | cd /etc/kvmd 99 | chown kvmd:kvmd htpasswd 100 | chown kvmd-ipmi:kvmd-ipmi ipmipasswd 101 | chown kvmd-vnc:kvmd-vnc vncpasswd 102 | 103 | echo ; ls -l /etc/kvmd/*passwd 104 | } # end set-ownership 105 | 106 | perform-update() { 107 | printf "\n-> Perform kvmd update function\n" 108 | CURRENTVER=$( pikvm-info | grep kvmd-platform | awk '{print $1}' ) 109 | 110 | # get latest released kvmd and kvmd-platform versions from REPO 111 | KVMDMAJOR=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-[0-9]' | cut -d'-' -f2 | cut -d'.' -f1 | uniq ) 112 | KVMDMINOR=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-[0-9]' | cut -d'-' -f2 | sed 's/^[0-9]\.//g' | sort -nr | head -1 ) 113 | KVMDVER="$KVMDMAJOR.$KVMDMINOR" 114 | 115 | KVMDFILE=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-[0-9]' | grep $KVMDVER ) 116 | 117 | KVMDPLATFORMFILE=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-platform' | grep $INSTALLED_PLATFORM | grep $KVMDVER ) 118 | 119 | PYTHONVER=$( /usr/bin/python3 -V | cut -d' ' -f2 | cut -d'.' -f1,2 ) 120 | case $PYTHONVER in 121 | "3.7"|"3.9") PYTHON=3.9; KVMDVER=3.47 ;; 122 | #"3.10") PYTHON=$PYTHONVER ;; 123 | "3.10"|"3.11") PYTHON=3.11 ;; # kvmd 3.217 and higher now uses python 3.11 path 124 | *) echo "Unsupported python version $PYTHONVER. Exiting"; exit 1;; 125 | esac 126 | 127 | function do-update() { 128 | printf "\n -> Performing update to version [ ${KVMDVER} ] now.\n" 129 | 130 | # Install new version of kvmd and kvmd-platform 131 | printf " 132 | cd / 133 | tar xfJ $KVMDCACHE/$KVMDFILE 134 | tar xfJ $KVMDCACHE/$KVMDPLATFORMFILE 135 | 136 | rm $PYTHONPACKAGES/kvmd*info* 137 | ln -sf /usr/lib/python${PYTHON}/site-packages/kvmd*info* $PYTHONPACKAGES 138 | 139 | echo Updated pikvm to kvmd-platform-$INSTALLED_PLATFORM-$KVMDVER on $( date ) >> $KVMDCACHE/installed_ver.txt 140 | " 141 | 142 | cd /; tar xfJ $KVMDCACHE/$KVMDFILE 2> /dev/null 143 | tar xfJ $KVMDCACHE/$KVMDPLATFORMFILE 2> /dev/null 144 | rm $PYTHONPACKAGES/kvmd*info* 2> /dev/null 145 | ln -sf /usr/lib/python${PYTHON}/site-packages/kvmd*info* $PYTHONPACKAGES 2> /dev/null 146 | echo "Updated pikvm to kvmd-platform-$INSTALLED_PLATFORM-$KVMDVER on $( date )" >> $KVMDCACHE/installed_ver.txt 147 | } # end do-update 148 | 149 | _libgpiodver=$( gpioinfo -v | head -1 | awk '{print $NF}' ) 150 | case $KVMDVER in 151 | $CURRENTVER) 152 | printf "\n -> Update not required. Version installed is ${CURRENTVER} and REPO version is ${KVMDVER}.\n" 153 | ;; 154 | 3.29[2-9]*|3.[3-9][0-9]*) # kvmd 3.29[2-9] to 3.3xx 155 | case $_libgpiodver in 156 | v1.6*) 157 | echo "** kvmd 3.292 and higher is not supported due to libgpiod v2.x requirement. Staying on kvmd ${CURRENTVER}" 158 | ;; 159 | v2.*) 160 | echo "libgpiod $_libgpiodver found. Performing update." 161 | do-update 162 | ;; 163 | *) 164 | echo "libgpiod $_libgpiodver found. Nothing to do." 165 | ;; 166 | esac 167 | ;; 168 | 4.*) 169 | echo "** kvmd 4.x is EXPERIMENTAL. If issues arise, please restore to previous working kvmd version. **" 170 | case $_libgpiodver in 171 | v1.6*) 172 | echo "** kvmd 3.292 and higher is not supported due to libgpiod v2.x requirement. Staying on kvmd ${CURRENTVER}" 173 | ;; 174 | v2.*) 175 | echo "libgpiod $_libgpiodver found. Performing update." 176 | do-update 177 | ### kvmd 4.1 and higher requires python3.12 path 178 | cd /lib/python3/dist-packages/ 179 | ln -sf /usr/lib/python3.12/site-packages/kvmd . 180 | ;; 181 | *) 182 | echo "libgpiod $_libgpiodver found. Nothing to do." 183 | ;; 184 | esac 185 | ;; 186 | *) 187 | do-update 188 | ;; 189 | esac 190 | } # end perform-update 191 | 192 | get-installed-platform() { 193 | INSTALLED_PLATFORM=$( grep platform $KVMDCACHE/installed_ver.txt | awk '{print $4}' | cut -d'-' -f3,4,5 | uniq ) 194 | printf "\nINSTALLED_PLATFORM: $INSTALLED_PLATFORM\n" 195 | } # 196 | 197 | build-ustreamer() { 198 | printf "\n\n-> Building ustreamer\n\n" 199 | # Install packages needed for building ustreamer source 200 | echo "apt install -y build-essential libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev janus-dev janus libdrm-dev" 201 | apt install -y build-essential libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev janus-dev janus libdrm-dev 2> /dev/null 202 | 203 | # fix refcount.h 204 | sed -i -e 's|^#include "refcount.h"$|#include "../refcount.h"|g' /usr/include/janus/plugins/plugin.h 205 | 206 | # Download ustreamer source and build it 207 | cd /tmp; rm -rf ustreamer 208 | git clone --depth=1 https://github.com/pikvm/ustreamer 209 | cd ustreamer/ 210 | make WITH_GPIO=1 WITH_SYSTEMD=1 WITH_JANUS=1 WITH_V4P=1 -j 211 | make install 212 | # kvmd service is looking for /usr/bin/ustreamer 213 | ln -sf /usr/local/bin/ustreamer* /usr/bin/ 214 | 215 | # add janus support 216 | mkdir -p /usr/lib/ustreamer/janus 217 | cp /tmp/ustreamer/janus/libjanus_ustreamer.so /usr/lib/ustreamer/janus 218 | } # end build-ustreamer 219 | 220 | update-ustreamer() { 221 | printf "\n-> Perform ustreamer update function\n" 222 | INSTALLEDVER=$( ustreamer -v ) 223 | USTREAMMINOR=$( echo $INSTALLEDVER | cut -d'.' -f2 ) 224 | REPOMINOR=$( echo $REPOVER | cut -d'.' -f2 ) 225 | echo 226 | ls -l $KVMDCACHE/ustreamer* 227 | echo "ustreamer version: $INSTALLEDVER" 228 | echo "Repo ustreamer version: $REPOVER" 229 | if [[ "$INSTALLEDVER" != "$REPOVER" ]]; then 230 | build-ustreamer 231 | echo "-> Updated ustreamer to $REPOVER on $( date )" >> $KVMDCACHE/installed_ver.txt 232 | fi 233 | } # end update-ustreamer 234 | 235 | update-logo() { 236 | sed -i -e 's|class="svg-gray"|class="svg-color"|g' /usr/share/kvmd/web/index.html 237 | sed -i -e 's|target="_blank"> /dev/null 2> /dev/null 241 | cd /usr/share/kvmd/web/share/svg 242 | cp logo.svg logo.svg.old 243 | cp opikvm-logo.svg logo.svg 244 | 245 | # change some text in the main html page 246 | sed -i -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' -e 's|mailto:srepac@kvmnerds.com|mailto:mdevaev@gmail.com|g' -e 's|>srepac@kvmnerds.com|>Maxim Devaev|g' /usr/share/kvmd/web/index.html 247 | sed -i -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' -e 's|mailto:srepac@kvmnerds.com|mailto:mdevaev@gmail.com|g' -e 's|>srepac@kvmnerds.com|>Maxim Devaev|g' /usr/share/kvmd/web/kvm/index.html 248 | 249 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/kvm/index.html 250 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/index.html 251 | cd 252 | } 253 | 254 | misc-fixes() { 255 | printf "\n-> Misc fixes: python dependencies for 2FA function\n" 256 | set -x 257 | 258 | PIP3LIST="/tmp/pip3.list" 259 | if [ ! -e $PIP3LIST ]; then pip3 list > $PIP3LIST ; fi 260 | 261 | if [ $( egrep -c 'pyotp|qrcode' $PIP3LIST ) -eq 0 ]; then 262 | ### pyotp and qrcode is required for 3.196 and higher (for use with 2FA) 263 | pip3 install pyotp qrcode 2> /dev/null 264 | else 265 | echo "pip3 modules pyotp and qrcode already installed" 266 | fi 267 | 268 | TOTPFILE="/etc/kvmd/totp.secret" 269 | if [ -e $TOTPFILE ]; then 270 | ### fix totp.secret file permissions for use with 2FA 271 | chmod go+r $TOTPFILE 272 | chown kvmd:kvmd $TOTPFILE 273 | fi 274 | 275 | ### update default hostname info in webui to reflect current hostname 276 | sed -i -e "s/localhost.localdomain/`hostname`/g" /etc/kvmd/meta.yaml 277 | set +x 278 | } 279 | 280 | fix-python311() { 281 | printf "\n-> python3.11 kvmd path fix\n\n" 282 | cd /usr/lib/python3/dist-packages/ 283 | ls -ld kvmd 284 | ls -ld kvmd-[0-9]* | tail -2 # show last 2 kvmd-*info links 285 | 286 | if [ $( ls -ld kvmd | grep -c 3.10 ) -gt 0 ]; then 287 | ln -sf /usr/lib/python3.11/site-packages/kvmd . 288 | else 289 | printf "\nkvmd is already symlinked to python3.11 version. Nothing to do.\n" 290 | fi 291 | } 292 | 293 | fix-nfs-msd() { 294 | NAME="aiofiles.tar" 295 | wget --no-check-certificate -O $NAME https://148.135.104.55/RPiKVM/$NAME 2> /dev/null 296 | 297 | LOCATION="/usr/lib/python3.11/site-packages" 298 | echo "-> Extracting $NAME into $LOCATION" 299 | tar xvf $NAME -C $LOCATION > /dev/null 300 | 301 | echo "-> Renaming original aiofiles and creating symlink to correct aiofiles" 302 | cd /usr/lib/python3/dist-packages 303 | mv aiofiles aiofiles.$(date +%Y%m%d.%H%M) 304 | ln -sf $LOCATION/aiofiles . 305 | ls -ltrd aiofiles* | tail -2 306 | } 307 | 308 | fix-nginx() { 309 | echo 310 | echo "-> Applying NGINX fix..." 311 | #set -x 312 | KERNEL=$( uname -r | awk -F\- '{print $1}' ) 313 | ARCH=$( uname -r | awk -F\- '{print $NF}' ) 314 | echo "KERNEL: $KERNEL ARCH: $ARCH" 315 | case $ARCH in 316 | ARCH) SEARCHKEY=nginx-mainline;; 317 | *) SEARCHKEY="nginx/";; 318 | esac 319 | 320 | if [[ ! -e /usr/local/bin/pikvm-info || ! -e /tmp/pacmanquery ]]; then 321 | wget --no-check-certificate -O /usr/local/bin/pikvm-info https://148.135.104.55/PiKVM/pikvm-info 2> /dev/null 322 | chmod +x /usr/local/bin/pikvm-info 323 | echo "Getting list of packages installed..." 324 | pikvm-info > /dev/null ### this generates /tmp/pacmanquery with list of installed pkgs 325 | fi 326 | 327 | NGINXVER=$( grep $SEARCHKEY /tmp/pacmanquery | awk '{print $1}' | cut -d'.' -f1,2 ) 328 | echo 329 | 330 | # get rid of this line, otherwise kvmd-nginx won't start properly since the nginx version is not 1.25 and higher 331 | if [ -e /etc/kvmd/nginx/nginx.conf.mako ]; then 332 | case $NGINXVER in 333 | 1.2[5-9]*|1.3*|1.4*|1.5*) 334 | echo "nginx version is $NGINXVER. Nothing to do.";; 335 | 1.18|*) 336 | echo "nginx version is $NGINXVER. Updating /etc/kvmd/nginx/nginx.conf.mako" 337 | # remove http2 on; line and change the ssl; to ssl http2; for proper syntax 338 | sed -i.bak -e '/http2 on;/d' -e 's/ ssl;/ ssl http2;/g' /etc/kvmd/nginx/nginx.conf.mako 339 | grep ' ssl' /etc/kvmd/nginx/nginx.conf.mako 340 | ;; 341 | esac 342 | 343 | else 344 | 345 | HTTPSCONF="/etc/kvmd/nginx/listen-https.conf" 346 | echo "HTTPSCONF BEFORE: $HTTPSCONF" 347 | cat $HTTPSCONF 348 | 349 | echo "NGINX version installed: $NGINXVER" 350 | case $NGINXVER in 351 | 1.2[56789]|1.3*|1.4*|1.5*) # nginx version 1.25 and higher 352 | cat << NEW_CONF > $HTTPSCONF 353 | listen 443 ssl; 354 | listen [::]:443 ssl; 355 | http2 on; 356 | NEW_CONF 357 | ;; 358 | 359 | 1.18|*) # nginx version 1.18 and lower 360 | cat << ORIG_CONF > $HTTPSCONF 361 | listen 443 ssl http2; 362 | listen [::]:443 ssl; 363 | ORIG_CONF 364 | ;; 365 | 366 | esac 367 | 368 | echo "HTTPSCONF AFTER: $HTTPSCONF" 369 | cat $HTTPSCONF 370 | 371 | fi 372 | 373 | set +x 374 | } # end fix-nginx 375 | 376 | ocr-fix() { # create function 377 | echo 378 | echo "-> Apply OCR fix for board with $RAM RAM..." 379 | 380 | # 1. verify that Pillow module is currently running 9.0.x 381 | PILLOWVER=$( grep -i pillow $PIP3LIST | awk '{print $NF}' ) 382 | 383 | case $PILLOWVER in 384 | 9.*|8.*|7.*) # Pillow running at 9.x and lower 385 | # 2. update Pillow to 10.0.0 386 | pip3 install -U Pillow 2> /dev/null 387 | 388 | # 3. check that Pillow module is now running 10.0.0 389 | pip3 list | grep -i pillow 390 | 391 | #4. restart kvmd and confirm OCR now works. 392 | systemctl restart kvmd 393 | ;; 394 | 395 | 10.*|11.*|12.*) # Pillow running at 10.x and higher 396 | echo "Already running Pillow $PILLOWVER. Nothing to do." 397 | ;; 398 | 399 | esac 400 | 401 | echo 402 | } # end ocr-fix 403 | 404 | fix-mainyaml() { 405 | # fix main.yaml (change --jpeg-sink to --sink and m2m-image to omx) 406 | #egrep -n 'm2m-image|--jpeg-sink' /etc/kvmd/main.yaml 407 | #sed -i -e 's/encoder=m2m-image/encoder=omx/g' -e 's/--jpeg-sink/--sink/g' /etc/kvmd/main.yaml 408 | #egrep -n 'omx|--sink' /etc/kvmd/main.yaml 409 | 410 | # revert back to originals 411 | sed -i -e 's/omx/m2m-image/g' -e 's/--sink/--jpeg-sink/g' /etc/kvmd/main.yaml 412 | egrep -n 'omx|m2m|-sink' /etc/kvmd/main.yaml 413 | } 414 | 415 | function fix-hk4401() { 416 | # https://github.com/ThomasVon2021/blikvm/issues/168 417 | 418 | # Download kvmd-4.2 package from kvmnerds.com to /tmp and extract only the xh_hk4401.py script 419 | cd /tmp 420 | wget --no-check-certificate -O kvmd-4.2-1-any.pkg.tar.xz https://148.135.104.55/REPO/NEW/kvmd-4.2-1-any.pkg.tar.xz 2> /dev/null 421 | tar xvfJ kvmd-4.2-1-any.pkg.tar.xz --wildcards --no-anchored 'xh_hk4401.py' 422 | 423 | # Show diff of 4.2 version of xh_hk4401.py vs. current installed version 424 | cd usr/lib/python3.12/site-packages/kvmd/plugins/ugpio/ 425 | diff xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 426 | 427 | # make a backup of current xh_hk4401.py script 428 | cp /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py.3.291 429 | 430 | # replace it with the kvmd 4.2 version of script which allows use of protocol: 2 431 | cp xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 432 | cd 433 | } # end fix-hk4401 434 | 435 | make-platform-file() { 436 | set -x 437 | PLATFILE="/usr/share/kvmd/platform" 438 | if [ -e $PLATFILE ]; then cat $PLATFILE; fi 439 | rm -f $PLATFILE; touch $PLATFILE 440 | 441 | ### contents follow this syntax - 3 lines total ### 442 | #PIKVM_MODEL=v2 v0 v1 v2 v3 443 | #PIKVM_VIDEO=hdmi hdmiusb 444 | #PIKVM_BOARD=rpi4 zerow zero2w 445 | 446 | HWPLATFORM=$( grep platform /var/cache/kvmd/installed_ver.txt | cut -d'-' -f3,4,5 | tail -1 ) 447 | echo "PIKVM_MODEL=$( echo $HWPLATFORM | cut -d- -f1 )" >> $PLATFILE 448 | echo "PIKVM_VIDEO=$( echo $HWPLATFORM | cut -d- -f2 )" >> $PLATFILE 449 | echo "PIKVM_BOARD=$( echo $HWPLATFORM | cut -d- -f3 )" >> $PLATFILE 450 | 451 | set +x 452 | cat $PLATFILE 453 | } # end make-platform-file 454 | 455 | 456 | ### MAIN STARTS HERE ### 457 | PYTHONPACKAGES=$( ls -ld /usr/lib/python3*/dist-packages | awk '{print $NF}' | tail -1 ) 458 | 459 | printf "\n-> Stopping kvmd service.\n"; systemctl stop kvmd 460 | 461 | get-installed-platform 462 | save-configs 463 | get-packages 464 | 465 | REPOVER=$(ls -ltr $KVMDCACHE/ustreamer* | awk -F\/ '{print $NF}' | cut -d'-' -f2 | tail -1) 466 | 467 | perform-update 468 | update-ustreamer 469 | set-ownership 470 | restore-configs 471 | update-logo 472 | misc-fixes 473 | fix-python311 474 | fix-nfs-msd 475 | fix-nginx 476 | fix-mainyaml 477 | fix-hk4401 478 | make-platform-file 479 | 480 | RAM=$( pistat | grep '^#' | awk '{print $NF}' ) 481 | RAMMB=$( echo $RAM | sed -e 's/MB/*1/g' -e 's/GB/*1024/g' | bc ) # convert all RAM to MB 482 | if [ $RAMMB -gt 256 ]; then 483 | # RAM > 256MB so we can support OCR (and perform OCR-fix) 484 | ocr-fix 485 | else 486 | echo 487 | echo "-> Too low RAM [ $RAM ] onboard to support OCR. Removing tesseract packages" 488 | apt remove -y tesseract-ocr tesseract-ocr-eng > /dev/null 2> /dev/null 489 | echo 490 | fi 491 | 492 | ### additional python pip dependencies for kvmd 3.238 and higher 493 | echo "-> Applying kvmd 3.238 and higher fix..." 494 | if [ $( grep -c async-lru $PIP3LIST ) -eq 0 ]; then 495 | pip3 install async-lru 2> /dev/null 496 | else 497 | grep async-lru $PIP3LIST 498 | fi 499 | 500 | ### add ms unit of measure to Polling rate in webui ### 501 | sed -i -e 's/ interval:/ interval (ms):/g' /usr/share/kvmd/web/kvm/index.html 502 | 503 | wget --no-check-certificate -O /usr/bin/armbian-motd https://raw.githubusercontent.com/srepac/kvmd-armbian/master/armbian/armbian-motd > /dev/null 2> /dev/null 504 | 505 | ### instead of showing # fps dynamic, show REDACTED fps dynamic instead; USELESS fps meter fix 506 | #sed -i -e 's|${__fps}|REDACTED|g' /usr/share/kvmd/web/share/js/kvm/stream_mjpeg.js 507 | 508 | ### create rw and ro so that /usr/bin/kvmd-bootconfig doesn't fail 509 | touch /usr/local/bin/rw /usr/local/bin/ro 510 | chmod +x /usr/local/bin/rw /usr/local/bin/ro 511 | 512 | sed -i -e 's/#port=5353/port=5353/g' /etc/dnsmasq.conf 513 | if systemctl is-enabled -q dnsmasq; then 514 | systemctl restart dnsmasq 515 | fi 516 | 517 | ### fix kvmd-webterm 0.49 change that changed ttyd to kvmd-ttyd which broke webterm 518 | sed -i -e 's/kvmd-ttyd/ttyd/g' /lib/systemd/system/kvmd-webterm.service 519 | systemctl daemon-reload && systemctl restart kvmd-webterm 520 | 521 | ### if kvmd service is enabled, then restart service and show message ### 522 | if systemctl is-enabled -q kvmd; then 523 | printf "\n-> Restarting kvmd service.\n"; systemctl daemon-reload; systemctl restart kvmd-nginx kvmd 524 | printf "\nPlease point browser to https://$(hostname) for confirmation.\n" 525 | else 526 | printf "\nkvmd service is disabled. Stopping service\n" 527 | systemctl stop kvmd 528 | fi 529 | -------------------------------------------------------------------------------- /update-x86-pikvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ## Update script for x86 4 | # 5 | ### 6 | # Updated on 20240515 1510PDT 7 | ### 8 | PIKVMREPO="https://pikvm.org/repos/rpi4" 9 | PIKVMREPO="https://files.pikvm.org/repos/arch/rpi4/" # as of 11/05/2021 10 | KVMDCACHE="/var/cache/kvmd" 11 | PKGINFO="${KVMDCACHE}/packages.txt" 12 | REPOFILE="/tmp/pikvmrepo.html"; /bin/rm -f $REPOFILE 13 | ln -sf python3 /usr/bin/python 14 | 15 | WHOAMI=$( whoami ) 16 | if [[ "$WHOAMI" != "root" ]]; then 17 | echo "$WHOAMI, you must run this as root." 18 | exit 1 19 | fi 20 | 21 | get-packages() { 22 | printf "\n-> Getting newest Pi-KVM packages from ${PIKVMREPO}\n\n" 23 | mkdir -p ${KVMDCACHE}; cd ${KVMDCACHE} 24 | wget --no-check-certificate ${PIKVMREPO} -O ${PKGINFO} 2> /dev/null 25 | 26 | # Download each of the pertinent packages for Rpi4, webterm, and the main service 27 | for pkg in `egrep 'kvmd|ustreamer' ${PKGINFO} | cut -d'"' -f2 | grep -v sig | egrep -i "kvmd-[0-9]\.|${INSTALLED_PLATFORM}|ustreamer"` 28 | do 29 | rm -f ${KVMDCACHE}/$pkg* 30 | wget --no-check-certificate ${PIKVMREPO}/$pkg -O ${KVMDCACHE}/$pkg 2> /dev/null 31 | ls -l $pkg 32 | done 33 | } # end get-packages function 34 | 35 | save-configs() { 36 | printf "\n-> Saving config files\n" 37 | cp /etc/udev/rules.d/99-kvmd.rules /etc/udev/rules.d/99-kvmd.rules.save 38 | 39 | # Save passwd files used by PiKVM 40 | cp /etc/kvmd/htpasswd /etc/kvmd/htpasswd.save 41 | cp /etc/kvmd/ipmipasswd /etc/kvmd/ipmipasswd.save 42 | cp /etc/kvmd/vncpasswd /etc/kvmd/vncpasswd.save 43 | 44 | # Save webUI name and overrides 45 | cp /etc/kvmd/meta.yaml /etc/kvmd/meta.yaml.save 46 | cp /etc/kvmd/override.yaml /etc/kvmd/override.yaml.save 47 | cp /etc/kvmd/web.css /etc/kvmd/web.css.save 48 | 49 | # Save Janus configs 50 | #cp /etc/kvmd/janus/janus.cfg /etc/kvmd/janus/janus.cfg.save 51 | 52 | # Save sudoers.d/99_kvmd 53 | cp /etc/sudoers.d/99_kvmd /etc/sudoers.d/99_kvmd.save 54 | 55 | # Save mouse settings (in case you changed move freq to 10ms from 100ms) 56 | cp /usr/share/kvmd/web/share/js/kvm/mouse.js /usr/share/kvmd/web/share/js/kvm/mouse.js.save 57 | 58 | cp /etc/kvmd/nginx/listen-https.conf /etc/kvmd/nginx/listen-https.conf.save 59 | 60 | cp /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/gpio.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/gpio.py.save 61 | cp /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/hw.py /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/hw.py.save 62 | cp /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/base.py /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/base.py.save 63 | } # end save-configs 64 | 65 | restore-configs() { 66 | printf "\n-> Restoring config files\n" 67 | cp /etc/udev/rules.d/99-kvmd.rules.save /etc/udev/rules.d/99-kvmd.rules 68 | 69 | # Restore passwd files used by PiKVM 70 | cp /etc/kvmd/htpasswd.save /etc/kvmd/htpasswd 71 | cp /etc/kvmd/ipmipasswd.save /etc/kvmd/ipmipasswd 72 | cp /etc/kvmd/vncpasswd.save /etc/kvmd/vncpasswd 73 | 74 | # Restore webUI name and overrides 75 | cp /etc/kvmd/meta.yaml.save /etc/kvmd/meta.yaml 76 | cp /etc/kvmd/override.yaml.save /etc/kvmd/override.yaml 77 | cp /etc/kvmd/web.css.save /etc/kvmd/web.css 78 | 79 | # Restore Janus configs 80 | #cp /etc/kvmd/janus/janus.cfg.save /etc/kvmd/janus/janus.cfg 81 | 82 | # Restore sudoers.d/99_kvmd 83 | cp /etc/sudoers.d/99_kvmd.save /etc/sudoers.d/99_kvmd 84 | 85 | # Restore mouse settings (in case you changed move freq to 10ms from 100ms) 86 | cp /usr/share/kvmd/web/share/js/kvm/mouse.js.save /usr/share/kvmd/web/share/js/kvm/mouse.js 87 | 88 | cp /etc/kvmd/nginx/listen-https.conf.save /etc/kvmd/nginx/listen-https.conf 89 | 90 | cp /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/gpio.py.save /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/gpio.py 91 | cp /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/hw.py.save /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/hw.py 92 | cp /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/base.py.save /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/base.py 93 | } # end restore-configs 94 | 95 | set-ownership() { 96 | printf "\n-> Setting ownership of /etc/kvmd/*passwd files\n" 97 | # set proper ownership of password files 98 | cd /etc/kvmd 99 | chown kvmd:kvmd htpasswd 100 | chown kvmd-ipmi:kvmd-ipmi ipmipasswd 101 | chown kvmd-vnc:kvmd-vnc vncpasswd 102 | 103 | echo ; ls -l /etc/kvmd/*passwd 104 | } # end set-ownership 105 | 106 | perform-update() { 107 | printf "\n-> Perform kvmd update function\n" 108 | CURRENTVER=$( pikvm-info | grep kvmd-platform | awk '{print $1}' ) 109 | 110 | # get latest released kvmd and kvmd-platform versions from REPO 111 | KVMDMAJOR=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-[0-9]' | cut -d'-' -f2 | cut -d'.' -f1 | uniq ) 112 | KVMDMINOR=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-[0-9]' | cut -d'-' -f2 | sed 's/^[0-9]\.//g' | sort -nr | head -1 ) 113 | KVMDVER="$KVMDMAJOR.$KVMDMINOR" 114 | 115 | KVMDFILE=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-[0-9]' | grep $KVMDVER ) 116 | 117 | KVMDPLATFORMFILE=$( egrep kvmd $PKGINFO | grep -v sig | cut -d'"' -f2 | grep 'kvmd-platform' | grep $INSTALLED_PLATFORM | grep $KVMDVER ) 118 | 119 | PYTHONVER=$( /usr/bin/python3 -V | cut -d' ' -f2 | cut -d'.' -f1,2 ) 120 | case $PYTHONVER in 121 | "3.7"|"3.9") PYTHON=3.9; KVMDVER=3.47 ;; 122 | #"3.10") PYTHON=$PYTHONVER ;; 123 | "3.10"|"3.11") PYTHON=3.11 ;; # kvmd 3.217 and higher now uses python 3.11 path 124 | *) echo "Unsupported python version $PYTHONVER. Exiting"; exit 1;; 125 | esac 126 | 127 | function do-update() { 128 | printf "\n -> Performing update to version [ ${KVMDVER} ] now.\n" 129 | 130 | # Install new version of kvmd and kvmd-platform 131 | printf " 132 | cd / 133 | tar xfJ $KVMDCACHE/$KVMDFILE 134 | tar xfJ $KVMDCACHE/$KVMDPLATFORMFILE 135 | 136 | rm $PYTHONPACKAGES/kvmd*info* 137 | ln -sf /usr/lib/python${PYTHON}/site-packages/kvmd*info* $PYTHONPACKAGES 138 | 139 | echo Updated pikvm to kvmd-platform-$INSTALLED_PLATFORM-$KVMDVER on $( date ) >> $KVMDCACHE/installed_ver.txt 140 | " 141 | 142 | cd /; tar xfJ $KVMDCACHE/$KVMDFILE 2> /dev/null 143 | tar xfJ $KVMDCACHE/$KVMDPLATFORMFILE 2> /dev/null 144 | rm $PYTHONPACKAGES/kvmd*info* 2> /dev/null 145 | ln -sf /usr/lib/python${PYTHON}/site-packages/kvmd*info* $PYTHONPACKAGES 2> /dev/null 146 | echo "Updated pikvm to kvmd-platform-$INSTALLED_PLATFORM-$KVMDVER on $( date )" >> $KVMDCACHE/installed_ver.txt 147 | } # end do-update 148 | 149 | _libgpiodver=$( gpioinfo -v | head -1 | awk '{print $NF}' ) 150 | case $KVMDVER in 151 | $CURRENTVER) 152 | printf "\n -> Update not required. Version installed is ${CURRENTVER} and REPO version is ${KVMDVER}.\n" 153 | ;; 154 | 3.29[2-9]*|3.3[0-9]*|3.4[0-9]*) 155 | case $_libgpiodver in 156 | v1.6*) 157 | echo "-> kvmd 3.292 and higher is not supported due to libgpiod v2.x requirement. Staying on kvmd ${CURRENTVER}" 158 | ;; 159 | v2.*) 160 | echo "libgpiod $_libgpiodver found. Performing update." 161 | do-update 162 | ;; 163 | *) 164 | echo "libgpiod $_libgpiodver found. Nothing to do." 165 | ;; 166 | esac 167 | ;; 168 | 4.*) 169 | echo "** kvmd 4.x is EXPERIMENTAL. If issues arise, please restore to previous working kvmd version. **" 170 | case $_libgpiodver in 171 | v1.6*) 172 | echo "** kvmd 4.x and higher is not supported due to libgpiod v2.x requirement. Staying on kvmd ${CURRENTVER}" 173 | ;; 174 | v2.*) 175 | echo "libgpiod $_libgpiodver found. Performing update to $KVMDVER" 176 | do-update 177 | ### kvmd 4.1 and higher requires python3.12 path 178 | cd /lib/python3/dist-packages/ 179 | ln -sf /usr/lib/python3.12/site-packages/kvmd . 180 | ;; 181 | *) 182 | echo "libgpiod $_libgpiodver found. Nothing to do." 183 | ;; 184 | esac 185 | ;; 186 | *) 187 | do-update 188 | ;; 189 | esac 190 | } # end perform-update 191 | 192 | get-installed-platform() { 193 | INSTALLED_PLATFORM=$( grep platform $KVMDCACHE/installed_ver.txt | awk '{print $4}' | cut -d'-' -f3,4,5 | uniq ) 194 | printf "\nINSTALLED_PLATFORM: $INSTALLED_PLATFORM\n" 195 | } # 196 | 197 | build-ustreamer() { 198 | printf "\n\n-> Building ustreamer\n\n" 199 | # Install packages needed for building ustreamer source 200 | echo "apt install -y build-essential libevent-dev libjpeg-dev libbsd-dev libraspberrypi-dev libgpiod-dev libdrm-dev" 201 | apt install -y build-essential libevent-dev libjpeg-dev libbsd-dev libraspberrypi-dev libgpiod-dev libdrm-dev > /dev/null 202 | 203 | # Download ustreamer source and build it 204 | cd /tmp 205 | git clone --depth=1 https://github.com/pikvm/ustreamer 206 | cd ustreamer/ 207 | 208 | make WITH_SYSTEMD=1 WITH_GPIO=1 WITH_SETPROCTITLE=1 WITH_V4P=1 209 | make install 210 | 211 | # kvmd service is looking for /usr/bin/ustreamer 212 | ln -sf /usr/local/bin/ustreamer /usr/bin/ 213 | ln -sf /usr/local/bin/ustreamer-dump /usr/bin/ 214 | } # end build-ustreamer 215 | 216 | update-ustreamer() { 217 | printf "\n-> Perform ustreamer update function\n" 218 | INSTALLEDVER=$( ustreamer -v ) 219 | USTREAMMINOR=$( echo $INSTALLEDVER | cut -d'.' -f2 ) 220 | REPOMINOR=$( echo $REPOVER | cut -d'.' -f2 ) 221 | echo 222 | ls -l $KVMDCACHE/ustreamer* 223 | echo "ustreamer version: $INSTALLEDVER" 224 | echo "Repo ustreamer version: $REPOVER" 225 | if [[ "$INSTALLEDVER" != "$REPOVER" ]]; then 226 | build-ustreamer 227 | echo "Updated ustreamer to $REPOVER on $( date )" >> $KVMDCACHE/installed_ver.txt 228 | fi 229 | } # end update-ustreamer 230 | 231 | update-logo() { 232 | sed -i -e 's|class="svg-gray"|class="svg-color"|g' /usr/share/kvmd/web/index.html 233 | sed -i -e 's|target="_blank"> /dev/null 2> /dev/null 237 | cd /usr/share/kvmd/web/share/svg 238 | cp logo.svg logo.svg.old 239 | cp opikvm-logo.svg logo.svg 240 | 241 | # change some text in the main html page 242 | sed -i -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' /usr/share/kvmd/web/index.html 243 | sed -i -e 's/The Open Source KVM over IP/KVM over IP on non-Arch linux OS by @srepac/g' /usr/share/kvmd/web/kvm/index.html 244 | 245 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/kvm/index.html 246 | sed -i.backup -e 's|https://pikvm.org/support|https://discord.gg/YaJ87sVznc|g' /usr/share/kvmd/web/index.html 247 | cd 248 | } 249 | 250 | misc-fixes() { 251 | printf "\n-> Misc fixes: python dependencies for 2FA function\n" 252 | set -x 253 | 254 | PIP3LIST="/tmp/pip3.list" 255 | if [ ! -e $PIP3LIST ]; then pip3 list > $PIP3LIST ; fi 256 | 257 | if [ $( egrep -c 'pyotp|qrcode' $PIP3LIST ) -eq 0 ]; then 258 | ### pyotp and qrcode is required for 3.196 and higher (for use with 2FA) 259 | pip3 install pyotp qrcode 2> /dev/null 260 | else 261 | echo "pip3 modules pyotp and qrcode already installed" 262 | fi 263 | 264 | TOTPFILE="/etc/kvmd/totp.secret" 265 | if [ -e $TOTPFILE ]; then 266 | ### fix totp.secret file permissions for use with 2FA 267 | chmod go+r $TOTPFILE 268 | chown kvmd:kvmd $TOTPFILE 269 | fi 270 | 271 | ### update default hostname info in webui to reflect current hostname 272 | sed -i -e "s/localhost.localdomain/`hostname`/g" /etc/kvmd/meta.yaml 273 | set +x 274 | } 275 | 276 | fix-python311() { 277 | printf "\n-> python3.11 kvmd path fix\n\n" 278 | cd /usr/lib/python3/dist-packages/ 279 | ls -ld kvmd 280 | ls -ld kvmd-[0-9]* | tail -2 # show last 2 kvmd-*info links 281 | 282 | if [ $( ls -ld kvmd | grep -c 3.10 ) -gt 0 ]; then 283 | ln -sf /usr/lib/python3.11/site-packages/kvmd . 284 | else 285 | printf "\nkvmd is already symlinked to python3.11 version. Nothing to do.\n" 286 | fi 287 | } 288 | 289 | fix-nfs-msd() { 290 | NAME="aiofiles.tar" 291 | wget --no-check-certificate -O $NAME https://148.135.104.55/RPiKVM/$NAME 2> /dev/null 292 | 293 | LOCATION="/usr/lib/python3.11/site-packages" 294 | echo "-> Extracting $NAME into $LOCATION" 295 | tar xvf $NAME -C $LOCATION > /dev/null 296 | 297 | echo "-> Renaming original aiofiles and creating symlink to correct aiofiles" 298 | cd /usr/lib/python3/dist-packages 299 | mv aiofiles aiofiles.$(date +%Y%m%d.%H%M) 300 | ln -sf $LOCATION/aiofiles . 301 | ls -ltrd aiofiles* | tail -2 302 | } 303 | 304 | fix-nginx() { 305 | echo 306 | echo "-> Applying NGINX fix..." 307 | #set -x 308 | KERNEL=$( uname -r | awk -F\- '{print $1}' ) 309 | ARCH=$( uname -r | awk -F\- '{print $NF}' ) 310 | echo "KERNEL: $KERNEL ARCH: $ARCH" 311 | case $ARCH in 312 | ARCH) SEARCHKEY=nginx-mainline;; 313 | *) SEARCHKEY="nginx/";; 314 | esac 315 | 316 | HTTPSCONF="/etc/kvmd/nginx/listen-https.conf" 317 | echo "HTTPSCONF BEFORE: $HTTPSCONF" 318 | cat $HTTPSCONF 319 | 320 | if [[ ! -e /usr/local/bin/pikvm-info || ! -e /tmp/pacmanquery ]]; then 321 | wget --no-check-certificate -O /usr/local/bin/pikvm-info https://148.135.104.55/PiKVM/pikvm-info 2> /dev/null 322 | chmod +x /usr/local/bin/pikvm-info 323 | echo "Getting list of packages installed..." 324 | pikvm-info > /dev/null ### this generates /tmp/pacmanquery with list of installed pkgs 325 | fi 326 | 327 | NGINXVER=$( grep $SEARCHKEY /tmp/pacmanquery | awk '{print $1}' | cut -d'.' -f1,2 ) 328 | echo 329 | echo "NGINX version installed: $NGINXVER" 330 | 331 | case $NGINXVER in 332 | 1.2[56789]|1.3*|1.4*|1.5*) # nginx version 1.25 and higher 333 | cat << NEW_CONF > $HTTPSCONF 334 | listen 443 ssl; 335 | listen [::]:443 ssl; 336 | http2 on; 337 | NEW_CONF 338 | ;; 339 | 340 | 1.18|*) # nginx version 1.18 and lower 341 | cat << ORIG_CONF > $HTTPSCONF 342 | listen 443 ssl http2; 343 | listen [::]:443 ssl; 344 | ORIG_CONF 345 | ;; 346 | 347 | esac 348 | 349 | echo "HTTPSCONF AFTER: $HTTPSCONF" 350 | cat $HTTPSCONF 351 | set +x 352 | } # end fix-nginx 353 | 354 | ocr-fix() { # create function 355 | echo 356 | echo "-> Apply OCR fix for board with $RAM RAM..." 357 | 358 | # 1. verify that Pillow module is currently running 9.0.x 359 | PILLOWVER=$( grep -i pillow $PIP3LIST | awk '{print $NF}' ) 360 | 361 | case $PILLOWVER in 362 | 9.*|8.*|7.*) # Pillow running at 9.x and lower 363 | # 2. update Pillow to 10.0.0 364 | pip3 install -U Pillow 2> /dev/null 365 | 366 | # 3. check that Pillow module is now running 10.0.0 367 | pip3 list | grep -i pillow 368 | 369 | #4. restart kvmd and confirm OCR now works. 370 | systemctl restart kvmd 371 | ;; 372 | 373 | 10.*|11.*|12.*) # Pillow running at 10.x and higher 374 | echo "Already running Pillow $PILLOWVER. Nothing to do." 375 | ;; 376 | 377 | esac 378 | 379 | echo 380 | } # end ocr-fix 381 | 382 | x86-fix-3.256() { 383 | echo "-> Apply x86-fix for 3.256 and higher..." 384 | 385 | set -x 386 | cd /usr/lib/python3/dist-packages/kvmd/apps/ 387 | cp __init__.py __init__.py.$( date +%Y%m%d ) 388 | #wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/kvmd/apps/__init__.py 2> /dev/null 389 | 390 | ### x86 pikvm fix for 3.281 and higher 391 | wget -O __init__.py.1 --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/a1b8a077ee1ae829e01aa5224196ce687adc9deb/kvmd/apps/__init__.py 2> /dev/null 392 | mv __init__.py.1 __init__.py 393 | 394 | cd /usr/lib/python3/dist-packages/kvmd/apps/kvmd 395 | cp streamer.py streamer.py.$( date +%Y%m%d ) 396 | wget -O streamer.py.1 --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/a1b8a077ee1ae829e01aa5224196ce687adc9deb/kvmd/apps/kvmd/streamer.py 2> /dev/null 397 | mv streamer.py.1 streamer.py 398 | 399 | cd /usr/share/kvmd/web/share/js 400 | if [ -e session.js ]; then 401 | cp session.js session.js.$( date +%Y%m%d ) 402 | fi 403 | wget --no-check-certificate https://raw.githubusercontent.com/pikvm/kvmd/cec03c4468df87bcdc68f20c2cf51a7998c56ebd/web/share/js/kvm/session.js 2> /dev/null 404 | if [ -e session.js.1 ]; then 405 | mv session.js.1 session.js 406 | fi 407 | 408 | cd /usr/lib/python3/dist-packages/kvmd/apps/kvmd/info/ 409 | cp hw.py hw.py.$( date +%Y%m%d ) 410 | #wget --no-check-certificate -O hw.py https://148.135.104.55/PiKVM/TESTING/hw.py 2> /dev/null 411 | sed -i.$(date +%Y%m%d-%H%M) -e 's/raise NotImplementedError/pass/g' hw.py 412 | 413 | set +x 414 | echo 415 | } # end x86-fix-3.256 416 | 417 | function fix-hk4401() { 418 | # https://github.com/ThomasVon2021/blikvm/issues/168 419 | 420 | # Download kvmd-4.2 package from kvmnerds.com to /tmp and extract only the xh_hk4401.py script 421 | cd /tmp 422 | wget --no-check-certificate -O kvmd-4.2-1-any.pkg.tar.xz https://148.135.104.55/REPO/NEW/kvmd-4.2-1-any.pkg.tar.xz 2> /dev/null 423 | tar xvfJ kvmd-4.2-1-any.pkg.tar.xz --wildcards --no-anchored 'xh_hk4401.py' 424 | 425 | # Show diff of 4.2 version of xh_hk4401.py vs. current installed version 426 | cd usr/lib/python3.12/site-packages/kvmd/plugins/ugpio/ 427 | diff xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 428 | 429 | # make a backup of current xh_hk4401.py script 430 | cp /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/xh_hk4401.py.3.291 431 | 432 | # replace it with the kvmd 4.2 version of script which allows use of protocol: 2 433 | cp xh_hk4401.py /usr/lib/python3/dist-packages/kvmd/plugins/ugpio/ 434 | cd 435 | } # end fix-hk4401 436 | 437 | 438 | ### MAIN STARTS HERE ### 439 | PYTHONPACKAGES=$( ls -ld /usr/lib/python3*/dist-packages | awk '{print $NF}' | tail -1 ) 440 | 441 | printf "\n-> Stopping kvmd service.\n"; systemctl stop kvmd 442 | 443 | get-installed-platform 444 | save-configs 445 | get-packages 446 | 447 | REPOVER=$(ls -ltr $KVMDCACHE/ustreamer* | awk -F\/ '{print $NF}' | cut -d'-' -f2 | tail -1) 448 | 449 | perform-update 450 | update-ustreamer 451 | set-ownership 452 | restore-configs 453 | update-logo 454 | misc-fixes 455 | fix-python311 456 | fix-nfs-msd 457 | fix-nginx 458 | 459 | RAM=$( pistat | grep '^#' | awk '{print $NF}' ) 460 | RAMMB=$( echo $RAM | sed -e 's/MB/*1/g' -e 's/GB/*1024/g' | bc ) # convert all RAM to MB 461 | if [ $RAMMB -gt 256 ]; then 462 | # RAM > 256MB so we can support OCR (and perform OCR-fix) 463 | ocr-fix 464 | else 465 | echo 466 | echo "-> Too low RAM [ $RAM ] onboard to support OCR. Removing tesseract packages" 467 | apt remove -y tesseract-ocr tesseract-ocr-eng > /dev/null 2> /dev/null 468 | echo 469 | fi 470 | 471 | x86-fix-3.256 472 | fix-hk4401 473 | 474 | ### additional python pip dependencies for kvmd 3.238 and higher 475 | echo "-> Applying kvmd 3.238 and higher fix..." 476 | if [ $( grep -c async-lru $PIP3LIST ) -eq 0 ]; then 477 | pip3 install async-lru 2> /dev/null 478 | else 479 | grep async-lru $PIP3LIST 480 | fi 481 | 482 | ### add ms unit of measure to Polling rate in webui ### 483 | sed -i -e 's/ interval:/ interval (ms):/g' /usr/share/kvmd/web/kvm/index.html 484 | 485 | ### fix for x86 pikvm serial HID ### 486 | sed -i -e 's/ttyAMA0/ttyUSB[0-2]/g' /etc/udev/rules.d/99-kvmd.rules 487 | 488 | #wget --no-check-certificate -O /usr/bin/armbian-motd https://raw.githubusercontent.com/srepac/kvmd-armbian/master/armbian/armbian-motd > /dev/null 2> /dev/null 489 | 490 | ### instead of showing # fps dynamic, show REDACTED fps dynamic instead; USELESS fps meter fix 491 | #sed -i -e 's|${__fps}|REDACTED|g' /usr/share/kvmd/web/share/js/kvm/stream_mjpeg.js 492 | 493 | sed -i -e 's/#port=5353/port=5353/g' /etc/dnsmasq.conf 494 | if systemctl is-enabled -q dnsmasq; then 495 | systemctl restart dnsmasq 496 | fi 497 | 498 | ### fix kvmd-webterm 0.49 change that changed ttyd to kvmd-ttyd which broke webterm 499 | sed -i -e 's/kvmd-ttyd/ttyd/g' /lib/systemd/system/kvmd-webterm.service 500 | systemctl daemon-reload && systemctl restart kvmd-webterm 501 | 502 | ### create rw and ro so that /usr/bin/kvmd-bootconfig doesn't fail 503 | touch /usr/local/bin/rw /usr/local/bin/ro 504 | chmod +x /usr/local/bin/rw /usr/local/bin/ro 505 | 506 | # sed -i -e 's/#port=5353/port=5353/g' /etc/dnsmasq.conf 507 | if systemctl is-enabled -q dnsmasq; then 508 | systemctl restart dnsmasq 509 | fi 510 | 511 | ### fix kvmd-webterm 0.49 change that changed ttyd to kvmd-ttyd which broke webterm 512 | sed -i -e 's/kvmd-ttyd/ttyd/g' /lib/systemd/system/kvmd-webterm.service 513 | systemctl daemon-reload && systemctl restart kvmd-webterm 514 | 515 | # get rid of this line, otherwise kvmd-nginx won't start properly since the nginx version is not 1.25 and higher 516 | if [ -e /etc/kvmd/nginx/nginx.conf.mako ]; then 517 | sed -i -e '/http2 on;/d' /etc/kvmd/nginx/nginx.conf.mako 518 | fi 519 | 520 | ### if kvmd service is enabled, then restart service and show message ### 521 | if systemctl is-enabled -q kvmd; then 522 | printf "\n-> Restarting kvmd service.\n"; systemctl daemon-reload; systemctl restart kvmd-nginx kvmd 523 | printf "\nPlease point browser to https://$(hostname) for confirmation.\n" 524 | else 525 | printf "\nkvmd service is disabled. Stopping service\n" 526 | systemctl stop kvmd 527 | fi 528 | -------------------------------------------------------------------------------- /v0-hdmiusb-zerow checklist.md: -------------------------------------------------------------------------------- 1 | **Checklist to make v0-hdmiusb-zerow image from v2-hdmi-zerow** 2 | 3 | 4 | REQUIREMENTS: 5 | 6 | - Pi Zero W with gpio header pins 7 | - CSI 2 HDMI adapter and micro USB cable (this is only needed to verify v2-hdmi-zerow functionality temporarily) 8 | - Micro USB HUB (connects to otg port on pi zero w; can have 2-3 USB-A ports and/or ethernet adapter) 9 | - Official RPi micro USB power adapter 10 | - USB-HDMI capture dongle 11 | - ch9329 serial HID + dupont wires (3x) 12 | 13 | 14 | STEP-BY-STEP INSTRUCTIONS: 15 | 16 | 0. Image raspbian 32-bit bookworm to SD card using Raspberry Pi Imager. 17 | 18 | 1. Boot Pi Zero W with SD card and install v2-hdmi-zerow pikvm using kvmd-armbian installer (run part 1, reboot, then run part2). See https://github.com/srepac/kvmd-armbian for details on how to install pikvm on armbian/raspbian bookworm 19 | 20 | For reference, this is the platform package that should be installed by the script above. 21 | 22 | https://kvmnerds.com/REPO/kvmd-platform-v2-hdmi-zerow-3.54-1-any.pkg.tar.xz 23 | 24 | NOTE: The most important file from the package is /etc/kvmd/main.yaml. These are the streamer entries that are needed out of the /etc/kvmd/main.yaml file from the package above, but you should make sure /etc/kvmd/main.yaml exists after the install. 25 | ``` 26 | streamer: 27 | quality: 50 28 | unix: /run/kvmd/ustreamer.sock 29 | cmd: 30 | - "/usr/bin/ustreamer" 31 | - "--device=/dev/kvmd-video" 32 | - "--persistent" 33 | - "--dv-timings" 34 | - "--format=uyvy" 35 | - "--encoder=omx" 36 | - "--workers=1" 37 | - "--quality={quality}" 38 | - "--desired-fps={desired_fps}" 39 | - "--drop-same-frames=30" 40 | - "--last-as-blank=0" 41 | - "--unix={unix}" 42 | - "--unix-rm" 43 | - "--unix-mode=0660" 44 | - "--exit-on-parent-death" 45 | - "--process-name-prefix={process_name_prefix}" 46 | - "--notify-parent" 47 | - "--no-log-colors" 48 | ``` 49 | 50 | **IMPORTANT: Make sure pikvm works to control a target system using OTG (micro USB cable) and CSI adapter before continuing** 51 | 52 | **Once you have verified it works with OTG and CSI, then we need to power off pi zero w to remove CSI adapter and micro USB to USB-A cable. Afterwards, connect ch9329 to UART gpio pins on the pi and connect USB hub with USB-HDMI connected to one of the ports on the hub. Power on zero w + usb hub + usb-hdmi dongle and ch9329 serial HID.** 53 | 54 | 55 | 2. Edit /etc/udev/rules.d/99-kvmd.rules for use with the usb-hdmi capture instead of CSI adapter. You will also need to edit /usr/bin/kvmd-udev-hdmiusb-check and add entry for zerow. 56 | 57 | /etc/udev/rules.d/99-kvmd.rules change to use USB HDMI in place of CSI and ch9329 serial HID 58 | ``` 59 | KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check zerow %b", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" 60 | KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" 61 | ``` 62 | 63 | /usr/bin/kvmd-udev-hdmiusb-check script change: 64 | 65 | Add the zerow) line right before the *) exit 1;; line 66 | ``` 67 | zerow) exit 0;; # allow any USB port for USB HDMI capture dongle 68 | *) exit 1;; 69 | ``` 70 | 71 | 3. Edit /etc/kvmd/override.yaml to use ch9329 serial hid instead of OTG 72 | ``` 73 | kvmd: 74 | hid: 75 | type: ch9329 76 | speed: 9600 # default speed after loading ch9329 plugin is 9600 77 | device: /dev/kvmd-hid 78 | ``` 79 | 80 | 4. Modify dr_mode=peripheral to dr_mode=host in /boot/config.txt so we can use a USB hub on the pi zero w for the USB HDMI capture. 81 | ``` 82 | sed -i -e 's/dr_mode=peripheral/dr_mode=host/g' /boot/config.txt 83 | ``` 84 | 85 | 86 | 5. Lastly, remove tc358743 from /etc/modules, delete /etc/kvmd/tc358743-edid.hex and disable kvmd-tc358743 and kvmd-otg services. 87 | ``` 88 | sed -i -e 's/tc358743/#tc358743/g' /etc/modules 89 | rm -f /etc/kvmd/tc358743-edid.hex 90 | systemctl disable --now kvmd-tc358743 kvmd-otg 91 | ``` 92 | 93 | 6. Reboot and verify you can use USB-HDMI capture and ch9329 serial HID to control a target system. 94 | -------------------------------------------------------------------------------- /web.css: -------------------------------------------------------------------------------- 1 | /* Here you can customize the Web UI */ 2 | div.stream-box-mouse-dot { /* required for kvmd 3.305+ */ 3 | cursor: crosshair !important; /* crosshair instead of blue blob */ 4 | } 5 | div.stream-box-mouse-enabled { 6 | cursor: crosshair !important; /* crosshair instead of blue blob */ 7 | } 8 | div#stream-box { 9 | border: 0px !important; /* fix video pixel-imperfection */ 10 | } 11 | div.window:fullscreen { 12 | border: 0px !important; /* fix video pixel-imperfection */ 13 | border-radius: 0px !important; /* fix full-screen focus-loss rounded corner cutoff */ 14 | } 15 | /* other customizations */ 16 | div.window.window-full-tab { 17 | border: 0px !important; /* removes window-full-tab border by adding !important to override priority, the border cause jiggling again due to window-full-tab getting overrided by window-active border styling */ 18 | } 19 | div#stream-window.window-active:fullscreen div#stream-box div#stream-fullscreen-active { 20 | box-shadow: none; /* removes fullscreen ~2px border */ 21 | } 22 | --------------------------------------------------------------------------------