Repository: xe5700/kvmd-armbian Branch: master Commit: ca73920f6cc7 Files: 25 Total size: 93.5 KB Directory structure: gitextract_k6lnbzxp/ ├── .gitignore ├── LICENSE ├── README-zh-CN.MD ├── README.MD ├── amglogic-dtb-mod.py ├── armbian/ │ ├── armbian-motd │ ├── opt/ │ │ ├── armbian-sysinfo │ │ └── vcgencmd │ └── udev/ │ └── rules.d/ │ └── 99-kvmd.rules ├── bin/ │ └── kvmd-helper-otgmsd-unlock ├── config.sh ├── dtb/ │ └── 4.4/ │ └── rk322x-box.dtb ├── install-mirror.sh ├── install.sh ├── libs/ │ ├── checksum.sh │ ├── download_aria2.sh │ └── download_wget.sh └── patches/ ├── custom/ │ └── old-kernel-msd/ │ ├── apply.sh │ ├── v3.124-v3.142/ │ │ └── 0001-Apply-old-kernel-msd-patch-for-v3.134.patch │ └── v3.84-v3.92/ │ ├── 0001-Revert-force-eject-feature-to-unlock-helper.patch │ └── 0003-Allow-skip-some-features-unsupports-on-old-linux-ker.patch ├── disable_gpio/ │ ├── v3.47-v3.81/ │ │ └── 0001-Disable-GPIO-For-TV-Box.patch │ ├── v3.82-v3.83/ │ │ └── 0001-Disable-GPIO-For-TV-Box.patch │ └── v3.84-v3.134/ │ └── 0001-Disable-GPIO-For-TV-Box.patch └── genernal/ └── v3.84-v3.92/ └── 0001-Allow-skip-some-features-unsupports-on-old-linux-ker.patch ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ workspace.code-workspace tmp .vscode ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: README-zh-CN.MD ================================================ # 感谢由toss-a编写的中文教程 该教程未验证,可能需要使用 https://github.com/toss-a/pikvm-armbian 该分叉才能运行。 你可以尝试其他的项目基于pikvm 分支版本 kvmd-armbian https://github.com/srepac/kvmd-armbian onekvm 项目 https://github.com/mofeng-git/One-KVM # 硬件准备 ## 开发版选择 建议选择能刷比较新的armbian的开发版作为PI-KVM ##### 1.选择原生带有OTG的开发版 例如: - Orange Pi Prime(貌似已停产) - Orange Pi Zero(需要制作一分二数据线 性能比较差 适合低成本1080P 30FPS方案) - Orange Pi Zero LTS(和Orange Pi Zero一样) - 等其他 未列举完 ##### 2.选择非原生支持修改dtb来实现OTG功能的开发版 例如: - King 3399 (1个USB3.0 和一个Type-c 3.0做为OTG 能够满足1080P 60FPS方案) - phicomm n1 (理论可以 把刷机那USB作为OTG 未做测试 不建议购买 性价比并不高) - 等其他 未列举完 ## 视频采集设备选择 HDMI转USB - 如果你的开发版只有USB 2.0 建议选择ms2109 而且价格便宜 tb 30 RMB左右的即可 - 如果你的开发版有USB 3.0 建议选择ms2130 淘宝偏贵点 可输出1080P 60FPS - 以上设备我都已测试可以用 但是 部分主板可能使用这两款视频采集设备都会导致进BIOS颜色输出有问题(又不是不能用) - 其他的视频采集设备也可以 你可以试试??? ## OTG数据线准备 - 开发版OTG口为单独的Type-C口或Micro USB 那么 你只需要准备一根数据线即可 - 开发版OTG口为开发版供电 你需要手动制作一分二数据线 如下图 ![1to2](https://raw.githubusercontent.com/toss-a/pikvm-armbian/master/1to2.png) - 开发版OTG口为USB 修改dtb后实现的 你需要准备USB-A 转 USB-A 线缆 建议切断 USB 线的电源线,它可能会导致 OTG 断开连接。 #### 第一步 - 刷入 armbian 用于您的开发版或电视盒(如果内核不支持 otg,您应该构建一个启用 otg 功能的内核) - 如果你的开发版或者电视机或者支持从SD卡启动或者U盘启动 那么你需要把armbian写入到可移动存储介质上,写入完成后将一部分空闲分区划分为10G左右的空间格式化为EXT4 作为PI KVM 镜像储存分区(可选 非必须) #### 第二步 - 修改您的 dtb 文件以启用 otg 功能。对于 otg USB 端口,将 dr_mode 从host更改为peripheral。 - 修改方法(Linux 推荐 Ubuntu) - sudo apt install device-tree-compiler - dtc 你开发版dtb -I dtb -O dts -o 输出的名称.dts - 修改 将dr_mode = "host" 修改为dr_mode = "peripheral" - dtc 刚刚修改的dts.dts -I dts -O dtb -o 你开发版.dtb - 替换,然后重启你的开发版 #### 第三步 - ``` git clone https://github.com/toss-a/pikvm-armbian.git cd pikvm-armbian ./install.sh ``` - 我们内核比较新 不需要 所以按n - 重启 #### 第四步 - 重启后再次运行刚刚运行的./install.sh - 安装完成!!! #### 启用大容量存储设备 需要格式化为EXT4 - 1.开发版或电视盒从U盘或者SD卡启动 使用内置emmc做为PI KVM 镜像存储 - 2.在armbian启动之前划分了PI KVM 镜像存储分区 - 3.再插入一个SD卡或U盘格式化为EXT4做为PI KVM 镜像存储 - 修改挂载点 - ``` vim /etc/fstab ``` - 添加(/dev/sda1需要修改为你的存储介质dev路径) - ``` /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 ``` - ``` vim /etc/kvmd/override.yaml ``` - 删除以下两行 - ``` msd: type:disable ``` - 重启 #### 修复重启后视频采集设备无法采集到视频 - 1.以root用户登陆后克隆 - ``` git clone https://github.com/jkulesza/usbreset ``` - 2.打开克隆的目录 - ``` cd usbreset ``` - 3.将源码编译成可执行文件(如果报错请安装gcc) - ``` cc usbreset.c -o usbreset ``` - 4.赋予可执行权限 - ``` chmod +x usbreset ``` - 4.获取需要重置的视频采集设备的总线和设备 ID - ``` lsusb ``` - 我的输出内容为输出: - ``` root@king3399:~# lsusb Bus 002 Device 002: ID 345f:2130 UltraSemi USB3 Video Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 003 Device 003: ID 0c45:768a Microdia USB DEVICE Bus 003 Device 002: ID 05e3:0608 Genesys Logic, Inc. Hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 006 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub root@king3399:~# ``` - 5.尝试重启你的视频采集设备(我的设备为UltraSemi USB3 Video 对应为002/002) - ``` ./usbreset /dev/bus/usb/002/002 ``` - 6.成功重启USB设备后添加到开机自启动 - ``` vim /etc/rc.local ``` - 添加内容 - ``` sleep 5 /root/usbreset /dev/bus/usb/002/002 ``` - 7.重启开发版再次查看 #### 更改PI KVM 登陆密码 - ``` kvmd-htpasswd set admin ``` - 输入你需要的密码即可 #### 感谢,如果没有他们我不可能做到 - [kvmd-armbian](https://github.com/xe5700/kvmd-armbian) - [peacokswiss](https://github.com/xe5700/kvmd-armbian/issues/12) - [armkvm](https://github.com/wxjiyc/amlogic-s9xxx-armbian/blob/main/rebuild#L629) - [usbreset](https://github.com/jkulesza/usbreset) ================================================ FILE: README.MD ================================================ # KVMD-ARMBIAN This project support non Raspberry Pi device to running pikvm on armbian You can try other project based on pikvm fork version of kvmd-armbian https://github.com/srepac/kvmd-armbian onekvm project https://github.com/mofeng-git/One-KVM # Chinese installation steps Chinese installation steps by toss-a [https://github.com/xe5700/pikvm-armbian/blob/master/README-zh-CN.MD] # Install KVMD Install for armbian It support Allwinner, Amlogic and Rockchip based tv box, tested on phicomm n1, mxq pro 4k, tqc a01. Chipset needs support USB OTG feature, lots of old amglogic chipset not support otg feature, such as s805 and s905. You should install armbian with debian buster or bullseye. Then running this script to install pikvm. Install scripts is fork from @srepac rasbian pikvm install script. Original Script [https://kvmnerds.com/RPiKVM/install-pikvm-raspbian.sh] # Hardware for kvmd-armbian project * A tv box/arm board supports otg feature: - Tests on phicomm n1(Amlogic s905d), mxq pro 4k (rk322x), tqc a01(Allwinner H6). - If you are use arm board you can remove gpio patch to enable gpio feature. * Video capture device: - HDMI to USB dongle (30 RMB On taobao, 10$ on aliexpress.) cheap hdmi to usb dongle all use physics USB2.0 port, but fake USB3.0(USB 5GBPS, USB3.2GEN1) version supports 720P 60FPS, usb 2.0 version only supports 720P 30FPS. * USB-A to USB-A cable: - Recommended cut off usb cable's power line, it might causes otg disconnect. ## Step 1 - Flash armbian debian [Recommended bullseye] for your tv box (If kernel not support otg you should build a kernel enable otg features) ## Step 2 - Modify your dtb file to enable otg feature. Change dr_mode from host to peripheral for otg usb port. - If you use rk322x (rk3228A rk3228B rk3229) series chipset, you can use dtb/4.4/rk332x-box.dtb ## Step 3 ``` git clone https://github.com/xe5700/kvmd-armbian.git cd kvmd-armbian ./install.sh ``` (If very slow, you can use install-mirror.sh to boost install speed.) ## Step 4 - running install.sh or install-mirror.sh after reboot os then running again. - Enjoy # Tested device - Phicomm N1 - TQC A01 (Ethernet port not working, only support wireless.) - RK322x based tvbox (MXQ, V88) - S905L2 based tvbox - Orange pi zero (tested by @MrSuicideParrot) # Update log ## Version 1.0 ## Version 2.0 Now support download hook, config file, diffrent version of kvmd, and fix lots of bug. ## Version 2.1 Fix #8 #7 #15, allow custom apt manager tools. ================================================ FILE: amglogic-dtb-mod.py ================================================ ================================================ FILE: armbian/armbian-motd ================================================ #!/bin/sh /etc/update-motd.d/10-armbian-header /etc/update-motd.d/30-armbian-sysinfo /etc/update-motd.d/41-armbian-config ================================================ FILE: armbian/opt/armbian-sysinfo ================================================ #!/bin/bash # # Copyright (c) Authors: http://www.armbian.com/authors # # This file is licensed under the terms of the GNU General Public # License version 2. This program is licensed "as is" without any # warranty of any kind, whether express or implied. # # DO NOT EDIT THIS FILE but add config options to /etc/default/armbian-motd # generate system information export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin THIS_SCRIPT="sysinfo" MOTD_DISABLE="" STORAGE=/dev/sda1 SHOW_IP_PATTERN="^bond.*|^[ewr].*|^br.*|^lt.*|^umts.*|^lan.*" CPU_TEMP_LIMIT=60 HDD_TEMP_LIMIT=60 AMB_TEMP_LIMIT=40 [[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd for f in $MOTD_DISABLE; do [[ $f == $THIS_SCRIPT ]] && exit 0 done # don't edit below here function display() { # $1=name $2=value $3=red_limit $4=minimal_show_limit $5=unit $6=after $7=acs/desc{ # battery red color is opposite, lower number if [[ "$1" == "Battery" ]]; then local great="<"; else local great=">"; fi if [[ -n "$2" && "$2" > "0" && (( "${2%.*}" -ge "$4" )) ]]; then printf "%-14s%s" "$1:" if awk "BEGIN{exit ! ($2 $great $3)}"; then echo -ne "\e[0;91m $2"; else echo -ne "\e[0;92m $2"; fi printf "%-1s%s\x1B[0m" "$5" printf "%-11s%s\t" "$6" return 1 fi } # display function getboardtemp() { if [ -f /etc/armbianmonitor/datasources/soctemp ]; then read raw_temp /dev/null if [ ! -z $(echo "$raw_temp" | grep -o "^[1-9][0-9]*\.\?[0-9]*$") ] && (( $(echo "${raw_temp} < 200" |bc -l) )); then # Allwinner legacy kernels output degree C board_temp=${raw_temp} else board_temp=$(awk '{printf("%d",$1/1000)}' <<<${raw_temp}) fi elif [ -f /etc/armbianmonitor/datasources/pmictemp ]; then # fallback to PMIC temperature board_temp=$(awk '{printf("%d",$1/1000)}' /dev/null if [[ "$status_battery_connected" == "1" ]]; then read status_battery_charging < $mainline_dir/charger/charging read status_ac_connect < $mainline_dir/ac/connected read battery_percent< $mainline_dir/battery/capacity # dispay charging / percentage if [[ "$status_ac_connect" == "1" && "$battery_percent" -lt "100" ]]; then status_battery_text=" charging" elif [[ "$status_ac_connect" == "1" && "$battery_percent" -eq "100" ]]; then status_battery_text=" charged" else status_battery_text=" discharging" fi fi elif [[ -e "$legacy_dir/axp813-ac" ]]; then read status_battery_connected < $legacy_dir/axp20x-battery/present if [[ "$status_battery_connected" == "1" ]]; then status_battery_text=" "$(awk '{print tolower($0)}' < $legacy_dir/axp20x-battery/status) read status_ac_connect < $legacy_dir/axp813-ac/present read battery_percent< $legacy_dir/axp20x-battery/capacity fi elif [[ -e "$legacy_dir/battery" ]]; then if [[ (("$(cat $legacy_dir/battery/voltage_now)" -gt "5" )) ]]; then status_battery_text=" "$(awk '{print tolower($0)}' < $legacy_dir/battery/status) read battery_percent <$legacy_dir/battery/capacity fi fi } # batteryinfo function ambienttemp() { # define where w1 usually shows up W1_DIR="/sys/devices/w1_bus_master1/" if [ -f /etc/armbianmonitor/datasources/ambienttemp ]; then read raw_temp /dev/null amb_temp=$(awk '{printf("%d",$1/1000)}' <<<${raw_temp}) echo $amb_temp elif [[ -d $W1_DIR && $ONE_WIRE == yes ]]; then device=$(ls -1 $W1_DIR | grep -Eo '^[0-9]{1,4}' | head -1) if [[ -n $device ]]; then if [[ -d ${W1_DIR}${device}/hwmon/hwmon0 ]]; then hwmon=0; else hwmon=1; fi read raw_temp < ${W1_DIR}${device}/hwmon/hwmon${hwmon}/temp1_input 2>/dev/null amb_temp=$(awk '{printf("%d",$1/1000)}' <<<${raw_temp}) echo $amb_temp fi else # read ambient temperature from USB device if available if [[ ! -f /usr/bin/temper ]]; then echo "" return fi amb_temp=$(temper -c 2>/dev/null) case ${amb_temp} in *"find the USB device"*) echo "" ;; *) amb_temp=$(awk '{print $NF}' <<<$amb_temp | sed 's/C//g') echo -n "scale=1;${amb_temp}/1" | grep -oE "\-?[[:digit:]]+\.[[:digit:]]" esac fi } # ambienttemp function get_ip_addresses() { local ips=() for f in /sys/class/net/*; do local intf=$(basename $f) # match only interface names starting with e (Ethernet), br (bridge), w (wireless), r (some Ralink drivers use ra format) if [[ $intf =~ $SHOW_IP_PATTERN ]]; then local tmp=$(ip -4 addr show dev $intf | awk '/inet/ {print $2}' | cut -d'/' -f1) # add both name and IP - can be informative but becomes ugly with long persistent/predictable device names #[[ -n $tmp ]] && ips+=("$intf: $tmp") # add IP only [[ -n $tmp ]] && ips+=("$tmp") fi done echo "${ips[@]}" } # get_ip_addresses function storage_info() { # storage info RootInfo=$(df -h /) root_usage=$(awk '/\// {print $(NF-1)}' <<<${RootInfo} | sed 's/%//g') root_total=$(awk '/\// {print $(NF-4)}' <<<${RootInfo}) StorageInfo=$(df -h $STORAGE 2>/dev/null | grep $STORAGE) if [[ -n "${StorageInfo}" && ${RootInfo} != *$STORAGE* ]]; then storage_usage=$(awk '/\// {print $(NF-1)}' <<<${StorageInfo} | sed 's/%//g') storage_total=$(awk '/\// {print $(NF-4)}' <<<${StorageInfo}) if [[ -n "$(command -v smartctl)" ]]; then DISK="${STORAGE::-1}" storage_temp+=$(sudo smartctl -A $DISK 2> /dev/null | grep -i temperature | awk '{print $(NF-2)}') fi fi } # storage_info # query various systems and send some stuff to the background for overall faster execution. # Works only with ambienttemp and batteryinfo since A20 is slow enough :) amb_temp=$(ambienttemp &) ip_address=$(get_ip_addresses &) batteryinfo storage_info getboardtemp critical_load=80 # get uptime, logged in users and load in one take UPTIME=$(LC_ALL=C uptime) UPT1=${UPTIME#*'up '} UPT2=${UPT1%'user'*} users=${UPT2//*','} users=${users//' '} time=${UPT2%','*} time=${time//','} time=$(echo $time | xargs) load=${UPTIME#*'load average: '} load=${load//','} load=$(echo $load | cut -d" " -f1) [[ $load == 0.0* ]] && load=0.10 cpucount=$(grep -c processor /proc/cpuinfo) load=$(awk '{printf("%.0f",($1/$2) * 100)}' <<< "$load $cpucount") # memory and swap mem_info=$(LC_ALL=C free -w 2>/dev/null | grep "^Mem" || LC_ALL=C free | grep "^Mem") memory_usage=$(awk '{printf("%.0f",(($2-($4+$6+$7))/$2) * 100)}' <<<${mem_info}) mem_info=$(echo $mem_info | awk '{print $2}') memory_total=$(( mem_info / 1024 )) swap_info=$(LC_ALL=C free -m | grep "^Swap") swap_usage=$( (awk '/Swap/ { printf("%3.0f", $3/$2*100) }' <<<${swap_info} 2>/dev/null || echo 0) | tr -c -d '[:digit:]') swap_total=$(awk '{print $(2)}' <<<${swap_info}) if [[ ${memory_total} -gt 1000 ]]; then memory_total=$(awk '{printf("%.2f",$1/1024)}' <<<${memory_total})"G" else memory_total+="M" fi if [[ ${swap_total} -gt 500 ]]; then swap_total=$(awk '{printf("%.2f",$1/1024)}' <<<${swap_total})"G" else swap_total+="M" fi ================================================ FILE: armbian/opt/vcgencmd ================================================ #!/bin/bash cd `dirname $0` source armbian-sysinfo case $1 in get_throttled) echo "throttled=0x0";; measure_temp) echo "temp=$board_temp'C";; get_config) case $2 in total_mem) KB=$( grep 'Memory:' /var/log/dmesg* | awk '{print $5}' | cut -d'/' -f2 | sed 's/K//g' | head -1 ) MB=$( echo $KB / 1024 | bc ) echo "total_mem=$MB";; *) echo "invalid option";; esac ;; esac ================================================ FILE: armbian/udev/rules.d/99-kvmd.rules ================================================ # https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name # https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names 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" KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" ================================================ FILE: bin/kvmd-helper-otgmsd-unlock ================================================ #!/usr/sbin/python # KVMD-ARMBIAN from kvmd.helpers.unlock import main if __name__ == "__main__": main() ================================================ FILE: config.sh ================================================ export APT_EXE="apt-get" #If you installed apt-fast can change it to apt-fast to boost install speed. export GIT_EXE="git" export MIRROR_GITHUB="https://github.com" # Use a github mirror to boost download speed in some place has no github cdn export MIRROR_GITHUB_API="https://api.github.com" export PIKVMREPO="https://files.pikvm.org/repos/arch/rpi4" export PIKVMREPO_PKG="/" #export PIKVMREPO="" #export KVMD_VERSION="3.47" # LEGECY KVMD VERSION SUPPORTS MSD AND RUNNING ON DEBIAN BULLSEYE OR BUSTER WITHOUT PATCH # export KVMD_VERSION="" export KVMD_COMMON_PKG_URL="$MIRROR_GITHUB/xe5700/kvmd-armbian-repo/raw/master/kvmd-common.tar.xz" export CUSTOM_KVMD_VERSION=1 # If you want install lastest version of kvmd set to 0 export KVMD_VERSION="3.142" # LAST KVMD VERSION SUPPORTS PYTHON3.9 export PIKVM_KEY="912C773ABBD1B584" export USE_GPIO=0 export DEBIAN_PYTHON=1 export KVMDCACHE="/var/cache/kvmd" export PKGINFO="${KVMDCACHE}/packages.txt" export DOWNLOAD_FUNC="./libs/download_wget.sh" # can change to ./lib/download_aria2.sh to boost download speed. export GIT_CLONE_WITH_DEPTH="--depth=1" export USE_JANUS=0 export USE_CSI=0 #export HID_MODE="" # Allow otg, ch9329, arduino, bluetooth mode export USE_MSD=0 export USE_UDEV=0 #export PLATFORM_PATCH # Apply patch for board platform ================================================ FILE: install-mirror.sh ================================================ # Github 增强加速脚本 # 加速地址参考github 增强加速下载脚本 # Created by xe5700 # @namespace https://greasyfork.org/scripts/412245 # @supportURL https://github.com/XIU2/UserScript # @homepageURL https://github.com/XIU2/UserScript url_clone=""; url_raw=""; url_raw2=""; clone_mirror(){ printf "Choose github clone mirror\n 0. github.com [Orginal] 1. hub.fastgit.org [China Hong Kong] \n 2. gitclone.com [China Zhe Jiang] \n 3. github.com.cnpmjs.org [Singapore]\n 4. kgithub.com \n 5. hub.njuu.cf \n 6. hub.yzuu.cf \n " tryagain=1 while [ $tryagain -eq 1 ]; do read -p "Please type [0-3]: " capture case $capture in 0) url_clone="https:\\/\\/github.com\\/"; tryagain=0;; 1) url_clone="https:\\/\\/hub.fastgit.org\\/"; tryagain=0;; 2) url_clone="https:\\/\\/gitclone.com\\/github.com\\/"; tryagain=0;; 3) url_clone="https:\\/\\/github.com.cnpmjs.org\\/"; tryagain=0;; 4) url_clone="https:\\/\\/kgithub.com\\/"; tryagin=0;; 5) url_clone="https:\\/\\/hub.njuu.cf\\/"; tryagin=0;; 6) url_clone="https:\\/\\/hub.yzuu.cf\\/"; tryagin=0;; *) printf "\nTry again.\n"; tryagain=1;; esac echo echo "Github clone URL -> $url_clone" echo done } raw_mirror(){ printf "Choose github raw mirror\n 0. https://raw.githubusercontent.com [Orginal] 1. https://raw.fastgit.org [China Hong Kong] 2. https://cdn.staticaly.com [Global] 3. https://ghproxy.com [South Korea] " #1. https://cdn.jsdelivr.net [Global] tryagain=1 while [ $tryagain -eq 1 ]; do read -p "Please type [0-3]: " capture case $capture in 0) url_raw="https:\\/\\/raw.githubusercontent.com\\/";url_raw2="https://raw.githubusercontent.com/"; tryagain=0;; 1) url_raw="https:\\/\\/raw.fastgit.org\\/";url_raw2="https://raw.fastgit.org/"; tryagain=0;; 2) url_raw="https:\\/\\/cdn.staticaly.com\\/gh\\/";url_raw2="https://cdn.staticaly.com/gh/"; tryagain=0;; 3) url_raw="https:\\/\\/ghproxy.com\\/https:\\/\\/raw.githubusercontent.com\\/";url_raw2="https://ghproxy.com/https://raw.githubusercontent.com/"; tryagain=0;; *) printf "\nTry again.\n"; tryagain=1;; esac echo echo "Github raw URL -> $url_raw" echo done } clone_mirror raw_mirror appPath=$(dirname $0) cd $appPath cat install.sh | sed "s/https:\\/\\/raw.githubusercontent.com\\//$url_raw/" | sed "s/https:\\/\\/github.com\\//$url_clone/" | tee .tmp.kvmd-install.sh > /dev/null chmod +x .tmp.kvmd-install.sh ./.tmp.kvmd-install.sh rm -f .tmp.kvmd-install.sh ================================================ FILE: install.sh ================================================ #!/bin/bash # modified by xe5700 2021-11-04 xe5700@outlook.com # modified by NewbieOrange 2021-11-04 # created by @srepac 08/09/2021 srepac@kvmnerds.com # Scripted Installer of Pi-KVM on Raspbian (32-bit) meant for RPi4 # # *** MSD is disabled by default *** # # Mass Storage Device requires the use of a USB thumbdrive or SSD and will need to be added in /etc/fstab : ' # SAMPLE /etc/fstab entry for USB drive with only one partition formatted as ext4 for the entire drive: /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 ' # NOTE: This was tested on a new install of raspbian desktop and lite versions, but should also work on an existing install. # # Last change 20210818 1830 PDT # VER=1.0 source config.sh source $DOWNLOAD_FUNC set +x APP_PATH=$(readlink -f $(dirname $0)) export KVMD_BV=`echo $KVMD_VERSION | awk '{print substr($1,1,1)}'` export KVMD_SV=`echo $KVMD_VERSION | awk '{print substr($1,3)}'` if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "usage: $0 [-f] where -f will force re-install new pikvm platform" exit 1 fi WHOAMI=$( whoami ) if [ "$WHOAMI" != "root" ]; then echo "$WHOAMI, please run script as root." exit 1 fi press-enter() { echo read -p "Press ENTER to continue or CTRL+C to break out of script." } # end press-enter gen-ssl-certs() { cd /etc/kvmd/nginx/ssl openssl ecparam -out server.key -name prime256v1 -genkey openssl req -new -x509 -sha256 -nodes -key server.key -out server.crt -days 3650 \ -subj "/C=US/ST=Denial/L=Denial/O=Pi-KVM/OU=Pi-KVM/CN=$(hostname)" cp server* /etc/kvmd/vnc/ssl/ cd ${APP_PATH} } # end gen-ssl-certs create-override() { if [ $( grep ^kvmd: /etc/kvmd/override.yaml | wc -l ) -eq 0 ]; then if [[ $( echo $platform | grep usb | wc -l ) -eq 1 ]]; then cat <> /etc/kvmd/override.yaml kvmd: hid: mouse_alt: device: /dev/kvmd-hid-mouse-alt # allow absolute/relative mouse mode msd: type: disabled atx: type: disabled streamer: forever: true cmd_append: - "--slowdown" # for usb dongle (so target doesn't have to reboot) resolution: default: 1280x720 USBOVERRIDE else cat <> /etc/kvmd/override.yaml kvmd: hid: mouse_alt: device: /dev/kvmd-hid-mouse-alt msd: type: disabled streamer: forever: true CSIOVERRIDE fi fi } # end create-override install-python-packages() { pkgs="" for i in $( echo "aiofiles appdirs asn1crypto async-timeout bottle cffi chardet click colorama cryptography dateutil dbus dev hidapi idna libgpiod marshmallow more-itertools multidict netifaces packaging passlib pillow ply psutil pycparser pyelftools pyghmi pygments pyparsing requests semantic-version setproctitle setuptools six spidev systemd tabulate urllib3 wrapt xlib yaml yarl" ) do pkgs="$pkgs python3-$i" done echo "-> Install python packages" $APT_EXE install $pkgs -y > /dev/null # U pip3 install dbus_next==0.2.3 zstandard==0.18.0 pyserial==3.5 aiohttp==3.8.3 } # end install python-packages otg-devices() { modprobe libcomposite if [ ! -e /sys/kernel/config/usb_gadget/kvmd ]; then mkdir -p /sys/kernel/config/usb_gadget/kvmd/functions cd /sys/kernel/config/usb_gadget/kvmd/functions mkdir hid.usb0 hid.usb1 hid.usb2 mass_storage.usb0 fi cd ${APP_PATH} } # end otg-device creation install-tc358743() { ### CSI Support for Raspbian ### wget -O- -q https://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | apt-key add - echo "deb https://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main" | tee /etc/apt/sources.list.d/uv4l.list apt-get update > /dev/null echo "$APT_EXE install uv4l-tc358743-extras -y" $APT_EXE install uv4l-tc358743-extras -y > /dev/null } # install package for tc358743 boot-files() { if [[ $( grep srepac /boot/config.txt | wc -l ) -eq 0 ]]; then if [[ $( echo $platform | grep usb | wc -l ) -eq 1 ]]; then # Armbian does not support config.txt, remove it. # amlogic does not support CSI, skip the following # add the tc358743 module to be loaded at boot for CSI # if [[ $( grep -w tc358743 /etc/modules | wc -l ) -eq 0 ]]; then # echo "tc358743" >> /etc/modules # fi # install-tc358743 : fi fi # end of check if entries are already in /boot/config.txt # Remove OTG serial (Orange pi zero's kernel not support it) sed -i '/^g_serial/d' /etc/modules # /etc/modules required entries for DWC2, HID and I2C if [[ $( grep -w dwc2 /etc/modules | wc -l ) -eq 0 ]]; then echo "dwc2" >> /etc/modules fi if [[ $( grep -w libcomposite /etc/modules | wc -l ) -eq 0 ]]; then echo "libcomposite" >> /etc/modules fi if [[ $( grep -w i2c-dev /etc/modules | wc -l ) -eq 0 ]]; then echo "i2c-dev" >> /etc/modules fi # printf "\n/boot/config.txt\n\n" # cat /boot/config.txt printf "\n/etc/modules\n\n" cat /etc/modules } # end of necessary boot files get-packages() { printf "\n\n-> Getting Pi-KVM packages from ${PIKVMREPO}\n\n" mkdir -p "${KVMDCACHE}" #echo "wget ${PIKVMREPO} -O ${PKGINFO}" rm -f "${PKGINFO}" download "${PIKVMREPO}${PIKVMREPO_PKG}" "${PKGINFO}" echo "import Pi-Kvm Repo Key" gpg --keyserver keyserver.ubuntu.com --recv-keys $PIKVM_KEY gpg -a --export $PIKVM_KEY | apt-key add - # Download each of the pertinent packages for Rpi4, webterm, and the main service PIKVM_PKGS_CMD="egrep 'janus|kvmd' \"${PKGINFO}\" | grep -v sig | cut -d'>' -f1 | cut -d'\"' -f2 | egrep -v 'fan|oled' | egrep 'janus|pi4|webterm|kvmd-[0-9]'" if [ $CUSTOM_KVMD_VERSION -eq 1 ]; then PIKVM_PKGS_CMD="$PIKVM_PKGS_CMD | egrep -v 'kvmd-[0-9]'" fi PIKVM_PKGS=`$PIKVM_PKGS_CMD` for pkg in $PIKVM_PKGS do rm -f "${KVMDCACHE}/$pkg.sig" download "${PIKVMREPO}/$pkg.sig" "${KVMDCACHE}/$pkg.sig" download "${PIKVMREPO}/$pkg ${KVMDCACHE}/$pkg gpg ${KVMDCACHE}/$pkg.sig" done echo echo "ls -l ${KVMDCACHE}" ls -l "${KVMDCACHE}" echo } # end get-packages function get-platform() { # tryagain=1 # while [ $tryagain -eq 1 ]; do # # amglogic tv box only has usb port, use usb dongle. # # printf "Choose which capture device you will use:\n\n 1 - USB dongle\n 2 - v2 CSI\n 3 - V3 HAT\n" # # read -p "Please type [1-3]: " capture # capture=1; # done case $USE_CSI in 0) platform="kvmd-platform-v2-hdmiusb-rpi4"; tryagain=0;; # 2) platform="kvmd-platform-v2-hdmi-rpi4"; tryagain=0;; 1) platform="kvmd-platform-v3-hdmi-rpi4"; tryagain=0;; *) printf "\nTry again.\n"; tryagain=1;; esac echo echo "Platform selected -> $platform" echo } # end get-platform install-kvmd-pkgs() { cd / INSTLOG="${KVMDCACHE}/installed_ver.txt"; rm -f "$INSTLOG" date > $INSTLOG # # uncompress platform package first # for i in $( ls "${KVMDCACHE}/${platform}-*.tar.xz" ) # do # echo "-> Extracting package $i into /" >> "$INSTLOG" # tar -vxf "$i" # done # then uncompress, kvmd-{version}, kvmd-webterm, and janus packages for i in $PIKVM_PKGS do echo "-> Extracting package $i into /" >> "$INSTLOG" tar -vxf $i done if [ $CUSTOM_KVMD_VERSION -eq 1 ]; then # Use custom kvmd version replace kvmd offical package download "${KVMD_COMMON_PKG_URL}" "${KVMDCACHE}/kvmd-common.tar.gz" echo "-> Extracting common kvmd package into /" >> "$INSTLOG" tar -vxf "${KVMDCACHE}/kvmd-common.tar.gz" echo "-> Install custom version kvmd" >> "$INSTLOG" $APT_EXE install python3-setuptools -y download "${MIRROR_GITHUB}/pikvm/kvmd/archive/refs/tags/v$KVMD_VERSION.tar.gz" "${KVMDCACHE}/kvmd.tar.gz" mkdir -p "${KVMDCACHE}/kvmd-tmp" tar axf "${KVMDCACHE}/kvmd.tar.gz" -C "${KVMDCACHE}/kvmd-tmp" cd "${KVMDCACHE}/kvmd-tmp/kvmd-$KVMD_VERSION/" ./setup.py install cd "$APP_PATH" rm -rf "${KVMDCACHE}/kvmd-tmp" fi cd "${APP_PATH}" cp bin/* /usr/bin/ } # end install-kvmd-pkgs fix-udevrules() { # for hdmiusb, replace %b with 1-1.4:1.0 in /etc/udev/rules.d/99-kvmd.rules sed -i -e 's+\%b+1-1.4:1.0+g' /etc/udev/rules.d/99-kvmd.rules echo cat /etc/udev/rules.d/99-kvmd.rules } # end fix-udevrules enable-kvmd-svcs() { # enable KVMD services but don't start them echo "-> Enabling kvmd-nginx kvmd-webterm kvmd-otg and kvmd services, but do not start them." systemctl enable kvmd-nginx kvmd-webterm kvmd-otg kvmd # in case going from CSI to USB, then disable kvmd-tc358743 service (in case it's enabled) if [[ $USE_CSI -eq 0 ]]; then systemctl disable --now kvmd-tc358743 else systemctl enable kvmd-tc358743 fi } # end enable-kvmd-svcs build-ustreamer() { printf "\n\n-> Building ustreamer\n\n" # Install packages needed for building ustreamer source echo "$APT_EXE install -y libevent-dev libjpeg-dev libbsd-dev libgpiod-dev libsystemd-dev janus-dev janus" $APT_EXE install -y libevent-dev libjpeg-dev libbsd-dev libsystemd-dev if [[ $USE_GPIO -eq 1 ]]; then $APT_EXE install -y libgpiod-dev fi if [[ $USE_JANUS -eq 1 ]]; then $APT_EXE install -y janus-dev janus fi # Download ustreamer source and build it cd /tmp $GIT_EXE clone $GIT_CLONE_WITH_DEPTH "$MIRROR_GITHUB/pikvm/ustreamer" cd ustreamer/ # if [[ $( uname -m ) == "aarch64" ]]; then # make WITH_OMX=0 WITH_GPIO=1 WITH_SETPROCTITLE=1 # ustreamer doesn't support 64-bit hardware OMX # else # make WITH_OMX=1 WITH_GPIO=1 WITH_SETPROCTITLE=1 # hardware OMX support with 32-bit ONLY # fi make WITH_GPIO=$USE_GPIO WITH_SYSTEMD=1 WITH_JANUS=$USE_JANUS -j make install # kvmd service is looking for /usr/bin/ustreamer ln -s /usr/local/bin/ustreamer /usr/bin/ } # end build-ustreamer install-dependencies() { echo echo "-> Installing dependencies for pikvm" apt-get update > /dev/null # for i in $( echo "" ) # do # echo "$APT_EXE install -y $i" # $APT_EXE install -y $i > /dev/null # done echo "-> Install basic packages" $APT_EXE install -y nginx python3 bc expect v4l-utils gpiod dialog git python3-pip tesseract-ocr tesseract-ocr-chi-sim jq install-python-packages echo "-> Make tesseract data link" ln -s /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata echo "-> Install TTYD" $APT_EXE install -y ttyd if [ ! -e /usr/bin/ttyd ]; then echo "-> Download from apt failed, try download offical lastest version binary file." # Build and install ttyd # cd /tmp # $APT_EXE install -y build-essential cmake git libjson-c-dev libwebsockets-dev # git clone --depth=1 https://github.com/tsl0922/ttyd.git # cd ttyd && mkdir build && cd build # cmake .. # make -j && make install # Install binary from GitHub arch=$(dpkg --print-architecture) latest=$(wget -q -O- $MIRROR_GITHUB_API/repos/tsl0922/ttyd/releases/latest | jq -r ".tag_name") if [ $arch = arm64 ]; then arch='aarch64' fi if [ $arch = amd64 ]; then arch='x86_64' fi wget "$MIRROR_GITHUB/tsl0922/ttyd/releases/download/$latest/ttyd.$arch" -O /usr/bin/ttyd chmod +x /usr/bin/ttyd fi echo "-> Install ustreamer" if [ ! -e /usr/bin/ustreamer ]; then # apt install ustreamer cd /tmp/ $APT_EXE install -y libevent-2.1-7 libevent-core-2.1-7 libevent-pthreads-2.1-7 build-essential # ### required dependent packages for ustreamer ### build-ustreamer cd ${APP_PATH} fi } # end install-dependencies python-pkg-dir() { # debian system python3 no alias # create quick python script to show where python packages need to go cat << MYSCRIPT > /tmp/syspath.py #!$(which python3) import sys print (sys.path) MYSCRIPT chmod +x /tmp/syspath.py export PYTHONDIR_SYS=$( /tmp/syspath.py | grep packages | sed -e 's/, /\n/g' -e 's/\[//g' -e 's/\]//g' -e "s+'++g" | tail -1 ) export PYTHONDIR_PIP=$( python3 -c "import site; print(site.getsitepackages()[0])" ) } # end python-pkg-dir fix-nginx-symlinks() { # disable default nginx service since we will use kvmd-nginx instead echo echo "-> Disabling nginx service, so that we can use kvmd-nginx instead" systemctl disable --now nginx # setup symlinks echo echo "-> Creating symlinks for use with kvmd python scripts" if [ ! -e /usr/bin/nginx ]; then ln -s /usr/sbin/nginx /usr/bin/; fi if [ ! -e /usr/sbin/python ]; then ln -s /usr/bin/python3 /usr/sbin/python; fi if [ ! -e /usr/bin/iptables ]; then ln -s /usr/sbin/iptables /usr/bin/iptables; fi # if [ ! -e /opt/vc/bin/vcgencmd ]; then mkdir -p /opt/vc/bin/; ln -s /usr/bin/vcgencmd /opt/vc/bin/vcgencmd; fi python-pkg-dir if [ ! -e $PYTHONDIR_PIP/kvmd ]; then # Debian python版本比 pikvm官方的低一些 ln -s /usr/lib/python3.10/site-packages/kvmd* ${PYTHONDIR_PIP} fi } # end fix-nginx-symlinks fix-python-symlinks(){ python-pkg-dir if [ ! -e $PYTHONDIR_PIP/kvmd ]; then # Debian python版本比 pikvm官方的低一些 ln -s /usr/lib/python3.10/site-packages/kvmd* ${PYTHONDIR_PIP} fi } apply-custom-patch(){ read -p "Do you want apply old kernel msd patch? [y/n]" answer case $answer in n|N|no|No) echo 'You skiped this patch.' ;; y|Y|Yes|yes) ./patches/custom/old-kernel-msd/apply.sh ;; *) echo "Try again.";; esac } fix-kvmd-for-tvbox-armbian(){ # 打补丁来移除一些对armbian和电视盒子不太支持的特性 python-pkg-dir if [[ "$CUSTOM_KVMD_VERSION" -eq 1 ]]; then cd "$PYTHONDIR_PIP/kvmd-$KVMD_VERSION-py${PYTHON_VERSION}.egg" else cd $PYTHONDIR_PIP fi # if [[ "$DEBIAN_PYTHON" -eq 1 ]]; then # if [ `$KVMD_VERSION < 3.134` -eq ]; then # PATCH_VER="v3.90" # fi # if [ `$KVMD_VERSION \>= 3.134` -eq 1 ]; then # PATCH_VER="v3.134" # fi # if [ ! -z "$PATCH_VER" ]; then # $GIT_EXE apply ${APP_PATH}/patches/debian_python/$PATCH_VER/*.patch # fi # fi if [[ "$USE_GPIO" -eq 0 ]] && [[ "$KVMD_BV" -eq "3" ]] ; then PATCH_VER="" if [ `expr $KVMD_SV \<= 81` -eq 1 ]; then PATCH_VER="v3.47-v3.81" fi if [ `expr $KVMD_SV \>= 82` -eq 1 ] && [ `expr $KVMD_SV \<= 83` -eq 1 ]; then PATCH_VER="v3.82-v3.83" fi if [ `expr $KVMD_SV \>= 84` -eq 1 ] && [ `expr $KVMD_SV \<= 134` -eq 1 ]; then PATCH_VER="v3.84-v3.134" fi if [ ! -z "$PATCH_VER" ]; then sh -c "$GIT_EXE apply '${APP_PATH}/patches/disable_gpio/$PATCH_VER/*.patch'" fi fi if [ `expr $KVMD_SV \>= 84` -eq 1 ] && [ `expr $KVMD_SV \<= 92` -eq 1 ]; then PATCH_VER="v3.84-v3.92" sh -c "$GIT_EXE apply '${APP_PATH}/patches/genernal/$PATCH_VER/*.patch'" fi cd ${APP_PATH} read -p "Do you want to apply custom patches? [y/n] " answer case $answer in n|N|no|No) return; ;; y|Y|Yes|yes) apply-custom-patch; return; ;; *) echo "Try again.";; esac } fix-webterm() { echo echo "-> Creating kvmd-webterm homedir" mkdir -p /home/kvmd-webterm chown kvmd-webterm /home/kvmd-webterm ls -ld /home/kvmd-webterm } # end fix-webterm create-kvmdfix() { # Create kvmd-fix service and script cat < /lib/systemd/system/kvmd-fix.service [Unit] Description=KVMD Fixes After=network.target network-online.target nss-lookup.target Before=kvmd.service [Service] User=root Type=simple ExecStart=/usr/bin/kvmd-fix [Install] WantedBy=multi-user.target ENDSERVICE cat < /usr/bin/kvmd-fix #!/bin/bash # Written by @srepac # 1. Properly set group ownership of /dev/gpio* # 2. fix /dev/kvmd-video symlink to point to /dev/video1 (Amglogic Device video0 is not usb device) # ### These fixes are required in order for kvmd service to start properly # set -x chgrp gpio /dev/gpio* chmod 660 /dev/gpio* ### this is required in case gpio (wiringpi) is installed ls -l /dev/gpio* ls -l /dev/kvmd-video rm /dev/kvmd-video # Need to use video0 for orange pi (if you don't, the video capture won't work) ln -s video1 /dev/kvmd-video SCRIPTEND chmod +x /usr/bin/kvmd-fix } # end create-kvmdfix set-ownership() { # set proper ownership of password files and kvmd-webterm homedir cd /etc/kvmd chown kvmd:kvmd htpasswd chown kvmd-ipmi:kvmd-ipmi ipmipasswd chown kvmd-vnc:kvmd-vnc vncpasswd chown kvmd-webterm /home/kvmd-webterm # add kvmd user to video group (this is required in order to use CSI bridge with OMX and h264 support) usermod -a -G video kvmd } # end set-ownership check-kvmd-works() { # check to make sure kvmd -m works before continuing invalid=1 while [ $invalid -eq 1 ]; do kvmd -m read -p "Did kvmd -m run properly? [y/n] " answer case $answer in n|N|no|No) echo "Please install missing packages as per the kvmd -m output in another ssh/terminal." ;; y|Y|Yes|yes) invalid=0 ;; *) echo "Try again.";; esac done } # end check-kvmd-works start-kvmd-svcs() { #### start the main KVM services in order #### # 1. nginx is the webserver # 2. kvmd-otg is for OTG devices (keyboard/mouse, etc..) # 3. kvmd is the main daemon systemctl restart kvmd-nginx kvmd-otg kvmd-webterm kvmd # systemctl status kvmd-nginx kvmd-otg kvmd-webterm kvmd } # end start-kvmd-svcs fix-motd() { rm /etc/motd cp armbian/armbian-motd /usr/bin/ sed -i 's/cat \/etc\/motd/armbian-motd/g' /lib/systemd/system/kvmd-webterm.service systemctl daemon-reload # systemctl restart kvmd-webterm } # end fix-motd # 安装armbian的包 armbian-packages() { mkdir -p /opt/vc/bin/ #cd /opt/vc/bin # Install vcgencmd for armbian platform cp -rf armbian/opt/* /opt/vc/bin #cp -rf armbian/udev /etc/ cd ${APP_PATH} # } #end armbian-packages $APT_EXE update $APT_EXE -y install python3 xz-utils tar wget aria2 curl ### MAIN STARTS HERE ### # Install is done in two parts # First part requires a reboot in order to create kvmd users and groups # Second part will start the necessary kvmd services # added option to re-install by adding -f parameter (for use as platform switcher) export PYTHON_VERSION=$( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) if [[ $( grep kvmd /etc/passwd | wc -l ) -eq 0 || "$1" == "-f" ]]; then printf "\nRunning part 1 of PiKVM installer script for Raspbian by @srepac\n" get-packages get-platform boot-files install-kvmd-pkgs create-override gen-ssl-certs fix-udevrules install-dependencies otg-devices armbian-packages systemctl disable --now janus fix-kvmd-for-tvbox-armbian # Fix paste-as-keys if running python 3.7 if [[ $( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) == "3.7" ]]; then sed -i -e 's/reversed//g' $PYTHONDIR/kvmd/keyboard/printer.py fi sync echo "-> Synced data, you can reboot system safety." printf "\n\nReboot is required to create kvmd users and groups.\nPlease re-run this script after reboot to complete the install.\n" # Ask user to press CTRL+C before reboot or ENTER to proceed with reboot press-enter reboot else printf "\nRunning part 2 of PiKVM installer script for Raspbian by @srepac\n" fix-nginx-symlinks fix-python-symlinks fix-webterm fix-motd set-ownership create-kvmdfix check-kvmd-works enable-kvmd-svcs start-kvmd-svcs sync printf "\nCheck kvmd devices\n\n" ls -l /dev/kvmd* printf "\nYou should see devices for keyboard, mouse, and video.\n" 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" fi ================================================ FILE: libs/checksum.sh ================================================ checksum(){ export sumRet=0 case $1 in gpg) checksum_gpg $2 $3;; esac } checksum_gpg(){ gpg --verify $2 $1 2> /dev/null case $? in 1) export sumRet=0;echo bad signature, skip checksum.;; *) export sumRet=$?;; esac return; } ================================================ FILE: libs/download_aria2.sh ================================================ #!/bin/bash source libs/checksum.sh download(){ tryCount=0 echo Downloading $1 To $2 download2 $1 $2 $3 $4 unset tryCount } download2() { filename=$(readlink -f $2) echo "aria2c -x 16 -s 16 --file-allocation=falloc --min-split-size 2M $1 -o $filename -d /" if [ ! -f "$2" ]; then aria2c -x 16 -s 16 --file-allocation=falloc --min-split-size 2M $1 -o $filename -d / else if [ ! -n "$3" ]; then echo "File $2 is exists, skip download." return fi fi echo "Checksum for $2" checksum $3 $2 $4 if [ "$sumRet" != 0 ]; then echo "File checksum failed, try redownload file. Result is $sumRet" if [[ "$tryCount" -lt 3 ]]; then tryCount=`expr $tryCount + 1` rm $2 download2 $1 $2 $3 $4 else echo "Try $tryCount times, download failed." fi else echo "File checksum successful." fi unset sumRet } ================================================ FILE: libs/download_wget.sh ================================================ #!/bin/bash source libs/checksum.sh download(){ tryCount=0 echo Downloading $1 To $2 download2 $1 $2 $3 $4 unset tryCount } download2() { if [ ! -f "$2" ]; then echo "wget $1 -O $2" wget $1 -O $2 else if [ ! -n "$3" ]; then echo "File $2 is exists, skip download." return fi fi echo "Checksum for $2" checksum $3 $2 $4 if [ "$sumRet" != 0 ]; then echo "File checksum failed, try redownload file. Result is $sumRet" if [[ "$tryCount" -lt 3 ]]; then tryCount=`expr $tryCount + 1` rm $2 download2 $1 $2 $3 $4 else echo "Try $tryCount times, download failed." fi else echo "File checksum successful." fi unset sumRet } ================================================ FILE: patches/custom/old-kernel-msd/apply.sh ================================================ #!/bin/bash # PYTHON_VERSION=$( python3 -V | awk '{print $2}' | cut -d'.' -f1,2 ) APP_PATH=$(readlink -f $(dirname $0)) echo "-> Apply patches" if [[ "$CUSTOM_KVMD_VERSION" -eq 1 ]]; then cd $PYTHONDIR_PIP/kvmd-$KVMD_VERSION-py${PYTHON_VERSION}.egg/ else cd $PYTHONDIR_PIP fi PATCH_VER="" if [ `expr $KVMD_SV \>= 84` -eq 1 ] && [ `expr $KVMD_SV \<= 92` -eq 1 ]; then PATCH_VER="v3.84-v3.134" fi if [ `expr $KVMD_SV \>= 124` -eq 1 ] && [ `expr $KVMD_SV \<= 142` -eq 1 ]; then PATCH_VER="v3.124-v3.142" fi git apply ${APP_PATH}/${PATCH_VER}/*.patch cd ${APP_PATH} # echo "-> Add otgmsd unlock link" # cp kvmd-helper-otgmsd-unlock /usr/bin/ echo "-> Add sudoer" echo "kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-unlock" >> /etc/sudoers.d/99_kvmd echo "-> Apply old kernel msd patch done." ================================================ FILE: patches/custom/old-kernel-msd/v3.124-v3.142/0001-Apply-old-kernel-msd-patch-for-v3.134.patch ================================================ From 18723b4c8e13a5cd0049982cfbbf61b4409ba159 Mon Sep 17 00:00:00 2001 From: xe5700 <9338143+xe5700@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:31:45 +0800 Subject: [PATCH] Apply old kernel msd patch for v3.134 --- kvmd/aiohelpers.py | 31 ++++++++++++----- kvmd/apps/otg/__init__.py | 3 +- kvmd/apps/otgmsd/__init__.py | 25 +++++++++++++- kvmd/helpers/unlock/__init__.py | 58 ++++++++++++++++++++++++++++++++ kvmd/helpers/unlock/__main__.py | 24 +++++++++++++ kvmd/plugins/msd/otg/__init__.py | 20 +++++++---- kvmd/plugins/msd/otg/drive.py | 5 +-- 7 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 kvmd/helpers/unlock/__init__.py create mode 100644 kvmd/helpers/unlock/__main__.py diff --git a/kvmd/aiohelpers.py b/kvmd/aiohelpers.py index ae943d23..e0e27fd3 100644 --- a/kvmd/aiohelpers.py +++ b/kvmd/aiohelpers.py @@ -40,11 +40,26 @@ async def remount(name: str, base_cmd: List[str], rw: bool) -> bool: ] logger.info("Remounting %s storage to %s: %s ...", name, mode.upper(), tools.cmdfmt(cmd)) try: - proc = await aioproc.log_process(cmd, logger) - if proc.returncode != 0: - assert proc.returncode is not None - raise subprocess.CalledProcessError(proc.returncode, cmd) - except Exception as err: - logger.error("Can't remount %s storage: %s", name, tools.efmt(err)) - return False - return True + await _run_helper(cmd) + except Exception: + logger.error("Can't remount internal storage") + raise + + +async def unlock_drive(base_cmd: List[str]) -> None: + logger = get_logger(0) + logger.info("Unlocking the drive ...") + try: + await _run_helper(base_cmd) + except Exception: + logger.error("Can't unlock the drive") + raise + + +# ===== +async def _run_helper(cmd: List[str]) -> None: + logger = get_logger(0) + logger.info("Executing helper %s ...", cmd) + proc = await aioproc.log_process(cmd, logger) + if proc.returncode != 0: + logger.error(f"Error while helper execution: pid={proc.pid}; retcode={proc.returncode}") diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py index 9b6f5e69..af6327b2 100644 --- a/kvmd/apps/otg/__init__.py +++ b/kvmd/apps/otg/__init__.py @@ -186,7 +186,6 @@ class _GadgetConfig: _chown(join(func_path, "lun.0/cdrom"), user) _chown(join(func_path, "lun.0/ro"), user) _chown(join(func_path, "lun.0/file"), user) - _chown(join(func_path, "lun.0/forced_eject"), user) _symlink(func_path, join(self.__profile_path, func)) name = ("Mass Storage Drive" if self.__msd_instance == 0 else f"Extra Drive #{self.__msd_instance}") self.__create_meta(func, name) @@ -295,7 +294,7 @@ def _cmd_stop(config: Section) -> None: logger.info("Disabling gadget %r ...", config.otg.gadget) _write(join(gadget_path, "UDC"), "\n") - _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), optional=True) + _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), True) profile_path = join(gadget_path, usb.G_PROFILE) for func in os.listdir(profile_path): diff --git a/kvmd/apps/otgmsd/__init__.py b/kvmd/apps/otgmsd/__init__.py index 0d32331b..26db4c8e 100644 --- a/kvmd/apps/otgmsd/__init__.py +++ b/kvmd/apps/otgmsd/__init__.py @@ -21,12 +21,15 @@ import os +import signal import errno import argparse from typing import List from typing import Optional +import psutil + from ...validators.basic import valid_bool from ...validators.basic import valid_int_f0 from ...validators.os import valid_abs_file @@ -56,6 +59,21 @@ def _set_param(gadget: str, instance: int, param: str, value: str) -> None: raise +def _unlock() -> None: + # https://github.com/torvalds/linux/blob/3039fad/drivers/usb/gadget/function/f_mass_storage.c#L2924 + found = False + for proc in psutil.process_iter(): + attrs = proc.as_dict(attrs=["name", "exe", "pid"]) + if attrs.get("name") == "file-storage" and not attrs.get("exe"): + try: + proc.send_signal(signal.SIGUSR1) + found = True + except Exception as err: + raise SystemExit(f"Can't send SIGUSR1 to MSD kernel thread with pid={attrs['pid']}: {err}") + if not found: + raise SystemExit("Can't find MSD kernel thread") + + # ===== def main(argv: Optional[List[str]]=None) -> None: (parent_parser, argv, config) = init( @@ -71,6 +89,8 @@ def main(argv: Optional[List[str]]=None) -> None: ) parser.add_argument("-i", "--instance", default=0, type=valid_int_f0, metavar="", help="Drive instance (0 for KVMD drive)") + parser.add_argument("--unlock", action="store_true", + help="Send SIGUSR1 to MSD kernel thread") parser.add_argument("--set-cdrom", default=None, type=valid_bool, metavar="<1|0|yes|no>", help="Set CD-ROM flag") parser.add_argument("--set-rw", default=None, type=valid_bool, @@ -90,8 +110,11 @@ def main(argv: Optional[List[str]]=None) -> None: set_param = (lambda param, value: _set_param(config.otg.gadget, options.instance, param, value)) get_param = (lambda param: _get_param(config.otg.gadget, options.instance, param)) + if options.unlock: + _unlock() + if options.eject: - set_param("forced_eject", "") + set_param("file", "") if options.set_cdrom is not None: set_param("cdrom", str(int(options.set_cdrom))) diff --git a/kvmd/helpers/unlock/__init__.py b/kvmd/helpers/unlock/__init__.py new file mode 100644 index 00000000..140e0e7c --- /dev/null +++ b/kvmd/helpers/unlock/__init__.py @@ -0,0 +1,58 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +# ========================================================================== # + + +import sys +import signal + +import psutil + + +# ===== +_PROCESS_NAME = "file-storage" + + +# ===== +def _log(msg: str) -> None: + print(msg, file=sys.stderr) + + +def _unlock() -> None: + # https://github.com/torvalds/linux/blob/3039fad/drivers/usb/gadget/function/f_mass_storage.c#L2924 + found = False + for proc in psutil.process_iter(): + attrs = proc.as_dict(attrs=["name", "exe", "pid"]) + if attrs.get("name") == _PROCESS_NAME and not attrs.get("exe"): + _log(f"Sending SIGUSR1 to MSD {_PROCESS_NAME!r} kernel thread with pid={attrs['pid']} ...") + try: + proc.send_signal(signal.SIGUSR1) + found = True + except Exception as err: + raise SystemExit(f"Can't send SIGUSR1 to MSD kernel thread with pid={attrs['pid']}: {err}") + if not found: + raise SystemExit(f"Can't find MSD kernel thread {_PROCESS_NAME!r}") + + +# ===== +def main() -> None: + if len(sys.argv) != 2 or sys.argv[1] != "unlock": + raise SystemExit(f"Usage: {sys.argv[0]} [unlock]") + _unlock() diff --git a/kvmd/helpers/unlock/__main__.py b/kvmd/helpers/unlock/__main__.py new file mode 100644 index 00000000..3849d1b9 --- /dev/null +++ b/kvmd/helpers/unlock/__main__.py @@ -0,0 +1,24 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +# ========================================================================== # + + +from . import main +main() diff --git a/kvmd/plugins/msd/otg/__init__.py b/kvmd/plugins/msd/otg/__init__.py index 5a8b86a6..d4de24b8 100644 --- a/kvmd/plugins/msd/otg/__init__.py +++ b/kvmd/plugins/msd/otg/__init__.py @@ -144,6 +144,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes storage_path: str, remount_cmd: List[str], + unlock_cmd: List[str], initial: Dict, @@ -159,6 +160,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes self.__meta_path = os.path.join(self.__storage_path, "meta") self.__remount_cmd = remount_cmd + self.__unlock_cmd = unlock_cmd self.__initial_image: str = initial["image"] self.__initial_cdrom: bool = initial["cdrom"] @@ -184,10 +186,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes "storage": Option("/var/lib/kvmd/msd", type=valid_abs_dir, unpack_as="storage_path"), - "remount_cmd": Option([ - "/usr/bin/sudo", "--non-interactive", - "/usr/bin/kvmd-helper-otgmsd-remount", "{mode}", - ], type=valid_command), + "remount_cmd": Option([*sudo, "/usr/bin/kvmd-helper-otgmsd-remount", "{mode}"], type=valid_command), + "unlock_cmd": Option([*sudo, "/usr/bin/kvmd-helper-otgmsd-unlock", "unlock"], type=valid_command), "initial": { "image": Option("", type=valid_printable_filename, if_empty=""), @@ -250,6 +250,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes async def reset(self) -> None: async with self.__state.busy(check_online=False): try: + await self.__unlock_drive() self.__drive.set_image_path("") self.__drive.set_cdrom_flag(False) self.__drive.set_rw_flag(False) @@ -314,6 +315,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if not os.path.exists(self.__state.vd.image.path): raise MsdUnknownImageError() + await self.__unlock_drive() self.__drive.set_rw_flag(self.__state.vd.rw) self.__drive.set_cdrom_flag(self.__state.vd.cdrom) if self.__state.vd.rw: @@ -323,6 +325,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes else: if not (self.__state.vd.connected or self.__drive.get_image_path()): raise MsdDisconnectedError() + + await self.__unlock_drive() self.__drive.set_image_path("") await self.__remount_rw(False, fatal=False) @@ -529,6 +533,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if os.path.exists(path): logger.info("Setting up initial image %r ...", self.__initial_image) try: + await self.__unlock_drive() self.__drive.set_rw_flag(False) self.__drive.set_cdrom_flag(self.__initial_cdrom) self.__drive.set_image_path(path) @@ -597,5 +602,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes async def __remount_rw(self, rw: bool, fatal: bool=True) -> None: if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)): - if fatal: - raise MsdError("Can't execute remount helper") + pass + #raise MsdError("Can't execute remount helper") + + async def __unlock_drive(self) -> None: + await aiohelpers.unlock_drive(self.__unlock_cmd) diff --git a/kvmd/plugins/msd/otg/drive.py b/kvmd/plugins/msd/otg/drive.py index 11af7f81..ee54e5e9 100644 --- a/kvmd/plugins/msd/otg/drive.py +++ b/kvmd/plugins/msd/otg/drive.py @@ -53,10 +53,7 @@ class Drive: # ===== def set_image_path(self, path: str) -> None: - if path: - self.__set_param("file", path) - else: - self.__set_param("forced_eject", "") + self.__set_param("file", path) def get_image_path(self) -> str: return self.__get_param("file") -- 2.34.1.windows.1 ================================================ FILE: patches/custom/old-kernel-msd/v3.84-v3.92/0001-Revert-force-eject-feature-to-unlock-helper.patch ================================================ From 3d137882ac38ac046b7d09cada1883b304b04319 Mon Sep 17 00:00:00 2001 From: xe5700 <9338143+xe5700@users.noreply.github.com> Date: Fri, 20 May 2022 18:34:21 +0800 Subject: [PATCH] Revert force eject feature to unlock helper --- kvmd/aiohelpers.py | 31 ++++++++++++----- kvmd/apps/otg/__init__.py | 3 +- kvmd/apps/otgmsd/__init__.py | 25 +++++++++++++- kvmd/helpers/unlock/__init__.py | 58 ++++++++++++++++++++++++++++++++ kvmd/helpers/unlock/__main__.py | 24 +++++++++++++ kvmd/plugins/msd/otg/__init__.py | 19 ++++++++--- kvmd/plugins/msd/otg/drive.py | 5 +-- 7 files changed, 145 insertions(+), 20 deletions(-) create mode 100644 kvmd/helpers/unlock/__init__.py create mode 100644 kvmd/helpers/unlock/__main__.py diff --git a/kvmd/aiohelpers.py b/kvmd/aiohelpers.py index 6357764c..37a5d4b9 100644 --- a/kvmd/aiohelpers.py +++ b/kvmd/aiohelpers.py @@ -40,11 +40,26 @@ async def remount(name: str, base_cmd: List[str], rw: bool) -> bool: ] logger.info("Remounting %s storage to %s: %s ...", name, mode.upper(), cmd) try: - proc = await aioproc.log_process(cmd, logger) - if proc.returncode != 0: - assert proc.returncode is not None - raise subprocess.CalledProcessError(proc.returncode, cmd) - except Exception as err: - logger.error("Can't remount %s storage: %s", name, tools.efmt(err)) - return False - return True + await _run_helper(cmd) + except Exception: + logger.error("Can't remount internal storage") + raise + + +async def unlock_drive(base_cmd: List[str]) -> None: + logger = get_logger(0) + logger.info("Unlocking the drive ...") + try: + await _run_helper(base_cmd) + except Exception: + logger.error("Can't unlock the drive") + raise + + +# ===== +async def _run_helper(cmd: List[str]) -> None: + logger = get_logger(0) + logger.info("Executing helper %s ...", cmd) + proc = await aioproc.log_process(cmd, logger) + if proc.returncode != 0: + raise MsdError(f"Error while helper execution: pid={proc.pid}; retcode={proc.returncode}") diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py index cbf7a197..d0ed0554 100644 --- a/kvmd/apps/otg/__init__.py +++ b/kvmd/apps/otg/__init__.py @@ -182,7 +182,6 @@ class _GadgetConfig: _chown(join(func_path, "lun.0/cdrom"), user) _chown(join(func_path, "lun.0/ro"), user) _chown(join(func_path, "lun.0/file"), user) - _chown(join(func_path, "lun.0/forced_eject"), user) _symlink(func_path, join(self.__profile_path, func)) name = ("Mass Storage Drive" if self.__msd_instance == 0 else f"Extra Drive #{self.__msd_instance}") self.__create_meta(func, name) @@ -291,7 +290,7 @@ def _cmd_stop(config: Section) -> None: logger.info("Disabling gadget %r ...", config.otg.gadget) _write(join(gadget_path, "UDC"), "\n") - _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), optional=True) + _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), True) profile_path = join(gadget_path, usb.G_PROFILE) for func in os.listdir(profile_path): diff --git a/kvmd/apps/otgmsd/__init__.py b/kvmd/apps/otgmsd/__init__.py index f57b3107..78f8e3c7 100644 --- a/kvmd/apps/otgmsd/__init__.py +++ b/kvmd/apps/otgmsd/__init__.py @@ -21,12 +21,15 @@ import os +import signal import errno import argparse from typing import List from typing import Optional +import psutil + from ...validators.basic import valid_bool from ...validators.basic import valid_int_f0 from ...validators.os import valid_abs_file @@ -56,6 +59,21 @@ def _set_param(gadget: str, instance: int, param: str, value: str) -> None: raise +def _unlock() -> None: + # https://github.com/torvalds/linux/blob/3039fad/drivers/usb/gadget/function/f_mass_storage.c#L2924 + found = False + for proc in psutil.process_iter(): + attrs = proc.as_dict(attrs=["name", "exe", "pid"]) + if attrs.get("name") == "file-storage" and not attrs.get("exe"): + try: + proc.send_signal(signal.SIGUSR1) + found = True + except Exception as err: + raise SystemExit(f"Can't send SIGUSR1 to MSD kernel thread with pid={attrs['pid']}: {err}") + if not found: + raise SystemExit("Can't find MSD kernel thread") + + # ===== def main(argv: Optional[List[str]]=None) -> None: (parent_parser, argv, config) = init( @@ -70,6 +88,8 @@ def main(argv: Optional[List[str]]=None) -> None: ) parser.add_argument("-i", "--instance", default=0, type=valid_int_f0, metavar="", help="Drive instance (0 for KVMD drive)") + parser.add_argument("--unlock", action="store_true", + help="Send SIGUSR1 to MSD kernel thread") parser.add_argument("--set-cdrom", default=None, type=valid_bool, metavar="<1|0|yes|no>", help="Set CD-ROM flag") parser.add_argument("--set-rw", default=None, type=valid_bool, @@ -89,8 +109,11 @@ def main(argv: Optional[List[str]]=None) -> None: set_param = (lambda param, value: _set_param(config.otg.gadget, options.instance, param, value)) get_param = (lambda param: _get_param(config.otg.gadget, options.instance, param)) + if options.unlock: + _unlock() + if options.eject: - set_param("forced_eject", "") + set_param("file", "") if options.set_cdrom is not None: set_param("cdrom", str(int(options.set_cdrom))) diff --git a/kvmd/helpers/unlock/__init__.py b/kvmd/helpers/unlock/__init__.py new file mode 100644 index 00000000..140e0e7c --- /dev/null +++ b/kvmd/helpers/unlock/__init__.py @@ -0,0 +1,58 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +# ========================================================================== # + + +import sys +import signal + +import psutil + + +# ===== +_PROCESS_NAME = "file-storage" + + +# ===== +def _log(msg: str) -> None: + print(msg, file=sys.stderr) + + +def _unlock() -> None: + # https://github.com/torvalds/linux/blob/3039fad/drivers/usb/gadget/function/f_mass_storage.c#L2924 + found = False + for proc in psutil.process_iter(): + attrs = proc.as_dict(attrs=["name", "exe", "pid"]) + if attrs.get("name") == _PROCESS_NAME and not attrs.get("exe"): + _log(f"Sending SIGUSR1 to MSD {_PROCESS_NAME!r} kernel thread with pid={attrs['pid']} ...") + try: + proc.send_signal(signal.SIGUSR1) + found = True + except Exception as err: + raise SystemExit(f"Can't send SIGUSR1 to MSD kernel thread with pid={attrs['pid']}: {err}") + if not found: + raise SystemExit(f"Can't find MSD kernel thread {_PROCESS_NAME!r}") + + +# ===== +def main() -> None: + if len(sys.argv) != 2 or sys.argv[1] != "unlock": + raise SystemExit(f"Usage: {sys.argv[0]} [unlock]") + _unlock() diff --git a/kvmd/helpers/unlock/__main__.py b/kvmd/helpers/unlock/__main__.py new file mode 100644 index 00000000..3849d1b9 --- /dev/null +++ b/kvmd/helpers/unlock/__main__.py @@ -0,0 +1,24 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +# ========================================================================== # + + +from . import main +main() diff --git a/kvmd/plugins/msd/otg/__init__.py b/kvmd/plugins/msd/otg/__init__.py index 409b899a..1342c6b4 100644 --- a/kvmd/plugins/msd/otg/__init__.py +++ b/kvmd/plugins/msd/otg/__init__.py @@ -140,6 +140,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes storage_path: str, remount_cmd: List[str], + unlock_cmd: List[str], initial: Dict, @@ -154,6 +155,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes self.__meta_path = os.path.join(self.__storage_path, "meta") self.__remount_cmd = remount_cmd + self.__unlock_cmd = unlock_cmd self.__initial_image: str = initial["image"] self.__initial_cdrom: bool = initial["cdrom"] @@ -178,10 +180,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes "storage": Option("/var/lib/kvmd/msd", type=valid_abs_dir, unpack_as="storage_path"), - "remount_cmd": Option([ - "/usr/bin/sudo", "--non-interactive", - "/usr/bin/kvmd-helper-otgmsd-remount", "{mode}", - ], type=valid_command), + "remount_cmd": Option([*sudo, "/usr/bin/kvmd-helper-otgmsd-remount", "{mode}"], type=valid_command), + "unlock_cmd": Option([*sudo, "/usr/bin/kvmd-helper-otgmsd-unlock", "unlock"], type=valid_command), "initial": { "image": Option("", type=valid_printable_filename, if_empty=""), @@ -241,6 +241,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes async def reset(self) -> None: async with self.__state.busy(check_online=False): try: + await self.__unlock_drive() self.__drive.set_image_path("") self.__drive.set_rw_flag(False) self.__drive.set_cdrom_flag(False) @@ -290,12 +291,15 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if not os.path.exists(self.__state.vd.image.path): raise MsdUnknownImageError() + await self.__unlock_drive() self.__drive.set_cdrom_flag(self.__state.vd.cdrom) self.__drive.set_image_path(self.__state.vd.image.path) else: if not (self.__state.vd.connected or self.__drive.get_image_path()): raise MsdDisconnectedError() + + await self.__unlock_drive() self.__drive.set_image_path("") self.__state.vd.connected = connected @@ -474,6 +478,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if os.path.exists(path): logger.info("Setting up initial image %r ...", self.__initial_image) try: + await self.__unlock_drive() self.__drive.set_cdrom_flag(self.__initial_cdrom) self.__drive.set_image_path(path) except Exception: @@ -541,4 +546,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes async def __remount_storage(self, rw: bool) -> None: if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)): - raise MsdError("Can't execute remount helper") + pass + #raise MsdError("Can't execute remount helper") + + async def __unlock_drive(self) -> None: + await helpers.unlock_drive(self.__unlock_cmd) \ No newline at end of file diff --git a/kvmd/plugins/msd/otg/drive.py b/kvmd/plugins/msd/otg/drive.py index 11af7f81..ee54e5e9 100644 --- a/kvmd/plugins/msd/otg/drive.py +++ b/kvmd/plugins/msd/otg/drive.py @@ -53,10 +53,7 @@ class Drive: # ===== def set_image_path(self, path: str) -> None: - if path: - self.__set_param("file", path) - else: - self.__set_param("forced_eject", "") + self.__set_param("file", path) def get_image_path(self) -> str: return self.__get_param("file") -- 2.34.1.windows.1 ================================================ FILE: patches/custom/old-kernel-msd/v3.84-v3.92/0003-Allow-skip-some-features-unsupports-on-old-linux-ker.patch ================================================ From 840b9af2bc6f65851bd45eeb0cb4d629aed3423a Mon Sep 17 00:00:00 2001 From: Frank Zhang Date: Thu, 19 May 2022 22:48:49 +0800 Subject: [PATCH] Allow skip some features unsupports on old linux kernel --- kvmd/apps/otg/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py index d0ed0554..4aaeb3f8 100644 --- a/kvmd/apps/otg/__init__.py +++ b/kvmd/apps/otg/__init__.py @@ -78,8 +78,11 @@ def _unlink(path: str, optional: bool=False) -> None: os.unlink(path) -def _write(path: str, value: Union[str, int]) -> None: +def _write(path: str, value: Union[str, int], optional: bool=False) -> None: get_logger().info("WRITE --- %s", path) + if optional and not os.access(path, os.F_OK): + get_logger().info("SKIP ---- %s", path) + return with open(path, "w") as param_file: param_file.write(str(value)) @@ -158,9 +161,9 @@ class _GadgetConfig: func = f"hid.usb{self.__hid_instance}" func_path = join(self.__gadget_path, "functions", func) _mkdir(func_path) - _write(join(func_path, "no_out_endpoint"), "1") + _write(join(func_path, "no_out_endpoint"), "1", optional=True) if remote_wakeup: - _write(join(func_path, "wakeup_on_write"), "1") + _write(join(func_path, "wakeup_on_write"), "1", optional=True) _write(join(func_path, "protocol"), hid.protocol) _write(join(func_path, "subclass"), hid.subclass) _write(join(func_path, "report_length"), hid.report_length) -- 2.34.1.windows.1 ================================================ FILE: patches/disable_gpio/v3.47-v3.81/0001-Disable-GPIO-For-TV-Box.patch ================================================ From 368eaa19ef1cc187c8012c00b95ad97f260d61c3 Mon Sep 17 00:00:00 2001 From: xe5700 <9338143+xe5700@users.noreply.github.com> Date: Sun, 23 Oct 2022 11:10:24 +0800 Subject: [PATCH] Disable-GPIO-For-TV-Box --- kvmd/apps/kvmd/ugpio.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/kvmd/apps/kvmd/ugpio.py b/kvmd/apps/kvmd/ugpio.py index a8fc9224..8bf17fff 100644 --- a/kvmd/apps/kvmd/ugpio.py +++ b/kvmd/apps/kvmd/ugpio.py @@ -281,16 +281,10 @@ class UserGpio: await self.__notifier.wait() def sysprep(self) -> None: - get_logger().info("Preparing User-GPIO drivers ...") - for (_, driver) in tools.sorted_kvs(self.__drivers): - driver.prepare() + pass async def systask(self) -> None: - get_logger(0).info("Running User-GPIO drivers ...") - await asyncio.gather(*[ - driver.run() - for (_, driver) in tools.sorted_kvs(self.__drivers) - ]) + await asyncio.Event().wait() async def cleanup(self) -> None: for driver in self.__drivers.values(): -- 2.34.1.windows.1 ================================================ FILE: patches/disable_gpio/v3.82-v3.83/0001-Disable-GPIO-For-TV-Box.patch ================================================ From 960b30ec20b370269ee43282f1867861e714e33f Mon Sep 17 00:00:00 2001 From: xe5700 <9338143+xe5700@users.noreply.github.com> Date: Thu, 19 May 2022 23:01:20 +0800 Subject: [PATCH] Disable-GPIO-For-TV-Box --- kvmd/apps/kvmd/ugpio.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kvmd/apps/kvmd/ugpio.py b/kvmd/apps/kvmd/ugpio.py index cea60bb9..9328496a 100644 --- a/kvmd/apps/kvmd/ugpio.py +++ b/kvmd/apps/kvmd/ugpio.py @@ -282,15 +282,15 @@ class UserGpio: def sysprep(self) -> None: get_logger(0).info("Preparing User-GPIO drivers ...") - for (_, driver) in tools.sorted_kvs(self.__drivers): - driver.prepare() +# for (_, driver) in tools.sorted_kvs(self.__drivers): +# driver.prepare() async def systask(self) -> None: get_logger(0).info("Running User-GPIO drivers ...") - await asyncio.gather(*[ - driver.run() - for (_, driver) in tools.sorted_kvs(self.__drivers) - ]) +# await asyncio.gather(*[ +# driver.run() +# for (_, driver) in tools.sorted_kvs(self.__drivers) +# ]) async def cleanup(self) -> None: for driver in self.__drivers.values(): -- 2.34.1.windows.1 ================================================ FILE: patches/disable_gpio/v3.84-v3.134/0001-Disable-GPIO-For-TV-Box.patch ================================================ From 960b30ec20b370269ee43282f1867861e714e33f Mon Sep 17 00:00:00 2001 From: xe5700 <9338143+xe5700@users.noreply.github.com> Date: Thu, 19 May 2022 23:01:20 +0800 Subject: [PATCH] Disable-GPIO-For-TV-Box --- kvmd/apps/kvmd/ugpio.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kvmd/apps/kvmd/ugpio.py b/kvmd/apps/kvmd/ugpio.py index cea60bb9..9328496a 100644 --- a/kvmd/apps/kvmd/ugpio.py +++ b/kvmd/apps/kvmd/ugpio.py @@ -282,15 +282,16 @@ class UserGpio: def sysprep(self) -> None: get_logger(0).info("Preparing User-GPIO drivers ...") - for (_, driver) in tools.sorted_kvs(self.__drivers): - driver.prepare() +# for (_, driver) in tools.sorted_kvs(self.__drivers): +# driver.prepare() async def systask(self) -> None: get_logger(0).info("Running User-GPIO drivers ...") - await asyncio.gather(*[ - driver.run() - for (_, driver) in tools.sorted_kvs(self.__drivers) - ]) +# await asyncio.gather(*[ +# driver.run() +# for (_, driver) in tools.sorted_kvs(self.__drivers) +# ]) + await asyncio.Event().wait() async def cleanup(self) -> None: for driver in self.__drivers.values(): -- 2.34.1.windows.1 ================================================ FILE: patches/genernal/v3.84-v3.92/0001-Allow-skip-some-features-unsupports-on-old-linux-ker.patch ================================================ From 840b9af2bc6f65851bd45eeb0cb4d629aed3423a Mon Sep 17 00:00:00 2001 From: Frank Zhang Date: Thu, 19 May 2022 22:48:49 +0800 Subject: [PATCH] Allow skip some features unsupports on old linux kernel --- kvmd/apps/otg/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py index d0ed0554..4aaeb3f8 100644 --- a/kvmd/apps/otg/__init__.py +++ b/kvmd/apps/otg/__init__.py @@ -78,8 +78,11 @@ def _unlink(path: str, optional: bool=False) -> None: os.unlink(path) -def _write(path: str, value: Union[str, int]) -> None: +def _write(path: str, value: Union[str, int], optional: bool=False) -> None: get_logger().info("WRITE --- %s", path) + if optional and not os.access(path, os.F_OK): + get_logger().info("SKIP ---- %s", path) + return with open(path, "w") as param_file: param_file.write(str(value)) @@ -158,9 +161,9 @@ class _GadgetConfig: func = f"hid.usb{self.__hid_instance}" func_path = join(self.__gadget_path, "functions", func) _mkdir(func_path) - _write(join(func_path, "no_out_endpoint"), "1") + _write(join(func_path, "no_out_endpoint"), "1", optional=True) if remote_wakeup: - _write(join(func_path, "wakeup_on_write"), "1") + _write(join(func_path, "wakeup_on_write"), "1", optional=True) _write(join(func_path, "protocol"), hid.protocol) _write(join(func_path, "subclass"), hid.subclass) _write(join(func_path, "report_length"), hid.report_length) -- 2.34.1.windows.1