├── .gitmodules ├── LICENSE ├── README.md ├── VERSION ├── bin ├── yahm-backup ├── yahm-ctl ├── yahm-lxc ├── yahm-module ├── yahm-network └── yahm-ui ├── share ├── include.sh ├── tools │ ├── checks │ │ ├── check_interfaces.sh │ │ └── check_rfd.sh │ ├── hmgw │ │ ├── Makefile │ │ ├── README.md │ │ ├── eq3 │ │ │ ├── bin │ │ │ │ └── eq3configcmd │ │ │ ├── firmware │ │ │ │ ├── coprocessor_update.eq3 │ │ │ │ └── fwmap │ │ │ └── lib │ │ │ │ ├── libLanDeviceUtils.so │ │ │ │ ├── libUnifiedLanComm.so │ │ │ │ ├── libelvutils.so │ │ │ │ └── libeq3config.so │ │ ├── hmframe.cpp │ │ ├── hmframe.h │ │ ├── hmframe.o │ │ ├── hmlangw │ │ ├── hmlangw.cpp │ │ ├── hmlangw.o │ │ ├── kill-hmlan.sh │ │ ├── run-hmlan.sh │ │ └── start-hmlan.sh │ └── ubi_reader │ │ ├── LICENSE │ │ ├── README.md │ │ ├── create_build_script.py │ │ ├── output │ │ └── .gitignore │ │ ├── ubi │ │ ├── __init__.py │ │ ├── block │ │ │ ├── __init__.py │ │ │ ├── layout.py │ │ │ └── sort.py │ │ ├── defines.py │ │ ├── display.py │ │ ├── headers │ │ │ ├── __init__.py │ │ │ └── errors.py │ │ ├── image.py │ │ └── volume │ │ │ └── __init__.py │ │ ├── ubi_extract.py │ │ ├── ubi_extract_files.py │ │ ├── ubi_extract_ubifs.py │ │ ├── ubi_info.py │ │ ├── ubi_io │ │ └── __init__.py │ │ ├── ubi_utils_info.py │ │ ├── ubifs │ │ ├── __init__.py │ │ ├── defines.py │ │ ├── log.py │ │ ├── misc.py │ │ ├── nodes │ │ │ ├── __init__.py │ │ │ └── extract.py │ │ ├── output.py │ │ └── walk.py │ │ ├── ubifs_extract_files.py │ │ ├── ubifs_info.py │ │ └── ui │ │ ├── __init__.py │ │ └── common.py └── yahm_completion └── yahm-init /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "share/tools/Network-Interfaces-Script"] 2 | path = share/tools/Network-Interfaces-Script 3 | url = https://github.com/leonsio/Network-Interfaces-Script.git 4 | [submodule "share/tools/arm-board-detect"] 5 | path = share/tools/arm-board-detect 6 | url = https://github.com/leonsio/arm-board-detect.git 7 | [submodule "share/tools/rpi-source"] 8 | path = share/tools/rpi-source 9 | url = https://github.com/notro/rpi-source 10 | [submodule "share/modules"] 11 | path = share/modules 12 | url = https://github.com/leonsio/YAHM-Module 13 | [submodule "share/firmware"] 14 | path = share/firmware 15 | url = https://github.com/leonsio/YAHM-Firmware 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Spenden](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9WRZHSCVYL6XL) Falls Sie dieses Projekt unterstützen möchten, würde ich mich über einen Pull-Request oder eine Spende: [![Spenden](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9WRZHSCVYL6XL) freuen. 3 | 4 | # YAHM 5 | **Yet Another Homematic Management** - Skripte zur Einrichtung der Homematic CCU2 Oberfläche in einem LXC Container unter Debian basierten Distribution auf ARM-Basis (x86 experimentell). 6 | 7 | 8 | **Bitte lesen Sie diese Dokumentation bis zum Ende durch, bevor Sie die Anwendung installieren** 9 | 10 | Zur Zeit wurde dieses Skript auf folgender Hardware erfolgreich getestet: 11 | * Rapsberry Pi 2/3 12 | * ASUS Tinker Board 13 | * Odroid XU4 (ohne Homematic-IP) 14 | * Orange PI Plus 2 (ohne Homematic-IP) 15 | * Experimentell: x86 16 | 17 | Folgende Betrebssysteme werden aktuell unterstützt: 18 | * Debian Jessie/Stretch (ARM/x86) 19 | * Raspbian Jessie/Stretch 20 | * Armbian (Debian) Stretch 21 | * Experimentell: Ubuntu 16.04 22 | 23 | _(* Die automatische Einrichtung des HM-MOD-RPI-PCB erfolgt ->aktuell<- ausschließlich auf Rapsberry Pi 2/3 und ASUS Tinker Board)_ 24 | 25 | **Weitere Informationen und Anleitungen können dem [Wiki](https://github.com/leonsio/YAHM/wiki) bzw. dem [Homematic-Forum](https://homematic-forum.de/forum/viewforum.php?f=67) entnommen werden.** 26 | 27 | # Installation: 28 | 29 | ## Automatisiert: 30 | Es wird automatisch ein aktuelles CCU2 Image installiert und das Netzwerk konfiguriert. Diese Installation ist für wenig erfahrene Benutzer auf einem **frischen minimalen Debian/Raspbian** empfehlenswert. Die frisch installierte CCU2 wird eine IP per DHCP abrufen, diese kann durch **sudo yahm-ctl info** nach dem Start des Containers angezeigt werden. 31 | 32 | ``` 33 | wget -nv -O- https://raw.githubusercontent.com/leonsio/YAHM/master/yahm-init | sudo -E bash -s quickinstall - 34 | ``` 35 | 36 | **Hinweis:** Für die Unterstützung von Homematic-IP ist Funkmodul (HM-MOD-RPI-PCB) FW-Version ab 2.0.0 notwendig. Falls die Unterstützung nicht gebraucht wird, besteht die Möglichkeit Homematic-IP zu deaktivieren. Eine automatisierte Aktualisierung der HM-MOD-RPI-PCB Modul Firmware erfolgt nicht. 37 | 38 | 39 | ## UI Modus: 40 | "Grafisches" Installationswerkzeug. Diese Möglichkeit ist für wenig bis erfahrene Benutzer geeignet. 41 | 42 | ``` 43 | wget -nv -O- https://raw.githubusercontent.com/leonsio/YAHM/master/yahm-init | sudo -E bash -s ui - 44 | ``` 45 | 46 | ## Angepasst: 47 | 48 | Mit dieser Methode wird lediglich die aktuelle YAHM Version runtergeladen und unter **/opt/YAHM/bin** installiert, anschließend muss mit Hilfe von YAHM Tools ein [LXC Container](https://github.com/leonsio/YAHM/wiki/YAHM-LXC) mit der jeweiligen gewünschten CCU2 Version angelegt und das [Netzwerk](https://github.com/leonsio/YAHM/wiki/YAHM-Netzwerk) konfiguriert werden. Sollten Sie bereits **andere Anwendungen und Tools** installiert haben, bzw. eine **angepasste Netzerkkonfiguration** besitzen/wünschen ist diese Möglichkeit genau das richtige für Sie. 49 | 50 | ``` 51 | wget -nv -O- https://raw.githubusercontent.com/leonsio/YAHM/master/yahm-init | sudo -E bash - 52 | ``` 53 | 54 | Folgende Schritte sind **mindestens** notwendig um ein CCU2 Image innerhalb von YAHM zu installieren. Bitte beachten Sie hierfür die Hinweise im [Wiki](https://github.com/leonsio/YAHM/wiki). 55 | Mit diesen Schritten wird ein YAHM-LXC Container in der aktuellsten Version installiert (yahm-lxc install), eine neue Bridge (yahmbr0) angelegt (yahm-network create_bridge) und die Bridge einem Container zugewiesen (yahm-network attach_bridge). 56 | 57 | ``` 58 | sudo yahm-lxc install 59 | sudo yahm-network create_bridge 60 | sudo yahm-network attach_bridge 61 | ``` 62 | 63 | **Hinweis:** Das Verhalten der jeweiligen Befehle kann durch verschiedene Parameter z.B.: "yahm-lxc **-b 2.17.16** install" an eigene Bedürfnisse angepasst werden, siehe hierzu die Beschreibung der Parameter in [Wiki](https://github.com/leonsio/YAHM/wiki). 64 | 65 | Anschließend kann mit **sudo yahm-ctl start** das Container gestartet werden 66 | 67 | # Updates 68 | Mit **sudo yahm-ctl update** kann YAHM Installation (nicht CCU2 Firmware) jederzeit aktualisiert werden. Für die Aktualisierung der CCU2 Installation, siehe [LXC Container](https://github.com/leonsio/YAHM/wiki/YAHM-LXC) 69 | 70 | 71 | # EQ3 HM-MOD-RPI-PCB Funkmodul 72 | Nach der erfolgreichen Installation von YAHM kann das Funkmodul aktiviert werden, für weitere Informationen siehe [YAHM-Module](https://github.com/leonsio/YAHM/wiki/YAHM-Module) 73 | 74 | ``` 75 | yahm-module -m pivccu-driver enable 76 | ``` 77 | 78 | **Achtung:** Im Zuge der Installation wird ein Reboot benötigt 79 | 80 | **Hinweis:** Die automatische Konfiguration des Funkmoduls durch das pivccu-driver Modul, erfolgt ->aktuell<- ausschließlich auf einem Raspberry Pi und ASUS Tinker Board. Für die Installation auf einer anderen Hardware sind die Installationsschritte im [Wiki](https://github.com/leonsio/YAHM/wiki/YAHM-Module:-HM-MOD-RPI-PCB) hinterlegt. In Zukunft ist eine Unterstützung für weitere Hardware durch pivccu-driver vorgesehen. 81 | 82 | # Homematic-IP 83 | Die aktuelle CCU2 Firmware (ab 2.15.x) beinhaltet standardmäßig die Unterstützung für Homematic-IP. Die Unterstützung in YAHM wird ab der YAHM Version 1.7 durch das [Homematic-IP Modul](https://github.com/leonsio/YAHM/wiki/YAHM-Module:-Homematic-IP) und ab der Version 1.9 durch das [pivccu-driver](https://github.com/leonsio/YAHM/wiki/YAHM-Module:-PIVCCU-Driver) realisiert.
84 | Die Aktivierung der Unterstützung kann je nach Bedarf erfolgen, wird die Unterstützung für Homematic-IP nicht benötigt **kann** die CCU2 Homematic-IP Funktionalität deaktiviert werden. 85 | 86 | ## Aktivierung von Homematic-IP 87 | * Es existieren aktuell zwei Treiber, die eine Unterstütztung für Homematic-IP ermöglichen. Hinsichtlich des Funktionsumfang unterscheiden sich die Treiber nicht, nur durch die Art der Installation. 88 | * Bei dem alten "homematic-ip" Modul wird das im Raspbian Kernel einkopilierte PL011 UART Treiber durch eine gesharte Version ersetzt, hierzu ist es notwendig den Kernel neu zu kompilieren. Anschließend werden die für den Raspberry Pi bzw. Broadcom Chipsatz erstellte Homematic-IP Module installiert. Dieses Modul unterstützt ausschließlich Raspberry Pi Systeme (Broadcom Chipsatz), somit wird dieser Ansatz nicht länger verfolgt. 89 | * Bei dem neuen "pivccu-driver" wird das bestehende Kernel nicht angefasst, sondern mit Hilfe des Device-Trees das vorhandene PL011 UART Treiber überschrieben. Auf diese Weise ist es nicht länger notwendig das vorhandene Kernel neu zu kompilieren. Auf diese Weise verkürzt sich die Installation von 3-4 Stunden auf 5-10 Minuten. Durch einen generichten Treiber können weitere SoC unterstützt werden, für eine Liste unterstützter Geräte siehe das [piVCCU](https://github.com/alexreinert/piVCCU) Projekt 90 | 91 | ### pivccu-driver Modul 92 | Mit pivccu-driver wird ein generischer Treiber für verschiedene Plattformen installiert, der die Homematic-IP Unterstützung mitbringt, es wird hierbei kein neuer Kernel benötigt, die Installationsdauer beträgt etwa 5-10 Minuten. Es werden jedoch Kernel-Souren benötigt, damit die Treiber gebaut werden können. 93 | 94 | **Achtung:** Im Zuge der Installation wird ein Reboot benötigt 95 | 96 | ``` 97 | sudo yahm-module -m pivccu-driver enable 98 | ``` 99 | 100 | ### homematic-ip Modul (deprecated) 101 | Für die Unterstützung der Homematic-IP muss das Raspberry Pi Kernel neu kompiliert werden, um das vorhandene PL011 Treiber zu ersetzen, sowie müssen einige Kernel Module eingebunden werden. Alle Schritte werden durch das Homematic-IP Modul automatisch durchgeführt. Eine Interaktion seitens des Benutzers ist nicht notwendig. 102 | 103 | **Achtung:** Die Installation kann zwischen 1 und 4 Stunden dauern 104 | 105 | **Achtung:** Im Zuge der Installation wird ein Reboot benötigt 106 | 107 | ``` 108 | sudo yahm-module -m homematic-ip enable 109 | ``` 110 | 111 | ## Deaktivierung von Homematic-IP 112 | Damit in der CCU2 Oberfläche keine Fehlermeldungen hinsichtlich **HMIP-RF** bzw. **VirtualDevices** auftauchen und kein Bedarf an der Homematic-IP Unterstützung bestehen, wird empfohlen die Unterstützung von Homematic-IP durch YAHM zu deaktivieren. Alternativ kann die Modul-Firmware auf die Version 2.0.0 und höher aktualisiert werden, in diesem Fall muss das pivccu-driver Modul aktiviert werden. 113 | 114 | ``` 115 | sudo yahm-module -f -m homematic-ip disable 116 | ``` 117 | 118 | # Hinweise 119 | 120 | ## Mehrfaches Ausführen eines Befehls 121 | Alle Skripte sind so ausgelegt, dass nur fehlende Operationen durchgeführt werden. So wird z.B. das erneute Aktivieren des Homematic-IP Moduls keine Kompilierung des Kernels durchführen, falls die Module bereits vorhanden sind. 122 | 123 | Da es jedoch passieren kann, dass bei der Durchführung einiger Operationen Fehler aufkommen, ist es im ersten Selbsthilfe-Schritt möglich die Skripte ggf mit **-f** Switch auszuführen. Oft sind damit alle Probleme bereits behoben. 124 | 125 | ## Migration von RaspberryMatic > 2.15 zu YAHM 126 | RaspberryMatic aktualisiert automatisch die FW des Funkmoduls auf die Version 2.x inkl. Homematic-IP Support. Damit dieser Funkmodul unter YAHM funktioniert, muss zwingenderweise die Homematic-IP Unterstützung aktiviert werden. 127 | 128 | ## Migration CCU2/LXCCU zu YAHM 129 | Für die Migration von CCU2 bzw. LXCCU zu YAHM bitte folgenden [Wiki-Eintrag](https://github.com/leonsio/YAHM/wiki/Migration-von-CCU-zu-YAHM) beachten. Es müssen keine Geräte neu angelernt werden. Sollten LAN-Gateways im Betrieb sein, muss einmalig unter **EINSTELLUNGEN - SYSTEMSTEUERUNG - LAN GATEWAY** die Zuordnung überprüft/angepasst werden 130 | 131 | ## Kostenfaktor 132 | Dieses Projekt wurde **nicht** dafür entworfen die Anschaffungskosten einer CCU2 zu reduzieren. 133 | Eine Kalkulation mit einen Raspberry Pi (35€) zuzüglich des Funkmoduls (30€), sowie Gehäuse/Netzteil (15€) übersteigt oder gleicht sich den Anschaffungskosten einer CCU2 (ca. 70€). 134 | 135 | Für erfahrene Benutzer mit mehreren hundert Geräten/Programmen reicht die Leistung einer CCU2 nicht aus, für diese Zielgruppe wurde diese Anwendung primär entworfen. Für unerfahrene Benutzer wird weiterhin empfohlen die CCU2 zu erwerben. 136 | 137 | ## Credits 138 | Die Arbeit/Idee basiert auf der Arbeit von [bullshit](https://github.com/bullshit/lxccu) bzw. des [LXCCU](http://www.lxccu.com) Projektes, welches nicht länger aktiv ist.
139 | Overlay und generischer UART Treiber by [piVCCU](https://github.com/alexreinert/piVCCU) 140 | 141 | **Wesentliche Unterschiede zu piVCCU:** 142 | - [x] Unterstützung aktueller/älterer CCU2 Firmware ([Liste unterstützer CCU2-Versionen](https://github.com/leonsio/CCU2-FW)) 143 | - [x] Die Installation kann manuell gesteuert werden und wird nicht durch DEB-Installer vorgenommen 144 | - [x] Modulare Bauweise, es können beliebige Module und weitere Anwendungen durch vorhandene und von Community beigesteuerte Module eingebunden werden 145 | - [x] Skript und GUI-Basierte Installation/Konfiuration 146 | - [x] Unterstützung für jeden Raspbian Kernel (per deb oder rpi-update) 147 | - [x] Eingebaute Backup und Restore Funktionen 148 | - [x] Installationsroutine für Einsteiger 149 | - [x] Ein-Klick-Installation (frisches Raspbian vorausgesetzt) 150 | - [x] Skriptgesteuerte Netzwerkkonfiguration 151 | - [ ] Aktuell Unterstützung des HM-MOD-RPI-PCB (ohne IP) nur für Raspberry Pi 152 | - [ ] Keine fertigen SD Images -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.9 2 | -------------------------------------------------------------------------------- /bin/yahm-backup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Leonid Kogan 4 | # Yet Another Homematic Management 5 | # 6 | # Backup & Restore script, create and remove backups form CCU2 container and CCU2 data 7 | # 8 | 9 | 10 | # Hilfe Output 11 | show_help () 12 | { 13 | cat << EOF 14 | YAHM Backup & Restore Script 15 | 16 | Usage Example: 17 | 18 | ./yahm-backup [FLAG] COMMAND 19 | ./yahm-backup -n mycccu backup_full 20 | 21 | Flags: 22 | -n - LXC container name. 23 | -f - Force operation (no halt on errors). 24 | -v - Verbose output 25 | -d - Backup/Restore file 26 | 27 | Commands: 28 | full_backup - Create a full backup from the LXC container 29 | full_restore - Restore full backup from the LXC container 30 | data_backup - Backup CCU2 data/configuration 31 | data_restore - Restore CCU2 data/configuration 32 | EOF 33 | exit 1 34 | } 35 | 36 | PARAMETER="d:vfn:" 37 | DATA_FILE="" 38 | 39 | # Include laden 40 | source /opt/YAHM/share/include.sh 41 | 42 | # Stop on Errors only if not force 43 | if [ $IS_FORCE -ne 1 ] 44 | then 45 | set -e 46 | fi 47 | 48 | 49 | mkdir -p ${YAHM_TMP} 50 | 51 | full_backup() 52 | { 53 | if [ `check_yahm_installed` -eq 0 ] && [ $IS_FORCE -ne 1 ] 54 | then 55 | die "ERROR: No container installed" 56 | fi 57 | 58 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 59 | then 60 | die "ERROR: Can not find $LXCNAME container" 61 | fi 62 | 63 | #thkl _ only stop if container is running 64 | if [ $(lxc-info -n ${LXCNAME}|grep STOPPED|wc -l) -eq 0 ]; then 65 | progress "Trying to stop ${LXCNAME} container" 66 | lxc-stop $QUIET -k -n ${LXCNAME} 67 | fi 68 | 69 | installed_version=`get_yahm_version ${LXCNAME}` 70 | 71 | progress "Creating new backup from ${LXCNAME} version ${installed_version}" 72 | 73 | backup_file_name="yahm_backup-${installed_version}.tgz" 74 | 75 | cd /var/lib/lxc 76 | 77 | tar $VERBOSE -czf ${YAHM_TMP}/${backup_file_name} ${LXCNAME} 78 | 79 | info "STATUS: Backup was successfully created: ${YAHM_TMP}/${backup_file_name}" 80 | info "Please start ${LXCNAME} container with 'sudo yahm-ctl -n ${LXCNAME} start' manually" 81 | } 82 | 83 | full_restore() 84 | { 85 | if [ ! -f $DATA_FILE ] 86 | then 87 | die "ERROR: Restore file can not be found, please specify it with -d flag" 88 | fi 89 | 90 | info "Clean up TMP directory" 91 | rm -rf ${YAHM_TMP}/* 92 | 93 | progress "Extracting LXC backup" 94 | 95 | tar $VERBOSE -xzf $DATA_FILE -C ${YAHM_TMP} 96 | 97 | content=$(ls ${YAHM_TMP}) 98 | 99 | count=0 100 | for file in $content 101 | do 102 | if [ $count -eq 1 ] ; then 103 | die "ERROR: This does not look like a YAHM backup" 104 | fi 105 | count=$((count+1)) 106 | done 107 | 108 | if [ ! -f ${YAHM_TMP}/${content}/config ] 109 | then 110 | die "ERROR: Can not find lxc config file" 111 | else 112 | LXCNAME=$(cat ${YAHM_TMP}/${content}/config | grep lxc.utsname | cut -d " " -f 3 ) 113 | LXC_ROOT=/var/lib/lxc/$LXCNAME 114 | LXC_ROOT_FS=/var/lib/lxc/$LXCNAME/root 115 | fi 116 | 117 | if [ -d ${LXC_ROOT} ] && [ $IS_FORCE -ne 1 ] 118 | then 119 | die "ERROR: ${LXC_ROOT} is already present, use -f for overwrite" 120 | else 121 | lxc-stop -k -n ${LXCNAME} || info "No running lxc instance found, continue" 122 | rm -rf ${LXC_ROOT} 123 | fi 124 | 125 | progress "Moving backup to ${LXC_ROOT}" 126 | mv ${YAHM_TMP}/${content} ${LXC_ROOT} 127 | 128 | installed_version=`get_yahm_version ${LXCNAME}` 129 | 130 | info "Clean up" 131 | rm -rf ${YAHM_TMP}/* 132 | 133 | info "STATUS: YAHM version ${installed_version} was successfully restored" 134 | info "Please start ${LXCNAME} container with 'sudo yahm-ctl -n ${LXCNAME} start' manually" 135 | } 136 | 137 | data_backup() 138 | { 139 | if [ `check_yahm_installed` -eq 0 ] && [ $IS_FORCE -ne 1 ] 140 | then 141 | die "ERROR: No container installed" 142 | fi 143 | 144 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 145 | then 146 | die "ERROR: Can not find $LXCNAME container" 147 | fi 148 | 149 | installed_version=`get_yahm_version ${LXCNAME}` 150 | 151 | info "Clean up TMP directory" 152 | rm -rf ${YAHM_TMP}/* 153 | 154 | echo "VERSION=${installed_version}" > ${YAHM_TMP}/firmware_version 155 | 156 | if [ $(lxc-info -n ${LXCNAME}|grep RUNNING |wc -l) -eq 0 ] 157 | then 158 | die "${LXCNAME} container is not running." 159 | fi 160 | 161 | progress "Ask Rega to save the DOM" 162 | echo "#!/bin/tclsh 163 | load tclrega.so 164 | rega system.Save()" > $LXC_ROOT_FS/opt/regasave.sh 165 | chmod +x $LXC_ROOT_FS/opt/regasave.sh 166 | lxc-attach -n ${LXCNAME} -- ./opt/regasave.sh 167 | rm $LXC_ROOT_FS/opt/regasave.sh 168 | 169 | progress "Creating Backup from /usr/local folder" 170 | cd ${LXC_ROOT_FS} 171 | tar $VERBOSE -czf ${YAHM_TMP}/usr_local.tar.gz usr/local 172 | #sign the stuff so ccu will also accept the backup file 173 | lxc-attach -n ${LXCNAME} -- crypttool -s -t 1 <${YAHM_TMP}/usr_local.tar.gz >${YAHM_TMP}/signature 174 | lxc-attach -n ${LXCNAME} -- crypttool -g -t 1 >${YAHM_TMP}/key_index 175 | 176 | progress "Creating Homematic backup file" 177 | timestamp=$(date +%s) 178 | cd ${YAHM_TMP} 179 | #add data, signature and key_index 180 | tar $VERBOSE -cf ${YAHM_TMP}/homematic-ccu2-${timestamp}.sbk usr_local.tar.gz firmware_version key_index signature 181 | 182 | info "Clean up" 183 | rm -rf ${YAHM_TMP}/usr_local.tar.gz ${YAHM_TMP}/firmware_version ${YAHM_TMP}/key_index ${YAHM_TMP}/signature 184 | 185 | #if there is a -d file move the backup to this file 186 | if [ ! $DATA_FILE ] || [ "$DATA_FILE" = "" ] 187 | then 188 | info "STATUS: CCU2 Backup was successfully created: ${YAHM_TMP}/homematic-ccu2-${timestamp}.sbk" 189 | else 190 | 191 | if [[ -d $DATA_FILE ]]; then 192 | #if -d is path create my own name with day in it 193 | FILENAME="$DATA_FILE/homematic-ccu2_$(date +%d).sbk" 194 | else 195 | #else set FILENAME to -d Arg 196 | FILENAME=$DATA_FILE 197 | fi 198 | mv ${YAHM_TMP}/homematic-ccu2-${timestamp}.sbk $FILENAME 199 | info "STATUS: CCU2 Backup was successfully created: $FILENAME" 200 | fi 201 | } 202 | 203 | data_restore() 204 | { 205 | if [ ! -f $DATA_FILE ] || [ "$DATA_FILE" = "" ] 206 | then 207 | die "ERROR: Restore file can not be found, please specify it with -d flag" 208 | fi 209 | 210 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 211 | then 212 | die "ERROR: Can not find $LXCNAME container" 213 | fi 214 | 215 | info "Clean up TMP directory" 216 | rm -rf ${YAHM_TMP}/* 217 | 218 | progress "Extracting CCU2 backup" 219 | 220 | tar $VERBOSE -xf $DATA_FILE -C ${YAHM_TMP} 221 | 222 | if [ ! -f ${YAHM_TMP}/usr_local.tar.gz ] || [ ! -f ${YAHM_TMP}/firmware_version ] 223 | then 224 | die "ERROR: This does not look like a CCU2 backup" 225 | fi 226 | 227 | installed_version=`get_yahm_version ${LXCNAME}` 228 | backup_version=$(cat ${YAHM_TMP}/firmware_version | cut -d'=' -f2 ) 229 | 230 | if [ $(lxc-info -n ${LXCNAME}|grep RUNNING |wc -l) -eq 1 ] 231 | then 232 | progress "Backup is valid, trying to stop ${LXCNAME} container" 233 | lxc-stop $QUIET -k -n ${LXCNAME} 234 | fi 235 | 236 | info "WARNING: /usr/local folder with all settings inside ${LXCNAME} container will be removed. You have 5 seconds to cancel this operation..." 237 | countdown 238 | info "... to late ;)" 239 | 240 | rm -rf ${LXC_ROOT_FS}/usr/local 241 | 242 | progress "Extracting Backup Data" 243 | tar $VERBOSE -xzf ${YAHM_TMP}/usr_local.tar.gz -C ${LXC_ROOT_FS} 244 | 245 | # progress "Starting ${LXCNAME} container" 246 | # lxc-start -n ${LXCNAME} -d 247 | 248 | info "STATUS: Backup version ${backup_version} was successfully restored into ${LXCNAME} container version ${installed_version}" 249 | info "Please start ${LXCNAME} container with 'sudo yahm-ctl -n ${LXCNAME} start' manually" 250 | } 251 | 252 | if [[ $# != 1 ]]; then 253 | show_help 254 | fi 255 | 256 | for key in "$@"; do 257 | case $key in 258 | full_backup) 259 | full_backup 260 | shift 261 | break; 262 | ;; 263 | full_restore) 264 | full_restore 265 | shift 266 | break; 267 | ;; 268 | data_backup) 269 | data_backup 270 | shift 271 | break; 272 | ;; 273 | data_restore) 274 | data_restore 275 | shift 276 | break; 277 | ;; 278 | *) 279 | show_help 280 | exit 0 281 | ;; 282 | esac 283 | done 284 | 285 | -------------------------------------------------------------------------------- /bin/yahm-ctl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Leonid Kogan 4 | # Yet Another Homematic Management 5 | # 6 | # Backup & Restore script, create and remove backups form CCU2 container and CCU2 data 7 | # 8 | 9 | 10 | # Stop on Errors 11 | set -e 12 | 13 | # Hilfe Output 14 | show_help () 15 | { 16 | cat << EOF 17 | YAHM Control Script 18 | 19 | Usage Example: 20 | 21 | ./yahm-ctl [FLAG] COMMAND 22 | ./yahm-ctl -n mycccu start 23 | 24 | Flags: 25 | -n - LXC container name. 26 | -f - Force operation (no halt on errors). 27 | -v - Verbose output 28 | 29 | Commands: 30 | start - Starting LXC container 31 | stop - Stopping LXC container 32 | restart - Restarting LXC container 33 | info - Show LXC information (IP) 34 | join - Enter LXC console 35 | update - Update YAHM installation 36 | fw_update - Update HM-MOD-RPI-PCB Firmware 37 | EOF 38 | exit 1 39 | } 40 | 41 | PARAMETER="vfn:" 42 | 43 | # Include laden 44 | source /opt/YAHM/share/include.sh 45 | 46 | if [[ $# != 1 ]]; then 47 | show_help 48 | fi 49 | 50 | do_start_container() 51 | { 52 | if [ $(lxc-info -n ${LXCNAME}|grep RUNNING |wc -l) -eq 1 ] 53 | then 54 | die "${LXCNAME} container is already running" 55 | fi 56 | 57 | if [ "$BOARD_TYPE" = "Raspberry Pi" ] && [ -e ${LXC_ROOT_MODULES}/hm-mod-rpi-pcb ] 58 | then 59 | echo 0 > /sys/class/gpio/gpio18/value 60 | sleep 0.1 61 | echo 1 > /sys/class/gpio/gpio18/value 62 | fi 63 | 64 | if [ $IS_VERBOSE -eq 1 ] 65 | then 66 | progress "Starting ${LXCNAME} LXC container in debug mode" 67 | lxc-start -n ${LXCNAME} 68 | else 69 | mkdir -p /var/log/yahm 70 | progress "Starting ${LXCNAME} LXC container" 71 | lxc-start $QUIET -n ${LXCNAME} -d -L /var/log/yahm/${LXCNAME}_console.log 72 | fi 73 | } 74 | 75 | do_stop_container() 76 | { 77 | if [ $(lxc-info -n ${LXCNAME}|grep STOPPED|wc -l) -eq 1 ] 78 | then 79 | die "${LXCNAME} container is already stopped" 80 | fi 81 | 82 | if [ "$BOARD_TYPE" = "Raspberry Pi" ] && [ -e ${LXC_ROOT_MODULES}/hm-mod-rpi-pcb ] 83 | then 84 | echo 0 > /sys/class/gpio/gpio18/value 85 | fi 86 | 87 | progress "Stopping ${LXCNAME} LXC container" 88 | lxc-stop $QUIET -k -n ${LXCNAME} 89 | } 90 | 91 | for key in "$@"; do 92 | case $key in 93 | start) 94 | do_start_container 95 | shift 96 | break; 97 | ;; 98 | stop) 99 | do_stop_container 100 | shift 101 | break; 102 | ;; 103 | restart) 104 | do_stop_container 105 | sleep 1 106 | do_start_container 107 | shift 108 | break; 109 | ;; 110 | join) 111 | if [ $(lxc-info -n ${LXCNAME}|grep STOPPED|wc -l) -eq 1 ] 112 | then 113 | die "${LXCNAME} container is stopped, please start it first" 114 | fi 115 | progress "Joining LXC container, you are now inside ${LXCNAME}" 116 | lxc-attach -n ${LXCNAME} 117 | progress "Leaving LXC container, your are now on your host system" 118 | shift 119 | break; 120 | ;; 121 | info|status) 122 | info "YAHM Version: ${YAHM_VERSION}" 123 | 124 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 125 | then 126 | die "ERROR: Can not find $LXCNAME container" 127 | fi 128 | 129 | CCU2current=`get_yahm_version ${LXCNAME}` 130 | info "CCU2 Version: ${CCU2current}" 131 | 132 | echo "" 133 | if [ -e ${LXC_ROOT_MODULES}/pivccu-driver ] && [ $(lxc-info -n ${LXCNAME}|grep RUNNING |wc -l) -eq 1 ] 134 | then 135 | if [ -f /sys/module/plat_eq3ccu2/parameters/board_extended_info ] 136 | then 137 | info "Module information" 138 | module_serial=$(cat /sys/module/plat_eq3ccu2/parameters/board_serial) 139 | module_firmware=$(cat /sys/module/plat_eq3ccu2/parameters/board_extended_info) 140 | echo "Module Serial: ${module_serial}" 141 | echo "Module Firmware: ${module_firmware}" 142 | echo "" 143 | else 144 | error "Can not read pivccu-driver module information" 145 | fi 146 | fi 147 | info "LXC container information" 148 | lxc-info -n ${LXCNAME} 149 | shift 150 | break; 151 | ;; 152 | fw_update) 153 | # Container muss laufen 154 | if [ $(lxc-info -n ${LXCNAME}|grep STOPPED|wc -l) -eq 1 ] 155 | then 156 | die "ERROR: ${LXCNAME} container is stopped, please start it first" 157 | fi 158 | 159 | progress "Detecting actual firmware version" 160 | if [ -c ${LXC_ROOT_FS}/dev/bcm2835-raw-uart ] 161 | then 162 | dev_int="bcm2835-raw-uart" 163 | elif [ -c ${LXC_ROOT_FS}/dev/mxs_auart_raw.0 ] 164 | then 165 | dev_int="mxs_auart_raw.0" 166 | else 167 | dev_int="ttyAMA0" 168 | fi 169 | 170 | lxc-attach -n ${LXCNAME} -- /etc/init.d/S60multimacd stop 3>&1 1>&2 2>&3 > /dev/null 171 | lxc-attach -n ${LXCNAME} -- /etc/init.d/S61rfd stop 3>&1 1>&2 2>&3 > /dev/null 172 | 173 | # Firmware check 174 | firmware_version=`lxc-attach -n ${LXCNAME} -- /bin/eq3configcmd update-coprocessor -p /dev/${dev_int} -c -v 3>&1 1>&2 2>&3 | grep -Po 'Version: \K[^"]*'` 175 | info "Existing firmware version: ${firmware_version}" 176 | 177 | progress "Downloading firmware files" 178 | mkdir -p ${LXC_ROOT_FS}/firmware/HM-MOD-UART 179 | wget $QUIET -O ${LXC_ROOT_FS}/firmware/HM-MOD-UART/fwmap https://raw.githubusercontent.com/eq-3/occu/master/firmware/HM-MOD-UART/fwmap 180 | wget $QUIET -O ${LXC_ROOT_FS}/firmware/HM-MOD-UART/dualcopro_si1002_update_blhm.eq3 https://github.com/eq-3/occu/raw/master/firmware/HM-MOD-UART/dualcopro_si1002_update_blhm.eq3 181 | cd ${LXC_ROOT_FS}/firmware/HM-MOD-UART 182 | # comment out dual firmware 183 | sed -i '/dualcopro/s/^#//g' fwmap 184 | # comment single firmware 185 | sed -i '/coprocessor/s/^/#/g' fwmap 186 | 187 | new_firmware=$(cat fwmap | grep -v "^#" | grep CCU | awk '$1=$1' | cut -d' ' -f 3) 188 | info "Newest firmware version: ${new_firmware}" 189 | 190 | if [ $(ver ${firmware_version}) -ge $(ver ${new_firmware}) ] && [ $IS_FORCE -ne 1 ] 191 | then 192 | lxc-attach -n ${LXCNAME} -- /etc/init.d/S60multimacd start 3>&1 1>&2 2>&3 > /dev/null 193 | lxc-attach -n ${LXCNAME} -- /etc/init.d/S61rfd start 3>&1 1>&2 2>&3 > /dev/null 194 | die "ERROR: No newer firmware version found, exiting" 195 | fi 196 | 197 | error "WARNING: Trying to update the module firmware to the newest version including homematic-ip support. To cancel this operation type CTRL+C you have 5 seconds..." 198 | countdown 199 | info "... too late ;)" 200 | 201 | progress "Updating firmware this cat take some time, please dont turn off your device" 202 | lxc-attach -n ${LXCNAME} -- eq3configcmd update-coprocessor -p /dev/${dev_int} -t HM-MOD-UART -u -c -d /firmware/HM-MOD-UART 203 | sleep 5 204 | do_stop_container 205 | ;; 206 | update) 207 | cd ${YAHM_DIR} 208 | progress "Updating YAHM installation, for updatate CCU2 please use 'yahm-lxc update'" 209 | git pull --recurse-submodules 210 | progress "Updating all YAHM modules" 211 | git submodule foreach git pull origin master 212 | git submodule foreach git submodule update --init --recursive 213 | progress "Updating CCU2-FW archive" 214 | cd ${YAHM_LIB} 215 | wget $QUIET -N https://raw.githubusercontent.com/leonsio/CCU2-FW/master/fw.list 216 | 217 | # wir machen nur Updates innerhalb von Releases durch 218 | if [ "$GIT_BRANCH" != "develop" ] 219 | then 220 | if [ ! -f $YAHM_DIR/VERSION ] 221 | then 222 | info "YAHM version 1.0 (or below) found" 223 | YAHM_VERSION="1.0" 224 | else 225 | info "YAHM version ${YAHM_VERSION} found" 226 | fi 227 | 228 | if [ $(ver ${NEW_YAHM_VERSION}) -gt $(ver ${YAHM_VERSION}) ] 229 | then 230 | info "New YAHM version ${NEW_YAHM_VERSION} found, updating" 231 | 232 | # Für das nächste Release müsste ich mir was besseres einfallen lassen 233 | if [ -f ${YAHM_DIR}/share/firmware/patches/YAHM/${YAHM_OLD_VERSION}_${NEW_YAHM_VERSION}.sh ] 234 | then 235 | progress "Update File ${YAHM_OLD_VERSION}_${NEW_YAHM_VERSION}.sh found, executing" 236 | ${YAHM_DIR}/share/firmware/patches/YAHM/${YAHM_VERSION}_${NEW_YAHM_VERSION}.sh ${YAHM_DIR} 237 | fi 238 | fi 239 | fi 240 | 241 | shift 242 | break; 243 | ;; 244 | *) 245 | show_help 246 | exit 0 247 | ;; 248 | esac 249 | done 250 | -------------------------------------------------------------------------------- /bin/yahm-lxc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Leonid Kogan 4 | # Yet Another Homematic Management 5 | # 6 | # LXC management script, create new lxc container from CCU2 image 7 | # 8 | 9 | # Stop on Errors 10 | set -e 11 | 12 | # Hilfe Output 13 | show_help () 14 | { 15 | cat << EOF 16 | YAHM LXC management script 17 | 18 | Usage Example: 19 | 20 | ./yahm-lxc [FLAG] COMMAND 21 | ./yahm-lxc -n mycccu install 22 | 23 | Flags: 24 | -n - LXC container name. 25 | -f - Force operation (no halt on errors). 26 | -v - Verbose output 27 | -d - Path to custom CCU2 firmware file 28 | -b - Build number (=version) of the CCU2-FW 29 | 30 | Commands: 31 | install - Create new lxc instance with actual ccu2 image. 32 | update - Updates lxc instance to the actual version. 33 | remove - Removes lxc instance. 34 | EOF 35 | exit 1 36 | } 37 | 38 | DATA_FILE="" 39 | BUILD="" 40 | PATCH_FILE="" 41 | PARAMETER="fvn:d:b:p:" 42 | 43 | # Include laden 44 | source /opt/YAHM/share/include.sh 45 | 46 | if [ "$DATA_FILE" != "" ] && [ "$BUILD" == "" ] 47 | then 48 | die "Please specify firmware file version number with -b flag (e.g. -b 2.17.16)" 49 | fi 50 | 51 | remove_lxc_container() 52 | { 53 | if [ `check_yahm_installed` -eq 0 ] && [ $IS_FORCE -ne 1 ] 54 | then 55 | die "ERROR: No container installed" 56 | fi 57 | 58 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 59 | then 60 | die "ERROR: Can not find $LXCNAME container" 61 | fi 62 | 63 | if [ $IS_FORCE -eq 1 ] 64 | then 65 | info "Entering force mode" 66 | fi 67 | 68 | installed_version=`get_yahm_version ${LXCNAME}` 69 | 70 | info "Deleting installed YAHM container: ${LXCNAME} version: ${installed_version} To cancel this operation type CTRL+C you have 5 seconds..." 71 | countdown 72 | info "... too late ;)" 73 | 74 | progress "Stopping LXC container" 75 | lxc-stop $QUIET -k -n $LXCNAME || info "Container can not be found or is not running" 76 | 77 | progress "Deleting LXC container" 78 | rm -rf ${LXC_ROOT} 79 | 80 | progress "Clean up" 81 | rm -rf ${YAHM_LIB}/* 82 | } 83 | 84 | create_lxc_container() 85 | { 86 | if [ `check_yahm_installed` -eq 1 ] && [ $IS_FORCE -ne 1 ] 87 | then 88 | die "ERROR: YAHM instance is already installed" 89 | fi 90 | 91 | if [ $IS_FORCE -eq 1 ] ; then 92 | progress "ATTENTION: FORCE-Switch is used, actual instance will be overwritten" 93 | fi 94 | 95 | if [ "${BUILD}" == "" ] 96 | then 97 | CCU2Version=`get_ccu2_actual_version` 98 | else 99 | CCU2Version=$BUILD 100 | fi 101 | 102 | # Compatibility Check 103 | if [ `yahm_compatibility $CCU2Version` -eq 1 ] && [ $IS_FORCE -ne 1 ] 104 | then 105 | die "ERROR: We dont have any patches for this version, please call 'yahm-ctl update' and try again" 106 | fi 107 | 108 | info "Creating new lxc container name: $LXCNAME, version $CCU2Version" 109 | # Verzeichnis anlegen 110 | mkdir -p $LXC_ROOT 111 | mkdir -p $YAHM_TMP 112 | 113 | if [ "$DATA_FILE" = "" ] 114 | then 115 | download_ccu2_fw $CCU2Version 116 | else 117 | if [ ! -f $DATA_FILE ] 118 | then 119 | die "ERROR: Restore file can not be found, please specify it with -d flag" 120 | fi 121 | 122 | EQ3_FW=$DATA_FILE 123 | fi 124 | 125 | # 100kb sind mehr als jede HTML5 Fehlerseite, aber kleiner als CCU2 FW 126 | if [ $(stat -c%s "${EQ3_FW}") -le 100000 ] && [ $IS_FORCE -ne 1 ] 127 | then 128 | die "ERROR: Firmware file size is to small, please take a look on ${EQ3_FW} file" 129 | fi 130 | 131 | progress "Extracting firmware" 132 | tar $VERBOSE -xzf $EQ3_FW -C $YAHM_TMP 133 | 134 | if [ $? -eq 1 ] 135 | then 136 | die "Something is wrong with downloaded archive, exit" # SRC Ordner fehlt 137 | fi 138 | 139 | progress "Extract ubi image" 140 | $YAHM_DIR/share/tools/ubi_reader/ubi_extract_files.py $QUIET -k -o $LXC_ROOT "${YAHM_TMP}/rootfs.ubi" 141 | 142 | # most lxc commands use rootfs folder and not the config option 143 | ln -s $LXC_ROOT_FS $LXC_ROOT/rootfs 144 | 145 | if [ "$DATA_FILE" != "" ] 146 | then 147 | info "Manually CCU2 image was selected, trying to detect installed version" 148 | CCU2Version=`get_yahm_version ${LXCNAME}` 149 | 150 | if [ ! -f ${YAHM_DIR}/share/firmware/patches/${CCU2Version}.patch ] && [ $IS_FORCE -ne 1 ] 151 | then 152 | die "ERROR: Detected Version: ${CCU2Version}, can not find patch file for this version use -f for patching with version ${$LastCCU2Version}" 153 | else 154 | CCU2Version=$LastCCU2Version 155 | fi 156 | fi 157 | 158 | # try last known configuration 159 | if [ `yahm_compatibility $CCU2Version` -eq 1 ] && [ $IS_FORCE -eq 1 ] 160 | then 161 | info "INFO: No patches for version ${CCU2Version} found, trying to use old patches from ${LastCCU2Version}" 162 | CCU2Version=$LastCCU2Version 163 | fi 164 | 165 | if [ $(ver ${CCU2Version}) -ge $(ver "2.17.15") ] 166 | then 167 | progress "Cleanup CCU2 source" 168 | cd $LXC_ROOT_FS 169 | find . -iname '*.ftl' | xargs dos2unix $QUIET 170 | find . -iname '*.cgi' | xargs dos2unix $QUIET 171 | fi 172 | 173 | progress "Applying patch" 174 | cd $LXC_ROOT_FS 175 | patch --no-backup-if-mismatch $QUIET -l --input "${YAHM_DIR}/share/firmware/patches/${CCU2Version}.patch" -p1 176 | 177 | progress "Applying scripts" 178 | cd $LXC_ROOT_FS 179 | sh "${YAHM_DIR}/share/firmware/scripts/${CCU2Version}.sh" 180 | 181 | progress "Creating SD card" 182 | mkdir -p ${YAHM_LIB}/SDcard/${LXCNAME}/sd-mmcblk0 183 | touch ${YAHM_LIB}/SDcard/${LXCNAME}/sd-mmcblk0/.initialised 184 | 185 | # clean up download sources 186 | progress "Clean Up" 187 | rm -rf $YAHM_TMP 188 | 189 | # create lxc config file 190 | create_lxc_config 191 | 192 | if [ "$ARCH" = "X86" ] 193 | then 194 | info "Found x86 CPU architecture, need to install QEMU" 195 | cp /usr/bin/qemu-arm-static ${LXC_ROOT_FS}/usr/bin/ 196 | echo "lxc.arch = armhf" >> ${LXC_ROOT}/config 197 | fi 198 | 199 | CCU2current=`get_yahm_version ${LXCNAME}` 200 | info "Container version ${CCU2current} is created, please use 'yahm-ctl -n ${LXCNAME} start' to start and 'yahm-ctl -n ${LXCNAME} join' for console access" 201 | info "For network configuration see yahm-network script" 202 | 203 | echo $LXCNAME > ${YAHM_LIB}/container_name 204 | touch ${YAHM_LIB}/is_installed 205 | # set right name under defaults 206 | sed -i /etc/default/yahm -e "s/LXCNAME=.*$/LXCNAME='${LXCNAME}'/" 207 | } 208 | 209 | create_lxc_config() 210 | { 211 | # lxc config file 212 | progress "Creating lxc config file" 213 | cat > "${LXC_ROOT}/config" < lxc.start.auto=0 224 | lxc.start.auto = 1 225 | 226 | lxc.tty = 1 227 | lxc.pts = 1 228 | 229 | # cgroups 230 | lxc.cgroup.devices.allow = a 231 | 232 | # /dev/null and zero 233 | lxc.cgroup.devices.allow = c 1:3 rwm 234 | lxc.cgroup.devices.allow = c 1:5 rwm 235 | 236 | # consoles 237 | lxc.cgroup.devices.allow = c 5:1 rwm 238 | lxc.cgroup.devices.allow = c 5:0 rwm 239 | lxc.cgroup.devices.allow = c 4:0 rwm 240 | lxc.cgroup.devices.allow = c 4:1 rwm 241 | 242 | # hm-mod-rpi 243 | lxc.cgroup.devices.allow = c 204:64 rwm 244 | 245 | # hmip-rf 246 | lxc.cgroup.devices.allow = c 241:* rwm 247 | lxc.cgroup.devices.allow = c 242:* rwm 248 | lxc.cgroup.devices.allow = c 243:* rwm 249 | lxc.cgroup.devices.allow = c 244:* rwm 250 | lxc.cgroup.devices.allow = c 245:* rwm 251 | lxc.cgroup.devices.allow = c 246:* rwm 252 | lxc.cgroup.devices.allow = c 247:* rwm 253 | 254 | # /dev/{,u}random 255 | lxc.cgroup.devices.allow = c 1:9 rwm 256 | lxc.cgroup.devices.allow = c 1:8 rwm 257 | 258 | # /dev/pts/* - pts namespaces are "coming soon" 259 | lxc.cgroup.devices.allow = c 136:* rwm 260 | lxc.cgroup.devices.allow = c 5:2 rwm 261 | lxc.cgroup.devices.allow = c 10:200 rwm 262 | 263 | # includes 264 | lxc.include = ${LXC_ROOT}/config.network 265 | 266 | EOF 267 | if [ "$CODENAME" = "stretch" ] ; 268 | then 269 | echo "lxc.autodev = 0" >> ${LXC_ROOT}/config 270 | fi 271 | 272 | # blank config file 273 | touch ${LXC_ROOT}/config.network 274 | } 275 | 276 | update_lxc_container() 277 | { 278 | 279 | if [ `check_yahm_installed` -eq 0 ] && [ $IS_FORCE -ne 1 ] 280 | then 281 | die "ERROR: YAHM is not installed" 282 | fi 283 | 284 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 285 | then 286 | die "ERROR: Can not find $LXCNAME container" 287 | fi 288 | 289 | if [ $(lxc-info -n ${LXCNAME}|grep RUNNING |wc -l) -eq 0 ] 290 | then 291 | die "${LXCNAME} container is not running (needed for data backup)." 292 | fi 293 | 294 | if [ "${BUILD}" == "" ] 295 | then 296 | CCU2Version=`get_ccu2_actual_version` 297 | else 298 | CCU2Version=$BUILD 299 | fi 300 | 301 | installed_version=`get_yahm_version ${LXCNAME}` 302 | info "\nInstalled version: $installed_version" 303 | info "Available version: $CCU2Version\n" 304 | 305 | if [ $(ver $installed_version) -ge $(ver $CCU2Version) ] && [ $IS_FORCE -ne 1 ] 306 | then 307 | die "ERROR: Installed version $installed_version is greater than or equal to available version $CCU2Version" 308 | fi 309 | 310 | # Compatibility Check 311 | if [ `yahm_compatibility $CCU2Version` -eq 1 ] && [ $IS_FORCE -ne 1 ] 312 | then 313 | die "ERROR: Can not find any patches for ${CCU2Version}, please call 'yahm-ctl update' and try again" 314 | fi 315 | 316 | [ $IS_FORCE -eq 1 ] && force_switch="-f" || force_switch="" 317 | 318 | progress "Trying to create a config-backup from installed version ${installed_version}, calling: 'yahm-backup data_backup' script" 319 | if [ $IS_DEBUG -eq 1 ] ; then 320 | info "Calling: /opt/YAHM/bin/yahm-backup ${VERBOSE} ${force_switch} -n ${LXCNAME} data_backup" 321 | fi 322 | /opt/YAHM/bin/yahm-backup ${VERBOSE} ${force_switch} -n ${LXCNAME} data_backup 323 | 324 | rm -rf ${YAHM_TMP}_update/${LXCNAME}/ 325 | mkdir -p ${YAHM_TMP}_update/${LXCNAME}/ 326 | mv ${YAHM_TMP}/*.sbk ${YAHM_TMP}_update/${LXCNAME}/ 327 | 328 | progress "Backup old LXC config" 329 | cp ${LXC_ROOT}/conf* ${YAHM_TMP}_update/${LXCNAME}/ 330 | progress "Backup installed modules" 331 | cp -rf ${LXC_ROOT}/.modules ${YAHM_TMP}_update/${LXCNAME}/ 332 | 333 | data_backup_file=$(ls ${YAHM_TMP}_update/${LXCNAME}/*.sbk) 334 | info "INFO: Config backup file location: ${data_backup_file}" 335 | 336 | progress "Creating full backup from installed version ${installed_version}, calling: 'yahm-backup full_backup' script" 337 | if [ $IS_DEBUG -eq 1 ] ; then 338 | info "Calling: /opt/YAHM/bin/yahm-backup ${VERBOSE} ${force_switch} -n ${LXCNAME} full_backup" 339 | fi 340 | /opt/YAHM/bin/yahm-backup ${VERBOSE} ${force_switch} -n ${LXCNAME} full_backup 341 | 342 | mv ${YAHM_TMP}/yahm_backup-${installed_version}.tgz ${YAHM_TMP}_update/${LXCNAME}/yahm_backup-${installed_version}_$(date +%Y%m%d).tgz 343 | full_backup_file=$(ls ${YAHM_TMP}_update/${LXCNAME}/yahm_backup-*.tgz) 344 | info "INFO: Full backup file location: ${full_backup_file}" 345 | 346 | progress "Delete old YAHM Container version: $installed_version" 347 | if [ $IS_DEBUG -eq 1 ] ; then 348 | info "Calling: remove_lxc_container" 349 | fi 350 | 351 | progress "Backup virtual SD-Card" 352 | if [ -d ${LXC_ROOT}/sd-mmcblk0 ] 353 | then 354 | mkdir -p ${YAHM_LIB}/SDcard/${LXCNAME}/sd-mmcblk0 355 | cp -rf ${LXC_ROOT}/sd-mmcblk0/* ${YAHM_LIB}/SDcard/${LXCNAME}/sd-mmcblk0/ 356 | fi 357 | 358 | remove_lxc_container 359 | 360 | progress "Create new YAHM Container version: $CCU2Version" 361 | if [ $IS_DEBUG -eq 1 ] ; then 362 | info "Calling: create_lxc_container" 363 | fi 364 | create_lxc_container 365 | 366 | progress "Restoring old LXC config" 367 | cp ${YAHM_TMP}_update/${LXCNAME}/conf* ${LXC_ROOT}/ 368 | 369 | # moving SDCard path 370 | if [ $( cat ${LXC_ROOT}/config | grep SDcard | wc -l ) -eq 0 ] 371 | then 372 | # removing old value 373 | sed -i ${LXC_ROOT}/config -e '/sd-mmcblk0/d' 374 | echo "lxc.mount.entry = /var/lib/yahm/SDcard/${LXCNAME}/sd-mmcblk0 media/sd-mmcblk0/ none defaults,bind 0 0" >> ${LXC_ROOT}/config 375 | fi 376 | 377 | progress "Trying to restore old CCU2 backup, calling 'yahm-backup data_restore'" 378 | if [ $IS_DEBUG -eq 1 ] ; then 379 | info "Calling: yahm-backup ${VERBOSE} ${force_switch} -n ${LXCNAME} -d ${data_backup_file} data_restore" 380 | fi 381 | /opt/YAHM/bin/yahm-backup ${VERBOSE} ${force_switch} -n ${LXCNAME} -d ${data_backup_file} data_restore 382 | 383 | if [ $IS_DEBUG -eq 1 ] ; then 384 | info "Calling: yahm-ctl -n ${LXCNAME} start" 385 | fi 386 | yahm-ctl -n ${LXCNAME} start 387 | 388 | progress "Restoring old installed modules" 389 | files=`ls ${YAHM_TMP}_update/${LXCNAME}/.modules` 390 | 391 | for file in $files 392 | do 393 | info "Restoring ${file} Module" 394 | if [ $IS_DEBUG -eq 1 ] ; then 395 | info "Calling: /opt/YAHM/bin/yahm-module -n ${LXCNAME} ${VERBOSE} -f -m $file enable" 396 | fi 397 | /opt/YAHM/bin/yahm-module -n ${LXCNAME} ${VERBOSE} -f -m $file enable 398 | done 399 | 400 | #rm -rf ${YAHM_TMP}_update/ 401 | CCU2current=`get_yahm_version ${LXCNAME}` 402 | info "STATUS: Update was successfully new version is: ${CCU2current}" 403 | info "STATUS: Old config backup file: ${data_backup_file}" 404 | info "STATUS: Old full lxc backup file: ${full_backup_file}" 405 | } 406 | 407 | if [[ $# != 1 ]]; then 408 | show_help 409 | fi 410 | 411 | for key in "$@"; do 412 | case $key in 413 | install) 414 | shift 415 | create_lxc_container 416 | break; 417 | ;; 418 | update) 419 | update_lxc_container 420 | shift 421 | break; 422 | ;; 423 | remove) 424 | shift 425 | remove_lxc_container 426 | break; 427 | ;; 428 | *) 429 | show_help 430 | exit 0 431 | ;; 432 | esac 433 | done 434 | -------------------------------------------------------------------------------- /bin/yahm-module: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Leonid Kogan 4 | # Yet Another Homematic Management 5 | # 6 | # Module management script, install or remove modules for CCU2 container 7 | # 8 | 9 | # Stop on Errors 10 | set -e 11 | 12 | MODULE="" 13 | 14 | # Hilfe Output 15 | show_help () 16 | { 17 | cat << EOF 18 | YAHM modules management script 19 | 20 | Usage Example: 21 | 22 | ./yahm-module [FLAG] COMMAND 23 | ./yahm-module -n mycccu -m module_name enable 24 | 25 | Flags: 26 | -n - LXC container name. 27 | -f - Force operation (no halt on errors). 28 | -v - Verbose output 29 | -m - Module name 30 | 31 | Commands: 32 | enable - Enable module 33 | disable - Disable module 34 | available - Show available modules 35 | installed - Show installed modules 36 | EOF 37 | exit 1 38 | } 39 | 40 | PARAMETER="m:vfn:" 41 | 42 | # Include laden 43 | source /opt/YAHM/share/include.sh 44 | 45 | mkdir -p ${LXC_ROOT_MODULES} 46 | 47 | _module_installed() 48 | { 49 | if [ -e ${LXC_ROOT_MODULES}/${MODULE} ] ; then 50 | echo 1 51 | else 52 | echo 0 53 | fi 54 | } 55 | 56 | available_modules() 57 | { 58 | local description 59 | progress "Available modules:" 60 | files=`ls ${YAHM_DIR}/share/modules/` 61 | for file in $files 62 | do 63 | if [[ -f ${YAHM_DIR}/share/modules/${file} ]]; then 64 | description=`cat ${YAHM_DIR}/share/modules/${file} | grep description -m1 | cut -d'"' -f2` 65 | printf "\t%-20s%s\n" "$file" "$description" 66 | fi 67 | done 68 | } 69 | 70 | installed_modules() 71 | { 72 | local description 73 | progress "Installed modules:" 74 | files=`ls ${LXC_ROOT_MODULES}/` 75 | for file in $files 76 | do 77 | description=`cat ${YAHM_DIR}/share/modules/${file} | grep description -m1 | cut -d'"' -f2` 78 | printf "\t%-20s%s\n" "$file" "$description" 79 | done 80 | } 81 | 82 | enable_modules() 83 | { 84 | if [ "$MODULE" = "" ] 85 | then 86 | die "ERROR: Please specify module name with -m MODULE, show available modules with 'yahm-modules available'" 87 | fi 88 | 89 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 90 | then 91 | die "ERROR: Can not find $LXCNAME container" 92 | fi 93 | 94 | if [ `_module_installed` -eq 1 ] && [ $IS_FORCE -ne 1 ] 95 | then 96 | die "ERROR: Module ${MODULE} is already installed, use -f to overwrite it" 97 | fi 98 | 99 | progress "Executing ${MODULE} module_install()" 100 | source ${YAHM_DIR}/share/modules/${MODULE} 101 | 102 | _module_install 103 | 104 | info "STATUS: Module ${MODULE} was successfully installed" 105 | 106 | touch ${LXC_ROOT_MODULES}/${MODULE} 107 | } 108 | 109 | disable_modules() 110 | { 111 | if [ "$MODULE" = "" ] 112 | then 113 | die "ERROR: Please specify module name with -m MODULE, show available modules with 'yahm-modules available'" 114 | fi 115 | 116 | if [ `check_yahm_name $LXCNAME` -eq 0 ] && [ $IS_FORCE -ne 1 ] 117 | then 118 | die "ERROR: Can not find $LXCNAME container" 119 | fi 120 | 121 | if [ `_module_installed` -eq 0 ] && [ $IS_FORCE -ne 1 ] 122 | then 123 | die "ERROR: Module ${MODULE} is not installed, use -f force this operation" 124 | fi 125 | 126 | progress "Executing ${MODULE} module_remove()" 127 | source ${YAHM_DIR}/share/modules/${MODULE} 128 | 129 | _module_remove 130 | 131 | rm ${LXC_ROOT_MODULES}/${MODULE} 132 | 133 | info "STATUS: Module ${MODULE} was successfully removed" 134 | } 135 | 136 | 137 | if [[ $# != 1 ]]; then 138 | show_help 139 | fi 140 | 141 | for key in "$@"; do 142 | case $key in 143 | enable) 144 | enable_modules 145 | shift 146 | break; 147 | ;; 148 | disable) 149 | disable_modules 150 | shift 151 | break; 152 | ;; 153 | available) 154 | available_modules 155 | shift 156 | break; 157 | ;; 158 | installed) 159 | installed_modules 160 | shift 161 | break; 162 | ;; 163 | *) 164 | show_help 165 | exit 0 166 | ;; 167 | esac 168 | done 169 | 170 | -------------------------------------------------------------------------------- /share/include.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Leonid Kogan 4 | # Yet Another Homematic Management 5 | # 6 | # Globale Funktionen 7 | # 8 | 9 | if [[ $EUID -ne 0 ]]; then 10 | echo "This script must be run as root!" 1>&2 11 | exit 1 12 | fi 13 | 14 | #echo ${0##*/} 15 | #exit; 16 | 17 | 18 | if [ ! -f /etc/default/yahm ] 19 | then 20 | cat > /etc/default/yahm < 1 42 | IS_DEBUG=0 43 | # always froce operations -> 1 44 | IS_FORCE=0 45 | # always show verbose output -> 1 46 | IS_VERBOSE=0 47 | # show update warning -> 1 48 | SHOW_YAHM_UPDATES=1 49 | 50 | EOF 51 | 52 | fi 53 | 54 | source /etc/default/yahm 55 | 56 | 57 | 58 | ####################################### 59 | ## DO NOT CHANGE THE FOLLOWING LINES ## 60 | ####################################### 61 | 62 | # Default options 63 | YAHM_VERSION=$(cat $YAHM_DIR/VERSION) 64 | OPTIND=1 65 | QUIET="--quiet" 66 | VERBOSE="" 67 | ARCH="" 68 | 69 | # Default behavior YAHM 70 | DRY_RUN=0 71 | RESTART=1 72 | LSB_RELEASE="/usr/bin/lsb_release" 73 | DIST_ID="$($LSB_RELEASE -is)" 74 | CODENAME="$($LSB_RELEASE -cs)" 75 | 76 | # Check if we can use colours in our output 77 | use_colour=0 78 | [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null && use_colour=1 79 | 80 | # Some useful functions 81 | progress() 82 | { 83 | [ $use_colour -eq 1 ] && echo -ne "\033[01;32m" 84 | echo -e "$@" 2>&1 85 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 86 | } 87 | 88 | info() 89 | { 90 | [ $use_colour -eq 1 ] && echo -ne "\033[01;34m" 91 | echo -e "$@" 2>&1 92 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 93 | } 94 | 95 | error() 96 | { 97 | [ $use_colour -eq 1 ] && echo -ne "\033[01;31m" 98 | echo -e "$@" >&2 99 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 100 | } 101 | 102 | die () { 103 | [ $use_colour -eq 1 ] && echo -ne "\033[01;31m" 104 | echo -e "$@" >&2 105 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 106 | exit 1 107 | } 108 | 109 | # Load system information 110 | if [ -f ${YAHM_LIB}/systeminfo ] 111 | then 112 | source ${YAHM_LIB}/systeminfo 113 | else 114 | mkdir -p ${YAHM_LIB} 115 | source ${YAHM_TOOLS}/arm-board-detect/armhwinfo.sh 116 | echo "BOARD_TYPE='$BOARD_TYPE'" >> ${YAHM_LIB}/systeminfo 117 | echo "ARCH='$ARCH'" >> ${YAHM_LIB}/systeminfo 118 | echo "BOARD_VERSION='$BOARD_VERSION'" >> ${YAHM_LIB}/systeminfo 119 | fi 120 | 121 | # check architecture 122 | #case `dpkg --print-architecture` in 123 | case $ARCH in 124 | armhf|armv6l|armv7l|arm64|aarch64) 125 | ARCH="ARM" 126 | ;; 127 | i386|amd64|x86_64|i686) 128 | ARCH="X86" 129 | ;; 130 | *) 131 | die "Unsupported CPU architecture, we support only ARM and x86" 132 | ;; 133 | esac 134 | 135 | while getopts "${PARAMETER}" OPTION 136 | do 137 | case $OPTION in 138 | f) 139 | IS_FORCE=1 140 | set +e 141 | ;; 142 | b) 143 | BUILD=$OPTARG 144 | BRIDGE=$OPTARG 145 | ;; 146 | i) 147 | INTERFACE=$OPTARG 148 | ;; 149 | w) 150 | WRITE=1 151 | ;; 152 | d) 153 | DRY_RUN=1 154 | DATA_FILE=$OPTARG 155 | ;; 156 | p) 157 | PATCH_FILE=$OPTARG 158 | if [ ! -f "${PATCH_FILE}" ] 159 | then 160 | die "Specified patch file can not be found" 161 | fi 162 | ;; 163 | m) 164 | MODULE=$OPTARG 165 | # Pruefen ob Modul existiert 166 | if [ ! -f "${YAHM_DIR}/share/modules/${MODULE}" ] 167 | then 168 | die "Specified module can not be found" 169 | fi 170 | ;; 171 | r) 172 | if [ $OPTARG == "false" ] || [ $OPTARG == "no" ] || [ $OPTARG == "0" ] 173 | then 174 | RESTART=0 175 | elif [ $OPTARG == "true" ] || [ $OPTARG == "yes" ] || [ $OPTARG == "1" ] 176 | then 177 | RESTART=1 178 | else 179 | die "Unsupported option" 180 | fi 181 | ;; 182 | v) 183 | IS_VERBOSE=1 184 | QUIET="" 185 | VERBOSE="-v" 186 | ;; 187 | n) 188 | LXCNAME=$OPTARG 189 | ;; 190 | h|\?) 191 | show_help 192 | ;; 193 | esac 194 | done 195 | 196 | shift $((OPTIND-1)) 197 | 198 | [ "$1" = "--" ] && shift 199 | 200 | LXC_ROOT=/var/lib/lxc/$LXCNAME 201 | LXC_ROOT_FS=/var/lib/lxc/$LXCNAME/root 202 | LXC_ROOT_MODULES=/var/lib/lxc/$LXCNAME/.modules 203 | 204 | # Develop Branch warning 205 | cd ${YAHM_DIR} 206 | GIT_BRANCH=$(git branch | grep -e "^*" | cut -d' ' -f 2) 207 | if [ "$GIT_BRANCH" == "develop" ] 208 | then 209 | error "\n!!! You are using develop branch, this branch is unstable. Using at your own risk !!!!\n" 210 | fi 211 | 212 | get_ccu2_actual_version() 213 | { 214 | # aktuelle Version bestimmen 215 | checkVersion="http://update.homematic.com/firmware/download?cmd=js_check_version&serial=0&product=HM-CCU2" 216 | curVersion=$(wget $QUIET -qO- -T 3 -t 1 "$checkVersion" | cut -d"'" -f2) 217 | # Bei misserfolg letze bekannte Version ausgeben 218 | if [ "$curVersion" = "" ] || [ "$curVersion" = "n/a" ] 219 | then 220 | curVersion=$CCU2Version 221 | fi 222 | # Ausgabe 223 | echo $curVersion 224 | } 225 | 226 | download_ccu2_fw() 227 | { 228 | BUILD=$1 229 | aBUILD=`get_ccu2_actual_version` 230 | 231 | if [ "${BUILD}" == "" ] 232 | then 233 | BUILD=$aBUILD 234 | fi 235 | 236 | progress "Downloading CCU Firmware" 237 | # Falls wir aktuelle FW runterladen 238 | if [ $(ver ${BUILD}) -eq $(ver ${aBUILD}) ] 239 | then 240 | curFWdl="http://update.homematic.com/firmware/download?cmd=download&product=HM-CCU2&serial=0" 241 | # Falls Download nicht funktioniert aus dem GIT runterladen (archiv) 242 | wget $QUIET -T 3 -t 1 --spider "$curFWdl" || { 243 | if [ `check_ccu2_archive $BUILD` -eq 0 ] && [ $IS_FORCE -ne 1 ] 244 | then 245 | die "ERROR: Can not find specified file in repository" 246 | fi 247 | error "EQ3 site, seems to be down. Trying to download from GIT" 248 | curFWdl="https://github.com/leonsio/CCU2-FW/raw/master/HM-CCU2-${BUILD}.tgz" 249 | } 250 | fi 251 | # Falls die FW unter aktuellen liegt, aus dem GIT runterladen (archiv) 252 | if [ $(ver ${BUILD}) -lt $(ver ${aBUILD}) ] 253 | then 254 | if [ `check_ccu2_archive $BUILD` -eq 0 ] && [ $IS_FORCE -ne 1 ] 255 | then 256 | die "ERROR: Can not find specified version in repository" 257 | fi 258 | curFWdl="https://github.com/leonsio/CCU2-FW/raw/master/HM-CCU2-${BUILD}.tgz" 259 | fi 260 | # Sollte nicht vorkommen 261 | if [ $(ver ${BUILD}) -gt $(ver ${aBUILD}) ] 262 | then 263 | die "ERROR: Specified version is greater than actual version" 264 | fi 265 | 266 | EQ3_FW="${YAHM_TMP}/HM-CCU2-${BUILD}.tar.gz" 267 | 268 | wget $QUIET --tries=3 --retry-connrefused -O "$EQ3_FW" "$curFWdl" || info "Can not download file" 269 | 270 | if [ ! -f "$EQ3_FW" ] && [ $IS_FORCE -ne 1 ] 271 | then 272 | die "ERROR: Can not download firmware. Are you connected to the internet? Try to download the file manually and use -d flag" 273 | fi 274 | } 275 | 276 | check_ccu2_archive() 277 | { 278 | CCU2_VERSION=$1 279 | if [ ! -f ${YAHM_LIB}/fw.list ] 280 | then 281 | wget $QUIET -O ${YAHM_LIB}/fw.list -N https://raw.githubusercontent.com/leonsio/CCU2-FW/master/fw.list 282 | fi 283 | ALL_FW=$(cat ${YAHM_LIB}/fw.list | grep -Po '(?<=CCU2-)\d.\d\d?.\d\d?') 284 | [[ $ALL_FW =~ $CCU2_VERSION ]] && echo 1 || echo 0 285 | } 286 | 287 | # Gibt installierte Bridge zurück 288 | get_ccu2_bridge() 289 | { 290 | CCU2_BRIDGE=$(cat ${LXC_ROOT}/config.network | grep "lxc.network.link" | cut -d" " -f3) 291 | echo $CCU2_BRIDGE 292 | } 293 | 294 | # Achtung gibt nur das Erste Interface zurück, mehrere Interfaces werden nicht unterstützt 295 | get_bridge_interface() 296 | { 297 | BRIDGE_NAME=$1 298 | BRIDGE_INTERFACE=$(brctl show ${BRIDGE_NAME} | awk 'NF>1 && NR>1 {print $4}') 299 | echo $BRIDGE_INTERFACE 300 | } 301 | 302 | get_yahm_name() 303 | { 304 | if [ `check_yahm_installed` -eq 1 ] 305 | then 306 | local installed_name=`cat ${YAHM_LIB}/container_name` 307 | else 308 | echo 0 309 | fi 310 | 311 | } 312 | 313 | check_yahm_name() 314 | { 315 | if [ `check_yahm_installed` -eq 1 ] ; then 316 | local container_name=$1 317 | local installed_name=`cat ${YAHM_LIB}/container_name` 318 | 319 | if [ "$container_name" = "$installed_name" ] ; then 320 | echo 1 321 | return 1 322 | fi 323 | fi 324 | echo 0 325 | return 1 326 | } 327 | 328 | check_yahm_installed() 329 | { 330 | file="${YAHM_LIB}/is_installed" 331 | if [ -f "$file" ] 332 | then 333 | echo 1 334 | else 335 | echo 0 336 | fi 337 | } 338 | 339 | check_container_installed() 340 | { 341 | file="${LXC_ROOT}/config" 342 | if [ -f "$file" ] 343 | then 344 | echo 1 345 | else 346 | echo 0 347 | fi 348 | } 349 | 350 | 351 | get_yahm_version() 352 | { 353 | local container_name=$1 354 | local yahm_version=`cat /var/lib/lxc/${container_name}/root/boot/VERSION | cut -d'=' -f2` 355 | echo $yahm_version 356 | } 357 | 358 | yahm_compatibility() 359 | { 360 | local ccufw_version=$1 361 | if [ ! -f "${YAHM_DIR}/share/firmware/patches/${ccufw_version}.patch" ] ; then 362 | echo 1 363 | return 1 364 | fi 365 | 366 | if [ ! -f "${YAHM_DIR}/share/firmware/scripts/${ccufw_version}.sh" ] ; then 367 | echo 1 368 | return 1 369 | fi 370 | 371 | echo 0 372 | } 373 | 374 | ver() 375 | { 376 | printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' ') 377 | } 378 | 379 | countdown() 380 | { 381 | secs=$((5)) 382 | while [ $secs -gt 0 ]; do 383 | echo -ne "$secs\033[0K\r" 384 | sleep 1 385 | : $((secs--)) 386 | done 387 | } 388 | 389 | check_install_deb() 390 | { 391 | progress "Installing dependencies" 392 | packages=$1 393 | for P in $packages 394 | do 395 | dpkg -s "$P" >/dev/null 2>&1 && { 396 | info $P is installed 397 | } || { 398 | install_package "$P" 399 | } 400 | done 401 | } 402 | 403 | install_package() { 404 | package=$1 405 | info "install ${package}" 406 | apt-get -qq -y install $package 2>&1 > /dev/null 407 | return $? 408 | } 409 | 410 | ### Shared Network functionality 411 | 412 | show_bridges() 413 | { 414 | info "Available Bridges" 415 | brctl show 416 | } 417 | 418 | check_bridge_name() 419 | { 420 | local brname=$1 421 | local bridges=$(brctl show | awk 'NF>1 && NR>1 {print $1}') 422 | for bridge in $bridges 423 | do 424 | if [ "$brname" = "$bridge" ] 425 | then 426 | echo 1 427 | return 1 428 | fi 429 | done 430 | echo 0 431 | } 432 | 433 | 434 | check_interface_name() 435 | { 436 | # interface name 437 | local int_name=$1 438 | 439 | if [[ ! `ip -d link show ${int_name} 2>/dev/null ` ]]; then 440 | die "ERROR: Interface ${int_name} does not exists" 441 | elif [[ `ip -d link show ${int_name} | tail -n +2 | grep loopback` ]] ; then 442 | echo "local" 443 | elif [[ `ip -d link show ${int_name} | tail -n +2 | grep vlan` ]] ; then 444 | echo "vlan" 445 | elif [[ `ip -d link show ${int_name} | tail -n +2 | grep bridge` ]] ; then 446 | echo "bridge" 447 | else 448 | echo "physical" 449 | fi 450 | } 451 | 452 | check_git_update() 453 | { 454 | cd ${YAHM_DIR} 455 | git remote update >/dev/null 456 | LOCAL=$(git rev-parse @) 457 | REMOTE=$(git rev-parse @{u}) 458 | BASE=$(git merge-base @ @{u}) 459 | 460 | if [ $LOCAL = $REMOTE ]; then 461 | echo 0 462 | elif [ $LOCAL = $BASE ]; then 463 | echo 1 464 | fi 465 | } 466 | 467 | # Check if we have new YAHM or CCU version and inform use 468 | if [ -f ${YAHM_LIB}/version_status ] 469 | then 470 | CREATED_TIME=$(stat -c %Y ${YAHM_LIB}/version_status) 471 | source ${YAHM_LIB}/version_status 472 | else 473 | echo -e "NEW_CCU2_VERSION=\nNEW_YAHM_VERSION=" > ${YAHM_LIB}/version_status 474 | CREATED_TIME=123456789 475 | fi 476 | 477 | RUNNING_TIME=$(date +%s) 478 | 479 | if [ $((RUNNING_TIME-CREATED_TIME)) -gt 86400 ] 480 | then 481 | # get newest CCU version 482 | NEW_CCU2_VERSION=`get_ccu2_actual_version` 483 | sed -i ${YAHM_LIB}/version_status -e "s/NEW_CCU2_VERSION=.*$/NEW_CCU2_VERSION=${NEW_CCU2_VERSION}/" 484 | # get newest YAHM version 485 | NEW_YAHM_VERSION=$(wget $QUIET -O- -T 3 -t 1 https://raw.githubusercontent.com/leonsio/YAHM/master/VERSION) 486 | sed -i ${YAHM_LIB}/version_status -e "s/NEW_YAHM_VERSION=.*$/NEW_YAHM_VERSION=${NEW_YAHM_VERSION}/" 487 | fi 488 | 489 | if [ `check_yahm_installed` -eq 1 ] 490 | then 491 | CCU2current=`get_yahm_version ${LXCNAME}` 492 | if [ $(ver $NEW_CCU2_VERSION) -gt $(ver $CCU2current) ] && [ $SHOW_YAHM_UPDATES -eq 1 ] 493 | then 494 | progress "New CCU2 firmware: ${NEW_CCU2_VERSION} available, update with 'yahm-lxc update' possible" 495 | fi 496 | fi 497 | 498 | if [ $(ver ${NEW_YAHM_VERSION}) -gt $(ver ${YAHM_VERSION}) ] && [ $SHOW_YAHM_UPDATES -eq 1 ] 499 | then 500 | progress "New YAHM version: ${NEW_YAHM_VERSION} available, update with 'yahm-ctl update' possible" 501 | fi -------------------------------------------------------------------------------- /share/tools/checks/check_interfaces.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Thomas Kluge 4 | # 5 | # InterfacesList.xml Config Check 6 | # 7 | 8 | 9 | _check_result=0 10 | _check_fail="Es wurde eine fehlerhafte InterfacesList.xml Datei in /usr/local/etc/config gefunden." 11 | _check_ok="Die InterfacesList.xml sieht ok aus." 12 | 13 | _check_proceed() { 14 | # Check Interfaces.xml 15 | if [ $(cat ${LXC_ROOT_FS}/usr/local/etc/config/InterfacesList.xml|grep "BidCos-RF"|wc -l) -eq 1 ];then 16 | _check_result=1 17 | fi 18 | echo $_check_result 19 | } 20 | 21 | 22 | _check_fix_issue() { 23 | progress "Not yet implemented" 24 | } -------------------------------------------------------------------------------- /share/tools/checks/check_rfd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Thomas Kluge 4 | # 5 | # RFD Config Check 6 | # 7 | 8 | 9 | _check_result=0 10 | _check_ok="Die rfd.conf sieht ok aus." 11 | _check_fail="Es wurde das HM-MOD-RPI-PCB Modul installiert, aber eine Anpassung in der Konfigurationsdatei fehlt. Eventuell wurde diese ja durch ein eingespieltes Backup überschrieben. Bitte reinstalliere das YAHM Modul HM-MOD-RPI-PCB um dieses Problem zu lösen." 12 | 13 | 14 | _check_proceed() { 15 | # Check RFD.conf 16 | if [ -e ${LXC_ROOT_MODULES}/hm-mod-rpi-pcb ] ; then 17 | if [ $(cat ${LXC_ROOT_FS}/usr/local/etc/config/rfd.conf|grep "\Improved\ Coprocessor\ Initialization\ =\ true"|wc -l) -eq 1 ];then 18 | _check_result=1 19 | fi 20 | fi 21 | echo $_check_result 22 | } 23 | 24 | 25 | _check_fix_issue() { 26 | progress "Not yet implemented" 27 | } -------------------------------------------------------------------------------- /share/tools/hmgw/Makefile: -------------------------------------------------------------------------------- 1 | #CC = gcc-4.6 2 | CC = gcc 3 | 4 | #CFLAGS = -O2 -Wall -Wno-deprecated 5 | #CFLAGS = -O2 -pipe -Wall 6 | CFLAGS = -Wall -O2 -pipe -march=armv6j -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard 7 | 8 | all: hmlangw 9 | 10 | 11 | hmlangw: hmlangw.o hmframe.o 12 | $(CC) -o hmlangw hmlangw.o hmframe.o -lpthread 13 | 14 | # $(CC) -o hmlangw hmlangw.o hmframe.o -lutil -lpthread -lrt -lm -lstdc++ 15 | 16 | .cpp.o: 17 | $(CC) -c $(CFLAGS) $< 18 | 19 | .c.o: 20 | $(CC) -c $(CFLAGS) $< 21 | 22 | 23 | # DO NOT DELETE 24 | 25 | hmlangw.o: hmlangw.cpp hmframe.h 26 | hmframe.o: hmframe.cpp hmframe.h 27 | 28 | -------------------------------------------------------------------------------- /share/tools/hmgw/README.md: -------------------------------------------------------------------------------- 1 | Für Support siehe: [Homematic-Forum](http://homematic-forum.de/forum/viewtopic.php?f=18&t=27705) 2 | 3 | Dokumentation bzgl. Benutzung innnerhalb von YAHM siehe: [Wiki](https://github.com/leonsio/YAHM/wiki/YAHM-Module:-HM-FAKE-LAN-GW) 4 | -------------------------------------------------------------------------------- /share/tools/hmgw/eq3/bin/eq3configcmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/eq3/bin/eq3configcmd -------------------------------------------------------------------------------- /share/tools/hmgw/eq3/firmware/fwmap: -------------------------------------------------------------------------------- 1 | #Type Filename Version 2 | 3 | #Coprozessor 4 | CCU2 coprocessor_update.eq3 1.4.1 #CoProzessor CCU2 5 | 6 | -------------------------------------------------------------------------------- /share/tools/hmgw/eq3/lib/libLanDeviceUtils.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/eq3/lib/libLanDeviceUtils.so -------------------------------------------------------------------------------- /share/tools/hmgw/eq3/lib/libUnifiedLanComm.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/eq3/lib/libUnifiedLanComm.so -------------------------------------------------------------------------------- /share/tools/hmgw/eq3/lib/libelvutils.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/eq3/lib/libelvutils.so -------------------------------------------------------------------------------- /share/tools/hmgw/eq3/lib/libeq3config.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/eq3/lib/libeq3config.so -------------------------------------------------------------------------------- /share/tools/hmgw/hmframe.cpp: -------------------------------------------------------------------------------- 1 | /* HM-LGW emulation for HM-MOD-RPI 2 | * 3 | * Copyright (c) 2015 Oliver Kastl 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to 7 | * deal in the Software without restriction, including without limitation the 8 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | * sell copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "hmframe.h" 31 | 32 | 33 | int writeall( int fd, const void *buffer, int len ) 34 | { 35 | int result = 0; 36 | while( len ) 37 | { 38 | int r = write( fd, buffer, len ); 39 | if( r == -1 ) 40 | { 41 | result = r; 42 | break; 43 | } 44 | result += r; 45 | len -= r; 46 | } 47 | return result; 48 | } 49 | 50 | static const unsigned char enterBootloader[] = 51 | { 52 | 0xfd, 0x00, 0x03, 0x00, 0x00, 0x03, 0x18, 0x0a 53 | }; 54 | 55 | static const unsigned char bootloaderReply[] = 56 | { 57 | 0xfd, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x42, 0x4c, 0x72, 0x51 58 | }; 59 | 60 | int sendEnterBootloader( int fd ) 61 | { 62 | int result = -1; 63 | while( 1 ) 64 | { 65 | char buffer[80]; 66 | tcflush(fd, TCIOFLUSH); 67 | result =writeall( fd, enterBootloader, sizeof( enterBootloader ) ); 68 | if( result <= 0 ) 69 | { 70 | break; 71 | } 72 | result = readBidcosFrame( fd, buffer, sizeof( buffer ) ); 73 | if( result <= 0 ) 74 | { 75 | break; 76 | } 77 | if( isBootloaderReply( buffer, result ) ) 78 | { 79 | break; 80 | } 81 | } 82 | return result; 83 | } 84 | 85 | int sendBootloaderReply( int fd ) 86 | { 87 | return writeall( fd, bootloaderReply, sizeof( bootloaderReply ) ); 88 | } 89 | 90 | bool isBootloaderReply( const void *buffer, int len ) 91 | { 92 | if( len != sizeof(bootloaderReply) ) 93 | return false; 94 | 95 | return (memcmp( buffer, bootloaderReply, len ) == 0); 96 | } 97 | 98 | 99 | int readBidcosFrame( int fd, char *buffer, int bufsize ) 100 | { 101 | int result = 0; 102 | int msgLen = 0; 103 | int escaped = 0; 104 | int count = bufsize; 105 | unsigned char *buf = (unsigned char *)buffer; 106 | char escapeValue = 0x00; 107 | bool haveLength = false; 108 | 109 | while ( count ) 110 | { 111 | int r = read( fd, buf, 1 ); 112 | if( r <= 0 ) 113 | { 114 | result = r; 115 | perror( "ERROR: readBidcosFrame" ); 116 | break; 117 | } 118 | if( *buf == 0xFD ) // sync byte 119 | { 120 | result = 0; 121 | msgLen = 0; 122 | escaped = 0; 123 | count = bufsize; 124 | buf = (unsigned char *)buffer; 125 | escapeValue = 0x00; 126 | haveLength = false; 127 | // fprintf( stderr, "readBidcosFrame reset\n" ); 128 | } 129 | else if( result == 0 ) 130 | { 131 | fprintf( stderr, "ERROR: readBidcosFrame sync error %2.2x\n", *buf ); 132 | // No sync at beginning? Not good... 133 | break; 134 | } 135 | 136 | result += r; 137 | 138 | if( *buf == 0xFC && result > 1 ) // escape byte 139 | { 140 | escaped++; 141 | escapeValue = 0x80; 142 | // fprintf( stderr, "ESCAPE msgLen set %d result %d\n", msgLen, result ); 143 | } 144 | else 145 | { 146 | escapeValue = 0x00; 147 | } 148 | 149 | if( false == haveLength ) 150 | { 151 | if( result == 2 + escaped ) 152 | { 153 | msgLen = *buf; // MSB 154 | msgLen |= escapeValue; 155 | msgLen = msgLen<<8; 156 | } 157 | else if( result == 3 + escaped ) 158 | { 159 | msgLen |= *buf; // LSB 160 | msgLen |= escapeValue; 161 | haveLength = true; 162 | // fprintf( stderr, "readBidcosFrame msgLen set %d result %d\n", msgLen, result ); 163 | } 164 | } 165 | else if( result >= msgLen + escaped + 5 ) 166 | { 167 | // fprintf( stderr, "readBidcosFrame done, msgLen %d result %d\n", msgLen, result ); 168 | break; 169 | } 170 | 171 | count -= r; 172 | buf+=r; 173 | } 174 | 175 | return result; 176 | } 177 | -------------------------------------------------------------------------------- /share/tools/hmgw/hmframe.h: -------------------------------------------------------------------------------- 1 | /* HM-LGW emulation for HM-MOD-RPI 2 | * 3 | * Copyright (c) 2015 Oliver Kastl 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to 7 | * deal in the Software without restriction, including without limitation the 8 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | * sell copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | */ 23 | 24 | int readBidcosFrame( int fd, char *buffer, int bufsize ); 25 | int sendEnterBootloader( int fd ); 26 | int sendBootloaderReply( int fd ); 27 | int writeall( int fd, const void *buffer, int len ); 28 | bool isBootloaderReply( const void *buffer, int len ); 29 | -------------------------------------------------------------------------------- /share/tools/hmgw/hmframe.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/hmframe.o -------------------------------------------------------------------------------- /share/tools/hmgw/hmlangw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/hmlangw -------------------------------------------------------------------------------- /share/tools/hmgw/hmlangw.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/hmgw/hmlangw.o -------------------------------------------------------------------------------- /share/tools/hmgw/kill-hmlan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo killall -w hmlangw 3 | -------------------------------------------------------------------------------- /share/tools/hmgw/run-hmlan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #cd /root/hmgw 3 | cd /home/pi/hmgw 4 | sudo killall -w hmlangw 5 | sudo ./hmlangw -n auto 1> /dev/null 2> /dev/null & 6 | 7 | -------------------------------------------------------------------------------- /share/tools/hmgw/start-hmlan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo killall -w hmlangw 3 | sudo ./hmlangw -n auto -D 4 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/README.md: -------------------------------------------------------------------------------- 1 | # UBI Reader 2 | 3 | 4 | UBI Reader is a program capable of reading UBI and UBIFS images. 5 | It will provide various information about images, volumes, physical erase 6 | blocks, along with UBIFS superblock and master nodes. It is capable of walking 7 | the UBIFS index node, gathering associated directory entry, data and inode nodes, 8 | ordered by inode number. Also possible is extracting the directory tree with in 9 | with all associated files. 10 | 11 | ## Install: 12 | 13 | Python is required. 14 | 15 | python-lzo is the only non-standard module, possibly included in your disto repo, if not. 16 | 17 | $ sudo apt-get install liblzo2-dev 18 | 19 | If its available in your repos. 20 | $ sudo apt-get install python-lzo 21 | 22 | Else 23 | $ git clone https://github.com/jd-boyd/python-lzo.git 24 | $ cd python-lzo 25 | $ python setup.py install 26 | 27 | 28 | ## Usage: 29 | All scripts save output into the ubi_reader/output/ directory, depending on information 30 | available to the script, names can vary from input file name to volume names or what order 31 | the image was found. 32 | 33 | Run program with -h or --help for explanation of options. 34 | 35 | ### UBI Images: 36 | 37 | __ubi_extract.py__ 38 | 39 | $ ubi_extract.py [options] /path/to/image.ubi 40 | 41 | Extract a UBI image from file. This is useful with a NAND dump or other such binary data. 42 | Will extract any images found, as long as they have different image_seq numbers. 43 | It is possible to specify keyword args start_offset and end\_offset in the ubi\_file() call. 44 | 45 | 46 | __ubi_extract_ubifs.py__ 47 | 48 | $ ubi_extract_ubifs.py [options] /path/to/image.ubi 49 | 50 | Extract UBIFS image from file. Like ubi_extract.py, but provides the additional step of 51 | stripping out UBI information and saving the UBIFS image. 52 | 53 | 54 | __ubi\_extract\_files.py__ 55 | 56 | $ ubi_extract_files.py [options] /path/to/image.ubi 57 | 58 | Extract contents of a UBI image, saving to output. If this is not run as root, depending 59 | on files and types of files being extracted and their permissions, not all actions may 60 | be taken, and you'll see a lot of warnings. To preserve all permissions and for things 61 | such as device files run as root. Internally this creates a virtual UBIFS image to read 62 | from. As such, it is faster to extract the UBIFS image, and use ubifs\_extract\_files.py 63 | but this will save a step, and doesn't take up file system space. 64 | 65 | __ubi_info.py__ 66 | 67 | $ ubi_info.py [options] /path/to/image.ubi 68 | 69 | With no Physical Erase Block number provided, this will print all found information about 70 | the UBI images and volumes found in the file. If PEB Number is provided, it will print 71 | out the header contents of that PEB. If PEB Number not found, will print the first block. 72 | 73 | 74 | __create\_build\_script.py__ 75 | 76 | $ create_build_script.py [options] /path/to/image.ubi 77 | 78 | Analyze provided image and creates the .ini configuration file and a shell script that accepts 79 | a path(s) to a folder(s) to build a similiar configured UBI image. If Image contains multiple 80 | volumes, script will accept multiple paths. Compare to .ini file for which path corrisponds 81 | to which input. 82 | 83 | __ubi\_utils\_info.py__ 84 | 85 | $ ubi_utils_info.py [options] /path/to/image.ubi 86 | 87 | Analyze provided image, and print to console the names, option flags and values of any relevant 88 | information for use with programs such as mkfs.ubi, ubinize, ubimkvol, ubiformat, etc. Make 89 | sure to check with the relevant application, as some option flags are duplicates. 90 | 91 | 92 | ### UBIFS Images: 93 | __ubifs\_extract\_files.py__ 94 | 95 | $ ubifs_extract_files.py [options] /path/to/image.ubifs 96 | 97 | Same as ubi\_extract\_files.py but works on UBIFS images. 98 | 99 | __ubifs\_info.py__ 100 | 101 | $ ubifs_info.py [options] /path/to/image.ubifs 102 | 103 | Prints the Superblock node and Master node. 104 | 105 | ### Known Issues 106 | 107 | * Will not create a SOCK file type, instead just saves an empty text file with its name. 108 | 109 | * For NAND dumps and the like, this will not fix anything ECC would take care of, so some bad data 110 | may be extracted. 111 | 112 | * This does not replay the journal, so uncommited data will not be retrieved. 113 | 114 | * Assumes things are in good condition and where it thinks they should be... 115 | 116 | ### TODO 117 | 118 | * Organize, document, the usual. 119 | 120 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/create_build_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | import ConfigParser 24 | 25 | from ui.common import get_ubi_params, output_dir 26 | from ubi_io import ubi_file 27 | from ubi import ubi, get_peb_size 28 | 29 | def make_files(ubi, output_path): 30 | ubi_params = get_ubi_params(ubi) 31 | 32 | for img_params in ubi_params: 33 | config = ConfigParser.ConfigParser() 34 | output_img_path = os.path.join(output_path, 'img-%s' % img_params) 35 | 36 | if not os.path.exists(output_img_path): 37 | os.mkdir(output_img_path) 38 | 39 | ini_path = os.path.join(output_img_path, 'img-%s.ini' % img_params) 40 | ubi_file = os.path.join('img-%s.ubi' % img_params) 41 | script_path = os.path.join(output_img_path, 'create_ubi_img-%s.sh' % img_params) 42 | ubifs_files =[] 43 | buf = '#!/bin/sh\n' 44 | print 'Writing to: %s' % script_path 45 | 46 | with open(script_path, 'w') as fscr: 47 | with open(ini_path, 'w') as fini: 48 | print 'Writing to: %s' % ini_path 49 | vol_idx = 0 50 | 51 | for volume in ubi_params[img_params]: 52 | ubifs_files.append(os.path.join('img-%s_%s.ubifs' % (img_params, vol_idx))) 53 | ini_params = ubi_params[img_params][volume]['ini'] 54 | ini_file = 'img-%s.ini' % img_params 55 | config.add_section(volume) 56 | config.set(volume, 'mode', 'ubi') 57 | config.set(volume, 'image', ubifs_files[vol_idx]) 58 | 59 | for i in ini_params: 60 | config.set(volume, i, ini_params[i]) 61 | 62 | ubi_flags = ubi_params[img_params][volume]['flags'] 63 | ubi_args = ubi_params[img_params][volume]['args'] 64 | 65 | leb = '%s %s' % (ubi_flags['leb_size'], ubi_args['leb_size']) 66 | peb = '%s %s' % (ubi_flags['peb_size'], ubi_args['peb_size']) 67 | min_io = '%s %s' % (ubi_flags['min_io_size'], ubi_args['min_io_size']) 68 | leb_cnt = '%s %s' % (ubi_flags['max_leb_cnt'], ubi_args['max_leb_cnt']) 69 | vid_hdr = '%s %s' % (ubi_flags['vid_hdr_offset'], ubi_args['vid_hdr_offset']) 70 | sub_page = '%s %s' % (ubi_flags['sub_page_size'], ubi_args['sub_page_size']) 71 | 72 | buf += '/usr/sbin/mkfs.ubifs %s %s %s -r $%s %s\n' % (min_io, leb, leb_cnt, (vol_idx+1), ubifs_files[vol_idx]) 73 | 74 | vol_idx += 1 75 | 76 | config.write(fini) 77 | 78 | buf += '/usr/sbin/ubinize %s %s %s %s -o %s %s\n' % (peb, min_io, sub_page, vid_hdr, ubi_file, ini_file) 79 | fscr.write(buf) 80 | os.chmod(script_path, 0755) 81 | 82 | if __name__ == '__main__': 83 | description = """Gather information from the UBI image and create a 84 | ubi file along with a shell script to create a UBI image from a given path.""" 85 | usage = 'ubi_utils_info.py [options] filepath' 86 | parser = argparse.ArgumentParser(usage=usage, description=description) 87 | 88 | parser.add_argument('-p', '--peb-size', type=int, dest='block_size', 89 | help='Specify PEB size of input image.') 90 | 91 | parser.add_argument('-o', '--output-dir', dest='output_path', 92 | help='Specify output directory path.') 93 | 94 | parser.add_argument('filepath', help='File to get info from.') 95 | 96 | if len(sys.argv) == 1: 97 | parser.print_help() 98 | sys.exit() 99 | 100 | args = parser.parse_args() 101 | 102 | if args.filepath: 103 | path = args.filepath 104 | if not os.path.exists(path): 105 | parser.error("filepath doesn't exist.") 106 | 107 | if args.output_path: 108 | output_path = args.output_path 109 | else: 110 | img_name = os.path.splitext(os.path.basename(path))[0] 111 | output_path = os.path.join(output_dir, img_name) 112 | 113 | if not os.path.exists(output_path): 114 | os.makedirs(output_path) 115 | 116 | # Determine block size if not provided 117 | if args.block_size: 118 | block_size = args.block_size 119 | else: 120 | block_size = get_peb_size(path) 121 | 122 | # Create file object 123 | ufile = ubi_file(path, block_size) 124 | # Create UBI object 125 | uubi = ubi(ufile) 126 | # Print info. 127 | make_files(uubi, output_path) 128 | sys.exit() -------------------------------------------------------------------------------- /share/tools/ubi_reader/output/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import re 21 | from ubi.volume import get_volumes 22 | from ubi.block import sort, get_blocks_in_list, extract_blocks 23 | from ubi.defines import * 24 | from ubi import display 25 | from ubi.image import description as image 26 | from ubi.block import layout 27 | 28 | 29 | class ubi(): 30 | """UBI object 31 | 32 | Arguments: 33 | Obj:image -- UBI image object 34 | 35 | Attributes: 36 | Int:leb_size -- Size of Logical Erase Blocks. 37 | Int:peb_size -- Size of Physical Erase Blocks. 38 | Int:first_peb_num -- Number of the first UBI PEB in file. 39 | Int:min_io -- Size of min I/O from vid_hdr_offset. 40 | List:images -- List of UBI image objects. 41 | List:data_blocks_list -- List of all data blocks in file. 42 | List:layout_blocks_list -- List of all layout blocks in file. 43 | List:int_vol_blocks_list -- List of internal volumes minus layout. 44 | List:unknown_blocks_list -- List of blocks with unknown types. * 45 | Dict:blocks -- Dict keyed by PEB number of all blocks. 46 | * More research into these is needed. 47 | """ 48 | def __init__(self, ubi_file): 49 | self._file = ubi_file 50 | self._first_peb_num = 0 51 | self._blocks = extract_blocks(self) 52 | self._block_count = len(self.blocks) 53 | 54 | if self._block_count <= 0: 55 | raise Exception('No blocks found.') 56 | 57 | layout_list, data_list, int_vol_list, unknown_list = sort.by_type(self.blocks) 58 | 59 | self._layout_blocks_list = layout_list 60 | self._data_blocks_list = data_list 61 | self._int_vol_blocks_list = int_vol_list 62 | self._unknown_blocks_list = unknown_list 63 | 64 | arbitrary_block = self.blocks.itervalues().next() 65 | self._min_io_size = arbitrary_block.ec_hdr.vid_hdr_offset 66 | self._leb_size = self.file.block_size - arbitrary_block.ec_hdr.data_offset 67 | 68 | layout_pairs = layout.group_pairs(self.blocks, self.layout_blocks_list) 69 | 70 | # Plus 2 to adjust for layout blocks 71 | layout_infos = layout.associate_blocks(self.blocks, layout_pairs, self.first_peb_num) 72 | 73 | self._images = [] 74 | for i in range(0, len(layout_infos)): 75 | self._images.append(image(self.blocks, layout_infos[i])) 76 | 77 | def _get_file(self): 78 | """UBI File object 79 | 80 | Returns: 81 | Obj -- UBI File object. 82 | """ 83 | return self._file 84 | file = property(_get_file) 85 | 86 | 87 | def _get_images(self): 88 | """Get UBI images. 89 | 90 | Returns: 91 | List -- Of volume objects groupled by image. 92 | """ 93 | return self._images 94 | images = property(_get_images) 95 | 96 | 97 | def _get_data_blocks_list(self): 98 | """Get all UBI blocks found in file that are data blocks. 99 | 100 | Returns: 101 | List -- List of block objects. 102 | """ 103 | return self._data_blocks_list 104 | data_blocks_list = property(_get_data_blocks_list) 105 | 106 | 107 | def _get_layout_blocks_list(self): 108 | """Get all UBI blocks found in file that are layout volume blocks. 109 | 110 | Returns: 111 | List -- List of block objects. 112 | """ 113 | return self._layout_blocks_list 114 | layout_blocks_list = property(_get_layout_blocks_list) 115 | 116 | 117 | def _get_int_vol_blocks_list(self): 118 | """Get all UBI blocks found in file that are internal volume blocks. 119 | 120 | Returns: 121 | List -- List of block objects. 122 | 123 | This does not include layout blocks. 124 | """ 125 | return self._int_vol_blocks_list 126 | int_vol_blocks_list = property(_get_int_vol_blocks_list) 127 | 128 | 129 | def _get_unknown_blocks_list(self): 130 | """Get all UBI blocks found in file of unknown type.. 131 | 132 | Returns: 133 | List -- List of block objects. 134 | """ 135 | return self._unknown_blocks_list 136 | unknown_blocks_list = property(_get_unknown_blocks_list) 137 | 138 | 139 | def _get_block_count(self): 140 | """Total amount of UBI blocks in file. 141 | 142 | Returns: 143 | Int -- Number of blocks 144 | """ 145 | return self._block_count 146 | block_count = property(_get_block_count) 147 | 148 | 149 | def _set_first_peb_num(self, i): 150 | self._first_peb_num = i 151 | def _get_first_peb_num(self): 152 | """First Physical Erase Block with UBI data 153 | 154 | Returns: 155 | Int -- Number of the first PEB. 156 | """ 157 | return self._first_peb_num 158 | first_peb_num = property(_get_first_peb_num, _set_first_peb_num) 159 | 160 | 161 | def _get_leb_size(self): 162 | """LEB size of UBI blocks in file. 163 | 164 | Returns: 165 | Int -- LEB Size. 166 | """ 167 | return self._leb_size 168 | leb_size = property(_get_leb_size) 169 | 170 | 171 | def _get_peb_size(self): 172 | """PEB size of UBI blocks in file. 173 | 174 | Returns: 175 | Int -- PEB Size. 176 | """ 177 | return self.file.block_size 178 | peb_size = property(_get_peb_size) 179 | 180 | 181 | def _get_min_io_size(self): 182 | """Min I/O Size 183 | 184 | Returns: 185 | Int -- Min I/O Size. 186 | """ 187 | return self._min_io_size 188 | min_io_size = property(_get_min_io_size) 189 | 190 | 191 | def _get_blocks(self): 192 | """Main Dict of UBI Blocks 193 | 194 | Passed around for lists of indexes to be made or to be returned 195 | filtered through a list. So there isn't multiple copies of blocks, 196 | as there can be thousands. 197 | """ 198 | return self._blocks 199 | blocks = property(_get_blocks) 200 | 201 | 202 | def display(self, tab=''): 203 | """Print information about this object. 204 | 205 | Argument: 206 | Str:tab -- '\t' for spacing this object. 207 | """ 208 | display.ubi(self, tab) 209 | 210 | 211 | def get_peb_size(path): 212 | """Determine the most likely block size 213 | 214 | Arguments: 215 | Str:path -- Path to file. 216 | 217 | Returns: 218 | Int -- PEB size. 219 | 220 | Searches file for Magic Number, picks most 221 | common length between them. 222 | """ 223 | file_offset = 0 224 | offsets = [] 225 | f = open(path, 'rb') 226 | f.seek(0,2) 227 | file_size = f.tell()+1 228 | f.seek(0) 229 | 230 | for i in range(0, file_size, FILE_CHUNK_SZ): 231 | buf = f.read(FILE_CHUNK_SZ) 232 | for m in re.finditer(UBI_EC_HDR_MAGIC, buf): 233 | start = m.start() 234 | 235 | if not file_offset: 236 | file_offset = start 237 | idx = start 238 | else: 239 | idx = start+file_offset 240 | 241 | offsets.append(idx) 242 | 243 | file_offset += FILE_CHUNK_SZ 244 | f.close() 245 | 246 | occurances = {} 247 | for i in range(0, len(offsets)): 248 | try: 249 | diff = offsets[i] - offsets[i-1] 250 | except: 251 | diff = offsets[i] 252 | 253 | if diff not in occurances: 254 | occurances[diff] = 0 255 | 256 | occurances[diff] += 1 257 | 258 | most_frequent = 0 259 | block_size = 0 260 | 261 | for offset in occurances: 262 | if occurances[offset] > most_frequent: 263 | most_frequent = occurances[offset] 264 | block_size = offset 265 | 266 | return block_size 267 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/block/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import re 21 | from ubi import display 22 | from ubi.defines import * 23 | from ubi.headers import * 24 | 25 | # build block object out of data 26 | # takes raw data divided up by ec magic number 27 | class description(object): 28 | """UBI Block description Object 29 | 30 | UBI Specifications: 31 | http://www.linux-mtd.infradead.org/ -- Home page 32 | /drivers/mtd/ubi/ubi-media.h -- Header structs 33 | and defines 34 | 35 | Attributes: 36 | Obj:ec_hdr -- Error Count Header 37 | Obj:vid_hdr -- Volume ID Header 38 | List:vtbl_recs -- (Optional) List of Volume Table Records. 39 | Bool:is_vtbl -- If contains volume records table. 40 | Bool:is_internal_vol -- If Vol ID is > UBI_INTERNAL_VOL_START 41 | Bool:is_valid -- If ec_hdr & vid_hdr are error free. 42 | Int:peb_num -- Physical Erase Block number. 43 | Int:leb_num -- Logical Erase Block number. 44 | Int:file_offset -- Address location in file of this block. 45 | Int:size -- Size of total block data or PEB size. 46 | Will print out all information when invoked as a string. 47 | """ 48 | 49 | def __init__(self, block_buf): 50 | 51 | self.file_offset = -1 52 | self.peb_num = -1 53 | self.leb_num = -1 54 | self.size = -1 55 | 56 | self.vid_hdr = None 57 | self.is_internal_vol = False 58 | self.vtbl_recs = [] 59 | 60 | # TODO better understanding of block types/errors 61 | self.ec_hdr = extract_ec_hdr(block_buf[0:UBI_EC_HDR_SZ]) 62 | 63 | if not self.ec_hdr.errors: 64 | self.vid_hdr = extract_vid_hdr(block_buf[self.ec_hdr.vid_hdr_offset:self.ec_hdr.vid_hdr_offset+UBI_VID_HDR_SZ]) 65 | 66 | self.is_internal_vol = self.vid_hdr.vol_id >= UBI_INTERNAL_VOL_START 67 | 68 | if self.vid_hdr.vol_id >= UBI_INTERNAL_VOL_START: 69 | self.vtbl_recs = extract_vtbl_rec(block_buf[self.ec_hdr.data_offset:]) 70 | 71 | self.leb_num = self.vid_hdr.lnum 72 | 73 | self.is_vtbl = bool(self.vtbl_recs) or False 74 | self.is_valid = not self.ec_hdr.errors and not self.vid_hdr.errors 75 | 76 | 77 | def __repr__(self): 78 | return 'Block: PEB# %s: LEB# %s' % (self.peb_num, self.leb_num) 79 | 80 | def display(self, tab=''): 81 | display.block(self, tab) 82 | 83 | 84 | def get_blocks_in_list(blocks, idx_list): 85 | """Retrieve block objects in list of indexes 86 | 87 | Arguments: 88 | List:blocks -- List of block objects 89 | List:idx_list -- List of block indexes 90 | 91 | Returns: 92 | Dict:blocks -- List of block objects generated 93 | from provided list of indexes in 94 | order of idx_list. 95 | """ 96 | return {i:blocks[i] for i in idx_list} 97 | 98 | 99 | def extract_blocks(ubi): 100 | """Get a list of UBI block objects from file 101 | 102 | Arguments:. 103 | Obj:ubi -- UBI object. 104 | 105 | Returns: 106 | Dict -- Of block objects keyed by PEB number. 107 | """ 108 | blocks = {} 109 | start_peb = 0 110 | ubi.file.seek(ubi.file.start_offset) 111 | peb_count = 0 112 | cur_offset = 0 113 | 114 | for i in range(ubi.file.start_offset, ubi.file.end_offset, ubi.file.block_size): 115 | buf = ubi.file.read(ubi.file.block_size) 116 | 117 | if buf.startswith(UBI_EC_HDR_MAGIC): 118 | blk = description(buf) 119 | blk.file_offset = i 120 | blk.peb_num = ubi.first_peb_num + peb_count 121 | blk.size = ubi.file.block_size 122 | blocks[blk.peb_num] = blk 123 | peb_count += 1 124 | else: 125 | cur_offset += ubi.file.block_size 126 | ubi.first_peb_num = cur_offset/ubi.file.block_size 127 | ubi.file.start_offset = cur_offset 128 | 129 | return blocks 130 | 131 | 132 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/block/layout.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | from ubi.block import sort 21 | 22 | 23 | def group_pairs(blocks, layout_blocks_list): 24 | """Sort a list of layout blocks into pairs 25 | 26 | Arguments: 27 | List:blocks -- List of block objects 28 | List:layout_blocks -- List of layout block indexes 29 | 30 | Returns: 31 | List -- Layout block pair indexes grouped in a list 32 | """ 33 | layouts_grouped = [[blocks[layout_blocks_list[0]].peb_num]] 34 | 35 | for l in layout_blocks_list[1:]: 36 | for lnd in layouts_grouped: 37 | if blocks[l].vtbl_recs[0].name == blocks[lnd[0]].vtbl_recs[0].name: 38 | lnd.append(blocks[l].peb_num) 39 | break 40 | 41 | else: 42 | layouts_grouped.append([blocks[l].peb_num]) 43 | 44 | return layouts_grouped 45 | 46 | 47 | def associate_blocks(blocks, layout_pairs, start_peb_num): 48 | """Group block indexes with appropriate layout pairs 49 | 50 | Arguments: 51 | List:blocks -- List of block objects 52 | List:layout_pairs -- List of grouped layout blocks 53 | Int:start_peb_num -- Number of the PEB to start from. 54 | 55 | Returns: 56 | List -- Layout block pairs grouped with associated block ranges. 57 | """ 58 | seq_blocks = [] 59 | for layout_pair in layout_pairs: 60 | seq_blocks = sort.by_image_seq(blocks, blocks[layout_pair[0]].ec_hdr.image_seq) 61 | 62 | layout_pair.append(seq_blocks) 63 | 64 | return layout_pairs 65 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/block/sort.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | def list_by_list(blist, slist): 21 | """Sort list of block indexes, by another list. 22 | 23 | Argument: 24 | List:blist -- List of block indexes. 25 | List:slist -- Secondary list of blocks. 26 | 27 | Returns: 28 | List -- List of block indexes matching slist from blist. 29 | """ 30 | slist_blocks = [] 31 | for block in blist: 32 | if block in slist: 33 | slist_blocks.append(block) 34 | 35 | return slist_blocks 36 | 37 | def by_image_seq(blocks, image_seq): 38 | """Sort blocks by provided image_seq number. 39 | 40 | Argument: 41 | List:blocks -- List of block objects to sort. 42 | Int:image_seq -- image_seq number found in ec_hdr. 43 | 44 | Returns: 45 | List -- List of block indexes matching image_seq number. 46 | """ 47 | seq_blocks = [] 48 | for block in blocks: 49 | if blocks[block].ec_hdr.image_seq == image_seq: 50 | seq_blocks.append(block) 51 | 52 | return seq_blocks 53 | 54 | 55 | def by_range(blocks, block_range): 56 | """Sort blocks by Logical Erase Block number. 57 | 58 | Arguments: 59 | List:blocks -- List of block objects to sort. 60 | List:block_range -- range[0] = start number, range[1] = length 61 | 62 | Returns: 63 | List -- Indexes of blocks sorted by LEB. 64 | """ 65 | peb_range = range(block_range[0],block_range[1]) 66 | return [i for i in blocks if i in peb_range] 67 | 68 | 69 | def by_leb(blocks): 70 | """Sort blocks by Logical Erase Block number. 71 | 72 | Arguments: 73 | List:blocks -- List of block objects to sort. 74 | 75 | Returns: 76 | List -- Indexes of blocks sorted by LEB. 77 | """ 78 | slist_len = len(blocks) 79 | slist = ['x'] * slist_len 80 | 81 | for block in blocks: 82 | if blocks[block].leb_num >= slist_len: 83 | add_elements = blocks[block].leb_num - slist_len + 1 84 | slist += (['x'] * add_elements) 85 | slist_len = len(slist) 86 | 87 | slist[blocks[block].leb_num] = block 88 | return slist 89 | return sorted(blocks.iterkeys(), key=lambda x: blocks[x].leb_num) 90 | 91 | 92 | def by_vol_id(blocks, slist=None): 93 | """Sort blocks by volume id 94 | 95 | Arguments: 96 | Obj:blocks -- List of block objects. 97 | List:slist -- (optional) List of block indexes. 98 | 99 | Return: 100 | Dict -- blocks grouped in lists with dict key as volume id. 101 | """ 102 | 103 | vol_blocks = {} 104 | 105 | # sort block by volume 106 | # not reliable with multiple partitions (fifo) 107 | 108 | for i in blocks: 109 | if slist and i not in slist: 110 | continue 111 | elif not blocks[i].is_valid: 112 | continue 113 | 114 | if blocks[i].vid_hdr.vol_id not in vol_blocks: 115 | vol_blocks[blocks[i].vid_hdr.vol_id] = [] 116 | 117 | vol_blocks[blocks[i].vid_hdr.vol_id].append(blocks[i].peb_num) 118 | 119 | return vol_blocks 120 | 121 | 122 | def clean_bad(blocks, slist=None): 123 | """Remove blocks from list with errors 124 | 125 | Arguments: 126 | Obj:blocks -- List of block objects. 127 | List:slist -- (optional) List of block indexes. 128 | 129 | Return: 130 | List -- Of error free block objects. 131 | """ 132 | 133 | clean_blocks = [] 134 | 135 | for i in range(0, len(blocks)): 136 | if slist and i not in slist: 137 | continue 138 | 139 | if blocks[i].is_valid: 140 | clean_blocks.append(i) 141 | 142 | return clean_blocks 143 | 144 | 145 | def by_type(blocks, slist=None): 146 | """Sort blocks into layout, internal volume, data or unknown 147 | 148 | Arguments: 149 | Obj:blocks -- List of block objects. 150 | List:slist -- (optional) List of block indexes. 151 | 152 | Returns: 153 | List:layout -- List of block indexes of blocks containing the 154 | volume table records. 155 | List:data -- List of block indexes containing filesystem data. 156 | List:int_vol -- List of block indexes containing volume ids 157 | greater than UBI_INTERNAL_VOL_START that are not 158 | layout volumes. 159 | List:unknown -- List of block indexes of blocks that failed validation 160 | of crc in ed_hdr or vid_hdr. 161 | """ 162 | 163 | layout = [] 164 | data = [] 165 | int_vol = [] 166 | unknown = [] 167 | 168 | for i in blocks: 169 | if slist and i not in slist: 170 | continue 171 | 172 | if blocks[i].is_vtbl and blocks[i].is_valid: 173 | layout.append(i) 174 | 175 | elif blocks[i].is_internal_vol and blocks[i].is_valid: 176 | int_vol.append(i) 177 | 178 | elif blocks[i].is_valid: 179 | data.append(i) 180 | 181 | else: 182 | unknown.append(i) 183 | 184 | return layout, data, int_vol, unknown 185 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/defines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ############################################################# 4 | # Adapted in part from linux-source-3.2/drivers/mtd/ubi/ubi-media.h 5 | # for use in Python. 6 | # Oct. 2013 by Jason Pruitt 7 | # 8 | # Original copyright notice. 9 | # -------------------------- 10 | # 11 | # Copyright (c) International Business Machines Corp., 2006 12 | # 13 | # This program is free software; you can redistribute it and/or modify 14 | # it under the terms of the GNU General Public License as published by 15 | # the Free Software Foundation; either version 2 of the License, or 16 | # (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 21 | # the GNU General Public License for more details. 22 | # 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program; if not, write to the Free Software 25 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 | # 27 | # Authors: Artem Bityutskiy (Битюцкий Артём) 28 | # Thomas Gleixner 29 | # Frank Haverkamp 30 | # Oliver Lohmann 31 | # Andreas Arnez 32 | # 33 | ############################################################# 34 | 35 | import struct 36 | # Initial CRC32 checksum value. 37 | UBI_CRC32_INIT = 4294967295 #0xFFFFFFFF 38 | 39 | # Max number of volumes allowed. 40 | UBI_MAX_VOLUMES = 128 41 | 42 | # Internal Volume ID start. 43 | UBI_INTERNAL_VOL_START = 2147479551 44 | 45 | # Error Count header. 46 | UBI_EC_HDR_MAGIC = '\x55\x42\x49\x23' # UBI# 47 | EC_HDR_FORMAT = '>4sB3sQIII32sI' 48 | EC_HDR_FIELDS = ['magic', # Magic string UBI# 49 | 'version', # UBI version meant to accept this image. 50 | 'padding', # Reserved for future, zeros. 51 | 'ec', # Erase counter 52 | 'vid_hdr_offset', # Where the VID header starts. 53 | 'data_offset', # Where user data starts. 54 | 'image_seq', # Image sequence number 55 | 'padding2', # Reserved for future, zeros. 56 | 'hdr_crc'] # EC header crc32 checksum. 57 | 58 | 59 | UBI_EC_HDR_SZ = struct.calcsize(EC_HDR_FORMAT) # 64 60 | 61 | # Volume ID header. 62 | UBI_VID_HDR_MAGIC ='\x55\x42\x49\x21' # UBI! 63 | VID_HDR_FORMAT = '>4sBBBBII4sIIII4sQ12sI' 64 | VID_HDR_FIELDS = ['magic', # Magic string UBI! 65 | 'version', # UBI version meant to accept this image. 66 | 'vol_type', # Volume type, Dynamic/Static 67 | 'copy_flag', # If this is a copied PEB b/c of wear leveling. 68 | 'compat', # Compatibility of this volume UBI_COMPAT_* 69 | 'vol_id', # ID of this volume. 70 | 'lnum', # LEB number. 71 | 'padding', # Reserved for future, zeros. 72 | 'data_size', # How many bytes of data this contains. 73 | # Used for static types only. 74 | 'used_ebs', # Total num of used LEBs in this volume. 75 | 'data_pad', # How many bytes at end of LEB are not used. 76 | 'data_crc', # CRC32 checksum of data, static type only. 77 | 'padding2', # Reserved for future, zeros. 78 | 'sqnum', # Sequence number. 79 | 'padding3', # Reserved for future, zeros. 80 | 'hdr_crc'] # VID header CRC32 checksum. 81 | 82 | 83 | UBI_VID_HDR_SZ = struct.calcsize(VID_HDR_FORMAT) # 64 84 | 85 | # Volume table records. 86 | VTBL_REC_FORMAT = '>IIIBBH128sB23sI' 87 | VTBL_REC_FIELDS = ['reserved_pebs', # How many PEBs reserved for this volume. 88 | 'alignment', # Volume alignment. 89 | 'data_pad', # Number of unused bytes at end of PEB. 90 | 'vol_type', # Volume type, static/dynamic. 91 | 'upd_marker', # If vol update started but not finished. 92 | 'name_len', # Length of name. 93 | 'name', # Volume name. 94 | 'flags', # Volume flags 95 | 'padding', # Reserved for future, zeros. 96 | 'crc'] # Vol record CRC32 checksum. 97 | 98 | 99 | UBI_VTBL_REC_SZ = struct.calcsize(VTBL_REC_FORMAT) # 172 100 | 101 | # Volume Identifier Header 102 | UBI_VID_DYNAMIC = 1 # Volume can be resized. 103 | UBI_VID_STATIC = 2 # Volume can not be resized. 104 | PRINT_VOL_TYPE_LIST = [0, 'dynamic', 'static'] 105 | 106 | # Volume table record 107 | UBI_VTBL_AUTORESIZE_FLG = 1 108 | 109 | UBI_COMPAT_DELETE = 1 # Delete this internal volume before anything written. 110 | UBI_COMPAT_RO = 2 # Attach this device in read-only mode. 111 | UBI_COMPAT_PRESERVE = 4 # Preserve this internal volume - touch nothing. 112 | UBI_COMPAT_REJECT = 5 # Reject this UBI image 113 | PRINT_COMPAT_LIST = [0, 'Delete', 'Read Only', 0, 'Preserve', 'Reject'] 114 | 115 | # File chunk size for reads. 116 | FILE_CHUNK_SZ = 5 * 1024 * 1024 -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/display.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | from ubi.defines import PRINT_COMPAT_LIST, PRINT_VOL_TYPE_LIST, UBI_VTBL_AUTORESIZE_FLG 21 | 22 | def ubi(ubi, tab=''): 23 | print '%sUBI File' % (tab) 24 | print '%s---------------------' % (tab) 25 | print '\t%sMin I/O: %s' % (tab, ubi.min_io_size) 26 | print '\t%sLEB Size: %s' % (tab, ubi.leb_size) 27 | print '\t%sPEB Size: %s' % (tab, ubi.peb_size) 28 | print '\t%sTotal Block Count: %s' % (tab, ubi.block_count) 29 | print '\t%sData Block Count: %s' % (tab, len(ubi.data_blocks_list)) 30 | print '\t%sLayout Block Count: %s' % (tab, len(ubi.layout_blocks_list)) 31 | print '\t%sInternal Volume Block Count: %s' % (tab, len(ubi.int_vol_blocks_list)) 32 | print '\t%sUnknown Block Count: %s' % (tab, len(ubi.unknown_blocks_list)) 33 | print '\t%sFirst UBI PEB Number: %s' % (tab, ubi.first_peb_num) 34 | 35 | 36 | def image(image, tab=''): 37 | print '%s%s' % (tab, image) 38 | print '%s---------------------' % (tab) 39 | print '\t%sImage Sequence Num: %s' % (tab, image.image_seq) 40 | for volume in image.volumes: 41 | print '\t%sVolume Name:%s' % (tab, volume) 42 | print '\t%sPEB Range: %s - %s' % (tab, image.peb_range[0], image.peb_range[1]) 43 | 44 | 45 | def volume(volume, tab=''): 46 | print '%s%s' % (tab, volume) 47 | print '%s---------------------' % (tab) 48 | print '\t%sVol ID: %s' % (tab, volume.vol_id) 49 | print '\t%sName: %s' % (tab, volume.name) 50 | print '\t%sBlock Count: %s' % (tab, volume.block_count) 51 | 52 | print '\n' 53 | print '\t%sVolume Record' % (tab) 54 | print '\t%s---------------------' % (tab) 55 | vol_rec(volume.vol_rec, '\t\t%s' % tab) 56 | 57 | print '\n' 58 | 59 | 60 | def block(block, tab='\t'): 61 | print '%s%s' % (tab, block) 62 | print '%s---------------------' % (tab) 63 | print '\t%sFile Offset: %s' % (tab, block.file_offset) 64 | print '\t%sPEB #: %s' % (tab, block.peb_num) 65 | print '\t%sLEB #: %s' % (tab, block.leb_num) 66 | print '\t%sBlock Size: %s' % (tab, block.size) 67 | print '\t%sInternal Volume: %s' % (tab, block.is_internal_vol) 68 | print '\t%sIs Volume Table: %s' % (tab, block.is_vtbl) 69 | print '\t%sIs Valid: %s' % (tab, block.is_valid) 70 | 71 | if not block.ec_hdr.errors: 72 | print '\n' 73 | print '\t%sErase Count Header' % (tab) 74 | print '\t%s---------------------' % (tab) 75 | ec_hdr(block.ec_hdr, '\t\t%s' % tab) 76 | 77 | if block.vid_hdr and not block.vid_hdr.errors: 78 | print '\n' 79 | print '\t%sVID Header Header' % (tab) 80 | print '\t%s---------------------' % (tab) 81 | vid_hdr(block.vid_hdr, '\t\t%s' % tab) 82 | 83 | if block.vtbl_recs: 84 | print '\n' 85 | print '\t%sVolume Records' % (tab) 86 | print '\t%s---------------------' % (tab) 87 | for vol in block.vtbl_recs: 88 | vol_rec(vol, '\t\t%s' % tab) 89 | 90 | print '\n' 91 | 92 | 93 | def ec_hdr(ec_hdr, tab=''): 94 | for key, value in ec_hdr: 95 | if key == 'errors': 96 | value = ','.join(value) 97 | 98 | print '%s%s: %r' % (tab, key, value) 99 | 100 | 101 | def vid_hdr(vid_hdr, tab=''): 102 | for key, value in vid_hdr: 103 | if key == 'errors': 104 | value = ','.join(value) 105 | 106 | elif key == 'compat': 107 | if value in PRINT_COMPAT_LIST: 108 | value = PRINT_COMPAT_LIST[value] 109 | else: 110 | value = -1 111 | 112 | elif key == 'vol_type': 113 | if value < len(PRINT_VOL_TYPE_LIST): 114 | value = PRINT_VOL_TYPE_LIST[value] 115 | else: 116 | value = -1 117 | 118 | print '%s%s: %s' % (tab, key, value) 119 | 120 | 121 | def vol_rec(vol_rec, tab=''): 122 | for key, value in vol_rec: 123 | if key == 'errors': 124 | value = ','.join(value) 125 | elif key == 'vol_type': 126 | if value < len(PRINT_VOL_TYPE_LIST): 127 | value = PRINT_VOL_TYPE_LIST[value] 128 | else: 129 | value = -1 130 | elif key == 'flags' and value == UBI_VTBL_AUTORESIZE_FLG: 131 | value = 'autoresize' 132 | elif key == 'name': 133 | value = value.strip('\x00') 134 | 135 | print '%s%s: %s' % (tab, key, value) 136 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/headers/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import struct 21 | from ubi.defines import * 22 | from ubi.headers import errors 23 | 24 | class ec_hdr(object): 25 | def __init__(self, buf): 26 | fields = dict(zip(EC_HDR_FIELDS, struct.unpack(EC_HDR_FORMAT,buf))) 27 | for key in fields: 28 | setattr(self, key, fields[key]) 29 | setattr(self, 'errors', []) 30 | 31 | def __repr__(self): 32 | return 'Error Count Header' 33 | 34 | def __iter__(self): 35 | for key in dir(self): 36 | if not key.startswith('_'): 37 | yield key, getattr(self, key) 38 | 39 | 40 | class vid_hdr(object): 41 | def __init__(self, buf): 42 | fields = dict(zip(VID_HDR_FIELDS, struct.unpack(VID_HDR_FORMAT,buf))) 43 | for key in fields: 44 | setattr(self, key, fields[key]) 45 | setattr(self, 'errors', []) 46 | 47 | def __iter__(self): 48 | for key in dir(self): 49 | if not key.startswith('_'): 50 | yield key, getattr(self, key) 51 | 52 | def __repr__(self): 53 | return 'VID Header' 54 | 55 | 56 | class vtbl_rec(object): 57 | def __init__(self, buf): 58 | fields = dict(zip(VTBL_REC_FIELDS, struct.unpack(VTBL_REC_FORMAT,buf))) 59 | for key in fields: 60 | setattr(self, key, fields[key]) 61 | setattr(self, 'errors', []) 62 | setattr(self, 'rec_index', -1) 63 | 64 | def __repr__(self): 65 | return 'Volume Table Record: %s' % getattr(self, 'name') 66 | 67 | def __iter__(self): 68 | for key in dir(self): 69 | if not key.startswith('_'): 70 | yield key, getattr(self, key) 71 | 72 | 73 | def extract_ec_hdr(buf): 74 | ec_hdr_buf = buf 75 | ec_hdr_ret = ec_hdr(ec_hdr_buf) 76 | 77 | errors.ec_hdr(ec_hdr_ret, ec_hdr_buf) 78 | return ec_hdr_ret 79 | 80 | 81 | def extract_vid_hdr(buf): 82 | vid_hdr_buf = buf 83 | vid_hdr_ret = vid_hdr(vid_hdr_buf) 84 | 85 | errors.vid_hdr(vid_hdr_ret, vid_hdr_buf) 86 | 87 | return vid_hdr_ret 88 | 89 | 90 | def extract_vtbl_rec(buf): 91 | data_buf = buf 92 | vtbl_recs = [] 93 | vtbl_rec_ret = '' 94 | 95 | for i in range(0, UBI_MAX_VOLUMES): 96 | offset = i*UBI_VTBL_REC_SZ 97 | vtbl_rec_buf = data_buf[offset:offset+UBI_VTBL_REC_SZ] 98 | 99 | if len(vtbl_rec_buf) == UBI_VTBL_REC_SZ: 100 | vtbl_rec_ret = vtbl_rec(vtbl_rec_buf) 101 | errors.vtbl_rec(vtbl_rec_ret, vtbl_rec_buf) 102 | 103 | if len(vtbl_rec_ret.errors) == 0: 104 | vtbl_rec_ret.rec_index = i 105 | vtbl_recs.append(vtbl_rec_ret) 106 | 107 | return vtbl_recs -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/headers/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | from zlib import crc32 20 | from ubi.defines import * 21 | 22 | def ec_hdr(ec_hdr, buf): 23 | if ec_hdr.hdr_crc != (~crc32(buf[:-4]) & 0xFFFFFFFF): 24 | ec_hdr.errors.append('crc') 25 | 26 | return ec_hdr 27 | 28 | def vid_hdr(vid_hdr, buf): 29 | vid_hdr.errors = [] 30 | 31 | if vid_hdr.hdr_crc != (~crc32(buf[:-4]) & 0xFFFFFFFF): 32 | vid_hdr.errors.append('crc') 33 | 34 | return vid_hdr 35 | 36 | def vtbl_rec(vtbl_rec, buf): 37 | likely_vtbl = True 38 | 39 | if vtbl_rec.name_len != len(vtbl_rec.name.strip('\x00')): 40 | likely_vtbl = False 41 | 42 | elif vtbl_rec.vol_type not in [1,2]: 43 | likely_vtbl = False 44 | 45 | if vtbl_rec.crc != (~crc32(buf[:-4]) & 0xFFFFFFFF): 46 | vtbl_rec.errors.append('crc') 47 | 48 | if not likely_vtbl: 49 | vtbl_rec.errors = ['False'] 50 | 51 | return vtbl_rec 52 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | from ubi import display 20 | from ubi.volume import get_volumes 21 | from ubi.block import get_blocks_in_list 22 | 23 | class description(object): 24 | def __init__(self, blocks, layout_info): 25 | self._image_seq = blocks[layout_info[0]].ec_hdr.image_seq 26 | self.vid_hdr_offset = blocks[layout_info[0]].ec_hdr.vid_hdr_offset 27 | self.version = blocks[layout_info[0]].ec_hdr.version 28 | self._start_peb = min(layout_info[2]) 29 | self._end_peb = max(layout_info[2]) 30 | self._volumes = get_volumes(blocks, layout_info) 31 | 32 | 33 | def __repr__(self): 34 | return 'Image: %s' % (self.image_seq) 35 | 36 | 37 | def get_blocks(self, blocks): 38 | return get_blocks_in_list(blocks, range(self._start_peb, self._end_peb+1)) 39 | 40 | 41 | def _get_peb_range(self): 42 | return [self._start_peb, self._end_peb] 43 | peb_range = property(_get_peb_range) 44 | 45 | 46 | 47 | def _get_image_seq(self): 48 | return self._image_seq 49 | image_seq = property(_get_image_seq) 50 | 51 | 52 | def _get_volumes(self): 53 | return self._volumes 54 | volumes = property(_get_volumes) 55 | 56 | 57 | def display(self, tab=''): 58 | display.image(self, tab) -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi/volume/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | from ubi import display 21 | from ubi.block import sort, get_blocks_in_list 22 | 23 | class description(object): 24 | """UBI Volume object 25 | 26 | Attributes: 27 | Int:vol_id -- Volume ID 28 | Str:vol_name -- Name of volume. 29 | Obj:vol_rec -- Volume record object 30 | Int:block_count -- Number of block associated with volume. 31 | 32 | Methods: 33 | display(tab) -- Print Volume information 34 | Str:tab -- (optional) '\t' to preface lines with. 35 | 36 | get_blocks(blocks) -- Returns list of block objects tied to this volume 37 | 38 | Volume object is basically a list of block indexes and some metadata 39 | describing a volume found in a UBI image. 40 | """ 41 | def __init__(self, vol_id, vol_rec, block_list): 42 | self._vol_id = vol_id 43 | self._vol_rec = vol_rec 44 | self._name = self._vol_rec.name 45 | self._block_list = block_list 46 | 47 | def __repr__(self): 48 | return 'Volume: %s' % (self.name) 49 | 50 | 51 | def _get_name(self): 52 | return self._name 53 | name = property(_get_name) 54 | 55 | 56 | def _get_vol_id(self): 57 | return self._vol_id 58 | vol_id = property(_get_vol_id) 59 | 60 | 61 | def _get_block_count(self): 62 | return len(self._block_list) 63 | block_count = property(_get_block_count) 64 | 65 | 66 | def _get_vol_rec(self): 67 | return self._vol_rec 68 | vol_rec = property(_get_vol_rec) 69 | 70 | 71 | def _get_block_list(self): 72 | return self._block_list 73 | block_list = property(_get_block_list) 74 | 75 | 76 | def get_blocks(self, blocks): 77 | return get_blocks_in_list(blocks, self._block_list) 78 | 79 | 80 | def display(self, tab=''): 81 | display.volume(self, tab) 82 | 83 | def reader(self, ubi): 84 | last_leb = 0 85 | for block in sort.by_leb(self.get_blocks(ubi.blocks)): 86 | if block == 'x': 87 | #while 0 != (ubi.blocks[block].leb_num - last_leb): 88 | last_leb += 1 89 | yield '\xff'*ubi.leb_size 90 | else: 91 | last_leb += 1 92 | yield ubi.file.read_block_data(ubi.blocks[block]) 93 | 94 | 95 | def get_volumes(blocks, layout_info): 96 | """Get a list of UBI volume objects from list of blocks 97 | 98 | Arguments: 99 | List:blocks -- List of layout block objects 100 | List:layout_info -- Layout info (indexes of layout blocks and 101 | associated data blocks.) 102 | 103 | Returns: 104 | Dict -- Of Volume objects by volume name, including any 105 | relevant blocks. 106 | """ 107 | volumes = {} 108 | 109 | vol_blocks_lists = sort.by_vol_id(blocks, layout_info[2]) 110 | 111 | for vol_rec in blocks[layout_info[0]].vtbl_recs: 112 | vol_name = vol_rec.name.strip('\x00') 113 | if vol_rec.rec_index not in vol_blocks_lists: 114 | vol_blocks_lists[vol_rec.rec_index] = [] 115 | volumes[vol_name] = description(vol_rec.rec_index, vol_rec, vol_blocks_lists[vol_rec.rec_index]) 116 | 117 | return volumes 118 | 119 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi_extract.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | 24 | from ui.common import output_dir 25 | from ubi import ubi, get_peb_size 26 | from ubi_io import ubi_file 27 | 28 | 29 | def extract_ubi(ubi, out_path): 30 | for image in ubi.images: 31 | img_path = os.path.join(out_path, 'img-%s.ubi' % image.image_seq) 32 | if os.path.exists(img_path): 33 | print 'File exists skipping: %s' % img_path 34 | else: 35 | print 'Writing to: %s' % img_path 36 | f = open(img_path, 'wb') 37 | # iterate through image blocks 38 | for block in image.get_blocks(ubi.blocks): 39 | if ubi.blocks[block].is_valid: 40 | # Write whole block to file 41 | f.write(ubi.file.read_block(ubi.blocks[block])) 42 | 43 | 44 | if __name__ == '__main__': 45 | description = 'Extract UBI image from file. Works with binary dumps containing multiple images if image_seq is not the same.' 46 | usage = 'ubi_extract.py [options] filepath' 47 | parser = argparse.ArgumentParser(usage=usage, description=description) 48 | 49 | parser.add_argument('-p', '--peb-size', type=int, dest='block_size', 50 | help='Specify PEB size.') 51 | 52 | parser.add_argument('-o', '--output-dir', dest='output_path', 53 | help='Specify output directory path.') 54 | 55 | parser.add_argument('filepath', help='File to extract UBI contents of.') 56 | 57 | if len(sys.argv) == 1: 58 | parser.print_help() 59 | sys.exit() 60 | 61 | args = parser.parse_args() 62 | 63 | if args.filepath: 64 | path = args.filepath 65 | if not os.path.exists(path): 66 | parser.error("filepath doesn't exist.") 67 | 68 | if args.output_path: 69 | output_path = args.output_path 70 | else: 71 | img_name = os.path.splitext(os.path.basename(path))[0] 72 | output_path = os.path.join(output_dir, img_name) 73 | 74 | # Determine block size if not provided 75 | if args.block_size: 76 | block_size = args.block_size 77 | else: 78 | block_size = get_peb_size(path) 79 | 80 | if not os.path.exists(output_path): 81 | os.makedirs(output_path) 82 | 83 | 84 | # Create file object. 85 | ufile = ubi_file(path, block_size) 86 | # Create UBI object 87 | uubi = ubi(ufile) 88 | # Run extract UBI. 89 | extract_ubi(uubi, output_path) 90 | sys.exit() -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi_extract_files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | import os 20 | import sys 21 | import argparse 22 | 23 | from ubi import ubi, get_peb_size 24 | from ubifs import ubifs 25 | from ubi_io import ubi_file, leb_virtual_file 26 | from ui.common import extract_files, output_dir 27 | 28 | if __name__ == '__main__': 29 | description = 'Extract contents of UBI image.' 30 | usage = 'ubi_extract_files.py [options] filepath' 31 | parser = argparse.ArgumentParser(usage=usage, description=description) 32 | 33 | parser.add_argument('-l', '--log-file', dest='logpath', 34 | help='Log output to file output/LOGPATH. (default: ubifs_output.log)') 35 | 36 | parser.add_argument('-k', '--keep-permissions', action='store_true', dest='permissions', 37 | help='Maintain file permissions, requires running as root. (default: False)') 38 | 39 | parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', 40 | help='Suppress warnings and non-fatal errors. (default: False)') 41 | 42 | parser.add_argument('-p', '--peb-size', type=int, dest='block_size', 43 | help='Specify PEB size.') 44 | 45 | parser.add_argument('-o', '--output-dir', dest='output_path', 46 | help='Specify output directory path.') 47 | 48 | parser.add_argument('filepath', help='File to extract contents of.') 49 | 50 | if len(sys.argv) == 1: 51 | parser.print_help() 52 | sys.exit() 53 | 54 | args = parser.parse_args() 55 | 56 | if args.filepath: 57 | path = args.filepath 58 | if not os.path.exists(path): 59 | parser.error("File path doesn't exist.") 60 | 61 | if args.output_path: 62 | output_path = args.output_path 63 | else: 64 | img_name = os.path.splitext(os.path.basename(path))[0] 65 | output_path = os.path.join(output_dir, img_name) 66 | 67 | if args.logpath: 68 | log_to_file = True 69 | log_file = args.logpath 70 | else: 71 | log_to_file = None 72 | log_file = None 73 | 74 | # Determine block size if not provided 75 | if args.block_size: 76 | block_size = args.block_size 77 | else: 78 | block_size = get_peb_size(path) 79 | 80 | perms = args.permissions 81 | quiet = args.quiet 82 | 83 | if not os.path.exists(output_path): 84 | os.makedirs(output_path) 85 | 86 | # Create file object. 87 | ufile = ubi_file(path, block_size) 88 | # Create UBI object 89 | uubi = ubi(ufile) 90 | 91 | # Traverse items found extracting files. 92 | for image in uubi.images: 93 | for volume in image.volumes: 94 | vol_out_path = os.path.join(output_path, volume) 95 | 96 | if not os.path.exists(vol_out_path): 97 | os.makedirs(vol_out_path) 98 | elif os.listdir(vol_out_path): 99 | parser.error('Volume output directory is not empty. %s' % vol_out_path) 100 | 101 | # Create file object backed by UBI blocks. 102 | ufsfile = leb_virtual_file(uubi, image.volumes[volume]) 103 | # Create UBIFS object 104 | uubifs = ubifs(ufsfile) 105 | # Set up logging. 106 | uubifs.log.log_file = log_file 107 | uubifs.log.log_to_file = log_to_file 108 | uubifs.log.quiet = quiet 109 | # Run extract all files. 110 | print 'Writing to: %s' % vol_out_path 111 | extract_files(uubifs, vol_out_path, perms) 112 | 113 | sys.exit(0) 114 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi_extract_ubifs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | 24 | from ubi import ubi, get_peb_size 25 | from ubi_io import ubi_file 26 | from ui.common import output_dir 27 | 28 | 29 | def extract_ubifs(ubi, output_path): 30 | for image in ubi.images: 31 | for volume in image.volumes: 32 | vol_path = os.path.join(output_path, 'img-%s_vol-%s.ubifs' % (image.image_seq, volume)) 33 | if os.path.exists(vol_path): 34 | print 'File exists skipping: %s' % vol_path 35 | else: 36 | print 'Writing to: %s' % vol_path 37 | f = open(vol_path, 'wb') 38 | # Get UBIFS image from volume. 39 | for block in image.volumes[volume].reader(ubi): 40 | f.write(block) 41 | 42 | if __name__ == '__main__': 43 | description = """Extract UBIFS image from UBI image. 44 | Works with binary dumps containing multiple images if image_seq is not the same.""" 45 | usage = 'ubi_extract_ubifs.py [options] filepath' 46 | parser = argparse.ArgumentParser(usage=usage, description=description) 47 | 48 | parser.add_argument('-p', '--peb-size', type=int, dest='block_size', 49 | help='Specify PEB size.') 50 | 51 | parser.add_argument('-o', '--output-dir', dest='output_path', 52 | help='Specify output directory path.') 53 | 54 | parser.add_argument('filepath', help='File to extract UBIFS contents of.') 55 | 56 | if len(sys.argv) == 1: 57 | parser.print_help() 58 | sys.exit() 59 | 60 | args = parser.parse_args() 61 | 62 | if args.filepath: 63 | path = args.filepath 64 | if not os.path.exists(path): 65 | parser.error("File path doesn't exist.") 66 | 67 | if args.output_path: 68 | output_path = args.output_path 69 | else: 70 | output_path = output_dir 71 | 72 | # Determine block size if not provided 73 | if args.block_size: 74 | block_size = args.block_size 75 | else: 76 | block_size = get_peb_size(path) 77 | 78 | if not os.path.exists(output_path): 79 | os.makedirs(output_path) 80 | 81 | # Create file object 82 | ufile = ubi_file(path, block_size) 83 | # Create UBI object 84 | uubi = ubi(ufile) 85 | # Run extract UBIFS 86 | extract_ubifs(uubi, output_path) 87 | sys.exit(0) -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | 24 | from ubi_io import ubi_file 25 | from ubi import ubi, get_peb_size 26 | 27 | def print_info(ubi, num=None): 28 | # Roll through all Objects pretty contained information 29 | if num is None: 30 | # Print main UBI Object attributes 31 | ubi.display() 32 | print '\n' 33 | # Loop through images found 34 | for image in ubi.images: 35 | # Print image Object attributes 36 | image.display('\t') 37 | print '\n' 38 | # Loop through volumes in image. 39 | for volume in image.volumes: 40 | # Print volume attribtures and UBI headers 41 | image.volumes[volume].display('\t\t') 42 | 43 | else: 44 | try: 45 | # Print Block Object with PEB # 46 | # If not in index, show first PEB of UBI data 47 | ubi.blocks[num].display() 48 | 49 | except Exception, e: 50 | print e 51 | 52 | print 'Block out of range, printing first UBI block' 53 | ubi.blocks[ubi.first_peb_num].display() 54 | return 55 | 56 | 57 | if __name__ == '__main__': 58 | description = 'Show info about provided UBI image.' 59 | usage = 'ubi_info.py [options] filepath' 60 | parser = argparse.ArgumentParser(usage=usage, description=description) 61 | 62 | parser.add_argument('-b', '--block-num', type=int, dest="block_num", 63 | help='Block Number: Show information about this PEB. Shows first one if doesn\'t exist') 64 | 65 | parser.add_argument('-p', '--peb-size', type=int, dest='block_size', 66 | help='Specify PEB size.') 67 | 68 | parser.add_argument('filepath', help='File to get info from.') 69 | 70 | if len(sys.argv) == 1: 71 | parser.print_help() 72 | sys.exit() 73 | 74 | args = parser.parse_args() 75 | 76 | if args.filepath: 77 | path = args.filepath 78 | if not os.path.exists(path): 79 | parser.error("filepath doesn't exist.") 80 | 81 | block_num = args.block_num 82 | 83 | # Determine block size if not provided 84 | if args.block_size: 85 | block_size = args.block_size 86 | else: 87 | block_size = get_peb_size(path) 88 | 89 | # Create file object 90 | ufile = ubi_file(path, block_size) 91 | # Create UBI object 92 | uubi = ubi(ufile) 93 | # Print info 94 | print_info(uubi, block_num) 95 | sys.exit() -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi_io/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubi_io 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | from ubi.block import sort 21 | 22 | class ubi_file(object): 23 | """UBI image file object 24 | 25 | Arguments: 26 | Str:path -- Path to file to parse 27 | Int:block_size -- Erase block size of NAND in bytes. 28 | Int:start_offset -- (optional) Where to start looking in the file for 29 | UBI data. 30 | Int:end_offset -- (optional) Where to stop looking in the file. 31 | 32 | Methods: 33 | seek -- Put file head to specified byte offset. 34 | Int:offset 35 | read -- Read specified bytes from file handle. 36 | Int:size 37 | tell -- Returns byte offset of current file location. 38 | read_block -- Returns complete PEB data of provided block 39 | description. 40 | Obj:block 41 | read_block_data -- Returns LEB data only from provided block. 42 | Obj:block 43 | reader -- Generator that returns data from file. 44 | reset -- Reset file position to start_offset 45 | 46 | Handles all the actual file interactions, read, seek, 47 | extract blocks, etc. 48 | """ 49 | 50 | def __init__(self, path, block_size, start_offset=0, end_offset=None): 51 | self._fhandle = open(path, 'rb') 52 | self._start_offset = start_offset 53 | 54 | if end_offset: 55 | self._end_offset = end_offset 56 | else: 57 | self._fhandle.seek(0,2) 58 | self._end_offset = self.tell() 59 | 60 | self._block_size = block_size 61 | 62 | if start_offset >= self._end_offset: 63 | raise Exception('Start offset larger than file size!') 64 | 65 | self._fhandle.seek(self._start_offset) 66 | 67 | 68 | def _set_start(self, i): 69 | self._start_offset = i 70 | def _get_start(self): 71 | return self._start_offset 72 | start_offset = property(_get_start, _set_start) 73 | 74 | 75 | def _get_end(self): 76 | return self._end_offset 77 | end_offset = property(_get_end) 78 | 79 | 80 | def _get_block_size(self): 81 | return self._block_size 82 | block_size = property(_get_block_size) 83 | 84 | 85 | def seek(self, offset): 86 | self._fhandle.seek(offset) 87 | 88 | 89 | def read(self, size): 90 | return self._fhandle.read(size) 91 | 92 | 93 | def tell(self): 94 | return self._fhandle.tell() 95 | 96 | 97 | def reset(self): 98 | self._fhandle.seek(self.start_offset) 99 | 100 | 101 | def reader(self): 102 | self.reset() 103 | while True: 104 | cur_loc = self._fhandle.tell() 105 | if self.end_offset and cur_loc > self.end_offset: 106 | break 107 | elif self.end_offset and self.end_offset - cur_loc < self.block_size: 108 | chunk_size = self.end_offset - cur_loc 109 | else: 110 | chunk_size = self.block_size 111 | 112 | buf = self.read(chunk_size) 113 | 114 | if not buf: 115 | break 116 | yield buf 117 | 118 | 119 | def read_block(self, block): 120 | """Read complete PEB data from file. 121 | 122 | Argument: 123 | Obj:block -- Block data is desired for. 124 | """ 125 | self.seek(block.file_offset) 126 | return self._fhandle.read(block.size) 127 | 128 | 129 | def read_block_data(self, block): 130 | """Read LEB data from file 131 | 132 | Argument: 133 | Obj:block -- Block data is desired for. 134 | """ 135 | self.seek(block.file_offset + block.ec_hdr.data_offset) 136 | buf = self._fhandle.read(block.size - block.ec_hdr.data_offset - block.vid_hdr.data_pad) 137 | return buf 138 | 139 | 140 | class leb_virtual_file(): 141 | def __init__(self, ubi, volume): 142 | self._ubi = ubi 143 | self._volume = volume 144 | self._blocks = sort.by_leb(self._volume.get_blocks(self._ubi.blocks)) 145 | self._seek = 0 146 | self.leb_data_size = len(self._blocks) * self._ubi.leb_size 147 | self._last_leb = -1 148 | self._last_buf = '' 149 | 150 | 151 | def read(self, i): 152 | buf = '' 153 | leb = int(self.tell() / self._ubi.leb_size) 154 | offset = self.tell() % self._ubi.leb_size 155 | 156 | if leb == self._last_leb: 157 | self.seek(self.tell() + i) 158 | return self._last_buf[offset:offset+i] 159 | else: 160 | buf = self._ubi.file.read_block_data(self._ubi.blocks[self._blocks[leb]]) 161 | self._last_buf = buf 162 | self._last_leb = leb 163 | self.seek(self.tell() + i) 164 | return buf[offset:offset+i] 165 | 166 | 167 | def reset(self): 168 | self.seek(0) 169 | 170 | 171 | def seek(self, offset): 172 | self._seek = offset 173 | 174 | 175 | def tell(self): 176 | return self._seek 177 | 178 | 179 | def reader(self): 180 | last_leb = 0 181 | for block in self._blocks: 182 | while 0 != (self._ubi.blocks[block].leb_num - last_leb): 183 | last_leb += 1 184 | yield '\xff'*self._ubi.leb_size 185 | 186 | last_leb += 1 187 | yield self._ubi.file.read_block_data(self._ubi.blocks[block]) 188 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubi_utils_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | 24 | from ui.common import get_ubi_params 25 | from ubi_io import ubi_file 26 | from ubi import ubi, get_peb_size 27 | 28 | def print_ubi_params(ubi): 29 | ubi_params = get_ubi_params(ubi) 30 | for img_params in ubi_params: 31 | for volume in ubi_params[img_params]: 32 | ubi_flags = ubi_params[img_params][volume]['flags'] 33 | ubi_args = ubi_params[img_params][volume]['args'] 34 | ini_params = ubi_params[img_params][volume]['ini'] 35 | sorted_keys = sorted(ubi_params[img_params][volume]['args']) 36 | 37 | print '\nVolume %s' % volume 38 | for key in sorted_keys: 39 | if len(key)< 8: 40 | name = '%s\t' % key 41 | else: 42 | name = key 43 | print '\t%s\t%s %s' % (name, ubi_flags[key], ubi_args[key]) 44 | 45 | print '\n\t#ubinize.ini#' 46 | print '\t[%s]' % ini_params['vol_name'] 47 | for key in ini_params: 48 | if key != 'name': 49 | print '\t%s=%s' % (key, ini_params[key]) 50 | if __name__ == '__main__': 51 | description = """Gather information from the UBI image useful for using mkfs.ubi, ubinize, ubiformat, etc. and print to screen. 52 | Some may be duplicates, be sure to check which ones apply.""" 53 | usage = 'ubi_utils_info.py [options] filepath' 54 | parser = argparse.ArgumentParser(usage=usage, description=description) 55 | 56 | parser.add_argument('-p', '--peb-size', type=int, dest='block_size', 57 | help='Specify PEB size.') 58 | 59 | parser.add_argument('filepath', help='File to get info from.') 60 | 61 | if len(sys.argv) == 1: 62 | parser.print_help() 63 | sys.exit() 64 | 65 | args = parser.parse_args() 66 | 67 | if args.filepath: 68 | path = args.filepath 69 | if not os.path.exists(path): 70 | parser.error("filepath doesn't exist.") 71 | 72 | # Determine block size if not provided 73 | if args.block_size: 74 | block_size = args.block_size 75 | else: 76 | block_size = get_peb_size(path) 77 | 78 | # Create file object 79 | ufile = ubi_file(path, block_size) 80 | # Create UBI object 81 | uubi = ubi(ufile) 82 | # Print info. 83 | print_ubi_params(uubi) 84 | sys.exit() 85 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | import re 20 | import struct 21 | 22 | from ubifs.defines import * 23 | from ubifs import nodes 24 | from ubifs.nodes import extract 25 | from ubifs.log import log 26 | 27 | class ubifs(): 28 | """UBIFS object 29 | 30 | Arguments: 31 | Str:path -- File path to UBIFS image. 32 | 33 | Attributes: 34 | Int:leb_size -- Size of Logical Erase Blocks. 35 | Int:min_io -- Size of min I/O from vid_hdr_offset. 36 | Obj:sb_node -- Superblock node of UBIFS image LEB0 37 | Obj:mst_node -- Master Node of UBIFS image LEB1 38 | Obj:log -- Log object for errors. 39 | 40 | Methods: 41 | key_search -- Search nodes for matching key. 42 | Str:key -- Hex string representation of key. 43 | """ 44 | def __init__(self, ubifs_file): 45 | self.log = log() 46 | self._file = ubifs_file 47 | self._sb_node = extract.sb_node(self, UBIFS_COMMON_HDR_SZ) 48 | self._min_io_size = self._sb_node.min_io_size 49 | self._leb_size = self._sb_node.leb_size 50 | self._mst_node = extract.mst_node(self, 1, UBIFS_COMMON_HDR_SZ) 51 | self._mst_node = extract.mst_node(self, 2, UBIFS_COMMON_HDR_SZ) 52 | 53 | 54 | def _get_file(self): 55 | return self._file 56 | file = property(_get_file) 57 | 58 | 59 | def _get_superblock(self): 60 | """ Superblock Node Object 61 | 62 | Returns: 63 | Obj:Superblock Node 64 | """ 65 | return self._sb_node 66 | superblock_node = property(_get_superblock) 67 | 68 | 69 | def _get_master_node(self): 70 | """Master Node Object 71 | 72 | Returns: 73 | Obj:Master Node 74 | """ 75 | return self._mst_node 76 | master_node = property(_get_master_node) 77 | 78 | 79 | def _get_master_node2(self): 80 | """Master Node Object 2 81 | 82 | Returns: 83 | Obj:Master Node 84 | """ 85 | return self._mst_node 86 | master_node2 = property(_get_master_node2) 87 | 88 | 89 | def _get_leb_size(self): 90 | """LEB size of UBI blocks in file. 91 | 92 | Returns: 93 | Int -- LEB Size. 94 | """ 95 | return self._leb_size 96 | leb_size = property(_get_leb_size) 97 | 98 | 99 | def _get_min_io_size(self): 100 | """Min I/O Size 101 | 102 | Returns: 103 | Int -- Min I/O Size. 104 | """ 105 | return self._min_io_size 106 | min_io_size = property(_get_min_io_size) 107 | 108 | 109 | def get_leb_size(path): 110 | """Get LEB size from superblock 111 | 112 | Arguments: 113 | Str:path -- Path to file. 114 | 115 | Returns: 116 | Int -- LEB size. 117 | 118 | Searches file superblock and retrieves leb size. 119 | """ 120 | 121 | f = open(path, 'rb') 122 | f.seek(0,2) 123 | file_size = f.tell()+1 124 | f.seek(0) 125 | block_size = 0 126 | 127 | for i in range(0, file_size, FILE_CHUNK_SZ): 128 | buf = f.read(FILE_CHUNK_SZ) 129 | 130 | for m in re.finditer(UBIFS_NODE_MAGIC, buf): 131 | start = m.start() 132 | chdr = nodes.common_hdr(buf[start:start+UBIFS_COMMON_HDR_SZ]) 133 | 134 | if chdr and chdr.node_type == UBIFS_SB_NODE: 135 | sb_start = start + UBIFS_COMMON_HDR_SZ 136 | sb_end = sb_start + UBIFS_SB_NODE_SZ 137 | 138 | if chdr.len != len(buf[sb_start:sb_end]): 139 | f.seek(sb_start) 140 | buf = f.read(UBIFS_SB_NODE_SZ) 141 | else: 142 | buf = buf[sb_start:sb_end] 143 | 144 | sbn = nodes.sb_node(buf) 145 | block_size = sbn.leb_size 146 | f.close() 147 | return block_size 148 | 149 | f.close() 150 | return block_size -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | 23 | import ui 24 | 25 | class log(): 26 | def __init__(self): 27 | self.log_to_file = False 28 | self.log_file = 'ubifs_output.log' 29 | self.exit_on_except = False 30 | self.quiet = False 31 | 32 | def _out(self, s): 33 | if not self.quiet: 34 | if self.log_to_file: 35 | with open(os.path.join(ui.common.output_dir, self.log_file), 'a') as f: 36 | f.write('%s\n' % s) 37 | f.close() 38 | else: 39 | print '%s' % s 40 | 41 | if self.exit_on_except: 42 | sys.exit() 43 | 44 | def write(self, s): 45 | self._out(s) 46 | 47 | def write_node(self, n): 48 | buf = '%s\n' % n 49 | for key, value in n: 50 | buf += '\t%s: %s\n' % (key, value) 51 | self._out(buf) -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/misc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | import lzo 20 | import struct 21 | import zlib 22 | from ubifs.defines import * 23 | 24 | # For happy printing 25 | ino_types = ['file', 'dir','lnk','blk','chr','fifo','sock'] 26 | node_types = ['ino','data','dent','xent','trun','pad','sb','mst','ref','idx','cs','orph'] 27 | key_types = ['ino','data','dent','xent'] 28 | 29 | 30 | def parse_key(key): 31 | """Parse node key 32 | 33 | Arguments: 34 | Str:key -- Hex string literal of node key. 35 | 36 | Returns: 37 | Int:key_type -- Type of key, data, ino, dent, etc. 38 | Int:ino_num -- Inode number. 39 | Int:khash -- Key hash. 40 | """ 41 | hkey, lkey = struct.unpack('> UBIFS_S_KEY_BLOCK_BITS 44 | khash = lkey 45 | 46 | #if key_type < UBIFS_KEY_TYPES_CNT: 47 | return {'type':key_type, 'ino_num':ino_num, 'khash': khash} 48 | 49 | 50 | def decompress(ctype, unc_len, data): 51 | """Decompress data. 52 | 53 | Arguments: 54 | Int:ctype -- Compression type LZO, ZLIB (*currently unused*). 55 | Int:unc_len -- Uncompressed data lenth. 56 | Str:data -- Data to be uncompessed. 57 | 58 | Returns: 59 | Uncompressed Data. 60 | """ 61 | if ctype == UBIFS_COMPR_LZO: 62 | return lzo.decompress(''.join(('\xf0', struct.pack('>I', unc_len), data))) 63 | elif ctype == UBIFS_COMPR_ZLIB: 64 | return zlib.decompress(data, -11) 65 | else: 66 | return data 67 | 68 | 69 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/nodes/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import struct 21 | from ubifs.defines import * 22 | from ubifs.misc import parse_key 23 | 24 | class common_hdr(object): 25 | def __init__(self, buf): 26 | fields = dict(zip(UBIFS_COMMON_HDR_FIELDS, struct.unpack(UBIFS_COMMON_HDR_FORMAT,buf))) 27 | for key in fields: 28 | setattr(self, key, fields[key]) 29 | setattr(self, 'errors', []) 30 | 31 | def __repr__(self): 32 | return 'UBIFS Common Header' 33 | 34 | def __iter__(self): 35 | for key in dir(self): 36 | if not key.startswith('_'): 37 | yield key, getattr(self, key) 38 | 39 | 40 | class sb_node(object): 41 | def __init__(self, buf): 42 | fields = dict(zip(UBIFS_SB_NODE_FIELDS, struct.unpack(UBIFS_SB_NODE_FORMAT,buf))) 43 | for key in fields: 44 | setattr(self, key, fields[key]) 45 | 46 | def __repr__(self): 47 | return 'UBIFS Super Block Node' 48 | 49 | def __iter__(self): 50 | for key in dir(self): 51 | if not key.startswith('_'): 52 | yield key, getattr(self, key) 53 | 54 | 55 | class mst_node(object): 56 | def __init__(self, buf): 57 | fields = dict(zip(UBIFS_MST_NODE_FIELDS, struct.unpack(UBIFS_MST_NODE_FORMAT,buf))) 58 | for key in fields: 59 | setattr(self, key, fields[key]) 60 | 61 | def __repr__(self): 62 | return 'UBIFS Master Block Node' 63 | 64 | def __iter__(self): 65 | for key in dir(self): 66 | if not key.startswith('_'): 67 | yield key, getattr(self, key) 68 | 69 | 70 | class dent_node(object): 71 | def __init__(self, buf): 72 | fields = dict(zip(UBIFS_DENT_NODE_FIELDS, struct.unpack(UBIFS_DENT_NODE_FORMAT,buf))) 73 | for key in fields: 74 | if key == 'key': 75 | setattr(self, key, parse_key(fields[key])) 76 | else: 77 | setattr(self, key, fields[key]) 78 | setattr(self, 'name', '') 79 | 80 | def __repr__(self): 81 | return 'UBIFS Directory Entry Node' 82 | 83 | def __iter__(self): 84 | for key in dir(self): 85 | if not key.startswith('_'): 86 | yield key, getattr(self, key) 87 | 88 | 89 | class data_node(object): 90 | def __init__(self, buf): 91 | fields = dict(zip(UBIFS_DATA_NODE_FIELDS, struct.unpack(UBIFS_DATA_NODE_FORMAT,buf))) 92 | for key in fields: 93 | if key == 'key': 94 | setattr(self, key, parse_key(fields[key])) 95 | else: 96 | setattr(self, key, fields[key]) 97 | setattr(self, 'offset', 0) 98 | setattr(self, 'compr_len', 0) 99 | 100 | def __repr__(self): 101 | return 'UBIFS Data Node' 102 | 103 | def __iter__(self): 104 | for key in dir(self): 105 | if not key.startswith('_'): 106 | yield key, getattr(self, key) 107 | 108 | 109 | class idx_node(object): 110 | def __init__(self, buf): 111 | fields = dict(zip(UBIFS_IDX_NODE_FIELDS, struct.unpack(UBIFS_IDX_NODE_FORMAT,buf))) 112 | for key in fields: 113 | setattr(self, key, fields[key]) 114 | setattr(self, 'branches', []) 115 | def __repr__(self): 116 | return 'UBIFS Index Node' 117 | 118 | def __iter__(self): 119 | for key in dir(self): 120 | if not key.startswith('_'): 121 | yield key, getattr(self, key) 122 | 123 | 124 | class ino_node(object): 125 | def __init__(self, buf): 126 | fields = dict(zip(UBIFS_INO_NODE_FIELDS, struct.unpack(UBIFS_INO_NODE_FORMAT,buf))) 127 | for key in fields: 128 | if key == 'key': 129 | setattr(self, key, parse_key(fields[key])) 130 | else: 131 | setattr(self, key, fields[key]) 132 | setattr(self, 'data', '') 133 | 134 | def __repr__(self): 135 | return 'UBIFS Ino Node' 136 | 137 | def __iter__(self): 138 | for key in dir(self): 139 | if not key.startswith('_'): 140 | yield key, getattr(self, key) 141 | 142 | 143 | class branch(object): 144 | def __init__(self, buf): 145 | fields = dict(zip(UBIFS_BRANCH_FIELDS, struct.unpack(UBIFS_BRANCH_FORMAT,buf))) 146 | for key in fields: 147 | setattr(self, key, fields[key]) 148 | 149 | def __repr__(self): 150 | return 'UBIFS Branch' 151 | 152 | def __iter__(self): 153 | for key in dir(self): 154 | if not key.startswith('_'): 155 | yield key, getattr(self, key) -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/nodes/extract.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | from ubifs import nodes 21 | from ubifs.defines import * 22 | 23 | 24 | def common_hdr(ubifs, lnum, offset=0): 25 | """Get common header at given LEB number + offset. 26 | 27 | Arguments: 28 | Obj:ubifs -- UBIFS object. 29 | Int:lnum -- LEB number common header is in. 30 | Int:offset -- Offset in LEB of common header. 31 | 32 | Returns: 33 | Obj:common_hdr -- Common header found at lnum/offset. 34 | """ 35 | ubifs.file.seek((ubifs.leb_size * lnum) + offset) 36 | # crc checks here 37 | return nodes.common_hdr(ubifs.file.read(UBIFS_COMMON_HDR_SZ)) 38 | 39 | 40 | def ino_node(ubifs, lnum, offset=0): 41 | """Get inode node at given LEB number + offset. 42 | 43 | Arguments: 44 | Obj:ubifs -- UBIFS object. 45 | Int:lnum -- LEB number inode node is in. 46 | Int:offset -- Offset in LEB of inode node. 47 | 48 | Returns: 49 | Obj:ino_node -- Inode node found at lnum/offset. 50 | """ 51 | ubifs.file.seek((ubifs.leb_size * lnum) + offset) 52 | inon = nodes.ino_node(ubifs.file.read(UBIFS_INO_NODE_SZ)) 53 | inon.data = ubifs.file.read(inon.data_len) 54 | return inon 55 | 56 | 57 | def mst_node(ubifs, lnum, offset=0): 58 | """Get master node at given LEB number + offset. 59 | 60 | Arguments: 61 | Obj:ubifs -- UBIFS object. 62 | Int:lnum -- LEB number master node is in. 63 | Int:offset -- Offset in LEB of master node. 64 | 65 | Returns: 66 | Obj:mst_node -- Master node found at lnum/offset. 67 | """ 68 | ubifs.file.seek((ubifs.leb_size * lnum) + offset) 69 | return nodes.mst_node(ubifs.file.read(UBIFS_MST_NODE_SZ)) 70 | 71 | 72 | def sb_node(ubifs, offset=0): 73 | """Get superblock node at given LEB number + offset. 74 | 75 | Arguments: 76 | Obj:ubifs -- UBIFS object. 77 | Int:offset -- Offset in LEB of superblock node. 78 | 79 | Returns: 80 | Obj:sb_node -- Superblock node found at lnum/offset. 81 | """ 82 | ubifs.file.seek(offset) 83 | return nodes.sb_node(ubifs.file.read(UBIFS_SB_NODE_SZ)) 84 | 85 | 86 | def dent_node(ubifs, lnum, offset=0): 87 | """Get dir entry node at given LEB number + offset. 88 | 89 | Arguments: 90 | Obj:ubifs -- UBIFS object. 91 | Int:lnum -- LEB number dir entry node is in. 92 | Int:offset -- Offset in LEB of dir entry node. 93 | 94 | Returns: 95 | Obj:dent_node -- Dir entry node found at lnum/offset. 96 | """ 97 | ubifs.file.seek((ubifs.leb_size * lnum) + offset) 98 | den = nodes.dent_node(ubifs.file.read(UBIFS_DENT_NODE_SZ)) 99 | den.name = '%s' % ubifs.file.read(den.nlen) 100 | return den 101 | 102 | 103 | def data_node(ubifs, lnum, offset=0, node_len=0): 104 | """Get data node at given LEB number + offset. 105 | 106 | Arguments: 107 | Obj:ubifs -- UBIFS object. 108 | Int:lnum -- LEB number data node is in. 109 | Int:offset -- Offset in LEB of data node. 110 | 111 | Returns: 112 | Obj:data_node -- Data node found at lnum/offset. 113 | """ 114 | ubifs.file.seek((ubifs.leb_size * lnum) + offset) 115 | datn = nodes.data_node(ubifs.file.read(UBIFS_DATA_NODE_SZ)) 116 | datn.offset = (ubifs.leb_size * lnum) + offset + UBIFS_DATA_NODE_SZ 117 | datn.compr_len = node_len - UBIFS_COMMON_HDR_SZ - UBIFS_DATA_NODE_SZ 118 | return datn 119 | 120 | 121 | def idx_node(ubifs, lnum, offset=0): 122 | """Get index node at given LEB number + offset. 123 | 124 | Arguments: 125 | Obj:ubifs -- UBIFS object. 126 | Int:lnum -- LEB number index node is in. 127 | Int:offset -- Offset in LEB of index node. 128 | 129 | Returns: 130 | Obj:idx_node -- Index node found at lnum/offset. 131 | """ 132 | ubifs.file.seek((ubifs.leb_size * lnum) + offset) 133 | idxn = nodes.idx_node(ubifs.file.read(UBIFS_IDX_NODE_SZ)) 134 | 135 | for i in range(0, idxn.child_cnt): 136 | idxn.branches.append(nodes.branch(ubifs.file.read(UBIFS_BRANCH_SZ))) 137 | 138 | return idxn 139 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/output.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import struct 22 | 23 | from ubifs.defines import * 24 | from ubifs.misc import decompress 25 | 26 | def dents(ubifs, inodes, dent_node, path='', perms=False): 27 | inode = inodes[dent_node.inum] 28 | dent_path = os.path.join(path, dent_node.name) 29 | 30 | if dent_node.type == UBIFS_ITYPE_DIR: 31 | try: 32 | if not os.path.exists(dent_path): 33 | os.mkdir(dent_path) 34 | if perms: 35 | set_file_perms(dent_path, inode) 36 | except Exception, e: 37 | ubifs.log.write('DIR Fail: %s' % e) 38 | 39 | if 'dent' in inode: 40 | for dnode in inode['dent']: 41 | dents(ubifs, inodes, dnode, dent_path, perms) 42 | 43 | elif dent_node.type == UBIFS_ITYPE_REG: 44 | try: 45 | if inode['ino'].nlink > 1: 46 | if 'hlink' not in inode: 47 | inode['hlink'] = dent_path 48 | buf = process_reg_file(ubifs, inode, dent_path) 49 | write_reg_file(dent_path, buf) 50 | else: 51 | os.link(inode['hlink'] ,dent_path) 52 | else: 53 | buf = process_reg_file(ubifs, inode, dent_path) 54 | write_reg_file(dent_path, buf) 55 | 56 | if perms: 57 | set_file_perms(dent_path, inode) 58 | 59 | except Exception, e: 60 | ubifs.log.write('FILE Fail: %s' % e) 61 | 62 | elif dent_node.type == UBIFS_ITYPE_LNK: 63 | try: 64 | # probably will need to decompress ino data if > UBIFS_MIN_COMPR_LEN 65 | os.symlink('%s' % inode['ino'].data, dent_path) 66 | except Exception, e: 67 | ubifs.log.write('SYMLINK Fail: %s : %s' % (inode['ino'].data, dent_path)) 68 | 69 | elif dent_node.type in [UBIFS_ITYPE_BLK, UBIFS_ITYPE_CHR]: 70 | try: 71 | dev = struct.unpack(' len(buf): 143 | buf += '\x00' * (inode['ino'].size - len(buf)) 144 | 145 | return buf -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs/walk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader/ubifs 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | from ubifs import extract 21 | from ubifs.defines import * 22 | 23 | def index(ubifs, lnum, offset, inodes={}): 24 | """Walk the index gathering Inode, Dir Entry, and File nodes. 25 | 26 | Arguments: 27 | Obj:ubifs -- UBIFS object. 28 | Int:lnum -- Logical erase block number. 29 | Int:offset -- Offset in logical erase block. 30 | Dict:inodes -- Dict of ino/dent/file nodes keyed to inode number. 31 | 32 | Returns: 33 | Dict:inodes -- Dict of ino/dent/file nodes keyed to inode number. 34 | 'ino' -- Inode node. 35 | 'data' -- List of data nodes if present. 36 | 'dent' -- List of directory entry nodes if present. 37 | """ 38 | chdr = extract.common_hdr(ubifs, lnum, offset) 39 | 40 | if chdr.node_type == UBIFS_IDX_NODE: 41 | idxn = extract.idx_node(ubifs, lnum, offset+UBIFS_COMMON_HDR_SZ) 42 | 43 | for branch in idxn.branches: 44 | index(ubifs, branch.lnum, branch.offs, inodes) 45 | 46 | elif chdr.node_type == UBIFS_INO_NODE: 47 | inon = extract.ino_node(ubifs, lnum, offset+UBIFS_COMMON_HDR_SZ) 48 | ino_num = inon.key['ino_num'] 49 | 50 | if not ino_num in inodes: 51 | inodes[ino_num] = {} 52 | 53 | inodes[ino_num]['ino'] = inon 54 | 55 | elif chdr.node_type == UBIFS_DATA_NODE: 56 | datn = extract.data_node(ubifs, lnum, offset+UBIFS_COMMON_HDR_SZ, chdr.len) 57 | ino_num = datn.key['ino_num'] 58 | 59 | if not ino_num in inodes: 60 | inodes[ino_num] = {} 61 | 62 | if not 'data' in inodes[ino_num]: 63 | inodes[ino_num]['data']= [] 64 | 65 | inodes[ino_num]['data'].append(datn) 66 | 67 | elif chdr.node_type == UBIFS_DENT_NODE: 68 | dn = extract.dent_node(ubifs, lnum, offset+UBIFS_COMMON_HDR_SZ) 69 | ino_num = dn.key['ino_num'] 70 | 71 | if not ino_num in inodes: 72 | inodes[ino_num] = {} 73 | 74 | if not 'dent' in inodes[ino_num]: 75 | inodes[ino_num]['dent']= [] 76 | 77 | inodes[ino_num]['dent'].append(dn) 78 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs_extract_files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | 24 | from ubi_io import ubi_file 25 | from ubifs import ubifs, get_leb_size 26 | from ui.common import extract_files, output_dir 27 | 28 | if __name__ == '__main__': 29 | description = """Extract file contents of UBIFS image.""" 30 | usage = 'ubifs_extract_files.py [options] filepath' 31 | parser = argparse.ArgumentParser(usage=usage, description=description) 32 | parser.add_argument('-l', '--log-file', dest='logpath', 33 | help='Log output to file output/LOGPATH. (default: ubifs_output.log)') 34 | 35 | parser.add_argument('-k', '--keep-permissions', action='store_true', dest='permissions', 36 | help='Maintain file permissions, requires running as root. (default: False)') 37 | 38 | 39 | parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', 40 | help='Suppress warnings and non-fatal errors. (default: False)') 41 | 42 | parser.add_argument('-e', '--leb-size', type=int, dest='block_size', 43 | help='Specify LEB size.') 44 | 45 | parser.add_argument('-o', '--output-dir', dest='output_path', 46 | help='Specify output directory path.') 47 | 48 | parser.add_argument('filepath', help='File to extract file contents of.') 49 | 50 | if len(sys.argv) == 1: 51 | parser.print_help() 52 | sys.exit() 53 | 54 | 55 | args = parser.parse_args() 56 | 57 | if args.filepath: 58 | path = args.filepath 59 | if not os.path.exists(path): 60 | parser.error("File path doesn't exist.") 61 | 62 | if args.output_path: 63 | output_path = args.output_path 64 | else: 65 | img_name = os.path.splitext(os.path.basename(path))[0] 66 | output_path = os.path.join(output_dir, img_name) 67 | 68 | if args.logpath: 69 | log_to_file = True 70 | log_file = args.logpath 71 | else: 72 | log_to_file = None 73 | log_file = None 74 | 75 | # Determine block size if not provided 76 | if args.block_size: 77 | block_size = args.block_size 78 | else: 79 | block_size = get_leb_size(path) 80 | 81 | if not os.path.exists(output_path): 82 | os.makedirs(output_path) 83 | 84 | perms = args.permissions 85 | quiet = args.quiet 86 | 87 | # Create file object 88 | ufsfile = ubi_file(path, block_size) 89 | # Create UBIFS object 90 | uubifs = ubifs(ufsfile) 91 | # Set up logging 92 | uubifs.log.log_file = log_file 93 | uubifs.log.log_to_file = log_to_file 94 | uubifs.quiet = quiet 95 | 96 | if not os.path.exists(output_path): 97 | os.makedirs(output_path) 98 | elif os.listdir(output_path): 99 | parser.error('Volume output directory is not empty. %s' % output_path) 100 | 101 | # Run extract all files 102 | print 'Writing to: %s' % output_path 103 | extract_files(uubifs, output_path, perms) 104 | sys.exit() 105 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ubifs_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | import sys 22 | import argparse 23 | 24 | from ubi_io import ubi_file 25 | from ubifs import ubifs, get_leb_size 26 | 27 | 28 | if __name__ == '__main__': 29 | 30 | description = """Print superblock and master node information.""" 31 | usage = 'ubifs_info.py [options] filepath' 32 | parser = argparse.ArgumentParser(usage=usage, description=description) 33 | 34 | parser.add_argument('-e', '--leb-size', type=int, dest='block_size', 35 | help='Specify LEB size.') 36 | 37 | parser.add_argument('filepath', help='File to get info from.') 38 | 39 | if len(sys.argv) == 1: 40 | parser.print_help() 41 | sys.exit() 42 | 43 | args = parser.parse_args() 44 | 45 | if args.filepath: 46 | path = args.filepath 47 | if not os.path.exists(path): 48 | parser.error("filepath doesn't exist.") 49 | 50 | # Determine block size if not provided 51 | if args.block_size: 52 | block_size = args.block_size 53 | else: 54 | block_size = get_leb_size(path) 55 | 56 | # Create file object. 57 | ufile = ubi_file(path, block_size) 58 | # Create UBIFS Object. 59 | uubifs = ubifs(ufile) 60 | # Write super block node info 61 | uubifs.log.write_node(uubifs.superblock_node) 62 | # Write first master node. 63 | uubifs.log.write_node(uubifs.master_node) 64 | uubifs.log.write_node(uubifs.master_node2) 65 | sys.exit() 66 | -------------------------------------------------------------------------------- /share/tools/ubi_reader/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonsio/YAHM/949b748b22fe2da0f3aff5b5681147c18c06cb03/share/tools/ubi_reader/ui/__init__.py -------------------------------------------------------------------------------- /share/tools/ubi_reader/ui/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ############################################################# 3 | # ubi_reader 4 | # (c) 2013 Jason Pruitt (jrspruitt@gmail.com) 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | ############################################################# 19 | 20 | import os 21 | 22 | from ubi_io import leb_virtual_file 23 | from ubifs import ubifs, walk, output 24 | from ubifs.defines import PRINT_UBIFS_KEY_HASH, PRINT_UBIFS_COMPR 25 | from ubi.defines import PRINT_VOL_TYPE_LIST, UBI_VTBL_AUTORESIZE_FLG 26 | 27 | output_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'output') 28 | 29 | 30 | def extract_files(ubifs, out_path, perms=False): 31 | """Extract UBIFS contents to_path/ 32 | 33 | Arguments: 34 | Obj:ubifs -- UBIFS object. 35 | Str:out_path -- Path to extract contents to. 36 | """ 37 | try: 38 | inodes = {} 39 | walk.index(ubifs, ubifs.master_node.root_lnum, ubifs.master_node.root_offs, inodes) 40 | 41 | for dent in inodes[1]['dent']: 42 | output.dents(ubifs, inodes, dent, out_path, perms) 43 | 44 | except Exception, e: 45 | import traceback 46 | ubifs.log.write('%s' % e) 47 | traceback.print_exc() 48 | 49 | 50 | def get_ubi_params(ubi): 51 | """Get UBI utils params 52 | 53 | Arguments: 54 | Obj:ubi -- UBI object. 55 | 56 | Returns: 57 | Dict -- Dict keyed to volume with Dict of args and flags. 58 | """ 59 | ubi_flags = {'min_io_size':'-m', 60 | 'max_bud_bytes':'-j', 61 | 'leb_size':'-e', 62 | 'default_compr':'-x', 63 | 'sub_page_size':'-s', 64 | 'fanout':'-f', 65 | 'key_hash':'-k', 66 | 'orph_lebs':'-p', 67 | 'log_lebs':'-l', 68 | 'max_leb_cnt': '-c', 69 | 'peb_size':'-p', 70 | 'sub_page_size':'-s', 71 | 'vid_hdr_offset':'-O', 72 | 'version':'-x', 73 | 'image_seq':'-Q', 74 | 'alignment':'-a', 75 | 'vol_id':'-n', 76 | 'name':'-N'} 77 | 78 | ubi_params = {} 79 | ubi_args = {} 80 | ini_params = {} 81 | for image in ubi.images: 82 | img_seq = image.image_seq 83 | ubi_params[img_seq] = {} 84 | ubi_args[img_seq] = {} 85 | ini_params[img_seq] = {} 86 | for volume in image.volumes: 87 | ubi_args[img_seq][volume] = {} 88 | ini_params[img_seq][volume] = {} 89 | 90 | # Get ubinize.ini settings 91 | ini_params[img_seq][volume]['vol_type'] = PRINT_VOL_TYPE_LIST[image.volumes[volume].vol_rec.vol_type] 92 | 93 | if image.volumes[volume].vol_rec.flags == UBI_VTBL_AUTORESIZE_FLG: 94 | ini_params[img_seq][volume]['vol_flags'] = 'autoresize' 95 | else: 96 | ini_params[img_seq][volume]['vol_flags'] = image.volumes[volume].vol_rec.flags 97 | 98 | ini_params[img_seq][volume]['vol_id'] = image.volumes[volume].vol_id 99 | ini_params[img_seq][volume]['vol_name'] = image.volumes[volume].name.rstrip('\x00') 100 | ini_params[img_seq][volume]['vol_alignment'] = image.volumes[volume].vol_rec.alignment 101 | 102 | ini_params[img_seq][volume]['vol_size'] = image.volumes[volume].vol_rec.reserved_pebs * ubi.leb_size 103 | 104 | # Create file object backed by UBI blocks. 105 | ufsfile = leb_virtual_file(ubi, image.volumes[volume]) 106 | # Create UBIFS object 107 | uubifs = ubifs(ufsfile) 108 | for key, value in uubifs.superblock_node: 109 | if key == 'key_hash': 110 | value = PRINT_UBIFS_KEY_HASH[value] 111 | elif key == 'default_compr': 112 | value = PRINT_UBIFS_COMPR[value] 113 | 114 | if key in ubi_flags: 115 | ubi_args[img_seq][volume][key] = value 116 | 117 | for key, value in image.volumes[volume].vol_rec: 118 | if key == 'name': 119 | value = value.rstrip('\x00') 120 | 121 | if key in ubi_flags: 122 | ubi_args[img_seq][volume][key] = value 123 | 124 | ubi_args[img_seq][volume]['version'] = image.version 125 | ubi_args[img_seq][volume]['vid_hdr_offset'] = image.vid_hdr_offset 126 | ubi_args[img_seq][volume]['sub_page_size'] = ubi_args[img_seq][volume]['vid_hdr_offset'] 127 | ubi_args[img_seq][volume]['sub_page_size'] = ubi_args[img_seq][volume]['vid_hdr_offset'] 128 | ubi_args[img_seq][volume]['image_seq'] = image.image_seq 129 | ubi_args[img_seq][volume]['peb_size'] = ubi.peb_size 130 | ubi_args[img_seq][volume]['vol_id'] = image.volumes[volume].vol_id 131 | 132 | ubi_params[img_seq][volume] = {'flags':ubi_flags, 'args':ubi_args[img_seq][volume], 'ini':ini_params[img_seq][volume]} 133 | 134 | return ubi_params -------------------------------------------------------------------------------- /share/yahm_completion: -------------------------------------------------------------------------------- 1 | shopt -s progcomp 2 | 3 | # @todo Wildcard Handling überarbeiten 4 | # @todo GLOBAL_FLAGS wirklich global machen (-n -f -v) und in Funktion mit lokalen mergen 5 | # @todo YAHM/share/include.sh verwenden (Problem mit Meldung bzgl. Dev branch) 6 | 7 | #check_interface_name() 8 | #{ 9 | # # interface name 10 | # local int_name=$1 11 | # 12 | # if [[ ! `ip -d link show ${int_name} 2>/dev/null ` ]]; then 13 | # echo -e "ERROR: Interface ${int_name} does not exists" 14 | # elif [[ `ip -d link show ${int_name} | tail -n +2 | grep loopback` ]] ; then 15 | # echo "local" 16 | # elif [[ `ip -d link show ${int_name} | tail -n +2 | grep vlan` ]] ; then 17 | # echo "vlan" 18 | # elif [[ `ip -d link show ${int_name} | tail -n +2 | grep bridge` ]] ; then 19 | # echo "bridge" 20 | # else 21 | # echo "physical" 22 | # fi 23 | #} 24 | 25 | _backup() 26 | { 27 | local cur prev complete_words complete_options 28 | 29 | # Don't break words at : and =, see [1] and [2] 30 | COMP_WORDBREAKS=${COMP_WORDBREAKS//[:=]} 31 | 32 | cur=${COMP_WORDS[COMP_CWORD]} 33 | prev=${COMP_WORDS[COMP_CWORD-1]} 34 | 35 | GLOBAL_COMMANDS="\ 36 | data_backup\ 37 | data_restore\ 38 | full_backup\ 39 | full_restore" 40 | 41 | GLOBAL_FLAGS="\ 42 | -n\ 43 | -f\ 44 | -v\ 45 | -d" 46 | 47 | case "${prev}" in 48 | -n) 49 | YAHM_DIR="/var/lib/lxc/" 50 | YAHM_NAMES=$(cd ${YAHM_DIR} && set -- */; printf "%s\n" "${@%/}") 51 | COMPREPLY=( $(compgen -W "${YAHM_NAMES}" -- $cur) ) 52 | return 0 53 | ;; 54 | -d) 55 | # Unescape space 56 | cur=${cur//\\ / } 57 | # Expand tilder to $HOME 58 | [[ ${cur} == "~/"* ]] && cur=${cur/\~/$HOME} 59 | # Show completion if path exist (and escape spaces) 60 | compopt -o filenames 61 | local files=("${cur}"*) 62 | [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// /\ }" ) 63 | return 0 64 | ;; 65 | data_backup|data_restore|full_backup|full_restore) 66 | return 0 67 | ;; 68 | *) 69 | complete_words="$GLOBAL_COMMANDS" 70 | complete_options="$GLOBAL_FLAGS" 71 | ;; 72 | esac 73 | 74 | # Either display words or options, depending on the user input 75 | if [[ $cur == -* ]]; then 76 | COMPREPLY=( $( compgen -W "$complete_options" -- $cur )) 77 | 78 | else 79 | COMPREPLY=( $( compgen -W "$complete_words" -- $cur )) 80 | fi 81 | 82 | return 0 83 | } 84 | 85 | _ctl() 86 | { 87 | local cur prev complete_words complete_options 88 | 89 | # Don't break words at : and =, see [1] and [2] 90 | COMP_WORDBREAKS=${COMP_WORDBREAKS//[:=]} 91 | 92 | cur=${COMP_WORDS[COMP_CWORD]} 93 | prev=${COMP_WORDS[COMP_CWORD-1]} 94 | 95 | GLOBAL_COMMANDS="\ 96 | start\ 97 | stop\ 98 | join\ 99 | info\ 100 | fw_update\ 101 | update" 102 | 103 | GLOBAL_FLAGS="\ 104 | -n\ 105 | -f\ 106 | -v" 107 | 108 | case "${prev}" in 109 | -n) 110 | YAHM_DIR="/var/lib/lxc/" 111 | YAHM_NAMES=$(cd ${YAHM_DIR} && set -- */; printf "%s\n" "${@%/}") 112 | COMPREPLY=( $(compgen -W "${YAHM_NAMES}" -- $cur) ) 113 | return 0 114 | ;; 115 | start|stop|info|join|update) 116 | return 0 117 | ;; 118 | *) 119 | complete_words="$GLOBAL_COMMANDS" 120 | complete_options="$GLOBAL_FLAGS" 121 | ;; 122 | esac 123 | 124 | # Either display words or options, depending on the user input 125 | if [[ $cur == -* ]]; then 126 | COMPREPLY=( $( compgen -W "$complete_options" -- $cur )) 127 | 128 | else 129 | COMPREPLY=( $( compgen -W "$complete_words" -- $cur )) 130 | fi 131 | 132 | return 0 133 | } 134 | 135 | _lxc() 136 | { 137 | # todo ggf um Beschreibung erweitern: http://stackoverflow.com/questions/7267185/bash-autocompletion-add-description-for-possible-completions 138 | local cur prev complete_words complete_options 139 | 140 | # Don't break words at : and =, see [1] and [2] 141 | COMP_WORDBREAKS=${COMP_WORDBREAKS//[:=]} 142 | 143 | cur=${COMP_WORDS[COMP_CWORD]} 144 | prev=${COMP_WORDS[COMP_CWORD-1]} 145 | 146 | GLOBAL_COMMANDS="\ 147 | install\ 148 | remove\ 149 | update" 150 | 151 | GLOBAL_FLAGS="\ 152 | -n\ 153 | -f\ 154 | -b\ 155 | -p\ 156 | -v\ 157 | -d" 158 | 159 | #echo -e "\nprev = $prev, cur = $cur, firstword = $firstword, lastword = $lastword\n" 160 | 161 | case "${prev}" in 162 | -n) 163 | #compopt -o nospace 164 | YAHM_DIR="/var/lib/lxc/" 165 | YAHM_NAMES=$(cd ${YAHM_DIR} && set -- */; printf "%s\n" "${@%/}") 166 | COMPREPLY=( $(compgen -W "${YAHM_NAMES}" -- $cur) ) 167 | return 0 168 | ;; 169 | -d) 170 | # Unescape space 171 | cur=${cur//\\ / } 172 | # Expand tilder to $HOME 173 | [[ ${cur} == "~/"* ]] && cur=${cur/\~/$HOME} 174 | # Show completion if path exist (and escape spaces) 175 | compopt -o filenames 176 | local files=("${cur}"*) 177 | [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// /\ }" ) 178 | return 0 179 | ;; 180 | -b) 181 | if [ ! -f /var/lib/yahm/fw.list ] 182 | then 183 | cd /var/lib/yahm/ 184 | wget -q -N https://raw.githubusercontent.com/leonsio/CCU2-FW/master/fw.list 185 | fi 186 | ALL_FW=$(cat /var/lib/yahm/fw.list | grep -Po '(?<=CCU2-)\d.\d\d?.\d\d?') 187 | COMPREPLY=( $(compgen -W "${ALL_FW}" -- $cur) ) 188 | return 0 189 | ;; 190 | install|update|remove) 191 | return 0 192 | ;; 193 | *) 194 | complete_words="$GLOBAL_COMMANDS" 195 | complete_options="$GLOBAL_FLAGS" 196 | ;; 197 | esac 198 | 199 | # Either display words or options, depending on the user input 200 | if [[ $cur == -* ]]; then 201 | COMPREPLY=( $( compgen -W "$complete_options" -- $cur )) 202 | 203 | else 204 | COMPREPLY=( $( compgen -W "$complete_words" -- $cur )) 205 | fi 206 | 207 | return 0 208 | } 209 | 210 | _module() 211 | { 212 | local cur prev complete_words complete_options 213 | 214 | # Don't break words at : and =, see [1] and [2] 215 | COMP_WORDBREAKS=${COMP_WORDBREAKS//[:=]} 216 | 217 | cur=${COMP_WORDS[COMP_CWORD]} 218 | prev=${COMP_WORDS[COMP_CWORD-1]} 219 | 220 | GLOBAL_COMMANDS="\ 221 | enable\ 222 | disable\ 223 | available\ 224 | installed" 225 | 226 | GLOBAL_FLAGS="\ 227 | -n\ 228 | -f\ 229 | -v\ 230 | -m" 231 | 232 | case "${prev}" in 233 | -n) 234 | YAHM_DIR="/var/lib/lxc/" 235 | YAHM_NAMES=$(cd ${YAHM_DIR} && set -- */; printf "%s\n" "${@%/}") 236 | COMPREPLY=( $(compgen -W "${YAHM_NAMES}" -- $cur) ) 237 | return 0 238 | ;; 239 | -m) 240 | YAHM_DIR="/opt/YAHM/share/modules/" 241 | YAHM_MODULES=$(find ${YAHM_DIR} -maxdepth 1 -type f -printf '%f\n') 242 | COMPREPLY=( $(compgen -W "${YAHM_MODULES}" -- $cur) ) 243 | return 0 244 | ;; 245 | enable|disable|available|installed) 246 | return 0 247 | ;; 248 | *) 249 | complete_words="$GLOBAL_COMMANDS" 250 | complete_options="$GLOBAL_FLAGS" 251 | ;; 252 | esac 253 | 254 | # Either display words or options, depending on the user input 255 | if [[ $cur == -* ]]; then 256 | COMPREPLY=( $( compgen -W "$complete_options" -- $cur )) 257 | 258 | else 259 | COMPREPLY=( $( compgen -W "$complete_words" -- $cur )) 260 | fi 261 | 262 | return 0 263 | } 264 | 265 | _network() 266 | { 267 | # todo ggf um Beschreibung erweitern: http://stackoverflow.com/questions/7267185/bash-autocompletion-add-description-for-possible-completions 268 | local cur prev complete_words complete_options 269 | 270 | # Don't break words at : and =, see [1] and [2] 271 | COMP_WORDBREAKS=${COMP_WORDBREAKS//[:=]} 272 | 273 | cur=${COMP_WORDS[COMP_CWORD]} 274 | prev=${COMP_WORDS[COMP_CWORD-1]} 275 | 276 | GLOBAL_COMMANDS="\ 277 | create_bridge\ 278 | delete_bridge\ 279 | attach_bridge\ 280 | dettach_bridge\ 281 | show_bridge" 282 | 283 | GLOBAL_FLAGS="\ 284 | -n\ 285 | -f\ 286 | -v\ 287 | -b\ 288 | -i\ 289 | -d\ 290 | -w" 291 | 292 | case "${prev}" in 293 | -n) 294 | YAHM_DIR="/var/lib/lxc/" 295 | YAHM_NAMES=$(cd ${YAHM_DIR} && set -- */; printf "%s\n" "${@%/}") 296 | COMPREPLY=( $(compgen -W "${YAHM_NAMES}" -- $cur) ) 297 | return 0 298 | ;; 299 | -i) 300 | # INTERFACES=$(find /sys/class/net/ -maxdepth 1 -type l -printf '%f\n') 301 | # unset PHYS_VLAN 302 | # for i in $INTERFACES 303 | # do 304 | # if [ `check_interface_name $i` == "physical" ] || [ `check_interface_name $i` == "vlan" ] 305 | # then 306 | # PHYS_VLAN+=($i) 307 | # fi 308 | # done 309 | # #echo ${PHYS_VLAN[*]} 310 | # COMPREPLY=( $(compgen -W "${PHYS_VLAN[@]}" -- $cur) ) 311 | 312 | # @todo geht bestimmt besser 313 | INTERFACES=$(systemctl -a | grep net-devices | grep -v "/sys/subsystem" | awk '{print $1}' | cut -d"-" -f5 | cut -d"." -f1) 314 | COMPREPLY=( $(compgen -W "${INTERFACES}" -- $cur) ) 315 | return 0 316 | ;; 317 | -b) 318 | BRIDGES=$(brctl show | awk 'NF>1 && NR>1 {print $1}') 319 | COMPREPLY=( $(compgen -W "${BRIDGES}" -- $cur) ) 320 | return 0 321 | ;; 322 | create_bridge|delete_bridge|attach_bridge|dettach_bridge|show_bridge) 323 | return 0 324 | ;; 325 | *) 326 | complete_words="$GLOBAL_COMMANDS" 327 | complete_options="$GLOBAL_FLAGS" 328 | ;; 329 | esac 330 | 331 | # Either display words or options, depending on the user input 332 | if [[ $cur == -* ]]; then 333 | COMPREPLY=( $( compgen -W "$complete_options" -- $cur )) 334 | 335 | else 336 | COMPREPLY=( $( compgen -W "$complete_words" -- $cur )) 337 | fi 338 | 339 | return 0 340 | } 341 | 342 | complete -F _backup yahm-backup 343 | complete -F _ctl yahm-ctl 344 | complete -F _lxc yahm-lxc 345 | complete -F _network yahm-network 346 | complete -F _module yahm-module -------------------------------------------------------------------------------- /yahm-init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # @todo Nach jedem Skript Fehler abfangen 4 | # 5 | # 6 | 7 | set -e 8 | 9 | LSB_RELEASE="/usr/bin/lsb_release" 10 | ERROR=1 11 | 12 | # Check if we can use colours in our output 13 | use_colour=0 14 | [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null && use_colour=1 15 | 16 | # Some useful functions 17 | progress() { 18 | [ $use_colour -eq 1 ] && echo -ne "\033[01;32m" 19 | echo -e "$@" >&2 20 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 21 | } 22 | 23 | info() { 24 | [ $use_colour -eq 1 ] && echo -ne "\033[01;34m" 25 | echo -e "$@" >&2 26 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 27 | } 28 | 29 | die () { 30 | [ $use_colour -eq 1 ] && echo -ne "\033[01;31m" 31 | echo -e "$@" >&2 32 | [ $use_colour -eq 1 ] && echo -ne "\033[00m" 33 | exit 1 34 | } 35 | 36 | if [ "$1" = "quickinstall" ] 37 | then 38 | exit_error() 39 | { 40 | if [ $ERROR -eq 1 ] 41 | then 42 | die "An error occurred while processing quickinstall mode. Pleasy try manually installation" 43 | fi 44 | } 45 | trap exit_error EXIT SIGINT SIGTERM 46 | fi 47 | 48 | check_install_deb() 49 | { 50 | progress "Installing dependencies.." 51 | packages=$1 52 | for P in $packages 53 | do 54 | dpkg -s "$P" >/dev/null 2>&1 && { 55 | info $P is installed 56 | } || { 57 | install_package "$P" 58 | } 59 | done 60 | info "\t..done" 61 | } 62 | 63 | install_package() { 64 | package=$1 65 | info "install ${package}" 66 | apt-get -qq -y install $package 2>&1 > /dev/null 67 | return $? 68 | } 69 | 70 | [ "x$(id -un)" == "xroot" ] || die "Sorry, this script must be run as root." 71 | 72 | [ -x $LSB_RELEASE ] || install_package "lsb-release" 73 | 74 | # Creating needed folders 75 | mkdir -p /var/log/yahm 76 | mkdir -p /var/lib/yahm 77 | 78 | # check architecture 79 | case `dpkg --print-architecture` in 80 | armhf|arm64) 81 | info "Found ARM based distribution" 82 | ;; 83 | i386|amd64|i686|x86_64) 84 | info "X86 CPU found, need to install QEMU" 85 | check_install_deb "qemu-user-static binfmt-support" 86 | ;; 87 | *) 88 | die "Unsupported CPU architecture, we support only ARM and x86" 89 | ;; 90 | esac 91 | 92 | DIST_ID="$($LSB_RELEASE -is)" 93 | CODENAME="$($LSB_RELEASE -cs)" 94 | INTERFACE="eth0" 95 | DIST="" 96 | IS_VERBOSE=0 97 | 98 | # Check the distribution is in the supported list 99 | case "$DIST_ID:$CODENAME" in 100 | Raspbian:jessie) DIST="debian";; 101 | Raspbian:stretch) DIST="debian";; 102 | Debian:jessie) DIST="debian";; 103 | Debian:stretch) DIST="debian";; 104 | Ubuntu:xenial) DIST="ubuntu";; 105 | *) die "Sorry, this script does not support your distribution/release ($DIST_ID $CODENAME)." ;; 106 | esac 107 | 108 | progress "Updating sources (can take some time).." 109 | apt-get -q=2 update 110 | info "\t..done" 111 | 112 | check_install_deb "bash-completion wget dos2unix python git lxc liblzo2-dev bridge-utils python-lzo patch gzip openssl" 113 | 114 | progress "Clean up YAHM directory (removing old versions).." 115 | rm -rf /opt/YAHM 116 | info "\t...done" 117 | 118 | # checkout all files 119 | progress "Downloading actual version from git repository.." 120 | mkdir -p /opt 121 | cd /opt 122 | 123 | ##################################### 124 | #### REMOVE --branch IN MASTER !!!!!# 125 | ##################################### 126 | git clone --recursive https://github.com/leonsio/YAHM.git 2>>/var/log/yahm/git.log >>/var/log/yahm/git.log 127 | cd /opt/YAHM 128 | # update modules 129 | git submodule foreach git pull origin master 2>>/var/log/yahm/git.log >>/var/log/yahm/git.log 130 | # update second level submodule 131 | git submodule foreach git submodule update --init --recursive 2>>/var/log/yahm/git.log >>/var/log/yahm/git.log 132 | info "\t..done" 133 | 134 | progress "Including YAHM into PATH.." 135 | chmod +x /opt/YAHM/bin/* 136 | ln -sf /opt/YAHM/bin/* /usr/sbin/ 137 | info "\t..done" 138 | 139 | progress "Installing bash command completion.." 140 | ln -sf /opt/YAHM/share/yahm_completion /etc/bash_completion.d/yahm_completion 141 | info "\tdone" 142 | 143 | if [ "$1" = "ui" ] && [ "$DIST" != "ubuntu" ] 144 | then 145 | info "\n\tEnter UI mode\n" 146 | /opt/YAHM/bin/yahm-ui 147 | info "\n\tuse 'yahm-ui' to launch the UI again\n" 148 | fi 149 | 150 | if [ "$1" = "quickinstall" ] && [ "$DIST" != "ubuntu" ] 151 | then 152 | info "\n\tEnter quick install mode\n" 153 | 154 | if [ "$DIST_ID" = "Raspbian" ] ; 155 | then 156 | 157 | if [ $(cat /etc/dhcpcd.conf | grep -v "^#" | grep "interface " | wc -l) -gt 0 ] ; 158 | then 159 | die "Custom network configuration is not supported, please use manual installation" 160 | fi 161 | 162 | info "Disable new dhcpcd on Raspbian" 163 | systemctl disable dhcpcd.service 164 | systemctl stop dhcpcd.service 165 | 166 | if [ "$CODENAME" = "jessie" ] ; 167 | then 168 | sed -i /etc/network/interfaces -e "s/iface eth0 inet manual/auto eth0\niface eth0 inet dhcp/" 169 | fi 170 | 171 | if [ "$CODENAME" = "stretch" ] ; 172 | then 173 | # INTERFACE=`ls /sys/class/net | grep enx` 174 | cat >> "/etc/network/interfaces" <>/var/log/yahm/network_create.log >>/var/log/yahm/network_create.log 191 | info "\tBridge yahmbr0 with interface eth0 was created\n" 192 | 193 | progress "\tAttaching network configuration to LXC container" 194 | /opt/YAHM/bin/yahm-network attach_bridge 2>>/var/log/yahm/network_attach.log >>/var/log/yahm/network_attach.log 195 | info "\tyahmbr0 was attached to YAHM LXC container\n" 196 | 197 | #progress "\n\tStarting LXC container (timeout 20 seconds )\n" 198 | #lxc-start -n yahm -d 2>>/var/log/yahm/ccu_start.log >>/var/log/yahm/ccu_start.log 199 | #sleep 20 200 | #info "\tYAHM started\n" 201 | 202 | source /opt/YAHM/share/tools/arm-board-detect/armhwinfo.sh 203 | 204 | if [ "$BOARD_TYPE" = "Raspberry Pi" ] || [ "$BOARD_TYPE" = "ASUS" ] 205 | then 206 | info "\tFound ${BOARD_TYPE} hardware, installing pivccu-driver\n" 207 | /opt/YAHM/bin/yahm-module -m pivccu-driver enable 208 | else 209 | info "Automatic module installation currently only supported on raspberry pi or asus tinker board" 210 | info "To disable homematic-ip warnings, please run 'yahm-module -f -m homematic-ip disable'" 211 | fi 212 | 213 | info "ATTENTION: Please verify your network configuration (/etc/network/interfaces)" 214 | info "WARNING: You may became new IP-address after reboot" 215 | info "YAHM was successfully installed, please restart your system, to activate new networking configuration" 216 | # Disable Error handling 217 | ERROR=0 218 | else 219 | # Info 220 | info "Please see 'yahm-lxc' for creating new container, 'yahm-network' for network configuration and 'yahm-modules' for additional modules" 221 | ERROR=0 222 | fi 223 | --------------------------------------------------------------------------------