commit 2c33ed73de7acda52bcac1d1c26416307415ab60 Author: mbusb Date: Sun Nov 6 16:48:53 2016 +0530 Upgrading to version 8.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea06744 --- /dev/null +++ b/.gitignore @@ -0,0 +1,93 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +.venv/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject + +# Created by PyCharm +.idea \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..0f1dbc2 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,137 @@ +Version - 8.0.0 +--------------- +* This version is written in python 3 +* GUI toolkit moved to pyqt5 +* Upgraded isodump.py to support python 3 (Thanks to LiQiong Lee) +* Option to choose the desired persistence size using slide +* Introduces command line option (install and uninstall distros as of now and will be extended later) +* Progress can be monitered when running from terminal +* Multibootusb should be started with admin/ root privilage under Linux +* Fixed the bug which causing USB disk to set read only +* Fixed 'Undef symbol FAIL: __syslinux_debug_enabled' error for gparted, clonezilla and some other (Thsnks to yurikhan for pointing to right direction) +* Fixed the bug which crashes multibootusb when non-ascii charecters are present in the ISO link +* Fixed the bug which prevent multibootusb not to install syslinux +* Now debain installer can be installed +* Updated dependency packages stddeb, pyinstaller and pyudev to latest version +* Fixed hiren's boot cd bug (but user has to avoid "'" in the path) +* Updated install.py script to include all missing dependencies +* pkexec is required now undaining Linux for obtaining admin permission +* Fixed dban not supported error +* Fixed AVG rescue disk not supported error +* Added Offline Windows Password and registry editor (latest version) +* Added F4UBCD iso +* Fixed the crash when mountpath contain space +* Dropped udisk version 1 for obtaining details of USB disks +* Now the GUI responds smoothly when using ISO Imager option (dd) +* Fixed superficial duplicate devices on Refresh USB under ISO Imager tab +* Corrected some typo errors +* version bumped to 8.0.0 as it is a major upgrade +* Added support for bl-Hydrogen Linux +* Many other improvements and bug fixes... + +Version - 7.5.0 +--------------- +* Introducing "ISO Imager". Now you can write ISO images directly to USB and it is cross platform too. +* util-linux is required now under Linux. +* Most of the reported bugs has been fixed. +* Inclusion of 64 bit syslinux (except version 5 as I could not find any) under Linux. +* Added alphine Linux and memtest +* Included missing install.py, uninstall.py and dd directory to source package. + +Version - 7.4.0 +--------------- +* Portion of the code has been rewritten. +* Added support for ext2/3/4 and Btrf filesystem. +* Corrected wrong naming of persistence files. +* Bug fix for PartedMagic update script. +* Few typo has been corrected. +* Included doc strings. +* Now install and uninstall script is written in python. +* Upgraded the debian build script (stdeb) to latest version. +* Lot of other minor bug fixes. +* GUI is slightly larger now. +* Generic way to remove files outside multibootusb directory. +* Various improvements to build script. + +Version - 7.3.0 +--------------- +* Rewrite of the source code. It is easier to read source code now. +* UDisks2 has been added for detecting and getting USB details under Linux. +* Patch for ubuntu 14.10 and above which uses isolinux version 6. +* Added persistence for ubuntu and its derivatives. Maximum persistence can be up to 4GB. +* No USB label error has been solved. It now works with USB drives even without names. +* Corrected wait time to 30 sec. +* Updating GUI is now handled by QThread +* Added following distros:- + - Trinity Rescue Kit + - DBan +* Check for QEMU installation before doing any QEMU related operations. +* Feedback after installing syslinux (under syslinux tab). +* psutil dependency has been dropped. + +Version - 7.2.0 +--------------- +* Updated syslinux version from 6.0.2 to 6.0.3 +* Added background image for syslinux. +* Increased timeout time to 30 sec. +* Added following distros:- + - CentOS minimal. + - Ubuntu Server. +* Patch from kbytesys to fix systemrescueCD menu items and other string manupulations. +* Fix for few other bugs. + +Version - 7.1.0 +--------------- +* ISO extraction is much faster than previous releases. +* Included pyudev for better detection of USB drives under Linux. +* Included install.sh for installing multibootusb under Linux. +* Included setup.py for installing directly from source. +* Improved USB detection under Linux. +* Lot of important bug fix and minor improvements. + +Version - 7.0.0 +--------------- +* 7Zip is no more used for extracting ISO files. Thanks to contibution by LiQiong Lee for isodump.py script. +* Improved ISO extraction speed on certain distros. +* Added solydx, antix and fixed puppy bug. +* ISO integrity check is much faster than previos releases. +* QEMU feature is back on both Linux and Windows. +* Fixed OpenSuse uninstall issue. +* Check if any running process while exiting. +* Added PCLinuxOS. +* Various other major and minor improvements to all scripts. +* Changed version numbering to three digit. + +Version -7.0 Beta2 +------------------ +* Now multibootusb installs correct version of syslinux shipped with distro. +* Various optimization for installation of syslinux. +* Extensive test has been done to make sure to fix bugs. +* Able to uninstall distros properly under windows. +* Greater attention is given to ensure that correct version of syslinux is installed in distro directory. +* Various code optimization. +* Copying iso files under windows is faster now. +* Added follwing distros:- + - Wifislax + - PcLinuxOS + - Salix live/install + - Slackel live/install + - Zenwalk live/install +* Few other distros which I dont remember +* Size of windows executable has been reduced (using upx) +* Added refresh usb button. So that restarting of application is not required to detect USB. +* Added new lable space to indicate progress of the process. +* Various other smaller improvements. + +Version -7.0 Beta +----------------- +* Code base is now in python 2.7 +* Code is hosted at github so that anybody can view the progress of the development. +* Various bugs present in the older version (6.4.1) has been fixed +* Stand alone for windows and Linux. +* PyQt choosen as GUI toolkit. +* Official website is up and running at and you are viewing it now :-) +* New logo. +* Single code base for Linux and windows +* other improvements which i don’t remember exactly :-) + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0f2008a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,998 @@ +GNU GENERAL PUBLIC LICENSE +<<<<<<< HEAD + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +======= + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + +<<<<<<< HEAD + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +======= + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +<<<<<<< HEAD +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. +======= +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 + + The precise terms and conditions for copying, distribution and +modification follow. + +<<<<<<< HEAD + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +======= + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +<<<<<<< HEAD +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or +======= +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +<<<<<<< HEAD + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +======= + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +<<<<<<< HEAD +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. +======= +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. +>>>>>>> f69e8b932e01aa3691f02e1ad3ebd67a3174cbe5 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bfc70d --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +####User guide is located here http://multibootusb.org/page_guide/ + +What is multibootusb? +--------------------- +MultiBootUSB is a cross platform* software/utility to create multi boot live Linux on a removable media i.e USB disk. +It is similar to unetbootin but many distros can be installed, provided you have enough space on the disk. +MultiBootUSB also provides an option to uninstall distro(s) at any time, if you wish. + +* Only works on windows and linux + +How to install? +--------------- + +The install.py script provided with multibootusb should take care of all dependency and install multibootusb. +Assume that you have downloaded the package named "multibootusb.tar.gz" in to your home directory. +Issue the following commands to install multibootusb:- + +``` +tar -xf ./multibootusb.tar.gz +cd multibootusb +chmod +x ./install.py +sudo ./install.py +``` + +That is it. You can find multibootusb under system menu or simply launch from terminal by typing "multibootusb". +If "install.py" script fails to install multibootusb successfully then manually install following packages and rerun the install.py script:- + +* mtools util-linux parted python3-qt5 python-dbus pkexec + +NOTE: install.py currently supports only distros based on apt-get, yum, zypper, pacman. +You can add more if you use other package manager system and email to me for adding into upstream. +The above how to is only for linux. Windows users may download pre compiled standalone binaries/ .exe from +https://sourceforge.net/projects/multibootusb/files/ + +How to uninstall? +----------------- +You can uninstall multibootusb at any time using the "uninstall.py" script provided with multibootusb. + +``` +cd multibootusb +chmod +x ./uninstall.py +sudo ./uninstall.py +``` + +Website: +-------- +www.multibootusb.org + +Development: +----------- +https://github.com/mbusb/multibootusb + +Help: +----- +Mail me at feedback.multibootusb@gmail.com for query, help, bug report or feature request. + +Contributor(s) +-------------- +LiQiong Lee +Ian Bruce +and many others who have given their valuable bug report/ feedback. + +Author(s) +--------- +MultiBootUSB is brought to you by Sundar and co-authored by Ian Bruce. diff --git a/build_pkg b/build_pkg new file mode 100644 index 0000000..37a30f7 --- /dev/null +++ b/build_pkg @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: build_pkg +# Purpose: Module to create package, executable, source archieve and upload to sourceforge site. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above +""" +This is an internal script to make automation of building binary/ source packages and uploading to Sourceforge. +This may not work for you without modification in to variables and paths. Please amend wherever required. +Originally written for cryptully and modified by me for multibootusb. + +Released under General Public Licence (GPL). + +Author: Sundar +""" + +import os +import shutil +import subprocess +import sys +import platform + + +#################################################################################################### +# Change the below variables to suit your needs. +if platform.system() == "Windows": + # pyinstaller_path = "F:\Documents\PyInstaller-3.2\pyinstaller.py" + # pyinstaller_path = "I:\home\sundar\Documents\pyInstaller\pyinstaller.py" + pyinstaller_path = 'D:\multibootusb\pyinstaller\pyinstaller.py' + # release_dir = os.path.join("D:", "multibootusb", "release") + release_dir = 'D:\\multibootusb\\release' +else: + from os.path import expanduser + home = expanduser("~") + pyinstaller_path = "/home/sundar/Documents/pyInstaller/pyinstaller.py" + release_dir = "/media/sundar/Data/multibootusb/release" +sourceforge_release_path = "multibootusb@frs.sourceforge.net:/home/frs/project/multibootusb/" +#################################################################################################### + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + + +class pkg(): + def __init__(self, name): + self.pkg_name = name + self.version = open(os.path.join("data", "version.txt"), 'r').read().strip() + self.release_upload_dir = os.path.join(release_dir, self.version) + + def build_package(self): + self.clean_dir() + if not os.path.exists(self.release_upload_dir): + os.mkdir(self.release_upload_dir) + if not os.path.exists(os.path.join(self.release_upload_dir, "Linux")): + os.mkdir(os.path.join(self.release_upload_dir, "Linux")) + elif not os.path.exists(os.path.join(self.release_upload_dir, "Windows")): + os.mkdir(os.path.join(self.release_upload_dir, "Windows")) + elif not os.path.exists(os.path.join(self.release_upload_dir, "Source")): + os.mkdir(os.path.join(self.release_upload_dir, "Source")) + if self.pkg_name == "deb": + print("Ensure thta you have python-stdeb package installed!") + stdcfg = ("[DEFAULT]\n" + "Package: multibootusb\n" + "Depends: python3-pyqt5, parted, util-linux, mtools, python-dbus, pkexec\n" + "Build-Depends: python3-all\n" + "Section: system\n" + "XS-Python-Version: = 3.5\n" + "Debian-Version: 1") + with open("stdeb.cfg", "w") as f: + f.write(stdcfg) + if subprocess.call('python3 setup.py --command-packages=stdeb.command bdist_deb', shell=True) == 0 and \ + os.path.exists(os.path.join("deb_dist", "python3-multibootusb_" + self.version + "-1_all.deb")): + try: + shutil.copy2(os.path.join("deb_dist", "python3-multibootusb_" + self.version + "-1_all.deb"), + os.path.join(self.release_upload_dir, "Linux"), follow_symlinks=True) #,follow_symlinks=False + except: + os.system('cp -rf ' + os.path.join("deb_dist", "python3-multibootusb_" + self.version + "-1_all.deb") + ' ' + + os.path.join(self.release_upload_dir, "Linux")) + if os.path.exists("stdeb.cfg"): + os.remove("stdeb.cfg") + print("\n\n\nDebian package has been created and can be found here::") + print((os.path.join("deb_dist", "python3-multibootusb_" + self.version + "-1_all.deb\n\n\n"))) + result = True + elif self.pkg_name == 'rpm' or self.pkg_name == 'suse' or self.pkg_name == 'mageia': + if self.pkg_name == 'suse' or self.pkg_name == 'mageia': + require = "python3-qt5, parted, util-linux, pkexec, mtools" + else: + require = "PyQt5, parted, util-linux, mtools" + setup_cfg = ("[bdist_rpm]\n" + "Group = Applications/System\n" + "Vendor = Sundar \n" + "Requires = " + require) + with open("setup.cfg", "w") as f: + f.write(setup_cfg) + if subprocess.call('python3 setup.py bdist_rpm', shell=True) == 0 and \ + os.path.exists(os.path.join("dist", "multibootusb-" + self.version + "-1.noarch.rpm")): + if self.pkg_name == 'suse': + package = "multibootusb-" + self.version + "-1suse.noarch.rpm" + elif self.pkg_name == 'mageia': + package = "multibootusb-" + self.version + "-1mageia.noarch.rpm" + else: + package = "multibootusb-" + self.version + "-1.noarch.rpm" + try: + shutil.copy2(os.path.join("dist", "multibootusb-" + self.version + "-1.noarch.rpm"), + os.path.join(self.release_upload_dir, "Linux", package)) + except: + os.system('cp -rf ' + os.path.join("dist", "multibootusb-" + self.version + "-1.noarch.rpm") + ' ' + + os.path.join(self.release_upload_dir, "Linux", package)) + if os.path.exists("setup.cfg"): + os.remove("setup.cfg") + print("\n\n\nRPM package has been created and can be found here::") + print((os.path.join("dist", "multibootusb-" + self.version + "-1.noarch.rpm\n\n\n"))) + + result = True + + elif self.pkg_name == "exe": + if not platform.system() == "Windows": + print("You can generate windows executable from windows host only.") + else: + # subprocess.call('python ' + pyinstaller_path + ' --upx-dir C:\\upx multibootusb.spec', shell=True) == 0 and \ + # os.path.exists(os.path.join("dist", 'multibootusb-' + self.version + ".exe")): + if subprocess.call('python ' + pyinstaller_path + ' --windowed multibootusb.spec', shell=True) == 0 and \ + os.path.exists(os.path.join("dist", 'multibootusb-' + self.version + ".exe")): + shutil.copy2(os.path.join("dist", 'multibootusb-' + self.version + ".exe"), + os.path.join(self.release_upload_dir, "Windows")) + print("\n\n\nWindows binary has been created and can be found here::") + print((os.path.join("dist", "multibootusb-" + self.version + ".exe\n\n\n"))) + result = True + elif self.pkg_name == 'install': + if subprocess.call("python3", "install.py", shell=True) == 0: + print("Installation is successful.") + result = True + elif self.pkg_name == 'src': + package = "multibootusb-" + self.version + ".tar.gz" + if subprocess.call('python3 setup.py sdist', shell=True) == 0 and \ + os.path.exists(os.path.join("dist", "multibootusb-" + self.version + ".tar.gz")): + try: + shutil.copy2(os.path.join("dist", "multibootusb-" + self.version + ".tar.gz"), + os.path.join(self.release_upload_dir, "Source"), follow_symlinks=False) + except: + os.system('cp -rf ' + os.path.join("dist", package) + ' ' + os.path.join(self.release_upload_dir, "Source", package)) + print("\n\n\nSource package has been created and can be found here::") + print((os.path.join("dist", "multibootusb-" + self.version + ".tar.gz\n\n\n"))) + result = True + elif self.pkg_name == 'run': + subprocess.call(['python3', 'multibootusb']) + elif self.pkg_name == 'clean': + self.clean_dir() + elif self.pkg_name == "upload": + self.upload() + else: + print("Option not found.") + usage() + ''' + if result: + return result + ''' + + def upload(self): + if platform.system() == "Windows": + print("You can upload to SF only from Linux as it needs rsync.") + else: + print("Uploading files...") + cmd = "rsync --rsh=ssh -l -p -r -t -z --stats /media/sundar/Data/multibootusb/release/" + self.version + " " + sourceforge_release_path + if os.system(cmd) == 0: + print((bcolors.OKGREEN + "\n\nVersion " + self.version + " has been successfully uploaded to SF.\n\n" + bcolors.ENDC)) + else: + print((bcolors.FAIL + "\n\n\nError while uploading to SF.\n\n\n" + bcolors.ENDC)) + + def clean_dir(self): + + if not os.path.exists('build') and not os.path.exists('dist') and not os.path.exists('deb_dist'): + print("Already clean. Nothing to do.") + else: + dir_list = ["build", "dist", "deb_dist"] + if os.path.exists('MANIFEST'): + self.deleteFile('MANIFEST') + for directory in dir_list: + if os.path.exists(directory): + shutil.rmtree(directory) + + print("Cleaning pthon byte files...") + + for path, subdirs, files in os.walk(os.curdir): + # print subdirs + for name in files: + if name.endswith('.pyc'): + print(("Cleaning " + os.path.join(path, name))) + os.chmod(os.path.join(path, name), 0o777) + os.unlink(os.path.join(path, name)) + # os.remove(os.path.join(path,name)) + + def deleteDirectory(self, path): + try: + for files in os.listdir(path): + if os.path.isdir(os.path.join(path, files)): + # print (os.path.join(path, files)) + os.chmod(os.path.join(path, files), 0o777) + shutil.rmtree(os.path.join(path, files)) + else: + # print (os.path.join(path, files)) + os.chmod(os.path.join(path, files), 0o777) + os.unlink(os.path.join(path, files)) + os.remove(os.path.join(path, files)) + if os.path.exists(path): + print("Path exist.") + os.rmdir(path) + shutil.rmtree(path) + else: + print("Path exist.") + + except OSError as ose: + # Ignore 'no such file or directory' errors + if ose.errno != 2: + print("OS Error.") + + # Useful to delete files. + def deleteFile(self, path): + try: + os.unlink(path) + except OSError as ose: + if ose.errno != 2: + print(ose) + +def usage(): + """ Prompt users how to use """ + print ("Invalid option(s)\n" + "Possible options are ::\n" + "\033[94mexe\033[0m <-- For making Windows/ Linux standalone executable using pyinstaller\n" + "\033[94mdeb\033[0m <-- For making creating package for debian/ubuntu\n" + "\033[94mrpm\033[0m <-- For creating package for fedora/redhat/centos\n" + "\033[94msuse\033[0m <-- For creating package for OpenSuse\n" + "\033[94mmageia\033[0m <-- For creating package for mageia/mandriva\n" + "\033[94msrc\033[0m <-- For creating source package for other distributions\n" + "\033[94mall\033[0m <-- For creating package for all distros\n" + "\033[94mclean\033[0m <-- For Cleaning dist, build directory and other temp/python byte files\n" + "\033[94minstall\033[0m <-- Directly install from source package\n" + "\033[94mrun\033[0m <-- Directly run multibootusb from source package\n" + "\033[92mupload\033[0m <-- Upload package directory to SourceForge") + + sys.exit(-1) + +if __name__ == '__main__': + argv = sys.argv + if not os.path.exists(release_dir): + print("Release directory does not exist.\nPlease mount and rerun the script.") + sys.exit(1) + elif not os.path.exists(pyinstaller_path): + print("Pyinstaller path does not exist.\nPlease correct the path and rerun the script.") + sys.exit(1) + + if len(argv) == 1: + usage() + else: + argv = argv[1] + if argv == "all": + all_arug = ["clean", "exe", "deb", "rpm", "suse", "mageia", "src"] + for package_name in all_arug: + print(("Creating package for argument " + package_name)) + build = pkg(package_name) + build.build_package() + else: + build = pkg(argv) + build.build_package() diff --git a/data/multibootusb.desktop b/data/multibootusb.desktop new file mode 100644 index 0000000..8e163c4 --- /dev/null +++ b/data/multibootusb.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=multibootusb +Comment=Install multiple Linux Operating System on USB +Icon=multibootusb.png +Exec=multibootusb-pkexec +Categories=System; +StartupNotify=true diff --git a/data/multibootusb/bg.png b/data/multibootusb/bg.png new file mode 100644 index 0000000..629ed91 Binary files /dev/null and b/data/multibootusb/bg.png differ diff --git a/data/multibootusb/chain.c32 b/data/multibootusb/chain.c32 new file mode 100644 index 0000000..52d4c52 Binary files /dev/null and b/data/multibootusb/chain.c32 differ diff --git a/data/multibootusb/extlinux.cfg b/data/multibootusb/extlinux.cfg new file mode 100644 index 0000000..e69de29 diff --git a/data/multibootusb/grub.exe b/data/multibootusb/grub.exe new file mode 100644 index 0000000..57b890d Binary files /dev/null and b/data/multibootusb/grub.exe differ diff --git a/data/multibootusb/memdisk b/data/multibootusb/memdisk new file mode 100644 index 0000000..411c07d Binary files /dev/null and b/data/multibootusb/memdisk differ diff --git a/data/multibootusb/menu.c32 b/data/multibootusb/menu.c32 new file mode 100644 index 0000000..90318f0 Binary files /dev/null and b/data/multibootusb/menu.c32 differ diff --git a/data/multibootusb/menu.lst b/data/multibootusb/menu.lst new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/data/multibootusb/menu.lst @@ -0,0 +1 @@ + diff --git a/data/multibootusb/syslinux.cfg b/data/multibootusb/syslinux.cfg new file mode 100644 index 0000000..445bd5f --- /dev/null +++ b/data/multibootusb/syslinux.cfg @@ -0,0 +1,35 @@ +# This file is created by MultiBootUSB. +default vesamenu.c32 +prompt 0 +menu title MultiBootUSB +MENU BACKGROUND /multibootusb/bg.png +TIMEOUT 300 +MENU WIDTH 80 +MENU MARGIN 10 +MENU PASSWORDMARGIN 3 +MENU ROWS 12 +MENU TABMSGROW 18 +MENU CMDLINEROW 18 +MENU ENDROW -1 +MENU PASSWORDROW 11 +MENU TIMEOUTROW 20 +MENU HELPMSGROW 22 +MENU HELPMSGENDROW -1 +MENU HIDDENROW -2 +MENU HSHIFT 0 +MENU VSHIFT 0 +MENU COLOR border 30;44 #40ffffff #a0000000 std +MENU COLOR title 1;36;44 #9033ccff #a0000000 std +MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all +MENU COLOR unsel 37;44 #50ffffff #a0000000 std +MENU COLOR help 37;40 #c0ffffff #a0000000 std +MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std +MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std +MENU COLOR msg07 37;40 #90ffffff #a0000000 std +MENU COLOR tabmsg 31;40 #30ffffff #00000000 std +label Boot from Hard Drive +MENU LABEL Boot from Hard Disk +KERNEL chain.c32 +APPEND hd1 +MENU DEFAULT + diff --git a/data/multibootusb/vesamenu.c32 b/data/multibootusb/vesamenu.c32 new file mode 100644 index 0000000..a92f4fe Binary files /dev/null and b/data/multibootusb/vesamenu.c32 differ diff --git a/data/tools/dd/dd.exe b/data/tools/dd/dd.exe new file mode 100644 index 0000000..8cf719c Binary files /dev/null and b/data/tools/dd/dd.exe differ diff --git a/data/tools/dd/diskio.dll b/data/tools/dd/diskio.dll new file mode 100644 index 0000000..1c6a7f0 Binary files /dev/null and b/data/tools/dd/diskio.dll differ diff --git a/data/tools/mbr.bin b/data/tools/mbr.bin new file mode 100644 index 0000000..646a684 Binary files /dev/null and b/data/tools/mbr.bin differ diff --git a/data/tools/mkfs/mke2fs.exe b/data/tools/mkfs/mke2fs.exe new file mode 100644 index 0000000..79690fd Binary files /dev/null and b/data/tools/mkfs/mke2fs.exe differ diff --git a/data/tools/multibootusb.ico b/data/tools/multibootusb.ico new file mode 100644 index 0000000..8f73b8b Binary files /dev/null and b/data/tools/multibootusb.ico differ diff --git a/data/tools/multibootusb.png b/data/tools/multibootusb.png new file mode 100644 index 0000000..92c2e82 Binary files /dev/null and b/data/tools/multibootusb.png differ diff --git a/data/tools/qemu/SDL.dll b/data/tools/qemu/SDL.dll new file mode 100644 index 0000000..7c56668 Binary files /dev/null and b/data/tools/qemu/SDL.dll differ diff --git a/data/tools/qemu/bios.bin b/data/tools/qemu/bios.bin new file mode 100644 index 0000000..d0d4b6a Binary files /dev/null and b/data/tools/qemu/bios.bin differ diff --git a/data/tools/qemu/fmod.dll b/data/tools/qemu/fmod.dll new file mode 100644 index 0000000..6b0e379 Binary files /dev/null and b/data/tools/qemu/fmod.dll differ diff --git a/data/tools/qemu/keymaps/ar b/data/tools/qemu/keymaps/ar new file mode 100644 index 0000000..c430c03 --- /dev/null +++ b/data/tools/qemu/keymaps/ar @@ -0,0 +1,98 @@ +# generated from XKB map ar +include common +map 0x401 +exclam 0x02 shift +at 0x03 shift +numbersign 0x04 shift +dollar 0x05 shift +percent 0x06 shift +asciicircum 0x07 shift +ampersand 0x08 shift +asterisk 0x09 shift +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +Arabic_dad 0x10 altgr +Arabic_fatha 0x10 shift altgr +Arabic_sad 0x11 altgr +Arabic_fathatan 0x11 shift altgr +Arabic_theh 0x12 altgr +Arabic_damma 0x12 shift altgr +Arabic_qaf 0x13 altgr +Arabic_dammatan 0x13 shift altgr +Arabic_feh 0x14 altgr +UFEF9 0x14 shift altgr +Arabic_ghain 0x15 altgr +Arabic_hamzaunderalef 0x15 shift altgr +Arabic_ain 0x16 altgr +grave 0x16 shift altgr +Arabic_ha 0x17 altgr +division 0x17 shift altgr +Arabic_khah 0x18 altgr +multiply 0x18 shift altgr +Arabic_hah 0x19 altgr +Arabic_semicolon 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Arabic_jeem 0x1a altgr +bracketright 0x1b +braceright 0x1b shift +Arabic_dal 0x1b altgr +Arabic_sheen 0x1e altgr +backslash 0x1e shift altgr +Arabic_seen 0x1f altgr +Arabic_yeh 0x20 altgr +bracketleft 0x20 shift altgr +Arabic_beh 0x21 altgr +bracketright 0x21 shift altgr +Arabic_lam 0x22 altgr +UFEF7 0x22 shift altgr +Arabic_alef 0x23 altgr +Arabic_hamzaonalef 0x23 shift altgr +Arabic_teh 0x24 altgr +Arabic_tatweel 0x24 shift altgr +Arabic_noon 0x25 altgr +Arabic_comma 0x25 shift altgr +Arabic_meem 0x26 altgr +slash 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Arabic_kaf 0x27 altgr +apostrophe 0x28 +quotedbl 0x28 shift +Arabic_tah 0x28 altgr +grave 0x29 +asciitilde 0x29 shift +Arabic_thal 0x29 altgr +Arabic_shadda 0x29 shift altgr +backslash 0x2b +bar 0x2b shift +less 0x2b altgr +greater 0x2b shift altgr +Arabic_hamzaonyeh 0x2c altgr +asciitilde 0x2c shift altgr +Arabic_hamza 0x2d altgr +Arabic_sukun 0x2d shift altgr +Arabic_hamzaonwaw 0x2e altgr +Arabic_kasra 0x2e shift altgr +Arabic_ra 0x2f altgr +Arabic_kasratan 0x2f shift altgr +UFEFB 0x30 altgr +UFEF5 0x30 shift altgr +Arabic_alefmaksura 0x31 altgr +Arabic_maddaonalef 0x31 shift altgr +Arabic_tehmarbuta 0x32 altgr +apostrophe 0x32 shift altgr +comma 0x33 +less 0x33 shift +Arabic_waw 0x33 altgr +period 0x34 +greater 0x34 shift +Arabic_zain 0x34 altgr +slash 0x35 +question 0x35 shift +Arabic_zah 0x35 altgr +Arabic_question_mark 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/common b/data/tools/qemu/keymaps/common new file mode 100644 index 0000000..0b53f1c --- /dev/null +++ b/data/tools/qemu/keymaps/common @@ -0,0 +1,157 @@ +include modifiers + +# +# Top row +# +1 0x2 +2 0x3 +3 0x4 +4 0x5 +5 0x6 +6 0x7 +7 0x8 +8 0x9 +9 0xa +0 0xb +BackSpace 0xe + +# +# QWERTY first row +# +Tab 0xf localstate +ISO_Left_Tab 0xf shift +q 0x10 addupper +w 0x11 addupper +e 0x12 addupper +r 0x13 addupper +t 0x14 addupper +y 0x15 addupper +u 0x16 addupper +i 0x17 addupper +o 0x18 addupper +p 0x19 addupper + +# +# QWERTY second row +# +a 0x1e addupper +s 0x1f addupper +d 0x20 addupper +f 0x21 addupper +g 0x22 addupper +h 0x23 addupper +j 0x24 addupper +k 0x25 addupper +l 0x26 addupper +Return 0x1c localstate + +# +# QWERTY third row +# +z 0x2c addupper +x 0x2d addupper +c 0x2e addupper +v 0x2f addupper +b 0x30 addupper +n 0x31 addupper +m 0x32 addupper + +space 0x39 localstate + +less 0x56 +greater 0x56 shift +bar 0x56 altgr +brokenbar 0x56 shift altgr + +# +# Esc and Function keys +# +Escape 0x1 localstate +F1 0x3b localstate +F2 0x3c localstate +F3 0x3d localstate +F4 0x3e localstate +F5 0x3f localstate +F6 0x40 localstate +F7 0x41 localstate +F8 0x42 localstate +F9 0x43 localstate +F10 0x44 localstate +F11 0x57 localstate +F12 0x58 localstate + +# Printscreen, Scrollock and Pause +# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37), +# but (0xe0, 0x37) seems to work. +Print 0xb7 localstate +Sys_Req 0xb7 localstate +Execute 0xb7 localstate +Scroll_Lock 0x46 + +# +# Insert - PgDown +# +Insert 0xd2 localstate +Delete 0xd3 localstate +Home 0xc7 localstate +End 0xcf localstate +Page_Up 0xc9 localstate +Page_Down 0xd1 localstate + +# +# Arrow keys +# +Left 0xcb localstate +Up 0xc8 localstate +Down 0xd0 localstate +Right 0xcd localstate + +# +# Numpad +# +Num_Lock 0x45 +KP_Divide 0xb5 +KP_Multiply 0x37 +KP_Subtract 0x4a +KP_Add 0x4e +KP_Enter 0x9c + +KP_Decimal 0x53 numlock +KP_Separator 0x53 numlock +KP_Delete 0x53 + +KP_0 0x52 numlock +KP_Insert 0x52 + +KP_1 0x4f numlock +KP_End 0x4f + +KP_2 0x50 numlock +KP_Down 0x50 + +KP_3 0x51 numlock +KP_Next 0x51 + +KP_4 0x4b numlock +KP_Left 0x4b + +KP_5 0x4c numlock +KP_Begin 0x4c + +KP_6 0x4d numlock +KP_Right 0x4d + +KP_7 0x47 numlock +KP_Home 0x47 + +KP_8 0x48 numlock +KP_Up 0x48 + +KP_9 0x49 numlock +KP_Prior 0x49 + +Caps_Lock 0x3a +# +# Inhibited keys +# +Multi_key 0x0 inhibit diff --git a/data/tools/qemu/keymaps/da b/data/tools/qemu/keymaps/da new file mode 100644 index 0000000..3884dcf --- /dev/null +++ b/data/tools/qemu/keymaps/da @@ -0,0 +1,120 @@ +# generated from XKB map dk +include common +map 0x406 +exclam 0x02 shift +exclamdown 0x02 altgr +onesuperior 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +twosuperior 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +threesuperior 0x04 shift altgr +currency 0x05 shift +dollar 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +cent 0x06 shift altgr +ampersand 0x07 shift +yen 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +division 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +guillemotleft 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +guillemotright 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +plus 0x0c +question 0x0c shift +plusminus 0x0c altgr +questiondown 0x0c shift altgr +dead_acute 0x0d +dead_grave 0x0d shift +bar 0x0d altgr +brokenbar 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +registered 0x13 altgr +thorn 0x14 altgr +THORN 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oe 0x18 altgr +OE 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +dead_tilde 0x1b altgr +dead_caron 0x1b shift altgr +ordfeminine 0x1e altgr +masculine 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ae 0x27 +AE 0x27 shift +oslash 0x28 +Ooblique 0x28 shift +dead_caron 0x28 shift altgr +onehalf 0x29 +section 0x29 shift +threequarters 0x29 altgr +paragraph 0x29 shift altgr +apostrophe 0x2b +asterisk 0x2b shift +dead_doubleacute 0x2b altgr +multiply 0x2b shift altgr +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +copyright 0x2e altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +dead_cedilla 0x33 altgr +dead_ogonek 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +dead_abovedot 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +hyphen 0x35 altgr +macron 0x35 shift altgr +nobreakspace 0x39 altgr +less 0x56 +greater 0x56 shift +backslash 0x56 altgr +notsign 0x56 shift altgr diff --git a/data/tools/qemu/keymaps/de b/data/tools/qemu/keymaps/de new file mode 100644 index 0000000..ed929c7 --- /dev/null +++ b/data/tools/qemu/keymaps/de @@ -0,0 +1,114 @@ +# generated from XKB map de +include common +map 0x407 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +section 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +currency 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +ssharp 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +acute 0x0d +dead_acute 0x0d +grave 0x0d shift +dead_grave 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +udiaeresis 0x1a +Udiaeresis 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +asciitilde 0x1b altgr +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +odiaeresis 0x27 +Odiaeresis 0x27 shift +dead_doubleacute 0x27 altgr +adiaeresis 0x28 +Adiaeresis 0x28 shift +dead_caron 0x28 shift altgr +asciicircum 0x29 +dead_circumflex 0x29 +degree 0x29 shift +notsign 0x29 altgr +numbersign 0x2b +apostrophe 0x2b shift +dead_breve 0x2b shift altgr +y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/de-ch b/data/tools/qemu/keymaps/de-ch new file mode 100644 index 0000000..f83837b --- /dev/null +++ b/data/tools/qemu/keymaps/de-ch @@ -0,0 +1,169 @@ +# rdesktop Swiss-German (de-ch) keymap file +# 2003-06-03 by noldi@tristar.ch +# +include common +map 0x00000807 +# +# Scan Code 1 +section 0x29 +degree 0x29 shift +notsign 0x29 altgr inhibit +# +# Scan Code 2 +plus 0x2 shift +brokenbar 0x02 altgr +# +# Scan Code 3 +quotedbl 0x03 shift +at 0x03 altgr +# +# Scan Code 4 +asterisk 0x04 shift +numbersign 0x04 altgr +# +# Scan Code 5 +ccedilla 0x05 shift +onequarter 0x05 altgr inhibit +# +# Scan Code 6 +percent 0x06 shift +onehalf 0x06 altgr inhibit +# +# Scan Code 7 +ampersand 0x07 shift +notsign 0x07 altgr +# +# Scan Code 8 +slash 0x08 shift +bar 0x08 altgr +# +# Scan Code 9 +parenleft 0x09 shift +cent 0x09 altgr +# +# Scan Code 10 +parenright 0x0a shift +# +# Scan Code 11 +equal 0x0b shift +braceright 0x0b altgr inhibit +# +# Scan Code 12 +apostrophe 0x0c +question 0x0c shift +dead_acute 0x0c altgr +# +# Scan Code 13 +dead_circumflex 0x0d +dead_grave 0x0d shift +dead_tilde 0x0d altgr +# +# Scan Code 19 +EuroSign 0x12 altgr +# +# Scan Code 22 +z 0x15 addupper +# +# Scan Code 27 +udiaeresis 0x1a +egrave 0x1a shift +bracketleft 0x1a altgr +# +# Scan Code 28 +dead_diaeresis 0x1b +exclam 0x1b shift +bracketright 0x1b altgr +# +# Scan Code 40 +odiaeresis 0x27 +eacute 0x27 shift +# +# Scan Code 41 +adiaeresis 0x28 +agrave 0x28 shift +braceleft 0x28 altgr +# +# Scan Code 42 (only on international keyboards) +dollar 0x2b +sterling 0x2b shift +braceright 0x2b altgr +# +# Scan Code 45 (only on international keyboards) +backslash 0x56 altgr +# +# Scan Code 46 +y 0x2c addupper +# +# Scan Code 53 +comma 0x33 +semicolon 0x33 shift +# +# Scan Code 54 +period 0x34 +colon 0x34 shift +# +# Scan Code 55 +minus 0x35 +underscore 0x35 shift +# +# Suppress Windows unsupported AltGr keys +# +# Scan Code 17 +paragraph 0x10 altgr inhibit +# +# Scan Code 21 +tslash 0x14 altgr inhibit +# +# Scan Code 22 +leftarrow 0x15 altgr inhibit +# +# Scan Code 23 +downarrow 0x16 altgr inhibit +# +# Scan Code 24 +rightarrow 0x17 altgr inhibit +# +# Scan Code 25 +oslash 0x18 altgr inhibit +# +# Scan Code 26 +thorn 0x19 altgr inhibit +# +# Scan Code 31 +ae 0x1e altgr inhibit +# +# Scan Code 32 +ssharp 0x1f altgr inhibit +# +# Scan Code 33 +eth 0x20 altgr inhibit +# +# Scan Code 34 +dstroke 0x21 altgr inhibit +# +# Scan Code 35 +eng 0x22 altgr inhibit +# +# Scan Code 36 +hstroke 0x23 altgr inhibit +# +# Scan Code 38 +kra 0x25 altgr inhibit +# +# Scan Code 39 +lstroke 0x26 altgr inhibit +# +# Scan Code 46 +guillemotleft 0x2c altgr inhibit +# +# Scan Code 47 +guillemotright 0x2d altgr inhibit +# +# Scan Code 49 +leftdoublequotemark 0x2f altgr inhibit +# +# Scan Code 50 +rightdoublequotemark 0x30 altgr inhibit +# +# Scan Code 52 +mu 0x32 altgr inhibit diff --git a/data/tools/qemu/keymaps/en-gb b/data/tools/qemu/keymaps/en-gb new file mode 100644 index 0000000..b45f06c --- /dev/null +++ b/data/tools/qemu/keymaps/en-gb @@ -0,0 +1,119 @@ +# generated from XKB map gb +include common +map 0x809 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +sterling 0x04 shift +threesuperior 0x04 altgr +dollar 0x05 shift +EuroSign 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +asciicircum 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenleft 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +parenright 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +equal 0x0d +plus 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +apostrophe 0x28 +at 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +grave 0x29 +notsign 0x29 shift +bar 0x29 altgr +numbersign 0x2b +asciitilde 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +less 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +greater 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +slash 0x35 +question 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr +backslash 0x56 +bar 0x56 shift diff --git a/data/tools/qemu/keymaps/en-us b/data/tools/qemu/keymaps/en-us new file mode 100644 index 0000000..f5784bb --- /dev/null +++ b/data/tools/qemu/keymaps/en-us @@ -0,0 +1,35 @@ +# generated from XKB map us +include common +map 0x409 +exclam 0x02 shift +at 0x03 shift +numbersign 0x04 shift +dollar 0x05 shift +percent 0x06 shift +asciicircum 0x07 shift +ampersand 0x08 shift +asterisk 0x09 shift +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +bracketleft 0x1a +braceleft 0x1a shift +bracketright 0x1b +braceright 0x1b shift +semicolon 0x27 +colon 0x27 shift +apostrophe 0x28 +quotedbl 0x28 shift +grave 0x29 +asciitilde 0x29 shift +backslash 0x2b +bar 0x2b shift +comma 0x33 +less 0x33 shift +period 0x34 +greater 0x34 shift +slash 0x35 +question 0x35 shift diff --git a/data/tools/qemu/keymaps/es b/data/tools/qemu/keymaps/es new file mode 100644 index 0000000..0c29eec --- /dev/null +++ b/data/tools/qemu/keymaps/es @@ -0,0 +1,105 @@ +# generated from XKB map es +include common +map 0x40a +exclam 0x02 shift +bar 0x02 altgr +quotedbl 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +periodcentered 0x04 shift +numbersign 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +asciitilde 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +notsign 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +seveneighths 0x08 shift altgr +parenleft 0x09 shift +trademark 0x09 shift altgr +parenright 0x0a shift +plusminus 0x0a shift altgr +equal 0x0b shift +degree 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +exclamdown 0x0d +questiondown 0x0d shift +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +dead_grave 0x1a +dead_circumflex 0x1a shift +bracketleft 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +bracketright 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ntilde 0x27 +Ntilde 0x27 shift +dead_doubleacute 0x27 shift altgr +dead_acute 0x28 +dead_diaeresis 0x28 shift +braceleft 0x28 altgr +masculine 0x29 +ordfeminine 0x29 shift +backslash 0x29 altgr +ccedilla 0x2b +Ccedilla 0x2b shift +braceright 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x56 +greater 0x56 shift +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/et b/data/tools/qemu/keymaps/et new file mode 100644 index 0000000..b5a73fe --- /dev/null +++ b/data/tools/qemu/keymaps/et @@ -0,0 +1,86 @@ +map 0x00000425 +include common + +# +# Top row +# +dead_caron 0x29 +dead_tilde 0x29 shift + +# 1 +exclam 0x2 shift + +# 2 +quotedbl 0x3 shift +at 0x3 altgr + +# 3 +numbersign 0x4 shift +sterling 0x4 altgr +# 4 +currency 0x5 shift +dollar 0x5 altgr +# 5 +percent 0x6 shift +# 6 +ampersand 0x7 shift +# 7 +slash 0x8 shift +braceleft 0x8 altgr +# 8 +parenleft 0x9 shift +bracketleft 0x9 altgr +# 9 +parenright 0xa shift +bracketright 0xa altgr +# 0 +equal 0xb shift +braceright 0xb altgr + +plus 0xc +question 0xc shift +backslash 0xc altgr + +acute 0xd +dead_acute 0xd +grave 0xd shift +dead_grave 0xd shift + +# +# QWERTY first row +# +EuroSign 0x12 altgr +udiaeresis 0x1a +Udiaeresis 0x1a shift +otilde 0x1b +Otilde 0x1b shift +section 0x1b altgr + +# +# QWERTY second row +# +scaron 0x1f altgr +Scaron 0x1f altgr shift +odiaeresis 0x27 +Odiaeresis 0x27 shift +adiaeresis 0x28 +Adiaeresis 0x28 shift +asciicircum 0x28 altgr +apostrophe 0x2b +asterisk 0x2b shift +onehalf 0x2b altgr +# +# QWERTY third row +# +less 0x56 +greater 0x56 shift +bar 0x56 altgr +zcaron 0x2c altgr +Zcaron 0x2c altgr shift +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +minus 0x35 +underscore 0x35 shift + diff --git a/data/tools/qemu/keymaps/fi b/data/tools/qemu/keymaps/fi new file mode 100644 index 0000000..2a4e0f0 --- /dev/null +++ b/data/tools/qemu/keymaps/fi @@ -0,0 +1,124 @@ +# generated from XKB map se_FI +include common +map 0x40b +exclam 0x02 shift +exclamdown 0x02 altgr +onesuperior 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +twosuperior 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +threesuperior 0x04 shift altgr +currency 0x05 shift +dollar 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +cent 0x06 shift altgr +ampersand 0x07 shift +yen 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +division 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +guillemotleft 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +guillemotright 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +plus 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +dead_acute 0x0d +dead_grave 0x0d shift +plusminus 0x0d altgr +notsign 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +registered 0x13 altgr +thorn 0x14 altgr +THORN 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oe 0x18 altgr +OE 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +dead_tilde 0x1b altgr +dead_caron 0x1b shift altgr +ordfeminine 0x1e altgr +masculine 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +ampersand 0x25 shift altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +odiaeresis 0x27 +Odiaeresis 0x27 shift +oslash 0x27 altgr +Ooblique 0x27 shift altgr +adiaeresis 0x28 +Adiaeresis 0x28 shift +ae 0x28 altgr +AE 0x28 shift altgr +section 0x29 +onehalf 0x29 shift +paragraph 0x29 altgr +threequarters 0x29 shift altgr +apostrophe 0x2b +asterisk 0x2b shift +acute 0x2b altgr +multiply 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +copyright 0x2e altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +dead_cedilla 0x33 altgr +dead_ogonek 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +dead_abovedot 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +hyphen 0x35 altgr +macron 0x35 shift altgr +nobreakspace 0x39 altgr diff --git a/data/tools/qemu/keymaps/fo b/data/tools/qemu/keymaps/fo new file mode 100644 index 0000000..83add42 --- /dev/null +++ b/data/tools/qemu/keymaps/fo @@ -0,0 +1,77 @@ +map 0x438 +include common + +# +# Top row +# +onehalf 0x29 +section 0x29 shift + +# 1 +exclam 0x2 shift + +# 2 +quotedbl 0x3 shift +at 0x3 altgr + +# 3 +numbersign 0x4 shift +sterling 0x4 altgr +# 4 +currency 0x5 shift +dollar 0x5 altgr +# 5 +percent 0x6 shift +# 6 +ampersand 0x7 shift +# 7 +slash 0x8 shift +braceleft 0x8 altgr +# 8 +parenleft 0x9 shift +bracketleft 0x9 altgr +# 9 +parenright 0xa shift +bracketright 0xa altgr +# 0 +equal 0xb shift +braceright 0xb altgr + +plus 0xc +question 0xc shift +plusminus 0xc altgr + +bar 0xd altgr +dead_acute 0xd + +# +# QWERTY first row +# +EuroSign 0x12 altgr +aring 0x1a +Aring 0x1a shift +eth 0x1b addupper +asciitilde 0x1b altgr + +# +# QWERTY second row +# +ae 0x27 addupper +oslash 0x28 +Ooblique 0x28 shift +apostrophe 0x2b +asterisk 0x2b shift + +# +# QWERTY third row +# +less 0x56 +greater 0x56 shift +backslash 0x56 altgr +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +minus 0x35 +underscore 0x35 shift + diff --git a/data/tools/qemu/keymaps/fr b/data/tools/qemu/keymaps/fr new file mode 100644 index 0000000..cbb4591 --- /dev/null +++ b/data/tools/qemu/keymaps/fr @@ -0,0 +1,181 @@ +include common +map 0x40c +# +# Top row +# +twosuperior 0x29 +notsign 0x29 altgr + +ampersand 0x02 +1 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr + +eacute 0x03 +2 0x03 shift +asciitilde 0x03 altgr +oneeighth 0x03 shift altgr + +quotedbl 0x04 +3 0x04 shift +numbersign 0x04 altgr + +apostrophe 0x05 +4 0x05 shift +braceleft 0x05 altgr + +parenleft 0x06 +5 0x06 shift +bracketleft 0x06 altgr +threeeighths 0x06 shift altgr + +minus 0x07 +6 0x07 shift +bar 0x07 altgr +fiveeighths 0x07 shift altgr + +egrave 0x08 +7 0x08 shift +grave 0x08 altgr +seveneighths 0x08 shift altgr + +underscore 0x09 +8 0x09 shift +backslash 0x09 altgr +trademark 0x09 shift altgr + +ccedilla 0x0a +9 0x0a shift +asciicircum 0x0a altgr +plusminus 0x0a shift altgr + +agrave 0x0b +0 0x0b shift +at 0x0b altgr + +parenright 0x0c +degree 0x0c shift +bracketright 0x0c altgr +questiondown 0x0c shift altgr + +equal 0x0d +plus 0x0d shift +braceright 0x0d altgr +dead_ogonek 0x0d shift altgr + +# +# AZERTY first row +# + +a 0x10 addupper +ae 0x10 altgr +AE 0x10 shift altgr + +z 0x11 addupper +guillemotleft 0x11 altgr + +EuroSign 0x12 altgr + +paragraph 0x13 altgr +registered 0x13 shift altgr + +tslash 0x14 altgr +Tslash 0x14 shift altgr + +leftarrow 0x15 altgr +yen 0x15 shift altgr + +downarrow 0x16 altgr +uparrow 0x16 shift altgr + +rightarrow 0x17 altgr +idotless 0x17 shift altgr + +oslash 0x18 altgr +Ooblique 0x18 shift altgr + +thorn 0x19 altgr +THORN 0x19 shift altgr + +dead_circumflex 0x1a +dead_diaeresis 0x1a shift +dead_abovering 0x1a shift altgr + +dollar 0x1b +sterling 0x1b shift +currency 0x1b altgr +dead_macron 0x1b shift altgr + +# +# AZERTY second row +# +q 0x1e addupper +Greek_OMEGA 0x1e shift altgr + +ssharp 0x1f altgr + +eth 0x20 altgr +ETH 0x20 shift altgr + +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr + +eng 0x22 altgr +ENG 0x22 shift altgr + +hstroke 0x23 altgr +Hstroke 0x23 shift altgr + +kra 0x25 altgr + +lstroke 0x26 altgr +Lstroke 0x26 shift altgr + +m 0x27 addupper +masculine 0x27 shift altgr + +ugrave 0x28 +percent 0x28 shift +dead_caron 0x28 shift altgr + +asterisk 0x2b +mu 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr + +# +# AZERTY third row +# +less 0x56 +greater 0x56 shift + +w 0x2c addupper + +guillemotright 0x2d altgr + +cent 0x2e altgr +copyright 0x2e shift altgr + +leftdoublequotemark 0x2f altgr + +rightdoublequotemark 0x30 altgr + +comma 0x32 +question 0x32 shift +dead_acute 0x32 altgr +dead_doubleacute 0x32 shift altgr + +semicolon 0x33 +period 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr + +colon 0x34 +slash 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr + +exclam 0x35 +section 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/fr-be b/data/tools/qemu/keymaps/fr-be new file mode 100644 index 0000000..92d668e --- /dev/null +++ b/data/tools/qemu/keymaps/fr-be @@ -0,0 +1,140 @@ +# generated from XKB map be +include common +map 0x80c +ampersand 0x02 +1 0x02 shift +bar 0x02 altgr +exclamdown 0x02 shift altgr +eacute 0x03 +2 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +quotedbl 0x04 +3 0x04 shift +numbersign 0x04 altgr +sterling 0x04 shift altgr +apostrophe 0x05 +4 0x05 shift +onequarter 0x05 altgr +dollar 0x05 shift altgr +parenleft 0x06 +5 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +section 0x07 +6 0x07 shift +asciicircum 0x07 altgr +fiveeighths 0x07 shift altgr +egrave 0x08 +7 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +exclam 0x09 +8 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +ccedilla 0x0a +9 0x0a shift +braceleft 0x0a altgr +plusminus 0x0a shift altgr +agrave 0x0b +0 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +parenright 0x0c +degree 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +minus 0x0d +underscore 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +a 0x10 addupper +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +z 0x11 addupper +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +dead_circumflex 0x1a +dead_diaeresis 0x1a shift +bracketleft 0x1a altgr +dead_abovering 0x1a shift altgr +dollar 0x1b +asterisk 0x1b shift +bracketright 0x1b altgr +dead_macron 0x1b shift altgr +q 0x1e addupper +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +ampersand 0x25 shift altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +m 0x27 addupper +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +ugrave 0x28 +percent 0x28 shift +dead_acute 0x28 altgr +dead_caron 0x28 shift altgr +twosuperior 0x29 +threesuperior 0x29 shift +notsign 0x29 altgr +mu 0x2b +sterling 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +w 0x2c addupper +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +comma 0x32 +question 0x32 shift +dead_cedilla 0x32 altgr +masculine 0x32 shift altgr +semicolon 0x33 +period 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +colon 0x34 +slash 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +equal 0x35 +plus 0x35 shift +dead_tilde 0x35 altgr +dead_abovedot 0x35 shift altgr +backslash 0x56 altgr diff --git a/data/tools/qemu/keymaps/fr-ca b/data/tools/qemu/keymaps/fr-ca new file mode 100644 index 0000000..b645208 --- /dev/null +++ b/data/tools/qemu/keymaps/fr-ca @@ -0,0 +1,50 @@ +# Canadian French +# By Simon Germain +include common +map 0xc0c + +backslash 0x29 altgr +plusminus 0x2 altgr +at 0x3 altgr +sterling 0x4 altgr +cent 0x5 altgr +currency 0x6 altgr +notsign 0x7 altgr +bar 0x29 shift +twosuperior 0x9 altgr +threesuperior 0xa altgr +onequarter 0xb altgr +onehalf 0xc altgr +threequarters 0xd altgr +section 0x18 altgr +paragraph 0x19 altgr +bracketleft 0x1a altgr +bracketright 0x1b altgr +asciitilde 0x27 altgr +braceleft 0x28 altgr +braceright 0x2b altgr +less 0x2b +greater 0x2b shift +guillemotleft 0x56 +guillemotright 0x56 shift +degree 0x56 altgr +mu 0x32 altgr +eacute 0x35 +dead_acute 0x35 altgr +dead_grave 0x28 +dead_circumflex 0x1a +dead_circumflex 0x1a shift +dead_cedilla 0x1b +dead_diaeresis 0x1b shift +exclam 0x2 shift +quotedbl 0x3 shift +slash 0x4 shift +dollar 0x5 shift +percent 0x6 shift +question 0x7 shift +ampersand 0x8 shift +asterisk 0x9 shift +parenleft 0xa shift +parenright 0xb shift +underscore 0xc shift +plus 0xd shift diff --git a/data/tools/qemu/keymaps/fr-ch b/data/tools/qemu/keymaps/fr-ch new file mode 100644 index 0000000..4620d20 --- /dev/null +++ b/data/tools/qemu/keymaps/fr-ch @@ -0,0 +1,114 @@ +# generated from XKB map fr_CH +include common +map 0x100c +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +section 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +currency 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +ssharp 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +acute 0x0d +dead_acute 0x0d +grave 0x0d shift +dead_grave 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +udiaeresis 0x1a +Udiaeresis 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +asciitilde 0x1b altgr +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +odiaeresis 0x27 +Odiaeresis 0x27 shift +dead_doubleacute 0x27 altgr +adiaeresis 0x28 +Adiaeresis 0x28 shift +dead_caron 0x28 shift altgr +asciicircum 0x29 +dead_circumflex 0x29 +degree 0x29 shift +notsign 0x29 altgr +numbersign 0x2b +apostrophe 0x2b shift +dead_breve 0x2b shift altgr +y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/hr b/data/tools/qemu/keymaps/hr new file mode 100644 index 0000000..613aa69 --- /dev/null +++ b/data/tools/qemu/keymaps/hr @@ -0,0 +1,125 @@ +# generated from XKB map hr +include common +map 0x41a +exclam 0x02 shift +asciitilde 0x02 altgr +dead_tilde 0x02 shift altgr +quotedbl 0x03 shift +dead_caron 0x03 altgr +caron 0x03 shift altgr +numbersign 0x04 shift +asciicircum 0x04 altgr +dead_circumflex 0x04 shift altgr +dollar 0x05 shift +dead_breve 0x05 altgr +breve 0x05 shift altgr +percent 0x06 shift +degree 0x06 altgr +dead_abovering 0x06 shift altgr +ampersand 0x07 shift +dead_ogonek 0x07 altgr +ogonek 0x07 shift altgr +slash 0x08 shift +grave 0x08 altgr +dead_grave 0x08 shift altgr +parenleft 0x09 shift +dead_abovedot 0x09 altgr +abovedot 0x09 shift altgr +parenright 0x0a shift +dead_acute 0x0a altgr +apostrophe 0x0a shift altgr +equal 0x0b shift +dead_doubleacute 0x0b altgr +doubleacute 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +dead_diaeresis 0x0c altgr +diaeresis 0x0c shift altgr +plus 0x0d +asterisk 0x0d shift +dead_cedilla 0x0d altgr +cedilla 0x0d shift altgr +backslash 0x10 altgr +Greek_OMEGA 0x10 shift altgr +bar 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +scaron 0x1a +Scaron 0x1a shift +division 0x1a altgr +dead_abovering 0x1a shift altgr +dstroke 0x1b +Dstroke 0x1b shift +multiply 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +bracketleft 0x21 altgr +ordfeminine 0x21 shift altgr +bracketright 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +lstroke 0x25 altgr +ampersand 0x25 shift altgr +Lstroke 0x26 altgr +ccaron 0x27 +Ccaron 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +cacute 0x28 +Cacute 0x28 shift +ssharp 0x28 altgr +dead_caron 0x28 shift altgr +dead_cedilla 0x29 +dead_diaeresis 0x29 shift +notsign 0x29 altgr +zcaron 0x2b +Zcaron 0x2b shift +currency 0x2b altgr +dead_breve 0x2b shift altgr +y 0x2c addupper +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +at 0x2f altgr +grave 0x2f shift altgr +braceleft 0x30 altgr +apostrophe 0x30 shift altgr +braceright 0x31 altgr +section 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/hu b/data/tools/qemu/keymaps/hu new file mode 100644 index 0000000..8aba444 --- /dev/null +++ b/data/tools/qemu/keymaps/hu @@ -0,0 +1,115 @@ +# Hungarian keyboard layout (QWERTZ) +# Created by: The NeverGone + +include common +map 0x40e + + +# AltGr keys: +notsign 0x29 altgr +asciitilde 0x02 altgr +caron 0x03 altgr +asciicircum 0x04 altgr +breve 0x05 altgr +degree 0x06 altgr +ogonek 0x07 altgr +grave 0x08 altgr +abovedot 0x09 altgr +acute 0x0a altgr +doubleacute 0x0b altgr +diaeresis 0x0c altgr +cedilla 0x0d altgr +backslash 0x10 altgr +bar 0x11 altgr +EuroSign 0x12 altgr +Iacute 0x17 altgr +division 0x1a altgr +multiply 0x1b altgr +dstroke 0x1f altgr +Dstroke 0x20 altgr +bracketleft 0x21 altgr +bracketright 0x22 altgr +iacute 0x24 altgr +lstroke 0x25 altgr +Lstroke 0x26 altgr +dollar 0x27 altgr +ssharp 0x28 altgr +currency 0x2b altgr +less 0x56 altgr +greater 0x2c altgr +numbersign 0x2d altgr +ampersand 0x2e altgr +at 0x2f altgr +braceleft 0x30 altgr +braceright 0x31 altgr +semicolon 0x33 altgr +asterisk 0x35 altgr + + +# Shift keys: +section 0x29 shift +apostrophe 0x02 shift +quotedbl 0x03 shift +plus 0x04 shift +exclam 0x05 shift +percent 0x06 shift +slash 0x07 shift +equal 0x08 shift +parenleft 0x09 shift +parenright 0x0a shift +Odiaeresis 0x0b shift +Udiaeresis 0x0c shift +Oacute 0x0d shift +Z 0x15 shift +Odoubleacute 0x1a shift +Uacute 0x1b shift +Eacute 0x27 shift +Aacute 0x28 shift +Udoubleacute 0x2b shift +Y 0x2c shift +question 0x33 shift +colon 0x34 shift +underscore 0x35 shift +F13 0x3b shift +F14 0x3c shift +F15 0x3d shift +F16 0x3e shift +F17 0x3f shift +F18 0x40 shift +F19 0x41 shift +F20 0x42 shift +F21 0x43 shift +F22 0x44 shift +F23 0x57 shift +F24 0x58 shift + + +# Ctrl keys: +F25 0x3b ctrl +F26 0x3c ctrl +F27 0x3d ctrl +F28 0x3e ctrl +F29 0x3f ctrl +F30 0x40 ctrl +F31 0x41 ctrl +F32 0x42 ctrl +F33 0x43 ctrl +F34 0x44 ctrl +F35 0x57 ctrl +#NoSymbol 0x58 ctrl + + +0 0x29 +odiaeresis 0x0b +udiaeresis 0x0c +oacute 0x0d +z 0x15 +odoubleacute 0x1a +uacute 0x1b +eacute 0x27 +aacute 0x28 +udoubleacute 0x2b +y 0x2c +comma 0x33 +period 0x34 +minus 0x35 diff --git a/data/tools/qemu/keymaps/is b/data/tools/qemu/keymaps/is new file mode 100644 index 0000000..8fde40f --- /dev/null +++ b/data/tools/qemu/keymaps/is @@ -0,0 +1,140 @@ +# 2004-03-16 Halldór Guðmundsson and Morten Lange +# Keyboard definition file for the Icelandic keyboard +# to be used in rdesktop 1.3.x ( See rdesktop.org) +# generated from XKB map de, and changed manually +# Location for example /usr/local/share/rdesktop/keymaps/is +include common +map 0x40f +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +#section 0x04 shift +numbersign 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +currency 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +#ssharp 0x0c +odiaeresis 0x0c +#question 0x0c shift +Odiaeresis 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +#acute 0x0d +minus 0x0d +#dead_acute 0x0d +#grave 0x0d shift +#dead_grave 0x0d shift +underscore 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +#z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +#thorn 0x19 altgr +#THORN 0x19 shift altgr +#udiaeresis 0x1a +#Udiaeresis 0x1a shift +#dead_diaeresis 0x1a altgr +#dead_abovering 0x1a shift altgr +eth 0x1a +ETH 0x1a shift +apostrophe 0x1b +question 0x1b shift +#plus 0x1b +#asterisk 0x1b shift +asciitilde 0x1b altgr +#grave 0x1b altgr +#dead_tilde 0x1b altgr +#dead_macron 0x1b shift altgr +#ae 0x1e altgr +#AE 0x1e shift altgr +#eth 0x20 altgr +#eth 0x20 +#ETH 0x20 shift altgr +#ETH 0x20 shift +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +#adiaeresis 0x27 +#Adiaeresis 0x27 shift +ae 0x27 +AE 0x27 shift +dead_doubleacute 0x27 altgr +#adiaeresis 0x28 +#Adiaeresis 0x28 shift +#dead_caron 0x28 shift altgr +#asciicircum 0x29 +acute 0x28 +dead_acute 0x28 +#dead_circumflex 0x29 +#degree 0x29 shift +#notsign 0x29 altgr +plus 0x2b +asterisk 0x2b shift +grave 0x2b altgr +#numbersign 0x2b +#apostrophe 0x2b shift +#dead_breve 0x2b shift altgr +#y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +#minus 0x35 +#underscore 0x35 shift +thorn 0x35 +THORN 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr + diff --git a/data/tools/qemu/keymaps/it b/data/tools/qemu/keymaps/it new file mode 100644 index 0000000..00ca73a --- /dev/null +++ b/data/tools/qemu/keymaps/it @@ -0,0 +1,115 @@ +# generated from XKB map it +include common +map 0x410 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +sterling 0x04 shift +threesuperior 0x04 altgr +dollar 0x05 shift +onequarter 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +trademark 0x09 shift altgr +parenright 0x0a shift +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +grave 0x0c altgr +questiondown 0x0c shift altgr +igrave 0x0d +asciicircum 0x0d shift +asciitilde 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +egrave 0x1a +eacute 0x1a shift +bracketleft 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +bracketright 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ograve 0x27 +ccedilla 0x27 shift +at 0x27 altgr +dead_doubleacute 0x27 shift altgr +agrave 0x28 +degree 0x28 shift +numbersign 0x28 altgr +backslash 0x29 +bar 0x29 shift +notsign 0x29 altgr +ugrave 0x2b +section 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/ja b/data/tools/qemu/keymaps/ja new file mode 100644 index 0000000..6947cb1 --- /dev/null +++ b/data/tools/qemu/keymaps/ja @@ -0,0 +1,108 @@ +# generated from XKB map jp106 +include common +map 0x411 +exclam 0x02 shift +kana_NU 0x02 altgr +quotedbl 0x03 shift +kana_FU 0x03 altgr +numbersign 0x04 shift +kana_A 0x04 altgr +kana_a 0x04 shift altgr +dollar 0x05 shift +kana_U 0x05 altgr +kana_u 0x05 shift altgr +percent 0x06 shift +kana_E 0x06 altgr +kana_e 0x06 shift altgr +ampersand 0x07 shift +kana_O 0x07 altgr +kana_o 0x07 shift altgr +apostrophe 0x08 shift +kana_YA 0x08 altgr +kana_ya 0x08 shift altgr +parenleft 0x09 shift +kana_YU 0x09 altgr +kana_yu 0x09 shift altgr +parenright 0x0a shift +kana_YO 0x0a altgr +kana_yo 0x0a shift altgr +asciitilde 0x0b shift +kana_WA 0x0b altgr +kana_WO 0x0b shift altgr +minus 0x0c +equal 0x0c shift +kana_HO 0x0c altgr +asciicircum 0x0d +asciitilde 0x0d shift +kana_HE 0x0d altgr +kana_TA 0x10 altgr +kana_TE 0x11 altgr +kana_I 0x12 altgr +kana_i 0x12 shift altgr +kana_SU 0x13 altgr +kana_KA 0x14 altgr +kana_N 0x15 altgr +kana_NA 0x16 altgr +kana_NI 0x17 altgr +kana_RA 0x18 altgr +kana_SE 0x19 altgr +at 0x1a +grave 0x1a shift +voicedsound 0x1a altgr +bracketleft 0x1b +braceleft 0x1b shift +semivoicedsound 0x1b altgr +kana_openingbracket 0x1b shift altgr +kana_CHI 0x1e altgr +kana_TO 0x1f altgr +kana_SHI 0x20 altgr +kana_HA 0x21 altgr +kana_KI 0x22 altgr +kana_KU 0x23 altgr +kana_MA 0x24 altgr +kana_NO 0x25 altgr +kana_RI 0x26 altgr +semicolon 0x27 +plus 0x27 shift +kana_RE 0x27 altgr +colon 0x28 +asterisk 0x28 shift +kana_KE 0x28 altgr +Zenkaku_Hankaku 0x29 +bracketright 0x2b +braceright 0x2b shift +kana_MU 0x2b altgr +kana_closingbracket 0x2b shift altgr +kana_TSU 0x2c altgr +kana_tsu 0x2c shift altgr +kana_SA 0x2d altgr +kana_SO 0x2e altgr +kana_HI 0x2f altgr +kana_KO 0x30 altgr +kana_MI 0x31 altgr +kana_MO 0x32 altgr +comma 0x33 +less 0x33 shift +kana_NE 0x33 altgr +kana_comma 0x33 shift altgr +period 0x34 +greater 0x34 shift +kana_RU 0x34 altgr +kana_fullstop 0x34 shift altgr +slash 0x35 +question 0x35 shift +kana_ME 0x35 altgr +kana_conjunctive 0x35 shift altgr +Eisu_toggle 0x3a shift +Execute 0x54 shift +Kanji 0x70 +backslash 0x73 +yen 0x7d +bar 0x7d shift +underscore 0x73 shift +Henkan_Mode 0x79 +Katakana 0x70 +Muhenkan 0x7b +Henkan_Mode_Real 0x79 +Henkan_Mode_Ultra 0x79 +backslash_ja 0x73 diff --git a/data/tools/qemu/keymaps/lt b/data/tools/qemu/keymaps/lt new file mode 100644 index 0000000..3d9d619 --- /dev/null +++ b/data/tools/qemu/keymaps/lt @@ -0,0 +1,57 @@ +# generated from XKB map lt +include common +map 0x427 +exclam 0x02 shift +aogonek 0x02 altgr +Aogonek 0x02 shift altgr +at 0x03 shift +ccaron 0x03 altgr +Ccaron 0x03 shift altgr +numbersign 0x04 shift +eogonek 0x04 altgr +Eogonek 0x04 shift altgr +dollar 0x05 shift +eabovedot 0x05 altgr +Eabovedot 0x05 shift altgr +percent 0x06 shift +iogonek 0x06 altgr +Iogonek 0x06 shift altgr +asciicircum 0x07 shift +scaron 0x07 altgr +Scaron 0x07 shift altgr +ampersand 0x08 shift +uogonek 0x08 altgr +Uogonek 0x08 shift altgr +asterisk 0x09 shift +umacron 0x09 altgr +Umacron 0x09 shift altgr +parenleft 0x0a shift +doublelowquotemark 0x0a altgr +parenright 0x0b shift +leftdoublequotemark 0x0b altgr +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +zcaron 0x0d altgr +Zcaron 0x0d shift altgr +bracketleft 0x1a +braceleft 0x1a shift +bracketright 0x1b +braceright 0x1b shift +semicolon 0x27 +colon 0x27 shift +apostrophe 0x28 +quotedbl 0x28 shift +grave 0x29 +asciitilde 0x29 shift +backslash 0x2b +bar 0x2b shift +comma 0x33 +less 0x33 shift +period 0x34 +greater 0x34 shift +slash 0x35 +question 0x35 shift +endash 0x56 +EuroSign 0x56 shift diff --git a/data/tools/qemu/keymaps/lv b/data/tools/qemu/keymaps/lv new file mode 100644 index 0000000..1d91727 --- /dev/null +++ b/data/tools/qemu/keymaps/lv @@ -0,0 +1,128 @@ +# generated from XKB map lv +include common +map 0x426 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +at 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +EuroSign 0x05 altgr +cent 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +asciicircum 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenleft 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +parenright 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +equal 0x0d +plus 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +emacron 0x12 altgr +Emacron 0x12 shift altgr +rcedilla 0x13 altgr +Rcedilla 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +umacron 0x16 altgr +Umacron 0x16 shift altgr +imacron 0x17 altgr +Imacron 0x17 shift altgr +omacron 0x18 altgr +Omacron 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ISO_Next_Group 0x1c shift +amacron 0x1e altgr +Amacron 0x1e shift altgr +scaron 0x1f altgr +Scaron 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +gcedilla 0x22 altgr +Gcedilla 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kcedilla 0x25 altgr +Kcedilla 0x25 shift altgr +lcedilla 0x26 altgr +Lcedilla 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +leftdoublequotemark 0x28 altgr +doublelowquotemark 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +notsign 0x29 altgr +backslash 0x2b +bar 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +zcaron 0x2c altgr +Zcaron 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +ccaron 0x2e altgr +Ccaron 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +ncedilla 0x31 altgr +Ncedilla 0x31 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +less 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +greater 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +slash 0x35 +question 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr +nobreakspace 0x39 altgr diff --git a/data/tools/qemu/keymaps/mk b/data/tools/qemu/keymaps/mk new file mode 100644 index 0000000..18c1504 --- /dev/null +++ b/data/tools/qemu/keymaps/mk @@ -0,0 +1,101 @@ +# generated from XKB map mk +include common +map 0x42f +exclam 0x02 shift +at 0x03 shift +doublelowquotemark 0x03 shift altgr +numbersign 0x04 shift +leftdoublequotemark 0x04 shift altgr +dollar 0x05 shift +percent 0x06 shift +asciicircum 0x07 shift +ampersand 0x08 shift +asterisk 0x09 shift +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +Cyrillic_lje 0x10 altgr +Cyrillic_LJE 0x10 shift altgr +Cyrillic_nje 0x11 altgr +Cyrillic_NJE 0x11 shift altgr +Cyrillic_ie 0x12 altgr +Cyrillic_IE 0x12 shift altgr +Cyrillic_er 0x13 altgr +Cyrillic_ER 0x13 shift altgr +Cyrillic_te 0x14 altgr +Cyrillic_TE 0x14 shift altgr +Macedonia_dse 0x15 altgr +Macedonia_DSE 0x15 shift altgr +Cyrillic_u 0x16 altgr +Cyrillic_U 0x16 shift altgr +Cyrillic_i 0x17 altgr +Cyrillic_I 0x17 shift altgr +Cyrillic_o 0x18 altgr +Cyrillic_O 0x18 shift altgr +Cyrillic_pe 0x19 altgr +Cyrillic_PE 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Cyrillic_sha 0x1a altgr +Cyrillic_SHA 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +Macedonia_gje 0x1b altgr +Macedonia_GJE 0x1b shift altgr +Cyrillic_a 0x1e altgr +Cyrillic_A 0x1e shift altgr +Cyrillic_es 0x1f altgr +Cyrillic_ES 0x1f shift altgr +Cyrillic_de 0x20 altgr +Cyrillic_DE 0x20 shift altgr +Cyrillic_ef 0x21 altgr +Cyrillic_EF 0x21 shift altgr +Cyrillic_ghe 0x22 altgr +Cyrillic_GHE 0x22 shift altgr +Cyrillic_ha 0x23 altgr +Cyrillic_HA 0x23 shift altgr +Cyrillic_je 0x24 altgr +Cyrillic_JE 0x24 shift altgr +Cyrillic_ka 0x25 altgr +Cyrillic_KA 0x25 shift altgr +Cyrillic_el 0x26 altgr +Cyrillic_EL 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Cyrillic_che 0x27 altgr +Cyrillic_CHE 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +Macedonia_kje 0x28 altgr +Macedonia_KJE 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +backslash 0x2b +bar 0x2b shift +Cyrillic_zhe 0x2b altgr +Cyrillic_ZHE 0x2b shift altgr +Cyrillic_ze 0x2c altgr +Cyrillic_ZE 0x2c shift altgr +Cyrillic_dzhe 0x2d altgr +Cyrillic_DZHE 0x2d shift altgr +Cyrillic_tse 0x2e altgr +Cyrillic_TSE 0x2e shift altgr +Cyrillic_ve 0x2f altgr +Cyrillic_VE 0x2f shift altgr +Cyrillic_be 0x30 altgr +Cyrillic_BE 0x30 shift altgr +Cyrillic_en 0x31 altgr +Cyrillic_EN 0x31 shift altgr +Cyrillic_em 0x32 altgr +Cyrillic_EM 0x32 shift altgr +comma 0x33 +less 0x33 shift +semicolon 0x33 shift altgr +period 0x34 +greater 0x34 shift +colon 0x34 shift altgr +slash 0x35 +question 0x35 shift diff --git a/data/tools/qemu/keymaps/modifiers b/data/tools/qemu/keymaps/modifiers new file mode 100644 index 0000000..d8b019f --- /dev/null +++ b/data/tools/qemu/keymaps/modifiers @@ -0,0 +1,17 @@ +Shift_R 0x36 +Shift_L 0x2a + +Alt_R 0xb8 +Mode_switch 0xb8 +Alt_L 0x38 + +Control_R 0x9d +Control_L 0x1d + +# Translate Super to Windows keys. +# This is hardcoded. See documentation for details. +Super_R 0xdb +Super_L 0xdc + +# Translate Menu to the Windows Application key. +Menu 0xdd diff --git a/data/tools/qemu/keymaps/nl b/data/tools/qemu/keymaps/nl new file mode 100644 index 0000000..bc823bd --- /dev/null +++ b/data/tools/qemu/keymaps/nl @@ -0,0 +1,60 @@ +# Dutch (Netherlands) +include common +map 0x413 + +exclam 0x02 shift +onesuperior 0x02 altgr +quotebl 0x03 shift +twosuperior 0x03 altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +dollar 0x05 shift +onequarter 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +ampersand 0x07 shift +threequarters 0x07 altgr +underscore 0x08 shift +sterling 0x08 altgr +parenleft 0x09 shift +braceleft 0x09 altgr +parenright 0x0a shift +braceright 0x0a altgr +apostrophe 0x0b shift +slash 0x0c +question 0x0c shift +backslash 0x0c altgr +degree 0x0d +dead_tilde 0x0d shift +dead_cedilla 0x0d altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +dead_diaeresis 0x1a +dead_circumflex 0x1a shift +asterisk 0x1b +bar 0x1b shift +ssharp 0x1f altgr +plus 0x27 +plusminus 0x27 shift +dead_acute 0x28 +dead_grave 0x28 shift +at 0x29 +section 0x29 shift +notsign 0x29 altgr +less 0x2b +greater 0x2b shift +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +copyright 0x2e altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +hyphen 0x35 +equal 0x35 shift +bracketright 0x56 +bracketleft 0x56 shift +brokenbar 0x56 altgr + diff --git a/data/tools/qemu/keymaps/nl-be b/data/tools/qemu/keymaps/nl-be new file mode 100644 index 0000000..34fc881 --- /dev/null +++ b/data/tools/qemu/keymaps/nl-be @@ -0,0 +1,3 @@ +# Dutch (Belgium) +map 0x813 +include common diff --git a/data/tools/qemu/keymaps/no b/data/tools/qemu/keymaps/no new file mode 100644 index 0000000..40a6479 --- /dev/null +++ b/data/tools/qemu/keymaps/no @@ -0,0 +1,119 @@ +# generated from XKB map no +include common +map 0x414 +exclam 0x02 shift +exclamdown 0x02 altgr +onesuperior 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +twosuperior 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +threesuperior 0x04 shift altgr +currency 0x05 shift +dollar 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +cent 0x06 shift altgr +ampersand 0x07 shift +yen 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +division 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +guillemotleft 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +guillemotright 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +plus 0x0c +question 0x0c shift +plusminus 0x0c altgr +questiondown 0x0c shift altgr +backslash 0x0d +dead_grave 0x0d shift +dead_acute 0x0d altgr +notsign 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +registered 0x13 altgr +thorn 0x14 altgr +THORN 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oe 0x18 altgr +OE 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +asciicircum 0x01b shift +dead_tilde 0x1b altgr +asciitilde 0x1b altgr +dead_caron 0x1b shift altgr +ordfeminine 0x1e altgr +masculine 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +oslash 0x27 +Ooblique 0x27 shift +dead_doubleacute 0x27 shift altgr +ae 0x28 +AE 0x28 shift +dead_caron 0x28 shift altgr +bar 0x29 +section 0x29 shift +brokenbar 0x29 altgr +paragraph 0x29 shift altgr +apostrophe 0x2b +asterisk 0x2b shift +multiply 0x2b shift altgr +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +copyright 0x2e altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +dead_cedilla 0x33 altgr +dead_ogonek 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +dead_abovedot 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +hyphen 0x35 altgr +macron 0x35 shift altgr +nobreakspace 0x39 altgr +onehalf 0x56 altgr +threequarters 0x56 shift altgr diff --git a/data/tools/qemu/keymaps/pl b/data/tools/qemu/keymaps/pl new file mode 100644 index 0000000..09c600d --- /dev/null +++ b/data/tools/qemu/keymaps/pl @@ -0,0 +1,122 @@ +# generated from XKB map pl +include common +map 0x415 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +at 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +asciicircum 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenleft 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +parenright 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +equal 0x0d +plus 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +eogonek 0x12 altgr +Eogonek 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +EuroSign 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oacute 0x18 altgr +Oacute 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +aogonek 0x1e altgr +Aogonek 0x1e shift altgr +sacute 0x1f altgr +Sacute 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +notsign 0x29 altgr +backslash 0x2b +bar 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +zabovedot 0x2c altgr +Zabovedot 0x2c shift altgr +zacute 0x2d altgr +Zacute 0x2d shift altgr +cacute 0x2e altgr +Cacute 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +nacute 0x31 altgr +Nacute 0x31 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +less 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +greater 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +slash 0x35 +question 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/pt b/data/tools/qemu/keymaps/pt new file mode 100644 index 0000000..c6941f6 --- /dev/null +++ b/data/tools/qemu/keymaps/pt @@ -0,0 +1,113 @@ +# generated from XKB map pt +include common +map 0x816 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +dollar 0x05 shift +section 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +guillemotleft 0x0d +guillemotright 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +plus 0x1a +asterisk 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_acute 0x1b +dead_grave 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ccedilla 0x27 +Ccedilla 0x27 shift +dead_doubleacute 0x27 shift altgr +masculine 0x28 +ordfeminine 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +backslash 0x29 +bar 0x29 shift +notsign 0x29 altgr +dead_tilde 0x2b +dead_circumflex 0x2b shift +dead_breve 0x2b shift altgr +less 0x56 +greater 0x56 shift +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/keymaps/pt-br b/data/tools/qemu/keymaps/pt-br new file mode 100644 index 0000000..54bafc5 --- /dev/null +++ b/data/tools/qemu/keymaps/pt-br @@ -0,0 +1,69 @@ +# generated from XKB map br +include common +map 0x416 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +at 0x03 shift +twosuperior 0x03 altgr +onehalf 0x03 shift altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +threequarters 0x04 shift altgr +dollar 0x05 shift +sterling 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +cent 0x06 altgr +dead_diaeresis 0x07 shift +notsign 0x07 altgr +diaeresis 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +parenleft 0x0a shift +bracketright 0x0a altgr +parenright 0x0b shift +braceright 0x0b altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +equal 0x0d +plus 0x0d shift +section 0x0d altgr +EuroSign 0x12 altgr +registered 0x13 altgr +dead_acute 0x1a +dead_grave 0x1a shift +acute 0x1a altgr +grave 0x1a shift altgr +bracketleft 0x1b +braceleft 0x1b shift +ordfeminine 0x1b altgr +ccedilla 0x27 +Ccedilla 0x27 shift +dead_tilde 0x28 +dead_circumflex 0x28 shift +asciitilde 0x28 altgr +asciicircum 0x28 shift altgr +apostrophe 0x29 +quotedbl 0x29 shift +bracketright 0x2b +braceright 0x2b shift +masculine 0x2b altgr +copyright 0x2e altgr +mu 0x32 altgr +comma 0x33 +less 0x33 shift +period 0x34 +greater 0x34 shift +semicolon 0x35 +colon 0x35 shift +comma 0x53 numlock +backslash 0x56 +bar 0x56 shift +slash 0x73 +question 0x73 shift +degree 0x73 altgr +KP_Decimal 0x34 diff --git a/data/tools/qemu/keymaps/ru b/data/tools/qemu/keymaps/ru new file mode 100644 index 0000000..b3e7d24 --- /dev/null +++ b/data/tools/qemu/keymaps/ru @@ -0,0 +1,109 @@ +# generated from XKB map ru +include common +map 0x419 +exclam 0x02 shift +at 0x03 shift +quotedbl 0x03 shift altgr +numbersign 0x04 shift +dollar 0x05 shift +asterisk 0x05 shift altgr +percent 0x06 shift +colon 0x06 shift altgr +asciicircum 0x07 shift +comma 0x07 shift altgr +ampersand 0x08 shift +period 0x08 shift altgr +asterisk 0x09 shift +semicolon 0x09 shift altgr +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +Cyrillic_shorti 0x10 altgr +Cyrillic_SHORTI 0x10 shift altgr +Cyrillic_tse 0x11 altgr +Cyrillic_TSE 0x11 shift altgr +Cyrillic_u 0x12 altgr +Cyrillic_U 0x12 shift altgr +Cyrillic_ka 0x13 altgr +Cyrillic_KA 0x13 shift altgr +Cyrillic_ie 0x14 altgr +Cyrillic_IE 0x14 shift altgr +Cyrillic_en 0x15 altgr +Cyrillic_EN 0x15 shift altgr +Cyrillic_ghe 0x16 altgr +Cyrillic_GHE 0x16 shift altgr +Cyrillic_sha 0x17 altgr +Cyrillic_SHA 0x17 shift altgr +Cyrillic_shcha 0x18 altgr +Cyrillic_SHCHA 0x18 shift altgr +Cyrillic_ze 0x19 altgr +Cyrillic_ZE 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Cyrillic_ha 0x1a altgr +Cyrillic_HA 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +Cyrillic_hardsign 0x1b altgr +Cyrillic_HARDSIGN 0x1b shift altgr +Cyrillic_ef 0x1e altgr +Cyrillic_EF 0x1e shift altgr +Cyrillic_yeru 0x1f altgr +Cyrillic_YERU 0x1f shift altgr +Cyrillic_ve 0x20 altgr +Cyrillic_VE 0x20 shift altgr +Cyrillic_a 0x21 altgr +Cyrillic_A 0x21 shift altgr +Cyrillic_pe 0x22 altgr +Cyrillic_PE 0x22 shift altgr +Cyrillic_er 0x23 altgr +Cyrillic_ER 0x23 shift altgr +Cyrillic_o 0x24 altgr +Cyrillic_O 0x24 shift altgr +Cyrillic_el 0x25 altgr +Cyrillic_EL 0x25 shift altgr +Cyrillic_de 0x26 altgr +Cyrillic_DE 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Cyrillic_zhe 0x27 altgr +Cyrillic_ZHE 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +Cyrillic_e 0x28 altgr +Cyrillic_E 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +Cyrillic_io 0x29 altgr +Cyrillic_IO 0x29 shift altgr +backslash 0x2b +bar 0x2b shift +Cyrillic_ya 0x2c altgr +Cyrillic_YA 0x2c shift altgr +Cyrillic_che 0x2d altgr +Cyrillic_CHE 0x2d shift altgr +Cyrillic_es 0x2e altgr +Cyrillic_ES 0x2e shift altgr +Cyrillic_em 0x2f altgr +Cyrillic_EM 0x2f shift altgr +Cyrillic_i 0x30 altgr +Cyrillic_I 0x30 shift altgr +Cyrillic_te 0x31 altgr +Cyrillic_TE 0x31 shift altgr +Cyrillic_softsign 0x32 altgr +Cyrillic_SOFTSIGN 0x32 shift altgr +comma 0x33 +less 0x33 shift +Cyrillic_be 0x33 altgr +Cyrillic_BE 0x33 shift altgr +period 0x34 +greater 0x34 shift +Cyrillic_yu 0x34 altgr +Cyrillic_YU 0x34 shift altgr +slash 0x35 +question 0x35 shift +slash 0x56 altgr +bar 0x56 shift altgr diff --git a/data/tools/qemu/keymaps/sl b/data/tools/qemu/keymaps/sl new file mode 100644 index 0000000..56835a9 --- /dev/null +++ b/data/tools/qemu/keymaps/sl @@ -0,0 +1,110 @@ +# generated from XKB map sl +include common +map 0x424 +exclam 0x02 shift +asciitilde 0x02 altgr +dead_tilde 0x02 shift altgr +quotedbl 0x03 shift +dead_caron 0x03 altgr +caron 0x03 shift altgr +numbersign 0x04 shift +asciicircum 0x04 altgr +dead_circumflex 0x04 shift altgr +dollar 0x05 shift +dead_breve 0x05 altgr +breve 0x05 shift altgr +percent 0x06 shift +degree 0x06 altgr +dead_abovering 0x06 shift altgr +ampersand 0x07 shift +dead_ogonek 0x07 altgr +ogonek 0x07 shift altgr +slash 0x08 shift +grave 0x08 altgr +dead_grave 0x08 shift altgr +parenleft 0x09 shift +dead_abovedot 0x09 altgr +abovedot 0x09 shift altgr +parenright 0x0a shift +dead_acute 0x0a altgr +equal 0x0b shift +dead_doubleacute 0x0b altgr +doubleacute 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +dead_diaeresis 0x0c altgr +diaeresis 0x0c shift altgr +plus 0x0d +asterisk 0x0d shift +dead_cedilla 0x0d altgr +cedilla 0x0d shift altgr +backslash 0x10 altgr +Greek_OMEGA 0x10 shift altgr +bar 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +scaron 0x1a +Scaron 0x1a shift +division 0x1a altgr +dstroke 0x1b +Dstroke 0x1b shift +multiply 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +bracketleft 0x21 altgr +ordfeminine 0x21 shift altgr +bracketright 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +lstroke 0x25 altgr +Lstroke 0x26 altgr +ccaron 0x27 +Ccaron 0x27 shift +cacute 0x28 +Cacute 0x28 shift +ssharp 0x28 altgr +dead_cedilla 0x29 +notsign 0x29 altgr +zcaron 0x2b +Zcaron 0x2b shift +currency 0x2b altgr +y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +at 0x2f altgr +braceleft 0x30 altgr +braceright 0x31 altgr +section 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr diff --git a/data/tools/qemu/keymaps/sv b/data/tools/qemu/keymaps/sv new file mode 100644 index 0000000..736d637 --- /dev/null +++ b/data/tools/qemu/keymaps/sv @@ -0,0 +1,82 @@ +map 0x0000041d +include common + +# +# Top row +# +section 0x29 +onehalf 0x29 shift + +# 1 +exclam 0x2 shift + +# 2 +quotedbl 0x3 shift +at 0x3 altgr + +# 3 +numbersign 0x4 shift +sterling 0x4 altgr +# 4 +currency 0x5 shift +dollar 0x5 altgr +# 5 +percent 0x6 shift +# 6 +ampersand 0x7 shift +# 7 +slash 0x8 shift +braceleft 0x8 altgr +# 8 +parenleft 0x9 shift +bracketleft 0x9 altgr +# 9 +parenright 0xa shift +bracketright 0xa altgr +# 0 +equal 0xb shift +braceright 0xb altgr + +plus 0xc +question 0xc shift +backslash 0xc altgr + +acute 0xd +dead_acute 0xd +grave 0xd shift +dead_grave 0xd shift + +# +# QWERTY first row +# +EuroSign 0x12 altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +dead_tilde 0x1b altgr + +# +# QWERTY second row +# +odiaeresis 0x27 +Odiaeresis 0x27 shift +adiaeresis 0x28 +Adiaeresis 0x28 shift +apostrophe 0x2b +asterisk 0x2b shift + +# +# QWERTY third row +# +less 0x56 +greater 0x56 shift +bar 0x56 altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +minus 0x35 +underscore 0x35 shift + diff --git a/data/tools/qemu/keymaps/th b/data/tools/qemu/keymaps/th new file mode 100644 index 0000000..b65b6da --- /dev/null +++ b/data/tools/qemu/keymaps/th @@ -0,0 +1,131 @@ +# generated from XKB map th +include common +map 0x41e +exclam 0x02 shift +Thai_lakkhangyao 0x02 altgr +plus 0x02 shift altgr +at 0x03 shift +slash 0x03 altgr +Thai_leknung 0x03 shift altgr +numbersign 0x04 shift +minus 0x04 altgr +Thai_leksong 0x04 shift altgr +dollar 0x05 shift +Thai_phosamphao 0x05 altgr +Thai_leksam 0x05 shift altgr +percent 0x06 shift +Thai_thothung 0x06 altgr +Thai_leksi 0x06 shift altgr +asciicircum 0x07 shift +Thai_sarau 0x07 altgr +Thai_sarauu 0x07 shift altgr +ampersand 0x08 shift +Thai_saraue 0x08 altgr +Thai_baht 0x08 shift altgr +asterisk 0x09 shift +Thai_khokhwai 0x09 altgr +Thai_lekha 0x09 shift altgr +parenleft 0x0a shift +Thai_totao 0x0a altgr +Thai_lekhok 0x0a shift altgr +parenright 0x0b shift +Thai_chochan 0x0b altgr +Thai_lekchet 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +Thai_khokhai 0x0c altgr +Thai_lekpaet 0x0c shift altgr +equal 0x0d +plus 0x0d shift +Thai_chochang 0x0d altgr +Thai_lekkao 0x0d shift altgr +Thai_maiyamok 0x10 altgr +Thai_leksun 0x10 shift altgr +Thai_saraaimaimalai 0x11 altgr +quotedbl 0x11 shift altgr +Thai_saraam 0x12 altgr +Thai_dochada 0x12 shift altgr +Thai_phophan 0x13 altgr +Thai_thonangmontho 0x13 shift altgr +Thai_saraa 0x14 altgr +Thai_thothong 0x14 shift altgr +Thai_maihanakat 0x15 altgr +Thai_nikhahit 0x15 shift altgr +Thai_saraii 0x16 altgr +Thai_maitri 0x16 shift altgr +Thai_rorua 0x17 altgr +Thai_nonen 0x17 shift altgr +Thai_nonu 0x18 altgr +Thai_paiyannoi 0x18 shift altgr +Thai_yoyak 0x19 altgr +Thai_yoying 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Thai_bobaimai 0x1a altgr +Thai_thothan 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +Thai_loling 0x1b altgr +comma 0x1b shift altgr +Thai_fofan 0x1e altgr +Thai_ru 0x1e shift altgr +Thai_hohip 0x1f altgr +Thai_khorakhang 0x1f shift altgr +Thai_kokai 0x20 altgr +Thai_topatak 0x20 shift altgr +Thai_dodek 0x21 altgr +Thai_sarao 0x21 shift altgr +Thai_sarae 0x22 altgr +Thai_chochoe 0x22 shift altgr +Thai_maitho 0x23 altgr +Thai_maitaikhu 0x23 shift altgr +Thai_maiek 0x24 altgr +Thai_maichattawa 0x24 shift altgr +Thai_saraaa 0x25 altgr +Thai_sorusi 0x25 shift altgr +Thai_sosua 0x26 altgr +Thai_sosala 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Thai_wowaen 0x27 altgr +Thai_soso 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +Thai_ngongu 0x28 altgr +period 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +underscore 0x29 altgr +percent 0x29 shift altgr +ISO_First_Group 0x2a shift +backslash 0x2b +bar 0x2b shift +Thai_khokhuat 0x2b altgr +Thai_khokhon 0x2b shift altgr +Thai_phophung 0x2c altgr +parenleft 0x2c shift altgr +Thai_popla 0x2d altgr +parenright 0x2d shift altgr +Thai_saraae 0x2e altgr +Thai_choching 0x2e shift altgr +Thai_oang 0x2f altgr +Thai_honokhuk 0x2f shift altgr +Thai_sarai 0x30 altgr +Thai_phinthu 0x30 shift altgr +Thai_sarauee 0x31 altgr +Thai_thanthakhat 0x31 shift altgr +Thai_thothahan 0x32 altgr +question 0x32 shift altgr +comma 0x33 +less 0x33 shift +Thai_moma 0x33 altgr +Thai_thophuthao 0x33 shift altgr +period 0x34 +greater 0x34 shift +Thai_saraaimaimuan 0x34 altgr +Thai_lochula 0x34 shift altgr +slash 0x35 +question 0x35 shift +Thai_fofa 0x35 altgr +Thai_lu 0x35 shift altgr +ISO_Last_Group 0x36 shift diff --git a/data/tools/qemu/keymaps/tr b/data/tools/qemu/keymaps/tr new file mode 100644 index 0000000..5650e1e --- /dev/null +++ b/data/tools/qemu/keymaps/tr @@ -0,0 +1,123 @@ +# generated from XKB map tr +include common +map 0x41f +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +apostrophe 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +dead_circumflex 0x04 shift +numbersign 0x04 altgr +sterling 0x04 shift altgr +plus 0x05 shift +dollar 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +asciicircum 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +asterisk 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +minus 0x0d +underscore 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +idotless 0x17 +I 0x17 shift +rightarrow 0x17 altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +gbreve 0x1a +Gbreve 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +udiaeresis 0x1b +Udiaeresis 0x1b shift +asciitilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +ampersand 0x25 shift altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +scedilla 0x27 +Scedilla 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +i 0x28 +Iabovedot 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +backslash 0x29 +quotedbl 0x29 shift +asciitilde 0x29 altgr +comma 0x2b +semicolon 0x2b shift +bar 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +odiaeresis 0x33 +Odiaeresis 0x33 shift +less 0x33 altgr +multiply 0x33 shift altgr +ccedilla 0x34 +Ccedilla 0x34 shift +greater 0x34 altgr +division 0x34 shift altgr +period 0x35 +colon 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/data/tools/qemu/libusb0.dll b/data/tools/qemu/libusb0.dll new file mode 100644 index 0000000..b9d17d3 Binary files /dev/null and b/data/tools/qemu/libusb0.dll differ diff --git a/data/tools/qemu/ppc_rom.bin b/data/tools/qemu/ppc_rom.bin new file mode 100644 index 0000000..0ad0282 Binary files /dev/null and b/data/tools/qemu/ppc_rom.bin differ diff --git a/data/tools/qemu/pxe-e1000.bin b/data/tools/qemu/pxe-e1000.bin new file mode 100644 index 0000000..7ac744e Binary files /dev/null and b/data/tools/qemu/pxe-e1000.bin differ diff --git a/data/tools/qemu/pxe-ne2k_pci.bin b/data/tools/qemu/pxe-ne2k_pci.bin new file mode 100644 index 0000000..5cb68ab Binary files /dev/null and b/data/tools/qemu/pxe-ne2k_pci.bin differ diff --git a/data/tools/qemu/pxe-pcnet.bin b/data/tools/qemu/pxe-pcnet.bin new file mode 100644 index 0000000..7a54bab Binary files /dev/null and b/data/tools/qemu/pxe-pcnet.bin differ diff --git a/data/tools/qemu/pxe-rtl8139.bin b/data/tools/qemu/pxe-rtl8139.bin new file mode 100644 index 0000000..db7d76d Binary files /dev/null and b/data/tools/qemu/pxe-rtl8139.bin differ diff --git a/data/tools/qemu/pxe-virtio.bin b/data/tools/qemu/pxe-virtio.bin new file mode 100644 index 0000000..6dde514 Binary files /dev/null and b/data/tools/qemu/pxe-virtio.bin differ diff --git a/data/tools/qemu/qemu-system-x86_64.exe b/data/tools/qemu/qemu-system-x86_64.exe new file mode 100644 index 0000000..5f8a8b0 Binary files /dev/null and b/data/tools/qemu/qemu-system-x86_64.exe differ diff --git a/data/tools/qemu/vgabios-cirrus.bin b/data/tools/qemu/vgabios-cirrus.bin new file mode 100644 index 0000000..4fa8f99 Binary files /dev/null and b/data/tools/qemu/vgabios-cirrus.bin differ diff --git a/data/tools/qemu/vgabios.bin b/data/tools/qemu/vgabios.bin new file mode 100644 index 0000000..fa6f815 Binary files /dev/null and b/data/tools/qemu/vgabios.bin differ diff --git a/data/tools/syslinux/syslinux_linux.zip b/data/tools/syslinux/syslinux_linux.zip new file mode 100644 index 0000000..f16165d Binary files /dev/null and b/data/tools/syslinux/syslinux_linux.zip differ diff --git a/data/tools/syslinux/syslinux_linux_64.zip b/data/tools/syslinux/syslinux_linux_64.zip new file mode 100644 index 0000000..7f84c74 Binary files /dev/null and b/data/tools/syslinux/syslinux_linux_64.zip differ diff --git a/data/tools/syslinux/syslinux_modules.zip b/data/tools/syslinux/syslinux_modules.zip new file mode 100644 index 0000000..7c4fdd9 Binary files /dev/null and b/data/tools/syslinux/syslinux_modules.zip differ diff --git a/data/tools/syslinux/syslinux_windows.zip b/data/tools/syslinux/syslinux_windows.zip new file mode 100644 index 0000000..3cb5897 Binary files /dev/null and b/data/tools/syslinux/syslinux_windows.zip differ diff --git a/data/version.txt b/data/version.txt new file mode 100644 index 0000000..ae9a76b --- /dev/null +++ b/data/version.txt @@ -0,0 +1 @@ +8.0.0 diff --git a/install.py b/install.py new file mode 100755 index 0000000..c089b5b --- /dev/null +++ b/install.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: install.py +# Purpose: Script to install multibootusb from source on different linux distros. This also pulls in dependencies. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import sys +import urllib.request, urllib.error, urllib.parse +import subprocess + + +if not os.getuid() == 0: + print("You must run this file with admin privilege.") + print("Try sudo ./install.py") + sys.exit(0) + + +class Install(): + + def mbusb(self): + try: + from PyQt5 import QtGui + if subprocess.call("python3 setup.py install --record ./.install_files.txt", shell=True) == 0: + print("Installation finished.") + print("Find multibootusb under system menu or run from terminal using the following command...") + print("\nmultibootusb\n") + print("You can uninstall multibootusb at any time using follwing command (with root/sudo previlage)") + print("\n./uninstall.sh\n") + except: + print("Installing missing package.") + if self.supported_pac_manager() is not True: + print("Unsupported package manager.") + print("Please install parted, util-linux and python3-pyqt5/PyQt5, mtools and python3-dbus\n" + "Whatever the package name is applicable to your distro and rerun this script.") + sys.exit(0) + elif self.internet_on() is False: + print("Unable to connect to internet.") + print("Please install parted, util-linux and python3-pyqt5/PyQt5, pkexec, mtools and python3-dbus \n" + "Whatever the package name is applicable to your distro and rerun this script.") + sys.exit(0) + elif self.internet_on() is True: + if self.install_dependency_package() is not True: + print("Error installing dependency packages.") + else: + if subprocess.call("python3 setup.py install --record ./.install_files.txt", shell=True) == 0: + print("Installation finished.") + print("Find multibootusb under system menu or run from terminal using the following command...") + print("\nmultibootusb\n") + print("You can uninstall multibootusb at any time using follwing command (with root/sudo previlage)") + print("\nsudo ./uninstall.sh\n") + + def internet_on(self): + try: + ret = urllib.request.urlopen('https://www.google.com', timeout=1) + print("Interconnection exist.") + result = True + + except urllib.error.URLError: + print("Interconnection does not exist.") + result = False + + return result + + def supported_pac_manager(self): + pac_managers = ["pacman", "yum", "apt-get", "zypper", "urpmi"] + result = "0" + for pac_man in pac_managers: + if subprocess.call("which " + pac_man, shell=True) == 0: + result = "1" + return True + + if not result == "1": + return False + + def install_dependency_package(self): + if subprocess.call("which pacman", shell=True) == 0: + subprocess.call("pacman -Sy --noconfirm", shell=True) + if subprocess.call("pacman -S --needed --noconfirm python-pyqt5 pkexec mtools parted util-linux python-dbus") == 0: # Thank you Neitsab for "--needed" argument. + result = True + elif subprocess.call("which yum", shell=True) == 0: + subprocess.call("yum check-update", shell=True) + if subprocess.call("yum install mtools PyQt5 pkexec util-linux parted python3-dbus -y", shell=True) == 0: + result = True + elif subprocess.call("which apt-get", shell=True) == 0: + subprocess.call("apt-get -q update", shell=True) + if subprocess.call("apt-get -q -y install python3-pyqt5 pkexec parted util-linux mtools python3-dbus", shell=True) == 0: + result = True + elif subprocess.call("which zypper", shell=True) == 0: + subprocess.call("zypper refresh", shell=True) + if subprocess.call("zypper install -y mtools python3-qt5 pkexec util-linux parted", shell=True) == 0: + result = True + elif subprocess.call("which urpmi", shell=True) == 0: + subprocess.call("urpmi.update -a", shell=True) + if subprocess.call("urpmi install -auto mtools util-linux pkexec parted python3-qt5", shell=True) == 0: + result = True + + if result is not True: + return False + else: + result + +install = Install() + +install.mbusb() diff --git a/multibootusb b/multibootusb new file mode 100644 index 0000000..2fd4b59 --- /dev/null +++ b/multibootusb @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Name: multibootusb +# Purpose: Main file which will determine if cli or gui is to be opened +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import getopt +import sys +import os +import platform + +# Had trouble in importing scripts directory. Had to add few lines below to ensure it works on source as well as +# post install +try: + from scripts.mbusb_cli import * + from scripts import admin +except: + try: + from .scripts.mbusb_cli import * + from .scripts import admin + except: + import scripts + +gui = True +iso_link = None +usb_disk = None +uninstall = False + +def usage(): + print('\nAn advanced multi boot live usb creator using command line.') + print('\nUsage: python3 multibootusb -c -i /path/to/iso [option(s)] -t /path/to/device\n') + print('[option(s)] are :\n') + print(' -h or --help : Print this help message and exit') + print(' -i or --iso : Path to ISO file') + print(' -t or --target : Path to target USB device partition (example /dev/sdb1)\n') + print(' -c or --command : This option is must for invoking multibootusb from command line\n') + print(' -u or --uninstall : List and uninstall distro from USB disk') + print(' Command line example for making a bootable USB from command line should look like this:-\n') + print(' python3 multibootusb -c -i ../../favourite.iso -t /dev/sdb1\n') + exit(2) + + +def start_gui(): + print('Starting multibootusb GUI...') + from scripts import mbusb_gui + mbusb_gui.main_gui() + + +if __name__ == '__main__': + + if platform.system() == 'Windows': + if not admin.isUserAdmin(): + admin.runAsAdmin() + sys.exit(0) + try: + opts, args = getopt.getopt(sys.argv[1:], 'i:t:vhcu', + ['iso=', 'target=', 'version', 'help', 'command', 'uninstall']) + except getopt.GetoptError: + usage() + sys.exit(2) + for opt, arg in opts: + if opt in ('-h', '--help'): + usage() + sys.exit(2) + elif opt in ('-v', '--version'): + print_version() + sys.exit() + elif opt in ('-i', '--iso'): + config.iso_link = arg + elif opt in ('-t', '--target'): + config.usb_disk = arg + elif opt in ('-c', '--command'): + gui = False + elif opt in ('-u', '--uninstall'): + uninstall = True + else: + gui = True + #start_gui() + ''' + usage() + sys.exit() + ''' + +if gui is False: + if uninstall is True and config.usb_disk is not None: + cli_uninstall_distro() + elif config.iso_link is None and config.usb_disk is None: + usage() + elif config.iso_link is None or config.usb_disk is None: + print('\nOptions \'-i\' and \'t\' must be supplied together. See the usage below.') + usage() + else: + cli_install_distro() + +elif gui is True: + start_gui() diff --git a/multibootusb-8.0.0.tar.gz b/multibootusb-8.0.0.tar.gz new file mode 100644 index 0000000..45a8313 Binary files /dev/null and b/multibootusb-8.0.0.tar.gz differ diff --git a/multibootusb-pkexec b/multibootusb-pkexec new file mode 100644 index 0000000..f9f7e98 --- /dev/null +++ b/multibootusb-pkexec @@ -0,0 +1,6 @@ +#!/bin/bash +if [ $(which pkexec) ]; then + pkexec --disable-internal-agent "/usr/bin/multibootusb" "$@" +else + /usr/bin/multibootusb "$@" +fi \ No newline at end of file diff --git a/org.debian.pkexec.run-multibootusb.policy b/org.debian.pkexec.run-multibootusb.policy new file mode 100644 index 0000000..e111748 --- /dev/null +++ b/org.debian.pkexec.run-multibootusb.policy @@ -0,0 +1,19 @@ + + + + + + Run the multibootusb program + Authentication is required to run the multibootusb + + auth_admin + auth_admin + auth_admin + + /usr/bin/multibootusb + true + + + diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..c5c69ae --- /dev/null +++ b/scripts/__init__.py @@ -0,0 +1,10 @@ +# Author(s) information +__authors__ = ['Sundar'] +__author__ = ','.join(__authors__) +__credits__ = ['Ian Bruce', 'Lee'] +__copyright__ = 'Copyright (c) 2014' +__license__ = 'GPL' + +# Maintanence information +__maintainer__ = 'Sundar' +__email__ = 'feedback.multibootusb@gmail.com' diff --git a/scripts/admin.py b/scripts/admin.py new file mode 100644 index 0000000..7c3986f --- /dev/null +++ b/scripts/admin.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4 +# Name: admin.py +# Purpose: Module to ask for admin rights under Linux and Windows +# Authors: Originally developed by Preston Landers (for windows) and modified for multibootusb by Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of the same license as Python 2.6.5 + +## +## (C) COPYRIGHT © Preston Landers 2010 +## Released under the same license as Python 2.6.5 +## + +""" +User Access Control for Microsoft Windows Vista and higher. This is +only for the Windows platform. + +This will relaunch either the current script - with all the same command +line parameters - or else you can provide a different script/program to +run. If the current user doesn't normally have admin rights, he'll be +prompted for an admin password. Otherwise he just gets the UAC prompt. + +This is meant to be used something like this:: + + if not pyuac.isUserAdmin(): + return pyuac.runAsAdmin() + + # otherwise carry on doing whatever... + +See L{runAsAdmin} for the main interface. + +""" +import os +import traceback +import types +import platform +import sys +import subprocess +from PyQt5 import QtWidgets + + +def isUserAdmin(): + """ + @return: True if the current user is an 'Admin' whatever that means + (root on Unix), otherwise False. + + Warning: The inner function fails unless you have Windows XP SP2 or + higher. The failure causes a traceback to be printed and this + function to return False. + """ + + if platform.system() == "Windows": + import ctypes + # WARNING: requires Windows XP SP2 or higher! + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + traceback.print_exc() + print("Admin check failed, assuming not an admin.") + return False + elif platform.system() == "Linux": + return os.getuid() == 0 + else: + raise RuntimeError("Unsupported operating system for this module: %s" % (os.name,)) + + +def runAsAdmin(cmdLine=None, wait=True): + """ + Attempt to relaunch the current script as an admin using the same + command line parameters. Pass cmdLine in to override and set a new + command. It must be a list of [command, arg1, arg2...] format. + + Set wait to False to avoid waiting for the sub-process to finish. You + will not be able to fetch the exit code of the process if wait is + False. + + Returns the sub-process return code, unless wait is False in which + case it returns None. + + @WARNING: this function only works on Windows. + """ + + #if os.name == 'nt': + # raise RuntimeError, "This function is only implemented on Windows." + if platform.system() == "Windows": + + import win32api, win32con, win32event, win32process + from win32com.shell.shell import ShellExecuteEx + from win32com.shell import shellcon + + python_exe = sys.executable + + if cmdLine is None: + cmdLine = [python_exe] + sys.argv + elif type(cmdLine) not in (types.TupleType, types.ListType): + raise ValueError("cmdLine is not a sequence.") + cmd = '"%s"' % (cmdLine[0],) + # XXX TODO: isn't there a function or something we can call to massage command line params? + params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]]) + cmdDir = '' + showCmd = win32con.SW_SHOWNORMAL + #showCmd = win32con.SW_HIDE + lpVerb = 'runas' # causes UAC elevation prompt. + + # print "Running", cmd, params + + # ShellExecute() doesn't seem to allow us to fetch the PID or handle + # of the process, so we can't get anything useful from it. Therefore + # the more complex ShellExecuteEx() must be used. + + # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd) + + procInfo = ShellExecuteEx(nShow=showCmd, + fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, + lpVerb=lpVerb, + lpFile=cmd, + lpParameters=params) + + if wait: + procHandle = procInfo['hProcess'] + obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE) + rc = win32process.GetExitCodeProcess(procHandle) + #print "Process handle %s returned code %s" % (procHandle, rc) + else: + rc = None + + return rc + + +def adminCmd(cmd, fork=False, gui=False): + """ + This simple function checks for a sudo command and runs a command using it. + This function tries to launch given script with root access using pkexec/gksu/gksudo/kdesu/kdesudo, + if one of them is already installed. + PyQt4 is used as GUI. + Author : sundar + """ + sudo_cmd = '' + if os.getuid() == 0: + sudo_cmd = cmd + else: + if os.system('which pkexec') == 0: + if gui: + cmd = ['export DISPLAY=$DISPLAY; export XAUTHORITY=$XAUTHORITY; '] + cmd # By default, pkexec disallows X11 apps. Restore DISPLAY & XAUTHORITY to allow it. man 1 pkexec/"SECURITY NOTES" section + sudo_cmd = ['pkexec', '/bin/sh', '-c'] + elif os.system('which gksudo') == 0: + sudo_cmd = ["gksudo", "--", "/bin/sh", "-c"] + elif os.system('which gksu') == 0: + sudo_cmd = ["gksu"] + elif os.system('which kdesudo') == 0: + sudo_cmd = ["kdesudo", "-t", "-c"] # http://www.unix.com/man-page/debian/1/kdesudo/ + elif os.system('which kdesu') == 0: + sudo_cmd = ["kdesu", "-t", "-c"] # http://linux.die.net/man/1/kdesu + else: + QtWidgets.QMessageBox.information('No root...', + 'Could not find any of: pkexec, sudo, gksu, kdesu, gksudo, or kdesudo. Please install one then restart multibootusb.') + sys.exit(0) + final_cmd = ' '.join(sudo_cmd + ['"' + ' '.join(cmd).replace('"', '\\"') + '"']) + print("Executing ==> " + final_cmd) + if fork: + return subprocess.Popen(final_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, shell=True) + else: + ret = subprocess.call(final_cmd, shell=True) + print("Process returned ==> " + str(ret)) + return ret diff --git a/scripts/config.py b/scripts/config.py new file mode 100644 index 0000000..a3e4432 --- /dev/null +++ b/scripts/config.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# Name: config.py +# Purpose: Module to share important variables between various modules. Mainly included so as not to call many +# functions again and again +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +iso_link = "" +usb_disk = "" +usb_mount = "" +persistence = 0 +distro = "" +status_text = "" +percentage = 0 +syslinux_version = '' +uninstall_distro_dir_name = "" +uninstall_distro_dir_path = "" +iso_file_list = '' + +imager_iso_link = "" +imager_usb_disk_selected = "" +imager_lock = "" +imager_percentage = "" +imager_status_text = "" + +install_size = "" + +editors_linux = ["gedit", "kate", "kwrite"] +editors_win = ["notepad++.exe", "notepad.exe"] + +imager_usb_disk = [] diff --git a/scripts/distro.py b/scripts/distro.py new file mode 100644 index 0000000..507d657 --- /dev/null +++ b/scripts/distro.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: distro.py +# Purpose: Module to detect if distro types supported by multibootusb (by extracting specific files) +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import string +import platform +import re +from .iso import * +from .isodump3 import ISO9660 +from .gen import * +from . import config + + + +def distro(iso_cfg_ext_dir, iso_link): + """ + Detect if distro is supported by multibootusb. + :param iso_cfg_ext_dir: Directory where *.cfg files are extracted. + :return: Detected distro name as string. + """ + iso9660fs = ISO9660(iso_link) + iso_file_list = iso9660fs.readDir("/") + if platform.system() == "Linux" or platform.system() == "Windows": + for path, subdirs, files in os.walk(iso_cfg_ext_dir): + for name in files: + if name.endswith('.cfg') or name.endswith('.CFG') or name.endswith('.txt') or name.endswith('.TXT'): + try: + # errors='ignore' is required as some files also contain non utf character + string = open(os.path.join(path, name), errors='ignore').read() + except IOError: + return "Read Error." + else: + if any("f4ubcd" in s.lower() for s in iso_file_list): + return "f4ubcd" + if re.search(r'ubcd', string, re.I): + return "ubcd" + elif re.search(r'Super Grub Disk', string, re.I): + return None + elif re.search(r'hbcd', string, re.I): + return "hbcd" + elif re.search(r'systemrescuecd', string, re.I): + return "systemrescuecd" + elif re.search(r'pmagic|partedmagic', string, re.I) and isolinux_bin_exist(iso_link): + return "parted-magic" + elif re.search(r'mgalive', string, re.I): # mounting fat filesystem hard coded in to initrd. + # Can be modified only under linux. + return "mageialive" + elif re.search(r'archisolabel|misolabel', string, re.I): + return "arch" + elif re.search(r'chakraisolabel', string, re.I): + return "chakra" + elif re.search(r'boot=live', string, re.I) and isolinux_bin_exist(iso_link): + return "debian" + elif re.search(r'debian-installer', string, re.I) and isolinux_bin_exist(iso_link): + return "debian-install" + elif re.search(r'solydx', string, re.I): + return "solydx" + elif re.search(r'knoppix', string, re.I): + return "knoppix" + elif re.search(r'root=live', string, re.I): + return "fedora" + elif re.search(r'redhat', string, re.I): + return "redhat" + # elif re.search(r'suse', string, re.I): + # return "suse" + elif re.search(r'opensuse', string, + re.I): + return "opensuse" + elif re.search( + r'slitaz|dban|ophcrack|tinycore|rescue.cpi|xpud|untangle|4mlinux|partition wizard|' + r'riplinux|lebel dummy|http://pogostick.net/~pnh/ntpasswd/|AVG Rescue CD', string, re.I): + return "slitaz" + elif re.search(r'boot=casper', string, re.I): + return "ubuntu" + elif re.search(r'wifislax', string, re.I): + return "wifislax" + elif re.search(r'slax', string, re.I): + return "slax" + elif re.search(r'sms|vector|autoexec', string, re.I): + return "sms" + elif re.search(r'antix', string, re.I): + return "antix" + elif re.search(r'porteus', string, re.I): + return "porteus" + elif re.search(r'livecd=livecd|PCLinuxOS', string, re.I): + return "pclinuxos" + elif re.search(r'looptype=squashfs', string, re.I): + return "gentoo" + elif re.search(r'finnix', string, re.I): + return "finnix" + elif re.search(r'wifiway', string, re.I): + return "wifiway" + elif re.search(r'puppy|quirky', string, re.I): + return "puppy" + elif re.search(r'ipcop', string, re.I): + return "ipcop" + elif re.search(r'ipfire', string, re.I): + return "ipfire" + elif re.search(r'zenwalk|slack|salix', string, re.I) and re.search(r'live', string, re.I): + return "salix-live" + elif re.search(r'zenwalk|slack|salix', string, re.I): + return "zenwalk" + elif re.search(r'ubuntu server', string, re.I): + return "ubuntu-server" + elif re.search(r'Welcome to CentOS', string, re.I): + return "centos-net-minimal" + elif re.search(r'Trinity Rescue Kit', string, re.I): + return "trinity-rescue" + elif re.search(r'alpine', string, re.I): + return "alpine" + elif re.search(r'http://support.kaspersky.com', string, re.I): + return "kaspersky" + + distro = detect_iso_from_file_list(iso_link) + if distro: + return distro + # FIXME: See the below comments. + ''' + else: + # FIXME: The idea of detecting as generic is to work like a unetbootin if other methods fails. + # This simply extracts distro to root of the USB and install syslinux on isolinux.bin directory. + # All works fine but unable to boot the distro successfully. Also, see the generic section from + # syslinux, update_cfg and install_distro modules. + if self.isolinux_bin_exist(): + return "generic" + ''' + else: + return None + + +def detect_iso_from_file_list(iso_link): + """ + Fallback detection script from the content of an ISO. + :return: + """ + if os.path.exists(iso_link): + iso9660fs = ISO9660(iso_link) + iso_file_list = iso9660fs.readDir("/") + if any("sources" in s.lower() for s in iso_file_list): + return "Windows" + elif any("config.isoclient" in s.lower() for s in iso_file_list): + return "opensuse" + elif any("dban" in s.lower() for s in iso_file_list): + return "slitaz" + elif any("memtest.img" in s.lower() for s in iso_file_list): + return "mentest" + else: + print(iso_file_list) + +if __name__ == '__main__': + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + #iso_link = '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso' + iso_link = '/media/sundar/Data/DISTROS/ubuntu_14_04_backup/Downloads/clonezilla-live-2.4.2-32-amd64.iso' + iso_extract_file(iso_link, iso_cfg_ext_dir, 'cfg') + print(distro(iso_cfg_ext_dir)) diff --git a/scripts/gen.py b/scripts/gen.py new file mode 100644 index 0000000..0490c28 --- /dev/null +++ b/scripts/gen.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: gen.py +# Purpose: This 'general' module contain many functions required to be called at many places +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import sys +import os +import platform +import shutil +import string +import zipfile + +def scripts_dir_path(): + return os.path.dirname(os.path.realpath(__file__)) + +def resource_path(relativePath): + """ + Function to detect the correct path of file when working with sourcecode/install or binary. + :param relativePath: Path to file/data. + :return: Modified path to file/data. + """ + + try: + basePath = sys._MEIPASS # Try if we are running as standalone executable + # print('Running stand alone executable.') + except: + basePath = '/usr/share/multibootusb' # Check if we run in installed environment + #if os.path.exists('/usr/share/multibootusb'): + #print('Running from installed machine.') + if not os.path.exists(basePath): + #basePath = os.path.abspath(".") + basePath = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + if os.path.exists(os.path.join(basePath, relativePath)): + path = os.path.join(basePath, relativePath) + return path + elif not os.path.exists(os.path.join(basePath, relativePath)): + if os.path.exists(os.path.join(os.path.abspath("."), relativePath)): + basePath = os.path.abspath(".") + elif os.path.exists(os.path.join(os.path.abspath(".."), relativePath)): + basePath = os.path.abspath("..") + path = os.path.join(basePath, relativePath) + return path + + +def print_version(): + """ + Simple print the version number of the multibootusb application + :return: + """ + print('multibootusb version : ', mbusb_version()) + + +def quote(text): + """ + Function to quote the input word or sentence. + :param text: Any word or sentence. + :return: Quoted text or sentence. If already quoted the same text is returned. + """ + if not is_quoted(text): + return '"' + text + '"' + else: + return text + + +def is_quoted(text): + """ + Function to check if word is quoted. + :param text: Any word or sentence with or without quote. + :return: True if text is quoted else False. + """ + if text.startswith("""") and text.endswith("""): + return True + else: + return False + + +def has_digit(word): + """ + Useful function to detect if input word contain digit. + :param word: Any alphanumeric word. + :return: True if the word has a digit else False. + """ + return any(char.isdigit() for char in word) + + +def sys_64bits(): + """ + Detect if the host system is 64 bit. + :return: True if system is 64 bit. + """ + return sys.maxsize > 2**32 + + +def multibootusb_host_dir(): + """ + Cross platform way to detect multibootusb directory on host system. + :return: Path to multibootusb directory of host system. + """ + import tempfile + + if platform.system() == "Linux": + home_dir = os.path.expanduser('~') + mbusb_dir = os.path.join(home_dir, ".multibootusb") + elif platform.system() == "Windows": + mbusb_dir = os.path.join(tempfile.gettempdir(), "multibootusb") + + return mbusb_dir + + +def iso_cfg_ext_dir(): + """ + Function to return the path to ISO configuration file extraction directory. + :return: Path to directory where ISO config files will be extracted. + """ + return os.path.join(multibootusb_host_dir(), 'iso_cfg_ext_dir') + + +def clean_iso_cfg_ext_dir(iso_cfg_ext_dir): + """ + Clean old ISO config files extracted by previous use of multibootusb. + :param iso_cfg_ext_dir: Path to config extract directory. + :return: + """ + if os.path.exists(iso_cfg_ext_dir): + filelist = [f for f in os.listdir(iso_cfg_ext_dir)] + for f in filelist: + if os.path.isdir(os.path.join(iso_cfg_ext_dir, f)): + shutil.rmtree(os.path.join(iso_cfg_ext_dir, f)) + else: + os.remove(os.path.join(iso_cfg_ext_dir, f)) + else: + print('iso_cfg_ext_dir directory does not exist.') + + +def copy_mbusb_dir_usb(usb_disk): + """ + Copy the multibootusb directory to USB mount path. + :param usb_mount_path: Path to USB mount. + :return: + """ + from .iso import iso_size + from .usb import details + + usb_details = details(usb_disk) + usb_mount_path = usb_details['mount_point'] + if not os.path.exists(os.path.join(usb_mount_path, "multibootusb")): + try: + print('Copying multibootusb directory to', usb_mount_path) + shutil.copytree(resource_path(os.path.join("data", "multibootusb")), os.path.join(usb_mount_path, "multibootusb")) + return True + except: + return False + else: + print('multibootus directory already exist. Not copying.') + + +def read_input_yes(): + """ + List option and read user input + :return: True if user selected yes or else false + """ + yes_list = ['Y', 'y', 'Yes', 'yes', 'YES'] + no_list = ['N', 'n', 'No', 'no', 'NO'] + response = input("Please enter the option listed above : ") + if response in yes_list: + return True + elif response in no_list: + return False + + +def strings(file_path): + """ + Similar to strings command in Linux. + :param file_path: Path to file as string. + :return: All printable character of a file. + """ + import re + nonprintable = re.compile(b'[^%s]+' % re.escape(string.printable.encode('ascii'))) + + with open(file_path, "rb") as f: + for result in nonprintable.split(f.read()): + if result: + yield result.decode('ASCII') + + +def size_not_enough(iso_link, usb_disk): + from .iso import iso_size + from .usb import details + isoSize = iso_size(iso_link) + usb_details = details(usb_disk) + usb_size = usb_details['size_free'] + if isoSize > usb_size: + return True + else: + return False + + +def mbusb_version(): + version = open(resource_path(os.path.join("data", "version.txt")), 'r').read().strip() + return version + + +def prepare_mbusb_host_dir(): + """ + Prepare multibootusb host directory and extract data files for use. + :return: + """ + home = multibootusb_host_dir() + if not os.path.exists(home): + os.makedirs(home) + else: + print("Cleaning old multibootusb directory...") + shutil.rmtree(home) + os.makedirs(home) + + if not os.path.exists(os.path.join(home, "preference")): + os.makedirs(os.path.join(home, "preference")) + + if not os.path.exists(os.path.join(home, "iso_cfg_ext_dir")): + os.makedirs(os.path.join(home, "iso_cfg_ext_dir")) + + if os.path.exists(os.path.join(home, "syslinux", "bin", "syslinux4")): + print("Syslinux exist in multibootusb directory...") + else: + print("Extracting syslinux to multibootusb directory...") + if platform.system() == "Linux": + if sys_64bits() is True: + print('Host OS is 64 bit...') + print("Extracting syslinux 64 bit...") + # print(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux_64.zip"))) + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux_64.zip")), "r") as z: + z.extractall(home) + else: + print("Extracting syslinux 32 bit...") + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux.zip")), "r") as z: + z.extractall(home) + else: + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_windows.zip")), "r") as z: + z.extractall(home) + print("Extracting syslinux modules to multibootusb directory...") + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_modules.zip")), "r") as z: + z.extractall(os.path.join(home, "syslinux")) + + ''' + if not os.path.exists(os.path.join(home, "persistence_data")): + print("Copying persistence data to multibootusb directory...") + shutil.copytree(resource_path(os.path.join("data", "tools", "persistence_data")), + os.path.join(home, "persistence_data")) + ''' + if platform.system() == "Windows": + if not os.path.exists(os.path.join(home, "dd")): + print("Copying dd to multibootusb directory.") + shutil.copytree(resource_path(os.path.join("data", "tools", "dd")), + os.path.join(home, "dd")) + + if os.listdir(os.path.join(home, "iso_cfg_ext_dir")): + print(os.listdir(os.path.join(home, "iso_cfg_ext_dir"))) + print("iso extract directory is not empty.") + print("Removing junk files...") + for files in os.listdir(os.path.join(home, "iso_cfg_ext_dir")): + if os.path.isdir(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))): + print (os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.chmod(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files)), 0o777) + shutil.rmtree(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + else: + try: + print (os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.chmod(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files)), 0o777) + os.unlink(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.remove(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + except OSError: + print("Can't remove the file. Skip it.") + +if __name__ == '__main__': + print(quote("""Test-string""")) + print(has_digit("test-string-with-01-digit")) + print(sys_64bits()) + print(multibootusb_host_dir()) + print(iso_cfg_ext_dir()) + strings_test = strings('../../text-stings.bin') + print(strings_test) diff --git a/scripts/gui/__init__.py b/scripts/gui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/gui/multibootusb.ui b/scripts/gui/multibootusb.ui new file mode 100644 index 0000000..4d3b0f8 --- /dev/null +++ b/scripts/gui/multibootusb.ui @@ -0,0 +1,820 @@ + + + Dialog + + + + 0 + 0 + 644 + 516 + + + + multibootusb + + + + + + 0 + + + + MultiBootUSB + + + + + + + + 0 MB + + + + + + + <html><head/><body><p align="center"><span style=" font-weight:600;">Step 3</span></p></body></html> + + + + + + + false + + + Choose Persistence size. Not all distros supports persistence... + + + false + + + Qt::Horizontal + + + QSlider::TicksBothSides + + + 0 + + + + + + + Close + + + + + + + + + Detect USB + + + + + + Refresh USB + + + + + + + + + + + + <html><head/><body><p align="center"><span style=" font-weight:600;">Step 1</span></p></body></html> + + + + + + + <html><head/><body><p align="center"><span style=" font-weight:600;">Step 2</span></p></body></html> + + + + + + + Create + + + + + + + + + + Uninstall (Optional) + + + + + + + + Uninstall Distro + + + + + + + + + + + + Browse ISO + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Persistence</span></p></body></html> + + + + + + + + + + 0 + + + + + + + + 0 + 0 + + + + false + + + false + + + QFrame::Plain + + + + + + false + + + + + + + + + + USB Details + + + + + + Drive :: + + + + + + + Vendor :: + + + + + + + Model:: + + + + + + + Size :: + + + + + + + Mount :: + + + + + + + + + + + + + ISO Imager + + + + + + + + Imager + + + + + + + + -------------- USB details ------------------- + + + + + + + + + Refresh USB + + + + + + + Disk Label :: + + + + + + + Disk Size + + + + + + + Disk Label :: + + + + + + + + + + ------------------------------ ISO details ---------------------------------- + + + + + + Browse ISO + + + + + + + + + + Bootable ISO + + + + + + + ISO Size + + + + + + + + + + + + + + + Imager Progress + + + + + + + 0 + 0 + + + + + + + + + + + + + Write/Create + + + + + + + Close + + + + + + + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">WARNING</span> : Any bootable USB made using<span style=" font-weight:600;"> ISO Imager will destroy all data </span>on the selected USB disk. </p><p>Use it at your own risk. Developers are not responsile for loss of any data.</p></body></html> + + + + + + + 0 + + + + + + + + + + + + + + + Syslinux + + + + + + + + false + + + Install Syslinux + + + + + + + + Install + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Install syslinux and copy all required files. + + + + + + + Install only syslinux (existing configurations will not be altred). + + + + + + + + + + + + Edit syslinux.cfg + + + + + + + + Edit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p align="justify">Using this option user can edit syslinux.cfg file directly. It directly uses </p><p align="justify">default editor of host system. Be careful while editing syslinux.cfg file.</p></body></html> + + + + + + + + + + + + + + + QEMU + + + + + + Boot ISO + + + + + + + + 2048 MB + + + + + + + 1024 MB + + + + + + + 256 MB + + + + + + + Browse ISO + + + + + + + <html><head/><body><p>Best way to test your downloaded ISOs. </p></body></html> + + + + + + + 512 MB + + + + + + + Boot ISO + + + + + + + 768 MB + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Choose desired RAM and click on Boot ISO button. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Boot USB + + + + + + + + 768 MB + + + + + + + 256 MB + + + + + + + <html><head/><body><p align="justify">Use this option if you want to check USB installation without reboot.</p></body></html> + + + + + + + 1024 MB + + + + + + + Choose desired RAM and click on Boot USB button. + + + + + + + 512 MB + + + + + + + Boot USB + + + + + + + 2048 MB + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + About + + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 30 + + + + + + + + <html><head/><body><p align="center">An advanced bootable usb creator with option to install/uninstall </p><p align="center">multiple distros. This software is written in python and pyqt. </p><p align="center">Copyright 2010-2016 Sundar</p><p align="center"><span style=" font-weight:600; text-decoration: underline;">Author(s)</span>: Sundar, Ian Bruce, Lee</p><p align="center"><span style=" font-weight:600; text-decoration: underline;">Licence:</span> GPL version 2 or later</p><p align="center"><span style=" font-weight:600; text-decoration: underline;">Home page</span>: <a href=" http://multibootusb.org"><span style=" text-decoration: underline; color:#0000ff;">http://multibootusb.org</span></a></p><p align="center"><span style=" font-weight:600; text-decoration: underline;">Help/Email:</span> feedback.multibootusb@gmail.com</p><p align="center"><span style=" font-weight:600; text-decoration: underline;">Source Code:</span><span style=" font-weight:600;"/><a href="https://github.com/mbusb/multibootusb"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/mbusb/multibootusb</span></a></p><p><br/></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + diff --git a/scripts/gui/ui_multibootusb.py b/scripts/gui/ui_multibootusb.py new file mode 100644 index 0000000..7bbb0d3 --- /dev/null +++ b/scripts/gui/ui_multibootusb.py @@ -0,0 +1,445 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'scripts/gui/multibootusb.ui' +# +# Created by: PyQt5 UI code generator 5.5.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(644, 516) + self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog) + self.horizontalLayout.setObjectName("horizontalLayout") + self.tabWidget = QtWidgets.QTabWidget(Dialog) + self.tabWidget.setObjectName("tabWidget") + self.tab_3 = QtWidgets.QWidget() + self.tab_3.setObjectName("tab_3") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tab_3) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.label_persistence_value = QtWidgets.QLabel(self.tab_3) + self.label_persistence_value.setObjectName("label_persistence_value") + self.gridLayout.addWidget(self.label_persistence_value, 5, 2, 1, 1) + self.labelstep3 = QtWidgets.QLabel(self.tab_3) + self.labelstep3.setObjectName("labelstep3") + self.gridLayout.addWidget(self.labelstep3, 5, 5, 1, 1) + self.slider_persistence = QtWidgets.QSlider(self.tab_3) + self.slider_persistence.setEnabled(False) + self.slider_persistence.setAutoFillBackground(False) + self.slider_persistence.setOrientation(QtCore.Qt.Horizontal) + self.slider_persistence.setTickPosition(QtWidgets.QSlider.TicksBothSides) + self.slider_persistence.setTickInterval(0) + self.slider_persistence.setObjectName("slider_persistence") + self.gridLayout.addWidget(self.slider_persistence, 5, 1, 1, 1) + self.close = QtWidgets.QPushButton(self.tab_3) + self.close.setObjectName("close") + self.gridLayout.addWidget(self.close, 5, 3, 1, 1) + self.verticalLayout_11 = QtWidgets.QVBoxLayout() + self.verticalLayout_11.setObjectName("verticalLayout_11") + self.groupBox_11 = QtWidgets.QGroupBox(self.tab_3) + self.groupBox_11.setObjectName("groupBox_11") + self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.groupBox_11) + self.horizontalLayout_8.setObjectName("horizontalLayout_8") + self.detect_usb = QtWidgets.QPushButton(self.groupBox_11) + self.detect_usb.setObjectName("detect_usb") + self.horizontalLayout_8.addWidget(self.detect_usb) + self.verticalLayout_11.addWidget(self.groupBox_11) + self.gridLayout.addLayout(self.verticalLayout_11, 1, 3, 1, 2) + self.labelstep1 = QtWidgets.QLabel(self.tab_3) + self.labelstep1.setObjectName("labelstep1") + self.gridLayout.addWidget(self.labelstep1, 0, 5, 1, 1) + self.labelstep2 = QtWidgets.QLabel(self.tab_3) + self.labelstep2.setObjectName("labelstep2") + self.gridLayout.addWidget(self.labelstep2, 4, 5, 1, 1) + self.create = QtWidgets.QPushButton(self.tab_3) + self.create.setObjectName("create") + self.gridLayout.addWidget(self.create, 5, 4, 1, 1) + self.comboBox = QtWidgets.QComboBox(self.tab_3) + self.comboBox.setObjectName("comboBox") + self.gridLayout.addWidget(self.comboBox, 0, 3, 1, 2) + self.groupBox = QtWidgets.QGroupBox(self.tab_3) + self.groupBox.setObjectName("groupBox") + self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout_5 = QtWidgets.QGridLayout() + self.gridLayout_5.setObjectName("gridLayout_5") + self.uninstall = QtWidgets.QPushButton(self.groupBox) + self.uninstall.setObjectName("uninstall") + self.gridLayout_5.addWidget(self.uninstall, 0, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_5) + self.gridLayout.addWidget(self.groupBox, 3, 3, 1, 2) + self.browse_iso = QtWidgets.QPushButton(self.tab_3) + self.browse_iso.setObjectName("browse_iso") + self.gridLayout.addWidget(self.browse_iso, 4, 4, 1, 1) + self.label_persistence = QtWidgets.QLabel(self.tab_3) + self.label_persistence.setObjectName("label_persistence") + self.gridLayout.addWidget(self.label_persistence, 5, 0, 1, 1) + self.listWidget = QtWidgets.QListWidget(self.tab_3) + self.listWidget.setObjectName("listWidget") + self.gridLayout.addWidget(self.listWidget, 0, 0, 4, 3) + self.progressBar = QtWidgets.QProgressBar(self.tab_3) + self.progressBar.setProperty("value", 0) + self.progressBar.setObjectName("progressBar") + self.gridLayout.addWidget(self.progressBar, 7, 0, 1, 6) + self.status = QtWidgets.QLabel(self.tab_3) + self.status.setMinimumSize(QtCore.QSize(0, 0)) + self.status.setAcceptDrops(False) + self.status.setAutoFillBackground(False) + self.status.setFrameShadow(QtWidgets.QFrame.Plain) + self.status.setText("") + self.status.setScaledContents(False) + self.status.setObjectName("status") + self.gridLayout.addWidget(self.status, 6, 0, 1, 6) + self.lineEdit = QtWidgets.QLineEdit(self.tab_3) + self.lineEdit.setObjectName("lineEdit") + self.gridLayout.addWidget(self.lineEdit, 4, 0, 1, 4) + self.groupBox_6 = QtWidgets.QGroupBox(self.tab_3) + self.groupBox_6.setObjectName("groupBox_6") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_6) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.usb_dev = QtWidgets.QLabel(self.groupBox_6) + self.usb_dev.setObjectName("usb_dev") + self.verticalLayout_5.addWidget(self.usb_dev) + self.usb_vendor = QtWidgets.QLabel(self.groupBox_6) + self.usb_vendor.setObjectName("usb_vendor") + self.verticalLayout_5.addWidget(self.usb_vendor) + self.usb_model = QtWidgets.QLabel(self.groupBox_6) + self.usb_model.setObjectName("usb_model") + self.verticalLayout_5.addWidget(self.usb_model) + self.usb_size = QtWidgets.QLabel(self.groupBox_6) + self.usb_size.setObjectName("usb_size") + self.verticalLayout_5.addWidget(self.usb_size) + self.usb_mount = QtWidgets.QLabel(self.groupBox_6) + self.usb_mount.setObjectName("usb_mount") + self.verticalLayout_5.addWidget(self.usb_mount) + self.gridLayout.addWidget(self.groupBox_6, 2, 3, 1, 3) + self.horizontalLayout_2.addLayout(self.gridLayout) + self.tabWidget.addTab(self.tab_3, "") + self.imager = QtWidgets.QWidget() + self.imager.setObjectName("imager") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.imager) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.gridLayout_9 = QtWidgets.QGridLayout() + self.gridLayout_9.setObjectName("gridLayout_9") + self.groupBox_7 = QtWidgets.QGroupBox(self.imager) + self.groupBox_7.setObjectName("groupBox_7") + self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_7) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.gridLayout_11 = QtWidgets.QGridLayout() + self.gridLayout_11.setObjectName("gridLayout_11") + self.groupBox_9 = QtWidgets.QGroupBox(self.groupBox_7) + self.groupBox_9.setObjectName("groupBox_9") + self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.groupBox_9) + self.verticalLayout_8.setObjectName("verticalLayout_8") + self.comboBox_2 = QtWidgets.QComboBox(self.groupBox_9) + self.comboBox_2.setObjectName("comboBox_2") + self.verticalLayout_8.addWidget(self.comboBox_2) + self.pushbtn_imager_refreshusb = QtWidgets.QPushButton(self.groupBox_9) + self.pushbtn_imager_refreshusb.setObjectName("pushbtn_imager_refreshusb") + self.verticalLayout_8.addWidget(self.pushbtn_imager_refreshusb) + self.imager_disk_label = QtWidgets.QLabel(self.groupBox_9) + self.imager_disk_label.setObjectName("imager_disk_label") + self.verticalLayout_8.addWidget(self.imager_disk_label) + self.imager_total_size = QtWidgets.QLabel(self.groupBox_9) + self.imager_total_size.setObjectName("imager_total_size") + self.verticalLayout_8.addWidget(self.imager_total_size) + self.imager_uuid = QtWidgets.QLabel(self.groupBox_9) + self.imager_uuid.setObjectName("imager_uuid") + self.verticalLayout_8.addWidget(self.imager_uuid) + self.gridLayout_11.addWidget(self.groupBox_9, 0, 0, 1, 1) + self.groupBox_10 = QtWidgets.QGroupBox(self.groupBox_7) + self.groupBox_10.setObjectName("groupBox_10") + self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.groupBox_10) + self.verticalLayout_9.setObjectName("verticalLayout_9") + self.pushButton = QtWidgets.QPushButton(self.groupBox_10) + self.pushButton.setObjectName("pushButton") + self.verticalLayout_9.addWidget(self.pushButton) + self.lineEdit_3 = QtWidgets.QLineEdit(self.groupBox_10) + self.lineEdit_3.setObjectName("lineEdit_3") + self.verticalLayout_9.addWidget(self.lineEdit_3) + self.imager_bootable = QtWidgets.QLabel(self.groupBox_10) + self.imager_bootable.setObjectName("imager_bootable") + self.verticalLayout_9.addWidget(self.imager_bootable) + self.imager_iso_size = QtWidgets.QLabel(self.groupBox_10) + self.imager_iso_size.setObjectName("imager_iso_size") + self.verticalLayout_9.addWidget(self.imager_iso_size) + self.gridLayout_11.addWidget(self.groupBox_10, 0, 1, 1, 1) + self.verticalLayout_6.addLayout(self.gridLayout_11) + self.gridLayout_9.addWidget(self.groupBox_7, 1, 0, 1, 1) + self.groupBox_8 = QtWidgets.QGroupBox(self.imager) + self.groupBox_8.setObjectName("groupBox_8") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_8) + self.verticalLayout_7.setObjectName("verticalLayout_7") + self.imager_label_status = QtWidgets.QLabel(self.groupBox_8) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.imager_label_status.sizePolicy().hasHeightForWidth()) + self.imager_label_status.setSizePolicy(sizePolicy) + self.imager_label_status.setText("") + self.imager_label_status.setObjectName("imager_label_status") + self.verticalLayout_7.addWidget(self.imager_label_status) + self.gridLayout_12 = QtWidgets.QGridLayout() + self.gridLayout_12.setObjectName("gridLayout_12") + self.imager_write = QtWidgets.QPushButton(self.groupBox_8) + self.imager_write.setObjectName("imager_write") + self.gridLayout_12.addWidget(self.imager_write, 2, 2, 1, 1) + self.imager_close = QtWidgets.QPushButton(self.groupBox_8) + self.imager_close.setObjectName("imager_close") + self.gridLayout_12.addWidget(self.imager_close, 2, 1, 1, 1) + self.label_10 = QtWidgets.QLabel(self.groupBox_8) + self.label_10.setObjectName("label_10") + self.gridLayout_12.addWidget(self.label_10, 1, 0, 1, 3) + self.imager_progressbar = QtWidgets.QProgressBar(self.groupBox_8) + self.imager_progressbar.setProperty("value", 0) + self.imager_progressbar.setObjectName("imager_progressbar") + self.gridLayout_12.addWidget(self.imager_progressbar, 0, 0, 1, 3) + self.verticalLayout_7.addLayout(self.gridLayout_12) + self.gridLayout_9.addWidget(self.groupBox_8, 2, 0, 1, 1) + self.horizontalLayout_7.addLayout(self.gridLayout_9) + self.tabWidget.addTab(self.imager, "") + self.syslinux_ab = QtWidgets.QWidget() + self.syslinux_ab.setObjectName("syslinux_ab") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.syslinux_ab) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.gridLayout_2 = QtWidgets.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.groupBox_2 = QtWidgets.QGroupBox(self.syslinux_ab) + self.groupBox_2.setAutoFillBackground(False) + self.groupBox_2.setObjectName("groupBox_2") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox_2) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.gridLayout_3 = QtWidgets.QGridLayout() + self.gridLayout_3.setObjectName("gridLayout_3") + self.install_syslinux = QtWidgets.QPushButton(self.groupBox_2) + self.install_syslinux.setObjectName("install_syslinux") + self.gridLayout_3.addWidget(self.install_syslinux, 2, 1, 1, 1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem, 2, 0, 1, 1) + self.install_sys_all = QtWidgets.QRadioButton(self.groupBox_2) + self.install_sys_all.setObjectName("install_sys_all") + self.gridLayout_3.addWidget(self.install_sys_all, 1, 0, 1, 2) + self.install_sys_only = QtWidgets.QRadioButton(self.groupBox_2) + self.install_sys_only.setObjectName("install_sys_only") + self.gridLayout_3.addWidget(self.install_sys_only, 0, 0, 1, 2) + self.horizontalLayout_4.addLayout(self.gridLayout_3) + self.gridLayout_2.addWidget(self.groupBox_2, 0, 0, 1, 1) + self.groupBox_3 = QtWidgets.QGroupBox(self.syslinux_ab) + self.groupBox_3.setObjectName("groupBox_3") + self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_3) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.gridLayout_4 = QtWidgets.QGridLayout() + self.gridLayout_4.setObjectName("gridLayout_4") + self.edit_syslinux = QtWidgets.QPushButton(self.groupBox_3) + self.edit_syslinux.setObjectName("edit_syslinux") + self.gridLayout_4.addWidget(self.edit_syslinux, 1, 1, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_4.addItem(spacerItem1, 1, 0, 1, 1) + self.label_2 = QtWidgets.QLabel(self.groupBox_3) + self.label_2.setObjectName("label_2") + self.gridLayout_4.addWidget(self.label_2, 0, 0, 1, 2) + self.horizontalLayout_5.addLayout(self.gridLayout_4) + self.gridLayout_2.addWidget(self.groupBox_3, 1, 0, 1, 1) + self.horizontalLayout_3.addLayout(self.gridLayout_2) + self.tabWidget.addTab(self.syslinux_ab, "") + self.tab = QtWidgets.QWidget() + self.tab.setObjectName("tab") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.groupBox_5 = QtWidgets.QGroupBox(self.tab) + self.groupBox_5.setObjectName("groupBox_5") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_5) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.gridLayout_7 = QtWidgets.QGridLayout() + self.gridLayout_7.setObjectName("gridLayout_7") + self.ram_iso_2048 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_2048.setObjectName("ram_iso_2048") + self.gridLayout_7.addWidget(self.ram_iso_2048, 4, 4, 1, 1) + self.ram_iso_1024 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_1024.setObjectName("ram_iso_1024") + self.gridLayout_7.addWidget(self.ram_iso_1024, 4, 3, 1, 1) + self.ram_iso_256 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_256.setObjectName("ram_iso_256") + self.gridLayout_7.addWidget(self.ram_iso_256, 4, 0, 1, 1) + self.browse_iso_qemu = QtWidgets.QPushButton(self.groupBox_5) + self.browse_iso_qemu.setObjectName("browse_iso_qemu") + self.gridLayout_7.addWidget(self.browse_iso_qemu, 2, 4, 1, 1) + self.label_7 = QtWidgets.QLabel(self.groupBox_5) + self.label_7.setObjectName("label_7") + self.gridLayout_7.addWidget(self.label_7, 0, 0, 1, 5) + self.ram_iso_512 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_512.setObjectName("ram_iso_512") + self.gridLayout_7.addWidget(self.ram_iso_512, 4, 1, 1, 1) + self.boot_iso_qemu = QtWidgets.QPushButton(self.groupBox_5) + self.boot_iso_qemu.setObjectName("boot_iso_qemu") + self.gridLayout_7.addWidget(self.boot_iso_qemu, 6, 4, 1, 1) + self.ram_iso_768 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_768.setObjectName("ram_iso_768") + self.gridLayout_7.addWidget(self.ram_iso_768, 4, 2, 1, 1) + self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_2.setObjectName("lineEdit_2") + self.gridLayout_7.addWidget(self.lineEdit_2, 2, 0, 1, 4) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem2, 3, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.groupBox_5) + self.label_3.setObjectName("label_3") + self.gridLayout_7.addWidget(self.label_3, 6, 0, 1, 4) + spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem3, 5, 0, 1, 1) + spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem4, 1, 0, 1, 1) + self.verticalLayout_3.addLayout(self.gridLayout_7) + self.verticalLayout_2.addWidget(self.groupBox_5) + spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_2.addItem(spacerItem5) + self.gridLayout_6 = QtWidgets.QGridLayout() + self.gridLayout_6.setObjectName("gridLayout_6") + self.groupBox_4 = QtWidgets.QGroupBox(self.tab) + self.groupBox_4.setObjectName("groupBox_4") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_4) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.gridLayout_8 = QtWidgets.QGridLayout() + self.gridLayout_8.setObjectName("gridLayout_8") + self.ram_usb_768 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_768.setObjectName("ram_usb_768") + self.gridLayout_8.addWidget(self.ram_usb_768, 2, 2, 1, 1) + self.ram_usb_256 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_256.setObjectName("ram_usb_256") + self.gridLayout_8.addWidget(self.ram_usb_256, 2, 0, 1, 1) + self.label_6 = QtWidgets.QLabel(self.groupBox_4) + self.label_6.setObjectName("label_6") + self.gridLayout_8.addWidget(self.label_6, 0, 0, 1, 5) + self.ram_usb_1024 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_1024.setObjectName("ram_usb_1024") + self.gridLayout_8.addWidget(self.ram_usb_1024, 2, 3, 1, 1) + self.label_4 = QtWidgets.QLabel(self.groupBox_4) + self.label_4.setObjectName("label_4") + self.gridLayout_8.addWidget(self.label_4, 4, 0, 1, 4) + self.ram_usb_512 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_512.setObjectName("ram_usb_512") + self.gridLayout_8.addWidget(self.ram_usb_512, 2, 1, 1, 1) + self.boot_usb_qemu = QtWidgets.QPushButton(self.groupBox_4) + self.boot_usb_qemu.setObjectName("boot_usb_qemu") + self.gridLayout_8.addWidget(self.boot_usb_qemu, 4, 4, 1, 1) + self.ram_usb_2048 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_2048.setObjectName("ram_usb_2048") + self.gridLayout_8.addWidget(self.ram_usb_2048, 2, 4, 1, 1) + spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem6, 1, 1, 1, 1) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem7, 3, 2, 1, 1) + spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem8, 5, 0, 1, 1) + self.verticalLayout_4.addLayout(self.gridLayout_8) + self.gridLayout_6.addWidget(self.groupBox_4, 0, 0, 1, 1) + self.verticalLayout_2.addLayout(self.gridLayout_6) + self.tabWidget.addTab(self.tab, "") + self.tab_2 = QtWidgets.QWidget() + self.tab_2.setObjectName("tab_2") + self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_2) + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.gridLayout_10 = QtWidgets.QGridLayout() + self.gridLayout_10.setObjectName("gridLayout_10") + spacerItem9 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem9, 0, 1, 1, 1) + spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem10, 1, 0, 1, 1) + spacerItem11 = QtWidgets.QSpacerItem(20, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem11, 2, 1, 1, 1) + self.label_5 = QtWidgets.QLabel(self.tab_2) + self.label_5.setObjectName("label_5") + self.gridLayout_10.addWidget(self.label_5, 1, 1, 1, 1) + spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem12, 1, 2, 1, 1) + self.horizontalLayout_6.addLayout(self.gridLayout_10) + self.tabWidget.addTab(self.tab_2, "") + self.horizontalLayout.addWidget(self.tabWidget) + + self.retranslateUi(Dialog) + self.tabWidget.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "multibootusb")) + self.label_persistence_value.setText(_translate("Dialog", "0 MB")) + self.labelstep3.setText(_translate("Dialog", "

Step 3

")) + self.slider_persistence.setToolTip(_translate("Dialog", "Choose Persistence size. Not all distros supports persistence...")) + self.close.setText(_translate("Dialog", "Close")) + self.groupBox_11.setTitle(_translate("Dialog", "Detect USB")) + self.detect_usb.setText(_translate("Dialog", "Refresh USB")) + self.labelstep1.setText(_translate("Dialog", "

Step 1

")) + self.labelstep2.setText(_translate("Dialog", "

Step 2

")) + self.create.setText(_translate("Dialog", "Create")) + self.groupBox.setTitle(_translate("Dialog", "Uninstall (Optional)")) + self.uninstall.setText(_translate("Dialog", "Uninstall Distro")) + self.browse_iso.setText(_translate("Dialog", "Browse ISO")) + self.label_persistence.setText(_translate("Dialog", "

Persistence

")) + self.groupBox_6.setTitle(_translate("Dialog", "USB Details")) + self.usb_dev.setText(_translate("Dialog", "Drive ::")) + self.usb_vendor.setText(_translate("Dialog", "Vendor ::")) + self.usb_model.setText(_translate("Dialog", "Model::")) + self.usb_size.setText(_translate("Dialog", "Size ::")) + self.usb_mount.setText(_translate("Dialog", "Mount ::")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Dialog", "MultiBootUSB")) + self.groupBox_7.setTitle(_translate("Dialog", "Imager")) + self.groupBox_9.setTitle(_translate("Dialog", "-------------- USB details -------------------")) + self.pushbtn_imager_refreshusb.setText(_translate("Dialog", "Refresh USB")) + self.imager_disk_label.setText(_translate("Dialog", "Disk Label ::")) + self.imager_total_size.setText(_translate("Dialog", "Disk Size")) + self.imager_uuid.setText(_translate("Dialog", "Disk Label ::")) + self.groupBox_10.setTitle(_translate("Dialog", "------------------------------ ISO details ----------------------------------")) + self.pushButton.setText(_translate("Dialog", "Browse ISO")) + self.imager_bootable.setText(_translate("Dialog", "Bootable ISO")) + self.imager_iso_size.setText(_translate("Dialog", "ISO Size")) + self.groupBox_8.setTitle(_translate("Dialog", "Imager Progress")) + self.imager_write.setText(_translate("Dialog", "Write/Create")) + self.imager_close.setText(_translate("Dialog", "Close")) + self.label_10.setText(_translate("Dialog", "

WARNING : Any bootable USB made using ISO Imager will destroy all data on the selected USB disk.

Use it at your own risk. Developers are not responsile for loss of any data.

")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.imager), _translate("Dialog", "ISO Imager")) + self.groupBox_2.setTitle(_translate("Dialog", "Install Syslinux")) + self.install_syslinux.setText(_translate("Dialog", "Install")) + self.install_sys_all.setText(_translate("Dialog", "Install syslinux and copy all required files.")) + self.install_sys_only.setText(_translate("Dialog", "Install only syslinux (existing configurations will not be altred).")) + self.groupBox_3.setTitle(_translate("Dialog", "Edit syslinux.cfg")) + self.edit_syslinux.setText(_translate("Dialog", "Edit")) + self.label_2.setText(_translate("Dialog", "

Using this option user can edit syslinux.cfg file directly. It directly uses

default editor of host system. Be careful while editing syslinux.cfg file.

")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.syslinux_ab), _translate("Dialog", "Syslinux")) + self.groupBox_5.setTitle(_translate("Dialog", "Boot ISO")) + self.ram_iso_2048.setText(_translate("Dialog", "2048 MB")) + self.ram_iso_1024.setText(_translate("Dialog", "1024 MB")) + self.ram_iso_256.setText(_translate("Dialog", "256 MB")) + self.browse_iso_qemu.setText(_translate("Dialog", "Browse ISO")) + self.label_7.setText(_translate("Dialog", "

Best way to test your downloaded ISOs.

")) + self.ram_iso_512.setText(_translate("Dialog", "512 MB")) + self.boot_iso_qemu.setText(_translate("Dialog", "Boot ISO")) + self.ram_iso_768.setText(_translate("Dialog", "768 MB")) + self.label_3.setText(_translate("Dialog", "Choose desired RAM and click on Boot ISO button.")) + self.groupBox_4.setTitle(_translate("Dialog", "Boot USB")) + self.ram_usb_768.setText(_translate("Dialog", "768 MB")) + self.ram_usb_256.setText(_translate("Dialog", "256 MB")) + self.label_6.setText(_translate("Dialog", "

Use this option if you want to check USB installation without reboot.

")) + self.ram_usb_1024.setText(_translate("Dialog", "1024 MB")) + self.label_4.setText(_translate("Dialog", "Choose desired RAM and click on Boot USB button.")) + self.ram_usb_512.setText(_translate("Dialog", "512 MB")) + self.boot_usb_qemu.setText(_translate("Dialog", "Boot USB")) + self.ram_usb_2048.setText(_translate("Dialog", "2048 MB")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "QEMU")) + self.label_5.setText(_translate("Dialog", "

An advanced bootable usb creator with option to install/uninstall

multiple distros. This software is written in python and pyqt.

Copyright 2010-2016 Sundar

Author(s): Sundar, Ian Bruce, Lee

Licence: GPL version 2 or later

Home page: http://multibootusb.org

Help/Email: feedback.multibootusb@gmail.com

Source Code:https://github.com/mbusb/multibootusb


")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Dialog", "About")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Dialog = QtWidgets.QDialog() + ui = Ui_Dialog() + ui.setupUi(Dialog) + Dialog.show() + sys.exit(app.exec_()) + diff --git a/scripts/imager.py b/scripts/imager.py new file mode 100644 index 0000000..f90d913 --- /dev/null +++ b/scripts/imager.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: imager.py +# Purpose: Module to write ISO image to selected USB disk. Uses dd as backend. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above +# WARNING : Any boot-able USB made using this module will destroy data stored on USB disk. + +import os +import subprocess +import collections +import platform +import signal +from PyQt5 import QtGui +from PyQt5 import QtWidgets +from PyQt5 import QtCore +from .gui.ui_multibootusb import Ui_Dialog +from .gen import * +from . import iso +from . import usb +from . import config +from . import progressbar + +if platform.system() == "Windows": + import win32com.client + + +def dd_linux(): + import time + input = "if=" + config.imager_iso_link + in_file_size = float(os.path.getsize(config.imager_iso_link)) + output = "of=" + config.imager_usb_disk + os.system("umount " + config.imager_usb_disk + "1") + command = ['dd', input, output, "bs=1M"] + print("Executing ==> ", command) + dd_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while dd_process.poll() is None: + time.sleep(.1) # If this time delay is not given, the Popen does not execute the actual command + dd_process.send_signal(signal.SIGUSR1) + dd_process.stderr.flush() + while True: + out_error = dd_process.stderr.readline().decode() + if out_error: + if 'bytes' in out_error: + copied = int(out_error.split(' ', 1)[0]) + config.imager_percentage = round((float(copied) / float(in_file_size) * 100)) + pbar.update(config.imager_percentage) + break + + if dd_process.poll() is not None: + print("Executing ==> sync") + os.system("sync") + print("ISO has been written to USB disk...") + return + + +def dd_win(): + + windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe")) + if os.path.exists(resource_path(os.path.join("data", "tools", "dd", "dd.exe"))): + print("dd exist") + input = "if=" + config.imager_iso_link + in_file_size = float(os.path.getsize(config.imager_iso_link) / 1024 / 1024) + output = "of=\\\.\\" + config.imager_usb_disk + command = [windd, input, output, "bs=1M", "--progress"] + print("Executing ==> ", command) + dd_process = subprocess.Popen(command, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, + shell=False) + while dd_process.poll() is None: + for line in iter(dd_process.stderr.readline, ''): + line = line.strip() + if 'error' in line.lower() or 'invalid' in line.lower(): + print("Error writing to disk...") + break + if line and line[-1] == 'M': + copied = float(line.strip('M').replace(',', '')) + config.imager_percentage = round((copied / float(in_file_size) * 100)) + + print("ISO has been written to USB disk...") + + return + + +class Imager(QtWidgets.QDialog, Ui_Dialog): + """ + Raw write to USB disk using dd. + """ + + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + def on_Imager_Browse_iso_Click(self): + """ + Browse and choose an ISO. + :return: + """ + self.ui.lineEdit_3.clear() + config.imager_iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', "", "ISO Files (*.iso)")[0] + if config.imager_iso_link: + if platform.system() == "Windows": + if "/" in config.imager_iso_link: + config.imager_iso_link = config.imager_iso_link.strip().replace("/", "\\") + self.ui.lineEdit_3.insert(str(config.imager_iso_link)) + self.add_iso_gui_label_text() + else: + print("File not selected...") + + def add_iso_gui_label_text(self): + """ + Simple function to add text label to GUI widgets. + :return: + """ + print("Testing ISO...") + if iso.is_bootable(config.imager_iso_link) is True: + self.ui.imager_bootable.setText("Bootable ISO :: Yes") + print("ISO is bootable.") + else: + self.ui.imager_bootable.setText("Bootable ISO :: No") + print("ISO is not bootable.") + + if os.path.exists(config.imager_iso_link): + print("Path " + config.imager_iso_link + " is exist...") + self.iso_size = str(round(os.path.getsize(config.imager_iso_link) / 1024 / 1024)) + self.ui.imager_iso_size.setText("ISO Size :: " + self.iso_size + " MB") + print("ISO Size is ", self.iso_size, " MB") + + def onImagerComboChange(self): + config.imager_usb_disk = str(self.ui.comboBox_2.currentText()) + if bool(config.imager_usb_disk): + self.ui.imager_disk_label.setText("Disk Type :: " + self.imager_usb_detail(config.imager_usb_disk, + partition=0).usb_type) + self.ui.imager_total_size.setText("Disk Size :: " + usb.bytes2human(int(self.imager_usb_detail + (config.imager_usb_disk, + partition=0).total_size))) + if platform.system() == "Linux": + self.ui.imager_uuid.setText("Disk Model :: " + str(self.imager_usb_detail(config.imager_usb_disk, + partition=0).model)) + else: + self.ui.imager_uuid.setText("Disk Label :: " + self.imager_usb_detail(config.imager_usb_disk, + partition=0).model) + + def imager_list_usb(self, partition=1): + """ + Function to detect whole USB disk. It uses lsblk package on Linux. + :param partition: What to return. By default partition is set. + :return: USB disk/partition as list + """ + disk = [] + if platform.system() == "Linux": + output = subprocess.check_output("lsblk -i", shell=True) + if not partition == 1: + for line in output.splitlines(): + line = line.split() + if (line[2].strip()) == b'1' and (line[5].strip()) == b'disk': + disk.append(str("/dev/" + str(line[0].strip().decode()))) + elif partition == 1: + for line in output.splitlines(): + line = line.split() + if (line[2].strip()) == b'1' and line[5].strip() == b'part': + disk.append(str("/dev/" + str(line[0].strip()[2:]))) + else: + if partition == 1 or not partition == 1: + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + oDrives = oFS.Drives + for drive in oDrives: + if drive.DriveType == 1 and drive.IsReady: + disk.append(drive) + return disk + + def imager_usb_detail(self, usb_disk, partition=1): + """ + Function to detect details of USB disk using lsblk + :param usb_disk: path to usb disk + :param partition: by default partition is set (but yet to code for it) + :return: details of size, type and model as tuples + """ + _ntuple_diskusage = collections.namedtuple('usage', 'total_size usb_type model') + + if platform.system() == "Linux": + output = subprocess.check_output("lsblk -ib " + usb_disk, shell=True) + for line in output.splitlines(): + line = line.split() + if not partition == 1: + if line[2].strip() == b'1' and line[5].strip() == b'disk': + total_size = line[3] + if not total_size: + total_size = "Unknown" + usb_type = "Removable" + model = subprocess.check_output("lsblk -in -f -o MODEL " + usb_disk, shell=True).decode() + if not model: + model = "Unknown" + else: + try: + selected_usb_part = str(usb_disk) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part))) + selected_usb_device = d.DriveLetter + label = (d.VolumeName).strip() + if not label.strip(): + label = "No label." + total_size = d.TotalSize + usb_type = "Removable" + model = label + except: + print("Error detecting USB details.") + + return _ntuple_diskusage(total_size, usb_type, model) + + def get_usb_size(self, usb_disk): + """ + Function to detect USB disk space. Useful but not used in multibootusb as of now. + :param usb_disk: USB disk like "/dev/sdb" + :return: Size of the disk as integer + """ + if platform.system() == "Linux": + cat_output = subprocess.check_output("cat /proc/partitions | grep " + usb_disk[5:], shell=True) + usb_size = int(cat_output.split()[2]) * 1024 + print(usb_size) + return usb_size + else: + usb_size = self.usb.disk_usage(self.usb.get_usb(usb_disk).mount).total + return usb_size + diff --git a/scripts/install.py b/scripts/install.py new file mode 100644 index 0000000..b1ecaa1 --- /dev/null +++ b/scripts/install.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: install.py +# Purpose: This module contain functions to install ISO files to USB disk non destructively. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import shutil +import sys +import platform +import threading +import subprocess +from .usb import * +from .gen import * +from .iso import * +from scripts.update_cfg_file import * +from . import config +from . import persistence + + +def install_distro(): + """ + Install selected ISO to USB disk. + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(config.iso_link)) + _iso_file_list = iso_file_list(config.iso_link) + if not os.path.exists(os.path.join(usb_mount, "multibootusb")): + print("Copying multibootusb directory to ", usb_mount) + shutil.copytree(resource_path(os.path.join("data", "tools", "multibootusb")), + os.path.join(config.usb_mount, "multibootusb")) + + if not os.path.exists(install_dir): + os.makedirs(install_dir) + with open(os.path.join(install_dir, "multibootusb.cfg"), "w") as f: + f.write(config.distro) + with open(os.path.join(install_dir, "iso_file_list.cfg"), 'w') as f: + for file_path in _iso_file_list: + f.write(file_path + "\n") + print("Installing ", iso_name(config.iso_link), "on", install_dir) + + if config.distro == "opensuse": + iso_extract_file(config.iso_link, install_dir, 'boot') + status_text = "Copying ISO..." + if platform.system() == "Windows": + subprocess.call(["xcopy", config.iso_link, usb_mount], shell=True) # Have to use xcopy as python file copy is dead slow. + elif platform.system() == "Linux": + print(config.iso_link, usb_mount) + shutil.copy(config.iso_link, usb_mount) + elif config.distro == "Windows": + print("Extracting iso to " + usb_mount) + iso_extract_full(config.iso_link, install_dir) + elif config.distro == "trinity-rescue": + iso_extract_file(config.iso_link, usb_mount, 'trk3') + elif config.distro == "ipfire": + iso_extract_file(config.iso_link, usb_mount, '.tlz') + iso_extract_file(config.iso_link, usb_mount, 'distro.img') + iso_extract_file(config.iso_link, install_dir, 'boot') + elif config.distro == "alpine": + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == "zenwalk": + iso_extract_file(config.iso_link, install_dir, "kernel") + if platform.system() == "Windows": + subprocess.call(["xcopy", config.iso_link, install_dir], shell=True) + elif platform.system() == "Linux": + shutil.copy(config.iso_link, install_dir) + elif config.distro == "salix-live": + iso_extract_file(config.iso_link, install_dir, "boot") + config.status_text = "Copying ISO..." + if platform.system() == "Windows": + subprocess.call("xcopy " + config.iso_link + " " + install_dir, shell=True) + elif platform.system() == "Linux": + shutil.copy(config.iso_link, install_dir) + elif config.distro == "generic": + with open(os.path.join(install_dir, "generic.cfg"), "w") as f: + f.write(os.path.join(isolinux_bin_dir(config.iso_link), "generic") + ".bs") + iso_extract_full(config.iso_link, usb_mount) + else: + iso_extract_full(config.iso_link, install_dir) + + if platform.system() == 'Linux': + print('ISO extracted successfully. Sync is in progress...') + os.system('sync') + + if config.persistence != 0: + config.status_text = 'Creating Persistence...' + persistence.create_persistence() + + install_patch() + + +def install_progress(): + """ + Function to calculate progress percentage of install. + :return: + """ + from . import progressbar + + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + usb_size_used = usb_details['size_used'] + + thrd = threading.Thread(target=install_distro, name="install_progress") + # thrd.daemon() + # install_size = usb_size_used / 1024 + install_size = iso_size(config.iso_link) / 1024 + final_size = (usb_size_used + iso_size(config.iso_link)) + config.persistence + thrd.start() + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while thrd.is_alive(): + current_size = details(config.usb_disk)['size_used'] + percentage = int((current_size / final_size) * 100) + if percentage > 100: + percentage = 100 + config.percentage = percentage + pbar.update(percentage) + + +def install_patch(): + if config.distro == 'debian': + if platform.system() == 'Linux': + os.system('sync') + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(config.iso_link)[1:]) + iso_linux_bin_dir = isolinux_bin_dir(config.iso_link) + config.syslinux_version = isolinux_version(isolinux_path) + + iso9660fs = ISO9660(config.iso_link) + iso_file_list = iso9660fs.readDir("/") + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link)[1:]) + if any("makeboot.sh" in s.lower() for s in iso_file_list): + for module in os.listdir(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link)[1:])): + if module.endswith(".c32"): + if os.path.exists(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link)[1:], module)): + try: + #os.chmod(os.path.join(config.usb_mount, "multibootusb", + # iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link)[1:], module), 0o777) + #os.unlink(os.path.join(config.usb_mount, "multibootusb", + # iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link)[1:], module)) + os.remove(os.path.join(config.usb_mount, "multibootusb", + iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link)[1:], module)) + print("Copying ", module) + print((resource_path( + os.path.join(multibootusb_host_dir(), "syslinux", "modules", config.syslinux_version, module)), + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link)[1:], module))) + shutil.copy(resource_path( + os.path.join(multibootusb_host_dir(), "syslinux", "modules", config.syslinux_version, module)), + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link)[1:], module)) + except Exception as err: + print(err) + print("Could not copy ", module) + else: + print('Patch not required...') + + + + + +if __name__ == '__main__': + config.iso_link = '../../../DISTROS/2016/slitaz-4.0.iso' + install_distro() diff --git a/scripts/iso.py b/scripts/iso.py new file mode 100644 index 0000000..7ecc8c4 --- /dev/null +++ b/scripts/iso.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: iso.py +# Purpose: Module to manupulate ISO file +# Authors: Sundar +# Depends: isodump3.py (Authored by Johni Lee for MultiBootUSB) +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import sys +import os +import string +import platform +import re +from .gen import * +from .isodump3 import ISO9660 + +_iso_cfg_ext_dir = iso_cfg_ext_dir() + +def iso_name(iso_link): + """ + Find the name of an ISO. + :return: Name of an ISO (with extension) as string. Returns If not returns None. + """ + if os.path.exists(iso_link): + try: + name = os.path.basename(str(iso_link)) + except: + name = None + else: + name = None + + return name + + +def iso_basename(iso_link): + """ + Find the base name of an ISO. + :return: Base name (without extension) of a selected ISO as string. If not returns None. + """ + try: + dir_name = str(os.path.splitext(os.path.basename(str(iso_link)))[0]) + except: + dir_name = None + + return dir_name + + +def isolinux_bin_exist(iso_link): + """ + Check if an "isolinux.bin" file exist. + :return: True if "isolinux.bin" file exist of False if not. + """ + if os.path.exists(iso_link): + iso9660fs = ISO9660(iso_link) + iso_file_list = iso9660fs.readDir("/") + if any("isolinux.bin" in s.lower() for s in iso_file_list): + return True + else: + return False + + +def iso_size(iso_link): + return os.path.getsize(iso_link) + + +def is_bootable(iso_link): + """ + Check if an ISO has the ability to boot. + :return: True if ISO is bootable and False if not. + """ + iso9660fs = ISO9660(iso_link) + isBootable = iso9660fs.checkISOBootable() + if isBootable: + return True + else: + return False + + +def isolinux_bin_dir(iso_link): + """ + Detects "isolinux.bin" directory. + :return: path of "isolinux.bin" directory as string. + """ + iso9660fs = ISO9660(iso_link) + if os.path.exists(iso_link): + bin_dir = False + iso_file_list = iso9660fs.readDir("/") + if any("isolinux.bin" in s.lower() for s in iso_file_list): + for f in iso_file_list: + if 'isolinux.bin' in f.lower(): + bin_dir = os.path.dirname(f) + break + + return bin_dir + + +def isolinux_bin_path(iso_link): + """ + Detects pat to "isolinux.bin". + :return: path of "isolinux.bin" directory as string. + """ + iso_bin_path = False + if isolinux_bin_exist(iso_link) is not False: + + iso9660fs = ISO9660(iso_link) + iso_file_list = iso9660fs.readDir("/") + for f in iso_file_list: + if 'isolinux.bin' in f.lower(): + iso_bin_path = f + break + + return iso_bin_path + + +def integrity(iso_link): + """ + Check the integrity of an ISO. + :return: True if integrity passes or False if it fails. + """ + if os.path.exists(iso_link): + iso9660fs = ISO9660(iso_link) + if iso9660fs.checkIntegrity(): + return True + else: + return False + + +def iso_file_list(iso_link): + """ + Function to return the content of an ISO. + :return: List of files of an ISO as list. + """ + iso9660fs = ISO9660(iso_link) + iso_file_list = iso9660fs.readDir("/") + return iso_file_list + + +def isolinux_version(isolinux_bin_path): + """ + Detect isolinux version shipped by distros. + :param isolinux_path: Path to "isolinux.bin" + :return: Version number as string. + """ + version = ["3", "4", "5", "6"] + if isolinux_bin_path is not None: + sl = list(strings(isolinux_bin_path)) + for strin in sl: + if re.search(r'isolinux ', strin, re.I): + for number in version: + if re.search(r'isolinux ' + number, strin, re.I): + print("\n\nFound syslinux version " + number + "\n\n") + return str(number) + + +def iso_extract_file(iso_link, dest_dir, filter): + """ + Extract the specific file(s) from an ISO + :param dest_dir: Path to destination directory. + :param filter: Filter to extract particular file(s) + :return: Extract file(s) to destination. + """ + if os.path.exists(iso_link) and os.path.exists(dest_dir): + iso9660fs = ISO9660(iso_link) + iso9660fs.writeDir("/", dest_dir, filter) + + +def extract_cfg_file(iso_link): + iso_extract_file(iso_link, _iso_cfg_ext_dir, '.cfg') + iso_extract_file(iso_link, _iso_cfg_ext_dir, '.CFG') + iso_extract_file(iso_link, _iso_cfg_ext_dir, '.TXT') + iso_extract_file(iso_link, _iso_cfg_ext_dir, '.txt') + iso_extract_file(iso_link, _iso_cfg_ext_dir, 'isolinux.bin') + iso_extract_file(iso_link, _iso_cfg_ext_dir, 'ISOLINUX.BIN') + + +def iso_extract_full(iso_link, dest_dir): + """ + Extract an ISO to destination directory + :param dest_dir: Destination path as string. + :return: False if it fails or extract ISO files to destination directory. + """ + iso9660fs = ISO9660(iso_link) + try: + iso9660fs.writeDir("/", dest_dir) + except: + print("ISO extraction failed.") + return False + + +if __name__ == '__main__': + #iso_path = '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso' + iso_path = '../../../DISTROS/2015/super_grub2_disk_hybrid_2.02s3.iso' + test_iso_bin_path = os.path.join('test', 'isolinux', 'isolinux.bin') + print('iso_name(iso_path) : ', iso_name(iso_path)) + print('iso_basename(iso_path) : ', iso_basename(iso_path)) + print('Integrity of ISO is : ', integrity(iso_path)) + f_list = (iso_file_list(iso_path)) + if f_list: + for f in f_list: + print(f) + print('isolinux_bin_exist(iso_path) : ', isolinux_bin_exist(iso_path)) + #print('is_bootable : ', is_bootable(iso_path)) + print('isolinux_bin_dir() : ', isolinux_bin_dir(iso_path)) + print('isolinux_bin_path(iso_path) : ', isolinux_bin_path(iso_path)) + iso_extract_full(iso_path, 'test') + iso_extract_file(iso_path, 'test', 'isolinux.bin') + print(isolinux_version(test_iso_bin_path)) + diff --git a/scripts/isodump3.py b/scripts/isodump3.py new file mode 100644 index 0000000..6d60dc9 --- /dev/null +++ b/scripts/isodump3.py @@ -0,0 +1,805 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: isodump3.py +# Purpose: Module to list and extract iso files. +# Authors: LiQiong Lee (written exclusively for multibootusb) +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License version 3 +# Credit : I am grateful to LiQiong Lee. He not only wrote this module for multibootusb, but also extended the same +# to python3 within short time after request. + +""" ISO9660fs +Dump raw meta data of iso9660 file system. +Extract directories and files. +""" +## +## Extract directory or file from iso. +## Support RRIP. +## + +# Author : joni +# version : 1.0 + +import sys +import struct +import os +import re +import stat +from ctypes import * +from . import config + + +BLOCK_SIZE = 2048 +S_IFSOCKET = 0o140000 +S_IFLINK = 0o120000 +S_IFREG = 0o100000 +S_IFBLK = 0o060000 +S_IFCHR = 0o020000 +S_IFDIR = 0o040000 +S_IFIFO = 0o010000 + +E_SUCCESS = 0 +E_FAILURE = -1 +E_DEVICEFILE = -2 # can't write device file + +class PrimaryVolume(Structure): + def __init__(self): + self.sysIdentifier = "" + self.volIdentifier = "" + self.volSize = 0 + self.volSeq = 0 + self.blockSize = 0 + self.ptSize = 0 + self.ptLRd = 0 + self.fsVer = 0 + self.rootLoc = 0 + self.rootTotal = 0 + +class Rrip(Structure): + def __init__(self): + self.offset = -1 + self.altname = "" + self.devH = 0 + self.devL = 0 + self.fMode = 0 + +class DirRecord(Structure): + def __init__(self): + self.lenDr = 0 + self.lenEattr = 0 + self.locExtent= 0 + self.lenData = 0 + self.dtYear = 0 + self.dtMonth = 0 + self.dtHour = 0 + self.dtMinute = 0 + self.dtSecond = 0 + self.dtOffset = 0 + self.fFlag = 0 + self.fUnitSize= 0 + self.gapSize = 0 + self.volSeqNr = 0 + self.lenFi = 0 + self.fIdentifier = "" + self.sysUseStar = 0 + self.suspBuf = "" + self.rrip = None + +class PathTabelItem(Structure): + def __init__(self): + self.lenDi = 0 + self.lenEattr = 0 + self.locExtenti = 0 + self.pdirNr = 0 + self.fIdentifier = "" + +class ISO9660: + """ + This class can dump iso9660 file system meta data and extract files. + Support: + RRIP extension. + """ + + def __init__(self, isofile): + try: + f = open(isofile, 'rb') + except(IOError): + sys.stderr.write("can't open {0}".format(isofile)) + sys.exit(-1) + + self.isoFile = f + self.priVol = None + self.rootDir = None + self.rripOffset = -1 + + desc_nr = 0 + while True: + desc_nr = desc_nr + 1 + try: + self.isoFile.seek(BLOCK_SIZE*(15+desc_nr)) + volume_dsc = self.isoFile.read(BLOCK_SIZE) + flag = struct.unpack('B',volume_dsc[0:1])[0] + if flag == 1: + self.__readPrimaryVolume__(volume_dsc) + continue + if flag == 255: + break + except Exception as e: + print("Got exception when init iso file:", sys.exc_info()[0]) + self.priVol = None + self.rootDir = None + break + + def __del__(self): + self.isoFile.close() + + def __readPrimaryVolume__(self, volume_dsc): + """ Dump primary volume descriptor """ + global BLOCK_SIZE + priVol = PrimaryVolume() + priVol.sysIdentifier = volume_dsc[8:40] + priVol.volIdentifier = volume_dsc[40:72] + priVol.volSize = struct.unpack(' 0: + entry_buf = desc_buf[self.rripOffset:] + print ("__rripLoop__ offset:%d"%(self.rripOffset)) + else: + entry_buf = desc_buf + + rr = Rrip() + while True: + ce_blk = 0 + ce_len = 0 + ce_off = 0 + head = 0 + len_entry = 0 + + while True: + #print (("\n%d, %d\n")%(len_buf, head)) + head += len_entry + if len_buf - head < 4: # less than one entry + break + entry_buf = entry_buf[len_entry:] + + sig1 = struct.unpack("B", entry_buf[0:1])[0] + sig2 = struct.unpack("B", entry_buf[1:2])[0] + len_entry = struct.unpack("B", entry_buf[2:3])[0] + ver = struct.unpack("B", entry_buf[3:4])[0] + #if len_entry == 0: + # print "Got a entry in __rripLoop__ (%c,%c) of SUSP with length:(%d),version:(%d)-->"%(sig1,sig2,len_entry, ver), + if len_entry == 0: + break; + + if sig1 == ord('S') and sig2 == ord('P'): + ck1 = struct.unpack("B", entry_buf[4:5])[0] + ck2 = struct.unpack("B", entry_buf[5:6])[0] + skip = struct.unpack("B", entry_buf[6:7])[0] + #print "-->(0x%x==0xBE,0x%x==EF,%d)" %(ck1, ck2, skip) + if ck1 == 0xBE and ck2 == 0xEF: + rr.offset = skip + continue + + if sig1 == ord('C') and sig2 == ord('E'): + ce_blk = struct.unpack("(%d,%d,%d)" %(ce_blk, ce_off, ce_len) + continue + + if sig1 == ord('N') and sig2 == ord('M'): + flag = struct.unpack("B", entry_buf[4:5])[0] + #print "-->(flag:(0x%x), name:(%s))" %(flag, entry_buf[5:len_entry]) + if flag == 0x02: # FLAG_CURRENT + rr.altname += "." + elif flag == 0x04: # FLAG_PARENT + rr.altname += ".." + elif flag == 0x01 or flag ==0: # 1:FLAG_CONTINUE + rr.altname += entry_buf[5:len_entry].decode() + continue + + if sig1 == ord('P') and sig2 == ord('N'): + rr.devH = struct.unpack(" 0: + #print " Read CE block, (%d, %d, %d)"%(ce_blk, ce_len, ce_off) + self.isoFile.seek(ce_blk*BLOCK_SIZE + ce_off) + entry_buf = self.isoFile.read(ce_len) + len_buf = ce_len + else: + break + # while (True) end # + return rr + + def checkISOBootable(self): + """ Struct of a classical generic MBR. + + 0x0000 Bootstrap Code area + ----------------------------------------- + 0x01BE + .. Partition table + 0x01EE + ------------------------------------------ + 0x01FE 55h + Boot signature + 0x01FF AAh + + """ + self.isoFile.seek(0x01FE) + h = self.isoFile.read(2) + s1 = struct.unpack('B', h[0:1])[0] + s2 = struct.unpack('B', h[1:2])[0] + + #print "-->(0x%x,0x%x)" %(s1, s2) + + if (s1 == 0x55) and (s2 == 0xAA): + result = True # "Bootable" + else: + result = False # "Not bootable" + + return result + + def searchDir(self, path): + # /root/abc/ - ['', 'root', 'abc', ''] + # /root/abc - ['', 'root', 'abc'] + # / - ['', ''] + dircomps = path.split('/') + if dircomps[-1] == '': + dircomps.pop() + if dircomps == []: + return + + if self.priVol == None: + return + + if len(dircomps) == 1: + return self.rootDir + + pdir_loc = self.priVol.rootLoc + pdir_len = self.priVol.rootTotal + i_dircomp = 1 + + while True: + found = False + dirs = self.readDirItems(pdir_loc, pdir_len) + for item in dirs: + if item.fIdentifier == dircomps[i_dircomp]: + pdir_loc = item.locExtent + pdir_len = item.lenData + found = True + #print "found (%s)"%(dircomps[i_dircomp]) + break + if found: # advacne + if i_dircomp < len(dircomps)-1: + i_dircomp = i_dircomp + 1 + else: + return item + else: + print ("can't find " + dircomps[i_dircomp]) + return None + + def readDirrecord(self, desc_buf): + """ Dump file dirctory record + Return a directory record reading from a Directory Descriptors. + """ + dirRec = DirRecord() + try: + dirRec.lenDr = struct.unpack("B", desc_buf[0:1])[0] + if dirRec.lenDr == 0: + return None + except: + return None + + dirRec.lenEattr = struct.unpack("B", desc_buf[1:2])[0] + dirRec.locExtent = struct.unpack(" dirRec.sysUseStar+4: + if dirRec.locExtent == self.priVol.rootLoc: + dirRec.suspBuf = desc_buf[dirRec.sysUseStar:dirRec.lenDr] + suspBuf = desc_buf[dirRec.sysUseStar:dirRec.lenDr] + if self.rripOffset != -1: + rripNode = self.__rripLoop__(suspBuf, dirRec.lenDr-dirRec.sysUseStar) + dirRec.rrip = rripNode + if rripNode != None: + if rripNode.altname != "": + dirRec.fIdentifier = rripNode.altname + dirRec.lenFi = len(rripNode.altname) + #print "rrip_altname: %s"%(dirRec.fIdentifier) + # if rripNode end # + # if self.rripOffset != -1 end # + # if dirRec.lenDr > .. end # + return dirRec + + def readDirItems(self, block_nr=None, total=None): + """ Read file dirctory records + Read dirctory records from 'block_nr' with a length of 'total'. + Return a list containing directory records(DirRecord). + """ + dirs = [] + total_blk = (total+BLOCK_SIZE-1)//BLOCK_SIZE + i_blk = 0 + while i_blk < total_blk: + self.isoFile.seek((block_nr+i_blk)*BLOCK_SIZE) + desc_buf = self.isoFile.read(BLOCK_SIZE) + i_blk = i_blk + 1 + while True: + dirItem = self.readDirrecord(desc_buf) + if dirItem == None: + break + + dirs.append(dirItem) + if desc_buf.__len__() > dirItem.lenDr: + desc_buf = desc_buf[dirItem.lenDr:] + else: + break + return dirs + + def readPathtableL(self): + """ Read path table of L typde """ + if self.priVol == None: + return + block_nr = self.priVol.ptLRd + total = self.priVol.ptSize + + path_table = [] + self.isoFile.seek(block_nr*BLOCK_SIZE) + ptbuf = self.isoFile.read((BLOCK_SIZE * ((total+BLOCK_SIZE-1)//BLOCK_SIZE))) + i = 0 + r_size = 0 + while True : + i = i+1 + t = PathTabelItem() + + t.lenDi = struct.unpack('B', ptbuf[0:1])[0] + t.lenEattr = struct.unpack('B', ptbuf[1:2])[0] + t.locExtent = struct.unpack('= total: + break + ptbuf = ptbuf[9+t.lenDi-1+len_pd:] + # while True + return path_table + + # @path -- path within iso file system. + # @output -- what local path you want write to. + # @pattern -- regular expression. + # @r -- recursion flag, write the whole sub-directories or not. + # @all_type -- which file type should be writed. + # False: Write regular type files only. + # True: Wirte all types files (regular, device file, link, socket, etc) + def writeDir(self, path, output, pattern="", r=True, all_type=False): + """ Extract a directory + Return 0 means success otherwise failure. + """ + d = self.searchDir(path) + if d != None: + if output.endswith("/"): + output = output[0:-1] + # Try to make target directory. + if not os.path.exists(output): + try: + os.makedirs(output) + except(OSError): + sys.stderr.write("can't make dirs({0})\n".format(output)) + return E_FAILURE + pp = None + if pattern != "": + p = r'{0}'.format(pattern) + pp = re.compile(p) + #print "writeDir: flag(%x)"%(d.fFlag) + if d.fFlag & 0x02 == 0x02: + # Check if a clean directory. + #try: + # if len(os.listdir(output)) > 0: + # sys.stderr.write("The target directory is not empty\n") + # return E_FAILURE + #except(OSError): + # sys.stderr.write("can't access dirs({0})\n".format(p)) + # return E_FAILURE + self.writeDir_r(output, d, pp, r, all_type) + return E_SUCCESS + else: + return self.writeFile(d, output+path, all_type) + else: + return E_FAILURE + + def writeDir_r(self, det_dir, dire, pp, r, all_type): + #print "writeDir_r:(%s)"%(det_dir) + dirs = self.readDirItems(dire.locExtent, dire.lenData) + for d in dirs: + if not d.fIdentifier in [".", ".."]: + if (pp != None) and (pp.search(d.fIdentifier) == None): + match = False + else: + match = True + #print "mathing %s, %s, (%x)"%(match, d.fIdentifier, d.fFlag) + p = det_dir + "/" + d.fIdentifier + if d.fFlag & 0x02 == 0x02: + if not os.path.exists(p): + os.makedirs(p, 0o777) + if r: + if match: + self.writeDir_r(p, d, None, r, all_type) # Don't need to match subdirectory. + else: + self.writeDir_r(p, d, pp, r, all_type) + elif match: + self.writeFile(d, p, all_type) + # if not d.fIdentifier end # + # for d in dirs end # + + def writeFile(self, dirRec, detFile, all_type): + """ Write a file to detFile + Return 0 means success otherwise failure. + """ + global file_out + if detFile == "" or dirRec == None: + sys.stderr.write("can't write file\n") + return E_FAILURE + + #print "write file (%s)"%(detFile) + config.status_text = detFile + + dirname = os.path.dirname(detFile) + if not os.path.exists(dirname): + try: + os.makedirs(dirname, 0o777) + except(OSError): + sys.stderr.write("can't makedirs\n") + return E_FAILURE + + if all_type == True: + # device file + if dirRec.rrip != None and (dirRec.rrip.devH != 0 or dirRec.rrip.devL != 0): + #fFlag == 0 + high = dirRec.rrip.devH + low = dirRec.rrip.devL + if high == 0: + device = os.makedev(os.major(low), os.minor(low)) + else: + device = os.makedev(high, os.minor(low)) + try: + mode = dirRec.rrip.fMode & 0o770000 + if mode == S_IFCHR: + os.mknod(detFile, 0o777|stat.S_IFCHR, device) + elif mode == S_IFBLK: + os.mknod(detFile, 0o777|stat.S_IFBLK, device) + except(OSError): + sys.stderr.write("can't mknode, maybe no permission\n") + return E_DEVICEFILE + + return E_SUCCESS + + loc = dirRec.locExtent + length = dirRec.lenData + self.isoFile.seek(BLOCK_SIZE * loc) + #print "file length(%d)"%(length) + r_size = BLOCK_SIZE*1024*50 #100M cache + + try: + f_output = open(detFile, 'wb', r_size) + except(IOError): + sys.stderr.write("can't open{0} for write\n".format(detFile)) + return E_FAILURE + + while True: + if length == 0: + break + elif length <= r_size: + r_size = length + length = 0 + else: + length = length - r_size + + buf = self.isoFile.read(r_size) + f_output.write(buf) + f_output.flush() + # while True end. + f_output.close() + return E_SUCCESS + + def readDir(self, dir_path, r=True): + file_list = [] + d = self.searchDir(dir_path) + if d != None: + if (d.fFlag & 0x02) == 0x02: + #print "readDir (%x, %x)"%(d.locExtent, d.lenData) + if dir_path.endswith("/"): + dir_path = dir_path[0:-1] + self.readDir_r(file_list, dir_path, d, r) + # if (d.fFlag & 0x02) == 0x02: # + # if d != None: + return file_list + + def readDir_r(self, file_list, dir_path, dire, r): + if (dire.fFlag & 0x02) != 0x02: + return + dirs = self.readDirItems(dire.locExtent, dire.lenData) + for d in dirs: + if not d.fIdentifier in [".", ".."]: + p = dir_path + "/" + d.fIdentifier + file_list.append(p) + if r: + self.readDir_r(file_list, p, d, r) + # if not d.fIdentifier # + # for d in dirs: # + + def checkIntegrity(self): + if self.priVol == None: # no primary volume + return False + + if self.priVol.ptSize == 0: # empty ? + return True + + path_table = self.readPathtableL() + if path_table == []: # pathtable record is broken. + return False + + # find last file item to check + for dr in reversed(path_table): + #print dr.fIdentifier + dirs = self.readDirItems(dr.locExtent, BLOCK_SIZE) + if len(dirs) > 2: + dot = dirs[0] + dirs2 = self.readDirItems(dot.locExtent, dot.lenData) # get the whole items. + for dr2 in reversed(dirs2): # search last file item. + if dr2.fFlag == 0: + #print "get last file(%s)"%(dr2.fIdentifier) + try: + #self.isoFile.seek(BLOCK_SIZE * dr2.locExtent+dr2.lenData) + lastfile_end = BLOCK_SIZE * dr2.locExtent + dr2.lenData + self.isoFile.seek(0, os.SEEK_END) + iso_end = self.isoFile.tell() + #print ("%d-->%d")%(lastfile_end, iso_end) + if iso_end >= lastfile_end: + return True + else: + return False + except(IOError): + #print "exception when seek. iso is broken" + return False + elif len(dirs) < 2: # Dir record is broken. At least, should have two entries. + return False + return True + +########################################################################### +def dump_dir_record(dirs): + """ Dump all the file dirctory records contained in desc_buf """ + + print("Dump file/deirectory record") + print("===========================", end="\n") + if dirs != None: + for f in dirs: + print("length of directory record:(0x%x), length of extend attribute:(%d), \ +location of record:(%d)BLOCK->(0x%x), data length(%d) size of file unit:(%d), \ +interleave gap size:(%d), file flag:(0x%x),name length:(%d) identify:(%s)\n" \ +%(f.lenDr, f.lenEattr, f.locExtent, f.locExtent*BLOCK_SIZE,f.lenData, \ + f.fUnitSize, f.gapSize, f.fFlag, f.lenFi, f.fIdentifier)) + +def dump_pathtable_L(path_table): + """ Dump path table of L typde """ + + print("Dump path table") + print("================", end="\n") + #path_table = readPathtableL() + i = 0 + for t in path_table: + i = i + 1 + if t.lenDi == 1: + if t.fIdentifier in [0, 1]: + print("is a root directory(%d)" %(is_root)) + print("%d->length of identify:(%d), length of extend attribute:(%d), \ +local:(%d)->(0x%x), parent dir number:(%d), identify:(%s)\n" \ +%(i, t.lenDi, t.lenEattr, t.locExtent, t.locExtent*BLOCK_SIZE, t.pdirNr, t.fIdentifier)) + +def dump_primary_volume(privol=None): + """ Dump primary volume descriptor """ + + if privol == None: + print("Can't dump, maybe iso is broken") + return + print("===== Dump primary volume descriptor ==") + + print("System Identifier:(%s)" %(privol.sysIdentifier.decode())) + print("Volume Identifier:(%s)" %privol.volIdentifier.decode()) + print("Volume Space size:(0x%x)BLOCKS(2kB)" %privol.volSize) + print("Volume sequence number:(%d)" %(privol.volSeq)) + print("logic block size:(0x%x)" %(privol.blockSize)) + print("Volume path talbe L's BLOCK number is :(0x%x-->0x%x), size(%d)" %(privol.ptLRd, privol.ptLRd*BLOCK_SIZE, privol.ptSize)) +# print "Abstract File Identifier: (%s)" %(volume_dsc[739:776]) +# print "Bibliographic File Identifier: (%s)" %(volume_dsc[776:813]) + print("pathtable locate (%d)" %(privol.ptLRd)) + print("File Structure Version:(%d)" %(privol.fsVer)) + print("Root directory is at (%d)block, have(0x%x)bytes" %(privol.rootLoc, privol.rootTotal)) +# dump_dir_record(None, 23, 1) + +def dump_boot_record(volume_dsc): + """ Dump boot record """ + + print("===== Dump boot record ==") + std_identifier = volume_dsc[1:6] + print("Standard Identifier:(%s)" %std_identifier) + + vol_ver = struct.unpack('B', volume_dsc[6]) + print("Volume descriptor version:(%d)" %vol_ver) + + bootsys_identifier = volume_dsc[7:39] + print("boot system identifier(%s)" %bootsys_identifier) + + boot_identifier = volume_dsc[39:71] + print("boot identifier(%s)" %boot_identifier) + +def usage(): + """ Prompt user how to use """ + print(""" +Usage: isodump dump-what [options] iso-file + [dump-what] + ----------- + boot - Dump boot record. + primary-volume - Dump primary volume. + pathtable - Dump path table. + dir-record [block number] [length] - Dump a raw data of a Directory Record + + iso:/dir [-r] [-o output] [-p pattern] - Dump a dirctory or file to [output] + -r recursively visit directory. + -p spcify a Regular expression pattern for re.search(pattern,). + +isodump xx.iso - Dump the root directory +isodump pathtable xx.iso - Dump the path table record. + +isodump iso:/ -r xx.iso + -- Dump the root directory of xx.iso recursively. + +isodump iso:/ -r -o /tmp/iso xx.iso + -- Extract the iso to /tmp/iso/. + +isodump iso:/boot -o /tmp/iso/boot xx.iso + -- Extract the /boot directory of xx.iso to /tmp/iso/boot. + +isodump iso:/boot/grup.cfg -o /tmp/grub.cfg xx.iso + -- Extract the file "grup.cfg" to "/tmp/grub.cfg" + +isodump iso:/boot -r -o /tmp/iso -p "*.cfg" xx.iso + -- Extract any files or directories under /boot maching "*.cfg" to /tmp/iso/. +""") + sys.exit(-1) + +if __name__ == '__main__': + argv = sys.argv + if len(argv) < 3: + usage() + + iso9660fs = ISO9660(argv[-1]) + integrity = iso9660fs.checkIntegrity() + if integrity == False: + print("iso file is broken") + sys.exit(-1) + + dump_what = argv[1] + + if dump_what == "primary-volume": + dump_primary_volume(iso9660fs.priVol) + elif dump_what == "pathtable": + path_table = iso9660fs.readPathtableL() + dump_pathtable_L(path_table) + if dump_what == "dir-record": + if len(argv) == 5: + print("dump dir-record (%s, %s)"%(argv[2], argv[3])) + dirs = iso9660fs.readDirItems(int(argv[2]), int(argv[3])) + dump_dir_record(dirs) + else: + usage() + elif dump_what.startswith("iso:"): + o_path = "" + r = False + o = False + p = False + pattern = "" + for arg in argv[2:-1]: + if arg == "-r": + r = True + o = False + p = False + elif arg == "-o": + o = True + p = False + elif arg == "-p": + o = False + p = True + elif o == True: + o_path = arg + o = False + elif p == True: + pattern = arg + p = False + + isodir = dump_what[4:] + if o_path == "": + print("dump_dir(%s)"%(isodir)) + filelist = iso9660fs.readDir(isodir, r) + if filelist == []: + print("can't read any file from (%s)"%(isodir)) + else: + for f in filelist: + print(f) + else: + print("writeDir(%s)->(%s) with pattern(%s)"%(isodir, o_path, pattern)) + sys.exit(iso9660fs.writeDir(isodir, o_path, pattern, r, True)) + diff --git a/scripts/isoparser/__init__.py b/scripts/isoparser/__init__.py new file mode 100644 index 0000000..cf60b22 --- /dev/null +++ b/scripts/isoparser/__init__.py @@ -0,0 +1,23 @@ +import iso +import source + + +def parse(path_or_url, cache_content=False, min_fetch=16): + """ + Returns an :class:`ISO` object for the given filesystem path or URL. + + cache_content: + Whether to store sectors backing file content in the sector cache. If true, this will + cause memory usage to grow to the size of the ISO as more file content get accessed. + Even if false (default), an individual Record object will cache its own file content + for the lifetime of the Record, once accessed. + + min_fetch: + The smallest number of sectors to fetch in a single operation, to speed up sequential + accesses, e.g. for directory traversal. Defaults to 16 sectors, or 32 KiB. + """ + if path_or_url.startswith("http"): + src = source.HTTPSource(path_or_url, cache_content=cache_content, min_fetch=min_fetch) + else: + src = source.FileSource(path_or_url, cache_content=cache_content, min_fetch=min_fetch) + return iso.ISO(src) diff --git a/scripts/isoparser/iso.py b/scripts/isoparser/iso.py new file mode 100644 index 0000000..37dc0d2 --- /dev/null +++ b/scripts/isoparser/iso.py @@ -0,0 +1,56 @@ +class ISO(object): + def __init__(self, source): + self._source = source + + # Unpack volume descriptors + self.volume_descriptors = {} + sector = 16 + while True: + self._source.seek(sector) + sector += 1 + + vd = self._source.unpack_volume_descriptor() + self.volume_descriptors[vd.name] = vd + + if vd.name == "terminator": + break + + # Unpack the path table + self._source.seek( + self.volume_descriptors['primary'].path_table_l_loc, + self.volume_descriptors['primary'].path_table_size) + self.path_table = self._source.unpack_path_table() + + # Save a reference to the root record + self.root = self.volume_descriptors['primary'].root_record + + def record(self, *path): + """ + Retrieves a record for the given path. + """ + path = [part.upper() for part in path] + record = None + pivot = len(path) + + # Resolve as much of the path as possible via the path table + while pivot > 0: + try: + record = self.path_table.record(*path[:pivot]) + except KeyError: + pivot -= 1 + else: + break + + if record is None: + record = self.root + + # Resolve the remainder of the path by walking record children + for part in path[pivot:]: + for child in record.children_unsafe: + if child.name == part: + record = child + break + else: + raise KeyError(part) + + return record \ No newline at end of file diff --git a/scripts/isoparser/path_table.py b/scripts/isoparser/path_table.py new file mode 100644 index 0000000..e2ed61e --- /dev/null +++ b/scripts/isoparser/path_table.py @@ -0,0 +1,31 @@ +import record + + +class PathTable(object): + def __init__(self, source): + self._source = source + self.paths = {} + + paths_list = [] + + while len(source) > 0: + name_length = source.unpack('B') + _ = source.unpack('B') + location = source.unpack(' 0: + path.extend(paths_list[parent_idx]) + if name != "\x00": + path.append(name) + + paths_list.append(path) + self.paths[tuple(path)] = location + + def record(self, *path): + location = self.paths[path] + self._source.seek(location) + return self._source.unpack_record() \ No newline at end of file diff --git a/scripts/isoparser/record.py b/scripts/isoparser/record.py new file mode 100644 index 0000000..6d9fdf7 --- /dev/null +++ b/scripts/isoparser/record.py @@ -0,0 +1,70 @@ +class Record(object): + def __init__(self, source, length): + self._source = source + self._content = None + target = source.cursor + length + + _ = source.unpack('B') # TODO: extended attributes length + self.location = source.unpack_both('I') + self.length = source.unpack_both('I') + self.datetime = source.unpack_dir_datetime() + flags = source.unpack('B') + self.is_hidden = flags & 1 + self.is_directory = flags & 2 + # TODO: other flags + _ = source.unpack('B') # TODO: interleave unit size + _ = source.unpack('B') # TODO: interleave gap size + _ = source.unpack_both('h') # TODO: volume sequence + name_length = source.unpack('B') + self.name = source.unpack_string(name_length).split(';')[0] + if self.name == "\x00": + self.name = "" + + # TODO: extended attributes + source.unpack_raw(target - source.cursor) + + def __repr__(self): + return "" % ( + "directory" if self.is_directory else "file", + self.name) + + @property + def children_unsafe(self): + """ + Assuming this is a directory record, this generator yields a record for each child. Use + with caution: at each iteration, the generator assumes that the source cursor has not moved + since the previous child was yielded. For safer behaviour, use :func:`children`. + """ + assert self.is_directory + self._source.seek(self.location, self.length) + _ = self._source.unpack_record() # current directory + _ = self._source.unpack_record() # parent directory + while len(self._source) > 0: + record = self._source.unpack_record() + + if record is None: + self._source.unpack_boundary() + continue + + yield record + + @property + def children(self): + """ + Assuming this is a directory record, this property contains records for its children. + """ + return list(self.children_unsafe) + + @property + def content(self): + """ + Assuming this is a file record, this property contains the file's contents + """ + assert not self.is_directory + if self._content is None: + self._source.seek(self.location, self.length, is_content=True) + self._content = self._source.unpack_all() + return self._content + + + diff --git a/scripts/isoparser/source.py b/scripts/isoparser/source.py new file mode 100644 index 0000000..dfc82cd --- /dev/null +++ b/scripts/isoparser/source.py @@ -0,0 +1,165 @@ +import datetime +import struct +import urllib + +import path_table +import record +import volume_descriptors + + +SECTOR_LENGTH = 2048 + + +class SourceError(Exception): + pass + + +class Source(object): + def __init__(self, cache_content=False, min_fetch=16): + self._buff = None + self._sectors = {} + self.cursor = None + self.cache_content = cache_content + self.min_fetch = min_fetch + + def __len__(self): + return len(self._buff) - self.cursor + + def unpack_raw(self, l): + if l > len(self): + raise SourceError("Source buffer under-run") + data = self._buff[self.cursor:self.cursor + l] + self.cursor += l + return data + + def unpack_all(self): + return self.unpack_raw(len(self)) + + def unpack_boundary(self): + return self.unpack_raw(SECTOR_LENGTH - (self.cursor % SECTOR_LENGTH)) + + def unpack_both(self, st): + a = self.unpack('<'+st) + b = self.unpack('>'+st) + if a != b: + raise SourceError("Both-endian value mismatch") + return a + + def unpack_string(self, l): + return self.unpack_raw(l).rstrip(' ') + + def unpack(self, st): + if st[0] not in '<>': + st = '<' + st + d = struct.unpack(st, self.unpack_raw(struct.calcsize(st))) + if len(st) == 2: + return d[0] + else: + return d + + def unpack_vd_datetime(self): + return self.unpack_raw(17) # TODO + + def unpack_dir_datetime(self): + epoch = datetime.datetime(1970, 1, 1) + date = self.unpack_raw(7) + t = [struct.unpack('= start_sector + n_sectors: + break + self._buff += self._sectors[sector] + elif need_start is None: + need_start = sector + + if need_start is not None: + fetch_needed(start_sector + fetch_sectors - need_start) + + self._buff = self._buff[:length] + + def _fetch(self, sector, count=1): + raise NotImplementedError + + +class FileSource(Source): + def __init__(self, path, **kwargs): + super(FileSource, self).__init__(**kwargs) + self._file = open(path, 'rb') + + def _fetch(self, sector, count=1): + self._file.seek(sector*SECTOR_LENGTH) + return self._file.read(SECTOR_LENGTH*count) + + +class HTTPSource(Source): + def __init__(self, url, **kwargs): + super(HTTPSource, self).__init__(**kwargs) + self._url = url + + def _fetch(self, sector, count=1): + opener = urllib.FancyURLopener() + opener.http_error_206 = lambda *a, **k: None + opener.addheader("Range", "bytes=%d-%d" % ( + SECTOR_LENGTH * sector, + SECTOR_LENGTH * (sector + count) - 1)) + return opener.open(self._url).read() diff --git a/scripts/isoparser/volume_descriptors.py b/scripts/isoparser/volume_descriptors.py new file mode 100644 index 0000000..5023286 --- /dev/null +++ b/scripts/isoparser/volume_descriptors.py @@ -0,0 +1,59 @@ +class VolumeDescriptor(object): + name = None + + def __init__(self, source): + pass + + def __repr__(self): + return "" % self.name + + +class BootVD(VolumeDescriptor): + name = "boot" + + +class PrimaryVD(VolumeDescriptor): + name = "primary" + + def __init__(self, source): + super(PrimaryVD, self).__init__(source) + + _ = source.unpack_raw(1) # unused + self.system_identifier = source.unpack_string(32) + self.volume_identifier = source.unpack_string(32) + _ = source.unpack_raw(8) # unused + self.volume_space_size = source.unpack_both('i') + _ = source.unpack_raw(32) # unused + self.volume_set_size = source.unpack_both('h') + self.volume_seq_num = source.unpack_both('h') + self.logical_block_size = source.unpack_both('h') + self.path_table_size = source.unpack_both('i') + self.path_table_l_loc = source.unpack('i') + self.path_table_opt_m_loc = source.unpack('>i') + self.root_record = source.unpack_record() + self.volume_set_identifier = source.unpack_string(128) + self.publisher_identifier = source.unpack_string(128) + self.data_preparer_identifier = source.unpack_string(128) + self.application_identifier = source.unpack_string(128) + self.copyright_file_identifier = source.unpack_string(38) + self.abstract_file_identifier = source.unpack_string(36) + self.bibliographic_file_identifier = source.unpack_string(37) + self.volume_datetime_created = source.unpack_vd_datetime() + self.volume_datetime_modified = source.unpack_vd_datetime() + self.volume_datetime_expires = source.unpack_vd_datetime() + self.volume_datetime_effective = source.unpack_vd_datetime() + self.file_structure_version = source.unpack('B') + + +class SupplementaryVD(VolumeDescriptor): + name = "supplementary" + + +class PartitionVD(VolumeDescriptor): + name = "partition" + + +class TerminatorVD(VolumeDescriptor): + name = "terminator" \ No newline at end of file diff --git a/scripts/mbusb_cli.py b/scripts/mbusb_cli.py new file mode 100644 index 0000000..6c89070 --- /dev/null +++ b/scripts/mbusb_cli.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: mbusb_cli.py +# Purpose: Module to handle command line options of multibootusb +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import re +import shutil +from . import usb +from . import gen +from .iso import * +from .uninstall_distro import * +from .distro import * +from .syslinux import * +from .install import * + + +def read_input_uninstall(): + response = False + try: + response = int(input("Please enter the number against the distro you need to uninstall: ")) + except ValueError: + print('\nPlease provide valid integer from the above list.\n') + + return response + + +def cli_install_distro(): + ''' + if platform.system() == 'Linux': + if os.getuid() != 0: + exit("You need to have root privileges to run this script.\nPlease try again using 'sudo'. Exiting.") + elif platform.system() == 'Windows': + + if admin.isUserAdmin(): + admin.elevate() + ''' + + print('Starting multibootusb from Command line...') + if usb.is_block(config.usb_disk) is False: + print(config.usb_disk, 'is not a valid device partition...') + exit(1) + elif integrity(config.iso_link) is not True: + print(config.iso_link, ' failed to pass integrity check...') + exit(1) + elif size_not_enough(config.iso_link, config.usb_disk) is True: + print(config.usb_disk, 'does not have enough space...') + else: + prepare_mbusb_host_dir() + extract_cfg_file(config.iso_link) + _distro = distro(iso_cfg_ext_dir(), config.iso_link) + print('Detected distro type is', _distro) + if _distro is not None: + print('\nSelected ISO is :', quote(iso_name(config.iso_link))) + print('Selected target device is:', quote(config.usb_disk), '\n') + print('Please confirm the option.') + print('Y/y/Yes/yes/YES or N/n/No/no/NO') + if read_input_yes() is True: + config.distro = _distro + copy_mbusb_dir_usb(config.usb_disk) + install_progress() + syslinux_distro_dir(config.usb_disk, config.iso_link, _distro) + syslinux_default(config.usb_disk) + update_distro_cfg_files(config.iso_link, config.usb_disk, _distro) + else: + print('Sorry', iso_name(config.iso_link), 'is not supported at the moment\n' + 'Please report tissue at https://github.com/mbusb/multibootusb/issues') + + +def cli_uninstall_distro(): + distro_list = install_distro_list() + if distro_list is not None: + for index, _distro_dir in enumerate(distro_list): + print(index, '--->>', _distro_dir) + user_input = read_input_uninstall() + if user_input is not False: + for index, _distro_dir in enumerate(distro_list): + if index == user_input: + config.uninstall_distro_dir_name = _distro_dir + unin_distro() + else: + print('No distro installed on', config.usb_disk) diff --git a/scripts/mbusb_gui.py b/scripts/mbusb_gui.py new file mode 100644 index 0000000..b80e3bb --- /dev/null +++ b/scripts/mbusb_gui.py @@ -0,0 +1,594 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: mbusb_gui.py +# Purpose: Module to handle multibootusb through gui +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import platform +import sys +import signal +from PyQt5 import QtCore, QtGui, QtWidgets +import subprocess +import time +from scripts.gui.ui_multibootusb import Ui_Dialog +from . import usb +from .gen import * +from .install import * +from .uninstall_distro import * +from .syslinux import * +from .distro import * +from .iso import * +from .imager import Imager, dd_linux, dd_win +from . import persistence +from . import config +from . import admin +from . import qemu +from .update_cfg_file import update_distro_cfg_files + + +class AppGui(qemu.Qemu, Imager, QtWidgets.QDialog, Ui_Dialog): + """ + Main multibootusb GUI manipulation class. + """ + + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + # Main Tab + self.ui.detect_usb.clicked.connect(self.onRefereshClick) + self.ui.close.clicked.connect(self.on_close_Click) + self.ui.browse_iso.clicked.connect(self.browse_iso) + self.ui.comboBox.activated[str].connect(self.onComboChange) + # self.ui.create.clicked.connect(self.update_progress) + self.ui.create.clicked.connect(self.onCreateClick) + self.ui.slider_persistence.valueChanged.connect(self.update_slider_text) + self.ui.uninstall.clicked.connect(self.OnUninstallClick) + + # ISO Imager Tab + self.ui.pushButton.clicked.connect(self.on_Imager_Browse_iso_Click) + self.ui.comboBox_2.activated[str].connect(self.onImagerComboChange) + self.ui.pushbtn_imager_refreshusb.clicked.connect(self.onRefereshClick) + self.ui.imager_close.clicked.connect(self.on_close_Click) + self.ui.imager_write.clicked.connect(self.dd_write) + + # Syslinux Tab + self.ui.install_syslinux.clicked.connect(self.onInstall_syslinuxClick) + self.ui.edit_syslinux.clicked.connect(self.onedit_syslinux) + + # QEMU Tab + self.ui.browse_iso_qemu.clicked.connect(self.on_Qemu_Browse_iso_Click) + self.ui.boot_iso_qemu.clicked.connect(self.on_Qemu_Boot_iso_Click) + self.ui.boot_usb_qemu.clicked.connect(lambda: self.on_Qemu_Boot_usb_Click(str(self.ui.comboBox.currentText()))) + # self.ui.tabWidget.removeTab(3) + + # Update progressbar and status (Main ISO install) + self.progress_thread_install = GuiInstallProgress() + self.progress_thread_install.finished.connect(self.install_syslinux) + self.progress_thread_install.update.connect(self.ui.progressBar.setValue) + self.progress_thread_install.status.connect(self.ui.status.setText) + + # Update progressbar and status (Uninstall from previous install) + self.progress_thread_uninstall = GuiUninstallProgress() + self.progress_thread_uninstall.finished.connect(self.uninstall_sys_file_update) + self.progress_thread_uninstall.update.connect(self.ui.progressBar.setValue) + self.progress_thread_uninstall.status.connect(self.ui.status.setText) + + # Update progressbar and status (dd ISO) + self.progress_thread_dd = DD_Progress() + self.progress_thread_dd.update.connect(self.ui.imager_progressbar.setValue) + self.progress_thread_dd.finished.connect(self.dd_finished) + self.progress_thread_dd.status.connect(self.ui.imager_label_status.setText) + + self.add_device() + prepare_mbusb_host_dir() + + def add_device(self): + """ + Adds list of available USB devices to GUI combobox. + :return: + """ + detected_device = usb.list() + if bool(detected_device): + for device in detected_device: + self.ui.comboBox.addItem(str(device)) + if self.ui.comboBox.currentText(): + self.onComboChange() + + imager_detected_device = self.imager_list_usb(partition=0) + if bool(imager_detected_device): + for disk in imager_detected_device: + self.ui.comboBox_2.addItem(str(disk)) + self.onImagerComboChange() + + def onComboChange(self): + """ + Detects and updates GUI with populated USB device details. + :return: + """ + self.ui.listWidget.clear() + config.usb_disk = str(self.ui.comboBox.currentText()) + config.imager_usb_disk = str(self.ui.comboBox_2.currentText()) + if bool(config.usb_disk): + self.update_gui_oncombobox(config.usb_disk) + else: + print("No USB disk found...") + + def onRefereshClick(self): + """ + Calls function to detect USB devices. + :return: + """ + self.ui.comboBox.clear() + self.ui.comboBox_2.clear() + self.add_device() + + def update_gui_oncombobox(self, usb_disk): + self.usb_details = usb.details(usb_disk) + config.usb_mount = self.usb_details['mount_point'] + self.ui.usb_dev.setText("Drive :: " + usb_disk) + # self.label.setFont(QtGui.QFont("Times",weight=QtGui.QFont.Bold)) + self.ui.usb_vendor.setText("Vendor :: " + self.usb_details['vendor']) + self.ui.usb_model.setText("Model :: " + self.usb_details['model']) + self.ui.usb_size.setText("Total Size :: " + str(usb.bytes2human(self.usb_details['size_total']))) + self.ui.usb_mount.setText("Mount :: " + self.usb_details['mount_point']) + self.update_list_box(usb_disk) + + + def update_list_box(self, usb_disk): + """ + Updates listbox with installed distros on selected USB disk. + :param usb_mount: Selected USB disk from combobox. + :return: + """ + distro_list = install_distro_list() + #sys_cfg_file = os.path.join(str(usb_mount), "multibootusb", "syslinux.cfg") + if distro_list is not None: + self.ui.listWidget.clear() + for name in distro_list: + self.ui.listWidget.addItem(name) + else: + if config.usb_mount == 'No_Mount': + print("UBS disk is not mounted and can't update list widget...") + #QtWidgets.QMessageBox.information(self, 'No Install...', + # 'syslinux.cfg does not exist for updating list widget.') + + def browse_iso(self): + if str(self.ui.lineEdit.text()): + self.ui.lineEdit.clear() + config.iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', '', 'ISO Files (*.iso)')[0] + if config.iso_link: + if platform.system() == "Windows": + if "/" in config.iso_link: + config.iso_link = config.iso_link.strip().replace("/", "\\") + self.ui.lineEdit.insert(str(config.iso_link)) + if os.path.exists(config.iso_link): + clean_iso_cfg_ext_dir( + os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned. + extract_cfg_file(config.iso_link) + if integrity(config.iso_link) is True: + config.distro = distro(iso_cfg_ext_dir(), config.iso_link) + else: + QtWidgets.QMessageBox.warning(self, 'ISO Error.', "ISO integrity failed.\n\n" + "Please check the downloaded ISO.") + if config.distro: + per_availability = persistence.persistence_distro(config.distro, config.usb_disk, config.iso_link)[0] + per_max_size = persistence.persistence_distro(config.distro, config.usb_disk, config.iso_link)[1] + if per_availability is not None: + self.ui.slider_persistence.setEnabled(True) + self.ui.slider_persistence.setTickInterval(10) + self.ui.slider_persistence.setSingleStep(10) + ui_per_max_size = per_max_size / 1024 / 1024 + config.persistence = per_max_size + self.ui.slider_persistence.setMaximum(ui_per_max_size) + print('Persistence Max Size: ', bytes2human(per_max_size)) + else: + print('Persistence is not available for', iso_name(config.iso_link)) + else: + print("File not selected...") + + def update_slider_text(self): + slide_value = self.ui.slider_persistence.value() * 1024 * 1024 + self.ui.label_persistence_value.setText(bytes2human(slide_value)) + config.persistence = slide_value + + def install_syslinux(self): + """ + Function to install syslinux on distro directory and on selected USB disks. + :return: + """ + self.ui.status.setText(str("Installing Syslinux...")) + syslinux_distro_dir(config.usb_disk, config.iso_link, config.distro) + syslinux_default(config.usb_disk) + update_distro_cfg_files(config.iso_link, config.usb_disk, config.distro, config.persistence) + self.update_list_box(config.usb_disk) + if sys.platform.startswith("linux"): + self.ui.status.setText("Sync is in progress...") + os.system('sync') + self.ui.status.clear() + QtWidgets.QMessageBox.information(self, 'Finished...', iso_name(config.iso_link) + ' has been successfully installed.') + + def onInstall_syslinuxClick(self): + """ + Function to install syslinux/extlinux on selected USB disk. + :return: + """ + if platform.system() == "Linux" or platform.system() == "Windows": + + if self.ui.install_sys_all.isChecked() or self.ui.install_sys_only.isChecked(): + print("Installing default syslinux on ", config.usb_disk) + ret = syslinux_default(config.usb_disk) + if ret is True: + if self.ui.install_sys_all.isChecked(): + print("Copying multibootusb directory to " + config.usb_mount) + for dirpath, dirnames, filenames in os.walk(resource_path(os.path.join("tools", "multibootusb"))): + for f in filenames: + print("Copying " + f) + shutil.copy(resource_path(os.path.join(dirpath, f)), os.path.join(self.usb.get_usb(config.usb_disk).mount, "multibootusb")) + QtWidgets.QMessageBox.information(self, 'Install Success...', + 'Syslinux installed successfully on ' + config.usb_disk) + elif ret is False: + QtWidgets.QMessageBox.information(self, 'Install error...', + 'Sorry. Syslinux failed to install on ' + config.usb_disk) + else: + QtWidgets.QMessageBox.information(self, 'No selection...', + 'Please select one of the option from above.') + + def onedit_syslinux(self): + """ + Function to edit main syslinux.cfg file. + :return: + """ + # Function to edit syslinux.cfg file on editors like gedit, notepad etc. + # Suggest me more editor which can be included in to this function. + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + print("Locating " + sys_cfg_file) + editor = '' + if not os.path.exists(sys_cfg_file): + print("syslinux.cfg file not found...") + QtWidgets.QMessageBox.information(self, 'File not found...', 'Sorry. Unable to locate syslinux.cfg file.\n' + 'You can only edit syslinux.cfg file generated by multibootusb.') + else: + if platform.system() == "Linux": + for e in config.editors_linux: + if subprocess.call('which ' + e, shell=True) == 0: + print("Editor found is " + e) + editor = e + break + elif platform.system() == "Windows": + for e in config.editors_win: + if not shutil.which(e) is None: + print("Editor found is " + e) + editor = e + break + if not editor: + QtWidgets.QMessageBox.information(self, 'Editor not found...', + 'Sorry. Installed editor is not supported by multibootusb\n' + 'Edit ' + sys_cfg_file + ' manually.\n') + else: + try: + subprocess.Popen(editor + " '" + sys_cfg_file + "'", shell=True).pid + except OSError: + QtWidgets.QMessageBox.warning(self, 'Error...', + 'Failed to open syslinux.cfg file.\n' + 'Edit syslinux.cfg file manually.\n') + + def OnUninstallClick(self): + """ + Triggers a function to uninstall a selected distro. + :return: + """ + if self.ui.listWidget.currentItem() is None: + print("Please select a distro from the list.") + QtWidgets.QMessageBox.information(self, 'No selection.', 'Please select a distro from the list.') + else: + config.uninstall_distro_dir_name = str(self.ui.listWidget.currentItem().text()).strip() + reply = QtWidgets.QMessageBox.question(self, "Review selection...", + "Are you sure to uninstall " + config.uninstall_distro_dir_name, + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + + if not os.path.exists(os.path.join(config.usb_mount, 'multibootusb', config.uninstall_distro_dir_name)): + print("Distro install directory not found. Just updating syslinux.cfg file.") + update_sys_cfg_file() + #self.uninstall.update_sys_cfg_file() + else: + self.progress_thread_uninstall.start() + + def uninstall_sys_file_update(self): + """ + Function to remove and update uninstall distro text. + :return: + """ + update_sys_cfg_file() + self.update_list_box(config.usb_mount) + if sys.platform.startswith("linux"): + self.ui.status.setText("Sync is in progress...") + os.system('sync') + self.ui.status.clear() + QtWidgets.QMessageBox.information(self, 'Uninstall Complete...', + config.uninstall_distro_dir_name + ' has been successfully removed.') + + def onCreateClick(self): + """ + Main function to create bootable USB disk. + :param usb_disk: ComboBox text as detected USB disk. + :param iso_link: LineEdit text as selected ISO link. + :return: + """ + if not config.usb_disk: + QtWidgets.QMessageBox.information(self, "No Device...", + "No USB device found.\n\nInsert USB and use Refresh USB button to detect USB.") + elif not config.iso_link: + QtWidgets.QMessageBox.information(self, "No ISO...", "No ISO found.\n\nPlease use step 2 to choose an ISO.") + elif usb.details(config.usb_disk)['mount_point'] == 'No_Mount': + QtWidgets.QMessageBox.information(self, "No Mount...", "USB disk is not mounted.\n" + "Please mount USB disk and press refresh USB button.") + else: + if not integrity(config.iso_link) is True: + QtWidgets.QMessageBox.information(self, "Integrity...", + "ISO integrity failed.\n\nPlease check the downloaded ISO.") + else: + if os.path.exists(config.iso_link): + self.ui.lineEdit.clear() + if config.distro: + print("Distro type detected is ", config.distro) + copy_mbusb_dir_usb(config.usb_disk) + if not os.path.exists(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link))): + install_size = iso_size(config.iso_link) + config.persistence + # print("Persistence choosen is " + str(persistence_size) + " MB") + if install_size >= disk_usage(config.usb_mount).free: + QtWidgets.QMessageBox.information(self, "No Space.", "No space available on " + + config.usb_disk) + else: + reply = QtWidgets.QMessageBox.question(self, 'Review selection...', + 'Selected USB disk:: %s\n' % config.usb_disk + + 'USB mount point:: %s\n' % config.usb_mount + + 'Selected distro:: %s\n\n' % iso_name(config.iso_link) + + 'Would you like to proceed for installation?', + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + self.ui.slider_persistence.setEnabled(False) + self.progress_thread_install.start() + + else: + QtWidgets.QMessageBox.information(self, 'Already Exist...', + os.path.basename(config.iso_link) + ' is already installed.') + else: + QtWidgets.QMessageBox.information(self, 'No support...', + 'Sorry.\n' + os.path.basename(config.iso_link) + + ' is not supported at the moment\n' + 'Please email this issue to feedback.multibootusb@gmail.com') + + # Added to refresh usb disk remaining size after distro installation + # self.update_gui_usb_info() + + def dd_finished(self): + """ + Re-enable the blocked widgets for newer use. + :return: + """ + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.comboBox_2.setEnabled(True) + self.ui.pushButton.setEnabled(True) + self.ui.imager_bootable.setText("Bootable ISO :: ") + self.ui.imager_iso_size.setText("ISO Size :: ") + QtWidgets.QMessageBox.information(self, 'Finished...', 'ISO has been written to USB disk.\nPlease reboot your ' + 'system to boot from USB.') + + def dd_start(self): + """ + Function to block the widgets under ISO Imager tab... + :return: + """ + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.lineEdit_3.clear() + self.ui.pushButton.setEnabled(False) + self.ui.comboBox_2.setEnabled(False) + self.ui.pushbtn_imager_refreshusb.setEnabled(False) + status_text = ("Writing " + os.path.basename(config.imager_iso_link) + "" + " to " + "" + + config.imager_usb_disk_selected + "") + self.ui.imager_label_status.setText(status_text) + + def dd_quit(self): + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.comboBox_2.setEnabled(True) + self.ui.pushButton.setEnabled(True) + QtWidgets.QMessageBox.information(self, 'Failed!', 'Writing ISO failed.') + + def dd_write(self): + if not config.imager_usb_disk: + QtWidgets.QMessageBox.information(self, 'No USB...', 'Please Insert USB disk and rerun multibootusb.') + elif not config.imager_iso_link: + QtWidgets.QMessageBox.information(self, 'No ISO...', 'Please select an ISO.') + else: + usb_disk_size = int(self.imager_usb_detail(config.imager_usb_disk, partition=0).total_size) + self.iso_size = os.path.getsize(config.imager_iso_link) + if self.iso_size >= usb_disk_size: + QtWidgets.QMessageBox.information(self, "No Space.", os.path.basename(config.imager_iso_link) + + " size is larger than the size of " + config.imager_usb_disk) + else: + reply = QtWidgets.QMessageBox.question \ + (self, 'Review selection...', + 'Selected USB disk:: %s\n' % config.imager_usb_disk + + 'Selected distro:: %s\n\n' % os.path.basename(config.imager_iso_link) + + 'Would you like to proceed for installation?', + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + self.dd_start() + self.progress_thread_dd.start() + + def on_close_Click(self): + """ + Closes main GUI. + :return: + """ + self.close() + + def closeEvent(self, event): + """ + To capture the main close event. + :param event: Close event. + :return: + """ + reply = QtWidgets.QMessageBox.question(self, 'Exit MultiBootUSB...', + "Do you really want to quit multibootusb?", QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + print("Closing multibootusb...") + event.accept() + sys.exit(0) + else: + print("Close event cancelled.") + event.ignore() + + +class GuiInstallProgress(QtCore.QThread): + """ + Update GUI thread during install. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + + def __del__(self): + self.wait() + + def run(self): + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + self.thread = GenericThread(install_progress) + status_text = "" + self.thread.start() + while self.thread.isRunning(): + if config.status_text.strip(): + config.status_text = config.status_text.replace(install_dir + "/", "Extracting ") + self.update.emit(config.percentage) + self.status.emit(config.status_text) + if not self.thread.isFinished() and config.percentage == 100: + config.status_text = "" + self.status.emit("Please wait...") + + self.update.emit(100) + self.update.emit(0) + + self.status.emit("Installing boot loader...") + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + print("Distro extraction completed...") + + return + + +class GuiUninstallProgress(QtCore.QThread): + """ + Update GUI thread during uninstall. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + self.thread = GenericThread(uninstall_progress) + + def __del__(self): + self.wait() + + def run(self): + self.thread.start() + while self.thread.isRunning(): + self.update.emit(config.percentage) + self.status.emit(config.status_text) + if not self.thread.isFinished() and config.percentage == 100: + config.status_text = "Please wait..." + self.update.emit(100) + self.update.emit(0) + config.percentage = 0 + self.status.emit("Updating syslinux.cfg file...") + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + print("Distro uninstall is complete...") + + return + + +class DD_Progress(QtCore.QThread): + """ + Update GUI progress bar without blocking rest of GUI element when dd process is in progress. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + + if platform.system() == 'Linux': + self.thread = GenericThread(dd_linux) + elif platform.system() == 'Windows': + self.thread = GenericThread(dd_win) + + def __del__(self): + self.wait() + + def run(self): + self.thread.start() + while self.thread.isRunning(): + if config.imager_percentage: + self.update.emit(config.imager_percentage) + if not self.thread.isFinished() and config.percentage == 100: + config.imager_status_text = "" + self.status.emit("Please wait...") + + self.update.emit(100) + self.update.emit(0) + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + return + + +class GenericThread(QtCore.QThread): + + def __init__(self, function, *args, **kwargs): + QtCore.QThread.__init__(self) + self.function = function + self.args = args + self.kwargs = kwargs + + def __del__(self): + self.wait() + + def run(self): + self.function(*self.args, **self.kwargs) + return + +def main_gui(): + app = QtWidgets.QApplication(sys.argv) + window = AppGui() + ui = Ui_Dialog() + window.show() + window.setWindowTitle("MultiBootUSB - " + mbusb_version()) + window.setWindowIcon(QtGui.QIcon(resource_path(os.path.join("data", "tools", "multibootusb.png")))) + sys.exit(app.exec_()) diff --git a/scripts/persistence.py b/scripts/persistence.py new file mode 100644 index 0000000..34ef9fb --- /dev/null +++ b/scripts/persistence.py @@ -0,0 +1,102 @@ +#!/usr/bin/python2.7 +# -*- coding: utf-8 -*- +# Name: persistence.py +# Purpose: Module to deal with persistence of a selected distro. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import sys +import os +import platform +import tarfile +import subprocess +from . import usb +from . import iso +from . import gen +from . import config + + +def persistence_distro(distro, usb_disk, iso_link): + """ + Function to detect if distro can have persistence option. + :param distro: Detected distro name. + :return: Distro name as string or None otherwise. + """ + iso_size = iso.iso_size(iso_link) + fat_max_size = (4096 * 1024 * 1024) + usb_details = usb.details(usb_disk) + usb_sf = usb_details['file_system'] + usb_free_size = usb_details['size_free'] + if usb_sf == 'vfat' or 'FAT32': + if usb_free_size > fat_max_size: + _max_size = fat_max_size + else: + _max_size = usb_free_size + else: + _max_size = usb_free_size + if distro == "ubuntu": + print("Persistence option is available.") + return "ubuntu", _max_size + else: + return None, None + # FIXME to get debian and fedora persistence workable... + # Able to add successfully but unable to keep persistence data. + ''' + elif distro == "debian": + print "Persistence option is available." + return "debian" + elif distro == "fedora": + print "Persistence option is available." + return "fedora" + ''' + + +def create_persistence(): + if config.distro == "ubuntu": + fs_name = 'casper-rw' + elif config.distro == 'debian': + fs_name = 'live-rw' + + persistence = config.persistence / 1024 / 1024 + + if platform.system() == 'Linux': + mkfs = 'mkfs.ext3' + dd = 'dd' + persistence_mkfs_cmd = mkfs + ' -F ' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), + fs_name) + elif platform.system() == 'Windows': + mkfs = gen.resource_path(os.path.join("data", "tools", "mkfs", "mke2fs.exe")) + dd = gen.resource_path(os.path.join("data", "tools", "dd", "dd.exe")) + persistence_mkfs_cmd = 'echo y|' + mkfs + ' -b 1024 -L ' + fs_name + ' ' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), fs_name) + + persistence_dd_cmd = dd + ' if=/dev/zero ' \ + 'of=' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), fs_name) +\ + ' bs=1M count=' + str(int(persistence)) + + print('Executing ==>', persistence_dd_cmd) + config.status_text = 'Creating persistence file...' + + if subprocess.call(persistence_dd_cmd, shell=True) == 0: + print("\nSuccessfully created persistence file...\n") + + print('Executing ==>', persistence_mkfs_cmd) + config.status_text = 'Applying filesystem to persistence file...' + if subprocess.call(persistence_mkfs_cmd, shell=True) == 0: + print("\nSuccessfully applied filesystem...\n") + + +def extract_file(file_path, install_dir): + """ + Function to extract persistence files to distro install directory. + :param file_path: Path to persistence file. + :param install_dir: Path to distro install directory. + :return: + """ + tar = tarfile.open(file_path, "r:bz2") + tar.extractall(install_dir) + tar.close() + diff --git a/scripts/progressbar/__init__.py b/scripts/progressbar/__init__.py new file mode 100644 index 0000000..fbab744 --- /dev/null +++ b/scripts/progressbar/__init__.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Text progress bar library for Python. + +A text progress bar is typically used to display the progress of a long +running operation, providing a visual cue that processing is underway. + +The ProgressBar class manages the current progress, and the format of the line +is given by a number of widgets. A widget is an object that may display +differently depending on the state of the progress bar. There are three types +of widgets: + - a string, which always shows itself + + - a ProgressBarWidget, which may return a different value every time its + update method is called + + - a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it + expands to fill the remaining width of the line. + +The progressbar module is very easy to use, yet very powerful. It will also +automatically enable features like auto-resizing when the system supports it. +""" + +__author__ = 'Nilton Volpato' +__author_email__ = 'first-name dot last-name @ gmail.com' +__date__ = '2011-05-14' +__version__ = '2.3' + +from .compat import * +from .widgets import * +from .progressbar import * diff --git a/scripts/progressbar/compat.py b/scripts/progressbar/compat.py new file mode 100644 index 0000000..a39f4a1 --- /dev/null +++ b/scripts/progressbar/compat.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Compatibility methods and classes for the progressbar module.""" + + +# Python 3.x (and backports) use a modified iterator syntax +# This will allow 2.x to behave with 3.x iterators +try: + next +except NameError: + def next(iter): + try: + # Try new style iterators + return iter.__next__() + except AttributeError: + # Fallback in case of a "native" iterator + return iter.next() + + +# Python < 2.5 does not have "any" +try: + any +except NameError: + def any(iterator): + for item in iterator: + if item: return True + return False diff --git a/scripts/progressbar/progressbar.py b/scripts/progressbar/progressbar.py new file mode 100644 index 0000000..7ab79b6 --- /dev/null +++ b/scripts/progressbar/progressbar.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Main ProgressBar class.""" + +from __future__ import division + +import math +import os +import signal +import sys +import time + +try: + from fcntl import ioctl + from array import array + import termios +except ImportError: + pass + +from .compat import * # for: any, next +from . import widgets + + +class UnknownLength: pass + + +class ProgressBar(object): + """The ProgressBar class which updates and prints the bar. + + A common way of using it is like: + >>> pbar = ProgressBar().start() + >>> for i in range(100): + ... # do something + ... pbar.update(i+1) + ... + >>> pbar.finish() + + You can also use a ProgressBar as an iterator: + >>> progress = ProgressBar() + >>> for i in progress(some_iterable): + ... # do something + ... + + Since the progress bar is incredibly customizable you can specify + different widgets of any type in any order. You can even write your own + widgets! However, since there are already a good number of widgets you + should probably play around with them before moving on to create your own + widgets. + + The term_width parameter represents the current terminal width. If the + parameter is set to an integer then the progress bar will use that, + otherwise it will attempt to determine the terminal width falling back to + 80 columns if the width cannot be determined. + + When implementing a widget's update method you are passed a reference to + the current progress bar. As a result, you have access to the + ProgressBar's methods and attributes. Although there is nothing preventing + you from changing the ProgressBar you should treat it as read only. + + Useful methods and attributes include (Public API): + - currval: current progress (0 <= currval <= maxval) + - maxval: maximum (and final) value + - finished: True if the bar has finished (reached 100%) + - start_time: the time when start() method of ProgressBar was called + - seconds_elapsed: seconds elapsed since start_time and last call to + update + - percentage(): progress in percent [0..100] + """ + + __slots__ = ('currval', 'fd', 'finished', 'last_update_time', + 'left_justify', 'maxval', 'next_update', 'num_intervals', + 'poll', 'seconds_elapsed', 'signal_set', 'start_time', + 'term_width', 'update_interval', 'widgets', '_time_sensitive', + '__iterable') + + _DEFAULT_MAXVAL = 100 + _DEFAULT_TERMSIZE = 80 + _DEFAULT_WIDGETS = [widgets.Percentage(), ' ', widgets.Bar()] + + def __init__(self, maxval=None, widgets=None, term_width=None, poll=1, + left_justify=True, fd=sys.stderr): + """Initializes a progress bar with sane defaults.""" + + # Don't share a reference with any other progress bars + if widgets is None: + widgets = list(self._DEFAULT_WIDGETS) + + self.maxval = maxval + self.widgets = widgets + self.fd = fd + self.left_justify = left_justify + + self.signal_set = False + if term_width is not None: + self.term_width = term_width + else: + try: + self._handle_resize() + signal.signal(signal.SIGWINCH, self._handle_resize) + self.signal_set = True + except (SystemExit, KeyboardInterrupt): raise + except: + self.term_width = self._env_size() + + self.__iterable = None + self._update_widgets() + self.currval = 0 + self.finished = False + self.last_update_time = None + self.poll = poll + self.seconds_elapsed = 0 + self.start_time = None + self.update_interval = 1 + self.next_update = 0 + + + def __call__(self, iterable): + """Use a ProgressBar to iterate through an iterable.""" + + try: + self.maxval = len(iterable) + except: + if self.maxval is None: + self.maxval = UnknownLength + + self.__iterable = iter(iterable) + return self + + + def __iter__(self): + return self + + + def __next__(self): + try: + value = next(self.__iterable) + if self.start_time is None: + self.start() + else: + self.update(self.currval + 1) + return value + except StopIteration: + if self.start_time is None: + self.start() + self.finish() + raise + + + # Create an alias so that Python 2.x won't complain about not being + # an iterator. + next = __next__ + + + def _env_size(self): + """Tries to find the term_width from the environment.""" + + return int(os.environ.get('COLUMNS', self._DEFAULT_TERMSIZE)) - 1 + + + def _handle_resize(self, signum=None, frame=None): + """Tries to catch resize signals sent from the terminal.""" + + h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] + self.term_width = w + + + def percentage(self): + """Returns the progress as a percentage.""" + if self.currval >= self.maxval: + return 100.0 + return (self.currval * 100.0 / self.maxval) if self.maxval else 100.00 + + percent = property(percentage) + + + def _format_widgets(self): + result = [] + expanding = [] + width = self.term_width + + for index, widget in enumerate(self.widgets): + if isinstance(widget, widgets.WidgetHFill): + result.append(widget) + expanding.insert(0, index) + else: + widget = widgets.format_updatable(widget, self) + result.append(widget) + width -= len(widget) + + count = len(expanding) + while count: + portion = max(int(math.ceil(width * 1. / count)), 0) + index = expanding.pop() + count -= 1 + + widget = result[index].update(self, portion) + width -= len(widget) + result[index] = widget + + return result + + + def _format_line(self): + """Joins the widgets and justifies the line.""" + + widgets = ''.join(self._format_widgets()) + + if self.left_justify: return widgets.ljust(self.term_width) + else: return widgets.rjust(self.term_width) + + + def _need_update(self): + """Returns whether the ProgressBar should redraw the line.""" + if self.currval >= self.next_update or self.finished: return True + + delta = time.time() - self.last_update_time + return self._time_sensitive and delta > self.poll + + + def _update_widgets(self): + """Checks all widgets for the time sensitive bit.""" + + self._time_sensitive = any(getattr(w, 'TIME_SENSITIVE', False) + for w in self.widgets) + + + def update(self, value=None): + """Updates the ProgressBar to a new value.""" + + if value is not None and value is not UnknownLength: + if (self.maxval is not UnknownLength + and not 0 <= value <= self.maxval): + + raise ValueError('Value out of range') + + self.currval = value + + + if not self._need_update(): return + if self.start_time is None: + raise RuntimeError('You must call "start" before calling "update"') + + now = time.time() + self.seconds_elapsed = now - self.start_time + self.next_update = self.currval + self.update_interval + self.fd.write(self._format_line() + '\r') + self.fd.flush() + self.last_update_time = now + + + def start(self): + """Starts measuring time, and prints the bar at 0%. + + It returns self so you can use it like this: + >>> pbar = ProgressBar().start() + >>> for i in range(100): + ... # do something + ... pbar.update(i+1) + ... + >>> pbar.finish() + """ + + if self.maxval is None: + self.maxval = self._DEFAULT_MAXVAL + + self.num_intervals = max(100, self.term_width) + self.next_update = 0 + + if self.maxval is not UnknownLength: + if self.maxval < 0: raise ValueError('Value out of range') + self.update_interval = self.maxval / self.num_intervals + + + self.start_time = self.last_update_time = time.time() + self.update(0) + + return self + + + def finish(self): + """Puts the ProgressBar bar in the finished state.""" + + if self.finished: + return + self.finished = True + self.update(self.maxval) + self.fd.write('\n') + if self.signal_set: + signal.signal(signal.SIGWINCH, signal.SIG_DFL) diff --git a/scripts/progressbar/widgets.py b/scripts/progressbar/widgets.py new file mode 100644 index 0000000..6434ad5 --- /dev/null +++ b/scripts/progressbar/widgets.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Default ProgressBar widgets.""" + +from __future__ import division + +import datetime +import math + +try: + from abc import ABCMeta, abstractmethod +except ImportError: + AbstractWidget = object + abstractmethod = lambda fn: fn +else: + AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) + + +def format_updatable(updatable, pbar): + if hasattr(updatable, 'update'): return updatable.update(pbar) + else: return updatable + + +class Widget(AbstractWidget): + """The base class for all widgets. + + The ProgressBar will call the widget's update value when the widget should + be updated. The widget's size may change between calls, but the widget may + display incorrectly if the size changes drastically and repeatedly. + + The boolean TIME_SENSITIVE informs the ProgressBar that it should be + updated more often because it is time sensitive. + """ + + TIME_SENSITIVE = False + __slots__ = () + + @abstractmethod + def update(self, pbar): + """Updates the widget. + + pbar - a reference to the calling ProgressBar + """ + + +class WidgetHFill(Widget): + """The base class for all variable width widgets. + + This widget is much like the \\hfill command in TeX, it will expand to + fill the line. You can use more than one in the same line, and they will + all have the same width, and together will fill the line. + """ + + @abstractmethod + def update(self, pbar, width): + """Updates the widget providing the total width the widget must fill. + + pbar - a reference to the calling ProgressBar + width - The total width the widget must fill + """ + + +class Timer(Widget): + """Widget which displays the elapsed seconds.""" + + __slots__ = ('format_string',) + TIME_SENSITIVE = True + + def __init__(self, format='Elapsed Time: %s'): + self.format_string = format + + @staticmethod + def format_time(seconds): + """Formats time as the string "HH:MM:SS".""" + + return str(datetime.timedelta(seconds=int(seconds))) + + + def update(self, pbar): + """Updates the widget to show the elapsed time.""" + + return self.format_string % self.format_time(pbar.seconds_elapsed) + + +class ETA(Timer): + """Widget which attempts to estimate the time of arrival.""" + + TIME_SENSITIVE = True + + def update(self, pbar): + """Updates the widget to show the ETA or total time when finished.""" + + if pbar.currval == 0: + return 'ETA: --:--:--' + elif pbar.finished: + return 'Time: %s' % self.format_time(pbar.seconds_elapsed) + else: + elapsed = pbar.seconds_elapsed + eta = elapsed * pbar.maxval / pbar.currval - elapsed + return 'ETA: %s' % self.format_time(eta) + + +class AdaptiveETA(Timer): + """Widget which attempts to estimate the time of arrival. + + Uses a weighted average of two estimates: + 1) ETA based on the total progress and time elapsed so far + 2) ETA based on the progress as per the last 10 update reports + + The weight depends on the current progress so that to begin with the + total progress is used and at the end only the most recent progress is + used. + """ + + TIME_SENSITIVE = True + NUM_SAMPLES = 10 + + def _update_samples(self, currval, elapsed): + sample = (currval, elapsed) + if not hasattr(self, 'samples'): + self.samples = [sample] * (self.NUM_SAMPLES + 1) + else: + self.samples.append(sample) + return self.samples.pop(0) + + def _eta(self, maxval, currval, elapsed): + return elapsed * maxval / float(currval) - elapsed + + def update(self, pbar): + """Updates the widget to show the ETA or total time when finished.""" + if pbar.currval == 0: + return 'ETA: --:--:--' + elif pbar.finished: + return 'Time: %s' % self.format_time(pbar.seconds_elapsed) + else: + elapsed = pbar.seconds_elapsed + currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) + eta = self._eta(pbar.maxval, pbar.currval, elapsed) + if pbar.currval > currval1: + etasamp = self._eta(pbar.maxval - currval1, + pbar.currval - currval1, + elapsed - elapsed1) + weight = (pbar.currval / float(pbar.maxval)) ** 0.5 + eta = (1 - weight) * eta + weight * etasamp + return 'ETA: %s' % self.format_time(eta) + + +class FileTransferSpeed(Widget): + """Widget for showing the transfer speed (useful for file transfers).""" + + FORMAT = '%6.2f %s%s/s' + PREFIXES = ' kMGTPEZY' + __slots__ = ('unit',) + + def __init__(self, unit='B'): + self.unit = unit + + def update(self, pbar): + """Updates the widget with the current SI prefixed speed.""" + + if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 + scaled = power = 0 + else: + speed = pbar.currval / pbar.seconds_elapsed + power = int(math.log(speed, 1000)) + scaled = speed / 1000.**power + + return self.FORMAT % (scaled, self.PREFIXES[power], self.unit) + + +class AnimatedMarker(Widget): + """An animated marker for the progress bar which defaults to appear as if + it were rotating. + """ + + __slots__ = ('markers', 'curmark') + + def __init__(self, markers='|/-\\'): + self.markers = markers + self.curmark = -1 + + def update(self, pbar): + """Updates the widget to show the next marker or the first marker when + finished""" + + if pbar.finished: return self.markers[0] + + self.curmark = (self.curmark + 1) % len(self.markers) + return self.markers[self.curmark] + +# Alias for backwards compatibility +RotatingMarker = AnimatedMarker + + +class Counter(Widget): + """Displays the current count.""" + + __slots__ = ('format_string',) + + def __init__(self, format='%d'): + self.format_string = format + + def update(self, pbar): + return self.format_string % pbar.currval + + +class Percentage(Widget): + """Displays the current percentage as a number with a percent sign.""" + + def update(self, pbar): + return '%3d%%' % pbar.percentage() + + +class FormatLabel(Timer): + """Displays a formatted label.""" + + mapping = { + 'elapsed': ('seconds_elapsed', Timer.format_time), + 'finished': ('finished', None), + 'last_update': ('last_update_time', None), + 'max': ('maxval', None), + 'seconds': ('seconds_elapsed', None), + 'start': ('start_time', None), + 'value': ('currval', None) + } + + __slots__ = ('format_string',) + def __init__(self, format): + self.format_string = format + + def update(self, pbar): + context = {} + for name, (key, transform) in self.mapping.items(): + try: + value = getattr(pbar, key) + + if transform is None: + context[name] = value + else: + context[name] = transform(value) + except: pass + + return self.format_string % context + + +class SimpleProgress(Widget): + """Returns progress as a count of the total (e.g.: "5 of 47").""" + + __slots__ = ('sep',) + + def __init__(self, sep=' of '): + self.sep = sep + + def update(self, pbar): + return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) + + +class Bar(WidgetHFill): + """A progress bar which stretches to fill the line.""" + + __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') + + def __init__(self, marker='#', left='|', right='|', fill=' ', + fill_left=True): + """Creates a customizable progress bar. + + marker - string or updatable object to use as a marker + left - string or updatable object to use as a left border + right - string or updatable object to use as a right border + fill - character to use for the empty part of the progress bar + fill_left - whether to fill from the left or the right + """ + self.marker = marker + self.left = left + self.right = right + self.fill = fill + self.fill_left = fill_left + + + def update(self, pbar, width): + """Updates the progress bar and its subcomponents.""" + + left, marked, right = (format_updatable(i, pbar) for i in + (self.left, self.marker, self.right)) + + width -= len(left) + len(right) + # Marked must *always* have length of 1 + if pbar.maxval: + marked *= int(pbar.currval / pbar.maxval * width) + else: + marked = '' + + if self.fill_left: + return '%s%s%s' % (left, marked.ljust(width, self.fill), right) + else: + return '%s%s%s' % (left, marked.rjust(width, self.fill), right) + + +class ReverseBar(Bar): + """A bar which has a marker which bounces from side to side.""" + + def __init__(self, marker='#', left='|', right='|', fill=' ', + fill_left=False): + """Creates a customizable progress bar. + + marker - string or updatable object to use as a marker + left - string or updatable object to use as a left border + right - string or updatable object to use as a right border + fill - character to use for the empty part of the progress bar + fill_left - whether to fill from the left or the right + """ + self.marker = marker + self.left = left + self.right = right + self.fill = fill + self.fill_left = fill_left + + +class BouncingBar(Bar): + def update(self, pbar, width): + """Updates the progress bar and its subcomponents.""" + + left, marker, right = (format_updatable(i, pbar) for i in + (self.left, self.marker, self.right)) + + width -= len(left) + len(right) + + if pbar.finished: return '%s%s%s' % (left, width * marker, right) + + position = int(pbar.currval % (width * 2 - 1)) + if position > width: position = width * 2 - position + lpad = self.fill * (position - 1) + rpad = self.fill * (width - len(marker) - len(lpad)) + + # Swap if we want to bounce the other way + if not self.fill_left: rpad, lpad = lpad, rpad + + return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) diff --git a/scripts/pyudev/.gitattributes b/scripts/pyudev/.gitattributes new file mode 100644 index 0000000..292ee48 --- /dev/null +++ b/scripts/pyudev/.gitattributes @@ -0,0 +1,4 @@ +* text + +*.py diff=python +*.html diff=html diff --git a/scripts/pyudev/.gitignore b/scripts/pyudev/.gitignore new file mode 100644 index 0000000..82acc9c --- /dev/null +++ b/scripts/pyudev/.gitignore @@ -0,0 +1,39 @@ +# Python bytecode +*.pyc + +# distutils/distribute metadata and outputs +pyudev.egg-info/* +build/* +dist/* + +# Sphinx outputs +doc/_build/* + +# Test environments and results +.tox/* +*-tests.xml + +# Rope configuration +.ropeproject/* + +# Vagrant files +.vagrant + +# coverage files +htmlcov +.coverage + +# pyreverse files +_pyreverse + +# hypothesis files +.hypothesis + +# vim +*.swp + +# zip +*.gz + +# pytest +.cache diff --git a/scripts/pyudev/.travis.yml b/scripts/pyudev/.travis.yml new file mode 100644 index 0000000..754afea --- /dev/null +++ b/scripts/pyudev/.travis.yml @@ -0,0 +1,16 @@ +branches: + only: + - master + - develop-0.22 +language: python +python: + - "2.7" + - "3.4" + - "pypy" +before_install: + - sudo apt-get update -qq + - sudo apt-get install -y libudev-dev +install: + - pip install . + - pip install -r requirements.txt +script: py.test --junitxml=tests.xml --enable-privileged -rfEsxX -v diff --git a/scripts/pyudev/__init__.py b/scripts/pyudev/__init__.py new file mode 100644 index 0000000..e757b74 --- /dev/null +++ b/scripts/pyudev/__init__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev + ====== + + A binding to libudev. + + The :class:`Context` provides the connection to the udev device database + and enumerates devices. Individual devices are represented by the + :class:`Device` class. + + Device monitoring is provided by :class:`Monitor` and + :class:`MonitorObserver`. With :mod:`pyudev.pyqt4`, :mod:`pyudev.pyside`, + :mod:`pyudev.glib` and :mod:`pyudev.wx` device monitoring can be integrated + into the event loop of various GUI toolkits. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ._errors import DeviceNotFoundAtPathError +from ._errors import DeviceNotFoundByFileError +from ._errors import DeviceNotFoundByNameError +from ._errors import DeviceNotFoundByNumberError +from ._errors import DeviceNotFoundError +from ._errors import DeviceNotFoundInEnvironmentError + +from .device import Attributes +from .device import Device +from .device import Devices +from .device import Tags + +from .discover import DeviceFileHypothesis +from .discover import DeviceNameHypothesis +from .discover import DeviceNumberHypothesis +from .discover import DevicePathHypothesis +from .discover import Discovery + +from .core import Context +from .core import Enumerator + +from .monitor import Monitor +from .monitor import MonitorObserver + +from .version import __version__ +from .version import __version_info__ + +from ._util import udev_version diff --git a/scripts/pyudev/_compat.py b/scripts/pyudev/_compat.py new file mode 100644 index 0000000..f21eb98 --- /dev/null +++ b/scripts/pyudev/_compat.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2011 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._compat + ============== + + Compatibility for Python versions, that lack certain functions. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from subprocess import Popen, CalledProcessError, PIPE + + +def check_output(command): + """ + Compatibility with :func:`subprocess.check_output` from Python 2.7 and + upwards. + """ + proc = Popen(command, stdout=PIPE) + output = proc.communicate()[0] + if proc.returncode != 0: + raise CalledProcessError(proc.returncode, command) + return output diff --git a/scripts/pyudev/_ctypeslib/__init__.py b/scripts/pyudev/_ctypeslib/__init__.py new file mode 100644 index 0000000..cb5ed88 --- /dev/null +++ b/scripts/pyudev/_ctypeslib/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib + ================= + + Wrappers for libraries. + + .. moduleauthor:: mulhern +""" + +from . import libc +from . import libudev diff --git a/scripts/pyudev/_ctypeslib/_errorcheckers.py b/scripts/pyudev/_ctypeslib/_errorcheckers.py new file mode 100644 index 0000000..f4de520 --- /dev/null +++ b/scripts/pyudev/_ctypeslib/_errorcheckers.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib._errorcheckers + ================================ + + Error checkers for ctypes wrappers. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + + +import os +import errno +from ctypes import get_errno + + +ERRNO_EXCEPTIONS = { + errno.ENOMEM: MemoryError, + errno.EOVERFLOW: OverflowError, + errno.EINVAL: ValueError +} + + +def exception_from_errno(errnum): + """Create an exception from ``errnum``. + + ``errnum`` is an integral error number. + + Return an exception object appropriate to ``errnum``. + + """ + exception = ERRNO_EXCEPTIONS.get(errnum) + errorstr = os.strerror(errnum) + if exception is not None: + return exception(errorstr) + else: + return EnvironmentError(errnum, errorstr) + + +def check_negative_errorcode(result, func, *args): + """Error checker for funtions, which return negative error codes. + + If ``result`` is smaller than ``0``, it is interpreted as negative error + code, and an appropriate exception is raised: + + - ``-ENOMEM`` raises a :exc:`~exceptions.MemoryError` + - ``-EOVERFLOW`` raises a :exc:`~exceptions.OverflowError` + - all other error codes raise :exc:`~exceptions.EnvironmentError` + + If result is greater or equal to ``0``, it is returned unchanged. + + """ + if result < 0: + # udev returns the *negative* errno code at this point + errnum = -result + raise exception_from_errno(errnum) + else: + return result + + +def check_errno_on_nonzero_return(result, func, *args): + """Error checker to check the system ``errno`` as returned by + :func:`ctypes.get_errno()`. + + If ``result`` is not ``0``, an exception according to this errno is raised. + Otherwise nothing happens. + + """ + if result != 0: + errnum = get_errno() + if errnum != 0: + raise exception_from_errno(errnum) + return result + + +def check_errno_on_null_pointer_return(result, func, *args): + """Error checker to check the system ``errno`` as returned by + :func:`ctypes.get_errno()`. + + If ``result`` is a null pointer, an exception according to this errno is + raised. Otherwise nothing happens. + + """ + # pylint: disable=invalid-name + if not result: + errnum = get_errno() + if errnum != 0: + raise exception_from_errno(errnum) + return result diff --git a/scripts/pyudev/_ctypeslib/libc.py b/scripts/pyudev/_ctypeslib/libc.py new file mode 100644 index 0000000..112c73b --- /dev/null +++ b/scripts/pyudev/_ctypeslib/libc.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib.libc + ====================== + + Wrappers for libc. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import c_int + +from ._errorcheckers import check_errno_on_nonzero_return + + +fd_pair = c_int * 2 + + +SIGNATURES = dict( + pipe2=([fd_pair, c_int], c_int), +) + +ERROR_CHECKERS = dict( + pipe2=check_errno_on_nonzero_return, +) diff --git a/scripts/pyudev/_ctypeslib/libudev.py b/scripts/pyudev/_ctypeslib/libudev.py new file mode 100644 index 0000000..901b124 --- /dev/null +++ b/scripts/pyudev/_ctypeslib/libudev.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._ctypeslib.libudev + ========================= + + Wrapper types for libudev. Use ``libudev`` attribute to access libudev + functions. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_uint +from ctypes import c_ulonglong +from ctypes import CDLL +from ctypes import Structure +from ctypes import POINTER + +from ctypes.util import find_library + +from ._errorcheckers import check_errno_on_nonzero_return +from ._errorcheckers import check_errno_on_null_pointer_return +from ._errorcheckers import check_negative_errorcode + + +class udev(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_p = POINTER(udev) # pylint: disable=invalid-name + + +class udev_enumerate(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_enumerate`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_enumerate_p = POINTER(udev_enumerate) # pylint: disable=invalid-name + + +class udev_list_entry(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_list_entry`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_list_entry_p = POINTER(udev_list_entry) # pylint: disable=invalid-name + + +class udev_device(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_device`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_device_p = POINTER(udev_device) # pylint: disable=invalid-name + + +class udev_monitor(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_device`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_monitor_p = POINTER(udev_monitor) # pylint: disable=invalid-name + +class udev_hwdb(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_hwdb`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_hwdb_p = POINTER(udev_hwdb) # pylint: disable=invalid-name + + +dev_t = c_ulonglong # pylint: disable=invalid-name + + +SIGNATURES = dict( + # context + udev_new=([], udev_p), + udev_unref=([udev_p], None), + udev_ref=([udev_p], udev_p), + udev_get_sys_path=([udev_p], c_char_p), + udev_get_dev_path=([udev_p], c_char_p), + udev_get_run_path=([udev_p], c_char_p), + udev_get_log_priority=([udev_p], c_int), + udev_set_log_priority=([udev_p, c_int], None), + udev_enumerate_new=([udev_p], udev_enumerate_p), + udev_enumerate_ref=([udev_enumerate_p], udev_enumerate_p), + udev_enumerate_unref=([udev_enumerate_p], None), + udev_enumerate_add_match_subsystem=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_nomatch_subsystem=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_property=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_match_sysattr=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_nomatch_sysattr=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_match_tag=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_sysname=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_parent=([udev_enumerate_p, udev_device_p], c_int), + udev_enumerate_add_match_is_initialized=([udev_enumerate_p], c_int), + udev_enumerate_scan_devices=([udev_enumerate_p], c_int), + udev_enumerate_get_list_entry=([udev_enumerate_p], udev_list_entry_p), + # list entries + udev_list_entry_get_next=([udev_list_entry_p], udev_list_entry_p), + udev_list_entry_get_name=([udev_list_entry_p], c_char_p), + udev_list_entry_get_value=([udev_list_entry_p], c_char_p), + # devices + udev_device_ref=([udev_device_p], udev_device_p), + udev_device_unref=([udev_device_p], None), + udev_device_new_from_syspath=([udev_p, c_char_p], udev_device_p), + udev_device_new_from_subsystem_sysname=( + [udev_p, c_char_p, c_char_p], + udev_device_p + ), + udev_device_new_from_devnum=([udev_p, c_char, dev_t], udev_device_p), + udev_device_new_from_device_id=([udev_p, c_char_p], udev_device_p), + udev_device_new_from_environment=([udev_p], udev_device_p), + udev_device_get_parent=([udev_device_p], udev_device_p), + udev_device_get_parent_with_subsystem_devtype=( + [udev_device_p, c_char_p, c_char_p], + udev_device_p + ), + udev_device_get_devpath=([udev_device_p], c_char_p), + udev_device_get_subsystem=([udev_device_p], c_char_p), + udev_device_get_syspath=([udev_device_p], c_char_p), + udev_device_get_sysnum=([udev_device_p], c_char_p), + udev_device_get_sysname=([udev_device_p], c_char_p), + udev_device_get_driver=([udev_device_p], c_char_p), + udev_device_get_devtype=([udev_device_p], c_char_p), + udev_device_get_devnode=([udev_device_p], c_char_p), + udev_device_get_property_value=([udev_device_p, c_char_p], c_char_p), + udev_device_get_sysattr_value=([udev_device_p, c_char_p], c_char_p), + udev_device_get_devnum=([udev_device_p], dev_t), + udev_device_get_action=([udev_device_p], c_char_p), + udev_device_get_seqnum=([udev_device_p], c_ulonglong), + udev_device_get_is_initialized=([udev_device_p], c_int), + udev_device_get_usec_since_initialized=([udev_device_p], c_ulonglong), + udev_device_get_devlinks_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_tags_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_properties_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_sysattr_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_set_sysattr_value=([udev_device_p, c_char_p, c_char_p], c_int), + udev_device_has_tag=([udev_device_p, c_char_p], c_int), + # monitoring + udev_monitor_ref=([udev_monitor_p], udev_monitor_p), + udev_monitor_unref=([udev_monitor_p], None), + udev_monitor_new_from_netlink=([udev_p, c_char_p], udev_monitor_p), + udev_monitor_enable_receiving=([udev_monitor_p], c_int), + udev_monitor_set_receive_buffer_size=([udev_monitor_p, c_int], c_int), + udev_monitor_get_fd=([udev_monitor_p], c_int), + udev_monitor_receive_device=([udev_monitor_p], udev_device_p), + udev_monitor_filter_add_match_subsystem_devtype=( + [udev_monitor_p, c_char_p, c_char_p], c_int), + udev_monitor_filter_add_match_tag=([udev_monitor_p, c_char_p], c_int), + udev_monitor_filter_update=([udev_monitor_p], c_int), + udev_monitor_filter_remove=([udev_monitor_p], c_int), + # hwdb + udev_hwdb_ref=([udev_hwdb_p], udev_hwdb_p), + udev_hwdb_unref=([udev_hwdb_p], None), + udev_hwdb_new=([udev_p], udev_hwdb_p), + udev_hwdb_get_properties_list_entry=( + [udev_hwdb_p, c_char_p, c_uint], + udev_list_entry_p + ) +) + + +ERROR_CHECKERS = dict( + udev_device_get_action=None, + udev_device_get_devlinks_list_entry=None, + udev_device_get_devnode=None, + udev_device_get_devnum=None, + udev_device_get_devpath=None, + udev_device_get_devtype=None, + udev_device_get_driver=None, + udev_device_get_is_initialized=None, + udev_device_get_parent=None, + udev_device_get_parent_with_subsystem_devtype=None, + udev_device_get_properties_list_entry=None, + udev_device_get_property_value=None, + udev_device_get_seqnum=None, + udev_device_get_subsystem=None, + udev_device_get_sysattr_list_entry=None, + udev_device_get_sysattr_value=None, + udev_device_get_sysname=None, + udev_device_get_sysnum=None, + udev_device_get_syspath=None, + udev_device_get_tags_list_entry=None, + udev_device_get_usec_since_initialized=None, + udev_device_has_tag=None, + udev_device_new_from_device_id=None, + udev_device_new_from_devnum=None, + udev_device_new_from_environment=None, + udev_device_new_from_subsystem_sysname=None, + udev_device_new_from_syspath=None, + udev_device_ref=None, + udev_device_unref=None, + udev_device_set_sysattr_value=check_negative_errorcode, + udev_enumerate_add_match_parent=check_negative_errorcode, + udev_enumerate_add_match_subsystem=check_negative_errorcode, + udev_enumerate_add_nomatch_subsystem=check_negative_errorcode, + udev_enumerate_add_match_property=check_negative_errorcode, + udev_enumerate_add_match_sysattr=check_negative_errorcode, + udev_enumerate_add_nomatch_sysattr=check_negative_errorcode, + udev_enumerate_add_match_tag=check_negative_errorcode, + udev_enumerate_add_match_sysname=check_negative_errorcode, + udev_enumerate_add_match_is_initialized=check_negative_errorcode, + udev_enumerate_get_list_entry=None, + udev_enumerate_new=None, + udev_enumerate_ref=None, + udev_enumerate_scan_devices=None, + udev_enumerate_unref=None, + udev_get_dev_path=None, + udev_get_log_priority=None, + udev_get_run_path=None, + udev_get_sys_path=None, + udev_hwdb_get_properties_list_entry=None, + udev_hwdb_new=None, + udev_hwdb_ref=None, + udev_hwdb_unref=None, + udev_list_entry_get_name=None, + udev_list_entry_get_next=None, + udev_list_entry_get_value=None, + udev_monitor_set_receive_buffer_size=check_errno_on_nonzero_return, + # libudev doc says, enable_receiving returns a negative errno, but tests + # show that this is not reliable, so query the real error code + udev_monitor_enable_receiving=check_errno_on_nonzero_return, + udev_monitor_receive_device=check_errno_on_null_pointer_return, + udev_monitor_ref=None, + udev_monitor_filter_add_match_subsystem_devtype=check_negative_errorcode, + udev_monitor_filter_add_match_tag=check_negative_errorcode, + udev_monitor_filter_update=check_errno_on_nonzero_return, + udev_monitor_filter_remove=check_errno_on_nonzero_return, + udev_monitor_get_fd=None, + udev_monitor_new_from_netlink=None, + udev_monitor_unref=None, + udev_new=None, + udev_ref=None, + udev_set_log_priority=None, + udev_unref=None +) diff --git a/scripts/pyudev/_ctypeslib/utils.py b/scripts/pyudev/_ctypeslib/utils.py new file mode 100644 index 0000000..d10b6c9 --- /dev/null +++ b/scripts/pyudev/_ctypeslib/utils.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib.utils + ======================= + + Utilities for loading ctypeslib. + + .. moduleauthor:: Anne Mulhern +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import CDLL +from ctypes.util import find_library + + +def load_ctypes_library(name, signatures, error_checkers): + """ + Load library ``name`` and return a :class:`ctypes.CDLL` object for it. + + :param str name: the library name + :param signatures: signatures of methods + :type signatures: dict of str * (tuple of (list of type) * type) + :param error_checkers: error checkers for methods + :type error_checkers: dict of str * ((int * ptr * arglist) -> int) + + The library has errno handling enabled. + Important functions are given proper signatures and return types to support + type checking and argument conversion. + + :returns: a loaded library + :rtype: ctypes.CDLL + :raises ImportError: if the library is not found + """ + library_name = find_library(name) + if not library_name: + raise ImportError('No library named %s' % name) + lib = CDLL(library_name, use_errno=True) + # Add function signatures + for funcname, signature in signatures.items(): + function = getattr(lib, funcname, None) + if function: + argtypes, restype = signature + function.argtypes = argtypes + function.restype = restype + errorchecker = error_checkers.get(funcname) + if errorchecker: + function.errcheck = errorchecker + return lib diff --git a/scripts/pyudev/_errors.py b/scripts/pyudev/_errors.py new file mode 100644 index 0000000..847d908 --- /dev/null +++ b/scripts/pyudev/_errors.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.device._errors + ===================== + + Errors raised by Device methods. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import abc + +from six import add_metaclass + +@add_metaclass(abc.ABCMeta) +class DeviceError(Exception): + """ + Any error raised when messing around w/ or trying to discover devices. + """ + + +@add_metaclass(abc.ABCMeta) +class DeviceNotFoundError(DeviceError): + """ + An exception indicating that no :class:`Device` was found. + + .. versionchanged:: 0.5 + Rename from ``NoSuchDeviceError`` to its current name. + """ + + +class DeviceNotFoundAtPathError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found at a given path. + """ + + def __init__(self, sys_path): + DeviceNotFoundError.__init__(self, sys_path) + + @property + def sys_path(self): + """ + The path that caused this error as string. + """ + return self.args[0] + + def __str__(self): + return 'No device at {0!r}'.format(self.sys_path) + + +class DeviceNotFoundByFileError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found from the given filename. + """ + +class DeviceNotFoundByInterfaceIndexError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found + from the given interface index. + """ + +class DeviceNotFoundByKernelDeviceError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found + from the given kernel device string. + + The format of the kernel device string is defined in the + systemd.journal-fields man pages. + """ + + +class DeviceNotFoundByNameError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found with a given name. + """ + + def __init__(self, subsystem, sys_name): + DeviceNotFoundError.__init__(self, subsystem, sys_name) + + @property + def subsystem(self): + """ + The subsystem that caused this error as string. + """ + return self.args[0] + + @property + def sys_name(self): + """ + The sys name that caused this error as string. + """ + return self.args[1] + + def __str__(self): + return 'No device {0.sys_name!r} in {0.subsystem!r}'.format(self) + + +class DeviceNotFoundByNumberError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` was found + for a given device number. + """ + + def __init__(self, typ, number): + DeviceNotFoundError.__init__(self, typ, number) + + @property + def device_type(self): + """ + The device type causing this error as string. Either ``'char'`` or + ``'block'``. + """ + return self.args[0] + + @property + def device_number(self): + """ + The device number causing this error as integer. + """ + return self.args[1] + + def __str__(self): + return ('No {0.device_type} device with number ' + '{0.device_number}'.format(self)) + + +class DeviceNotFoundInEnvironmentError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` could + be constructed from the process environment. + """ + + def __str__(self): + return 'No device found in environment' + + +class DeviceValueError(DeviceError): + """ + Raised when a parameter has an unacceptable value. + + May also be raised when the parameter has an unacceptable type. + """ + + _FMT_STR = "value '%s' for parameter %s is unacceptable" + + def __init__(self, value, param, msg=None): + """ Initializer. + + :param object value: the value + :param str param: the parameter + :param str msg: an explanatory message + """ + # pylint: disable=super-init-not-called + self._value = value + self._param = param + self._msg = msg + + def __str__(self): + if self._msg: + fmt_str = self._FMT_STR + ": %s" + return fmt_str % (self._value, self._param, self._msg) + else: + return self._FMT_STR % (self._value, self._param) diff --git a/scripts/pyudev/_os/__init__.py b/scripts/pyudev/_os/__init__.py new file mode 100644 index 0000000..3b434ba --- /dev/null +++ b/scripts/pyudev/_os/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._os + ========== + + Extras to compensate for deficiencies in python os module. + + .. moduleauthor:: mulhern +""" + +from . import pipe +from . import poll diff --git a/scripts/pyudev/_os/pipe.py b/scripts/pyudev/_os/pipe.py new file mode 100644 index 0000000..41890fb --- /dev/null +++ b/scripts/pyudev/_os/pipe.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._os.pipe + =============== + + Fallback implementations for pipe. + + 1. pipe2 from python os module + 2. pipe2 from libc + 3. pipe from python os module + + The Pipe class wraps the chosen implementation. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import fcntl +from functools import partial + +from .._ctypeslib.libc import fd_pair +from .._ctypeslib.libc import ERROR_CHECKERS +from .._ctypeslib.libc import SIGNATURES +from .._ctypeslib.utils import load_ctypes_library + +# Define O_CLOEXEC, if not present in os already +O_CLOEXEC = getattr(os, 'O_CLOEXEC', 0o2000000) + + +def _pipe2_ctypes(libc, flags): + """A ``pipe2`` implementation using ``pipe2`` from ctypes. + + ``libc`` is a :class:`ctypes.CDLL` object for libc. ``flags`` is an + integer providing the flags to ``pipe2``. + + Return a pair of file descriptors ``(r, w)``. + + """ + fds = fd_pair() + libc.pipe2(fds, flags) + return fds[0], fds[1] + + +def _pipe2_by_pipe(flags): + """A ``pipe2`` implementation using :func:`os.pipe`. + + ``flags`` is an integer providing the flags to ``pipe2``. + + .. warning:: + + This implementation is not atomic! + + Return a pair of file descriptors ``(r, w)``. + + """ + fds = os.pipe() + if flags & os.O_NONBLOCK != 0: + for fd in fds: + set_fd_status_flag(fd, os.O_NONBLOCK) + if flags & O_CLOEXEC != 0: + for fd in fds: + set_fd_flag(fd, O_CLOEXEC) + return fds + + +def _get_pipe2_implementation(): + """Find the appropriate implementation for ``pipe2``. + +Return a function implementing ``pipe2``.""" + if hasattr(os, 'pipe2'): + return os.pipe2 # pylint: disable=no-member + else: + try: + libc = load_ctypes_library("libc", SIGNATURES, ERROR_CHECKERS) + return (partial(_pipe2_ctypes, libc) + if hasattr(libc, 'pipe2') else + _pipe2_by_pipe) + except ImportError: + return _pipe2_by_pipe + + +_PIPE2 = _get_pipe2_implementation() + + +def set_fd_flag(fd, flag): + """Set a flag on a file descriptor. + + ``fd`` is the file descriptor or file object, ``flag`` the flag as integer. + + """ + flags = fcntl.fcntl(fd, fcntl.F_GETFD, 0) + fcntl.fcntl(fd, fcntl.F_SETFD, flags | flag) + + +def set_fd_status_flag(fd, flag): + """Set a status flag on a file descriptor. + + ``fd`` is the file descriptor or file object, ``flag`` the flag as integer. + + """ + flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | flag) + + +class Pipe(object): + """A unix pipe. + + A pipe object provides two file objects: :attr:`source` is a readable file + object, and :attr:`sink` a writeable. Bytes written to :attr:`sink` appear + at :attr:`source`. + + Open a pipe with :meth:`open()`. + + """ + + @classmethod + def open(cls): + """Open and return a new :class:`Pipe`. + + The pipe uses non-blocking IO.""" + source, sink = _PIPE2(os.O_NONBLOCK | O_CLOEXEC) + return cls(source, sink) + + def __init__(self, source_fd, sink_fd): + """Create a new pipe object from the given file descriptors. + + ``source_fd`` is a file descriptor for the readable side of the pipe, + ``sink_fd`` is a file descriptor for the writeable side.""" + self.source = os.fdopen(source_fd, 'rb', 0) + self.sink = os.fdopen(sink_fd, 'wb', 0) + + def close(self): + """Closes both sides of the pipe.""" + try: + self.source.close() + finally: + self.sink.close() diff --git a/scripts/pyudev/_os/poll.py b/scripts/pyudev/_os/poll.py new file mode 100644 index 0000000..e760a36 --- /dev/null +++ b/scripts/pyudev/_os/poll.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._os.poll + =============== + + Operating system interface for pyudev. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import select + +from .._util import eintr_retry_call + + +class Poll(object): + """A poll object. + + This object essentially provides a more convenient interface around + :class:`select.poll`. + + """ + + _EVENT_TO_MASK = {'r': select.POLLIN, + 'w': select.POLLOUT} + + @staticmethod + def _has_event(events, event): + return events & event != 0 + + @classmethod + def for_events(cls, *events): + """Listen for ``events``. + + ``events`` is a list of ``(fd, event)`` pairs, where ``fd`` is a file + descriptor or file object and ``event`` either ``'r'`` or ``'w'``. If + ``r``, listen for whether that is ready to be read. If ``w``, listen + for whether the channel is ready to be written to. + + """ + notifier = eintr_retry_call(select.poll) + for fd, event in events: + mask = cls._EVENT_TO_MASK.get(event) + if not mask: + raise ValueError('Unknown event type: {0!r}'.format(event)) + notifier.register(fd, mask) + return cls(notifier) + + def __init__(self, notifier): + """Create a poll object for the given ``notifier``. + + ``notifier`` is the :class:`select.poll` object wrapped by the new poll + object. + + """ + self._notifier = notifier + + def poll(self, timeout=None): + """Poll for events. + + ``timeout`` is an integer specifying how long to wait for events (in + milliseconds). If omitted, ``None`` or negative, wait until an event + occurs. + + Return a list of all events that occurred before ``timeout``, where + each event is a pair ``(fd, event)``. ``fd`` is the integral file + descriptor, and ``event`` a string indicating the event type. If + ``'r'``, there is data to read from ``fd``. If ``'w'``, ``fd`` is + writable without blocking now. If ``'h'``, the file descriptor was + hung up (i.e. the remote side of a pipe was closed). + + """ + # Return a list to allow clients to determine whether there are any + # events at all with a simple truthiness test. + return list(self._parse_events(eintr_retry_call(self._notifier.poll, timeout))) + + def _parse_events(self, events): + """Parse ``events``. + + ``events`` is a list of events as returned by + :meth:`select.poll.poll()`. + + Yield all parsed events. + + """ + for fd, event_mask in events: + if self._has_event(event_mask, select.POLLNVAL): + raise IOError('File descriptor not open: {0!r}'.format(fd)) + elif self._has_event(event_mask, select.POLLERR): + raise IOError('Error while polling fd: {0!r}'.format(fd)) + + if self._has_event(event_mask, select.POLLIN): + yield fd, 'r' + if self._has_event(event_mask, select.POLLOUT): + yield fd, 'w' + if self._has_event(event_mask, select.POLLHUP): + yield fd, 'h' diff --git a/scripts/pyudev/_qt_base.py b/scripts/pyudev/_qt_base.py new file mode 100644 index 0000000..dd227f2 --- /dev/null +++ b/scripts/pyudev/_qt_base.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._qt_base + =============== + + Base mixin class for Qt4,Qt5 support. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six + +from pyudev.device import Device + +class MonitorObserverMixin(object): + """ + Base mixin for pyqt monitor observers. + """ + # pylint: disable=too-few-public-methods + + def _setup_notifier(self, monitor, notifier_class): + self.monitor = monitor + self.notifier = notifier_class( + monitor.fileno(), notifier_class.Read, self) + self.notifier.activated[int].connect(self._process_udev_event) + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. This merely + reflects the state of the ``enabled`` property of the underlying + :attr:`notifier`. + + .. versionadded:: 0.14 + """ + return self.notifier.isEnabled() + + @enabled.setter + def enabled(self, value): + self.notifier.setEnabled(value) + + def _process_udev_event(self): + """ + Attempt to receive a single device event from the monitor, process + the event and emit corresponding signals. + + Called by ``QSocketNotifier``, if data is available on the udev + monitoring socket. + """ + device = self.monitor.poll(timeout=0) + if device is not None: + self._emit_event(device) + + def _emit_event(self, device): + self.deviceEvent.emit(device) + + +class QUDevMonitorObserverMixin(MonitorObserverMixin): + """ + Obsolete monitor observer mixin. + """ + # pylint: disable=too-few-public-methods + + def _setup_notifier(self, monitor, notifier_class): + MonitorObserverMixin._setup_notifier(self, monitor, notifier_class) + self._action_signal_map = { + 'add': self.deviceAdded, 'remove': self.deviceRemoved, + 'change': self.deviceChanged, 'move': self.deviceMoved, + } + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.pyqt4.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + self.deviceEvent.emit(device.action, device) + signal = self._action_signal_map.get(device.action) + if signal is not None: + signal.emit(device) + +def make_init(qobject, socket_notifier): + """ + Generates an initializer to observer the given ``monitor`` + (a :class:`~pyudev.Monitor`): + + ``parent`` is the parent :class:`~PyQt{4,5}.QtCore.QObject` of this + object. It is passed unchanged to the inherited constructor of + :class:`~PyQt{4,5}.QtCore.QObject`. + """ + + def __init__(self, monitor, parent=None): + qobject.__init__(self, parent) + # pylint: disable=protected-access + self._setup_notifier(monitor, socket_notifier) + + return __init__ + + +class MonitorObserverGenerator(object): + """ + Class to generate a MonitorObserver class. + """ + # pylint: disable=too-few-public-methods + + @staticmethod + def make_monitor_observer(qobject, signal, socket_notifier): + """Generates an observer for device events integrating into the + PyQt{4,5} mainloop. + + This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device + events into Qt signals: + + >>> from pyudev import Context, Monitor + >>> from pyudev.pyqt4 import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.deviceEvent.connect(device_event) + >>> monitor.start() + + This class is a child of :class:`~{PySide, PyQt{4,5}}.QtCore.QObject`. + + """ + return type( + str("MonitorObserver"), + (qobject, MonitorObserverMixin), + { + str("__init__") : make_init(qobject, socket_notifier), + str("deviceEvent") : signal(Device) + } + ) + + + +class QUDevMonitorObserverGenerator(object): + """ + Class to generate a MonitorObserver class. + """ + # pylint: disable=too-few-public-methods + + @staticmethod + def make_monitor_observer(qobject, signal, socket_notifier): + """Generates an observer for device events integrating into the + PyQt{4,5} mainloop. + + This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device + events into Qt signals: + + >>> from pyudev import Context, Monitor + >>> from pyudev.pyqt4 import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.deviceEvent.connect(device_event) + >>> monitor.start() + + This class is a child of :class:`~{PyQt{4,5}, PySide}.QtCore.QObject`. + + """ + return type( + str("QUDevMonitorObserver"), + (qobject, QUDevMonitorObserverMixin), + { + str("__init__") : make_init(qobject, socket_notifier), + #: emitted upon arbitrary device events + str("deviceEvent") : signal(six.text_type, Device), + #: emitted if a device was added + str("deviceAdded") : signal(Device), + #: emitted if a device was removed + str("deviceRemoved") : signal(Device), + #: emitted if a device was changed + str("deviceChanged") : signal(Device), + #: emitted if a device was moved + str("deviceMoved") : signal(Device) + } + ) diff --git a/scripts/pyudev/_util.py b/scripts/pyudev/_util.py new file mode 100644 index 0000000..d6d736c --- /dev/null +++ b/scripts/pyudev/_util.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._util + ============ + + Internal utilities + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +try: + from subprocess import check_output +except ImportError: + from pyudev._compat import check_output + +import os +import sys +import stat +import errno + +import six + + +def ensure_byte_string(value): + """ + Return the given ``value`` as bytestring. + + If the given ``value`` is not a byte string, but a real unicode string, it + is encoded with the filesystem encoding (as in + :func:`sys.getfilesystemencoding()`). + """ + if not isinstance(value, bytes): + value = value.encode(sys.getfilesystemencoding()) + return value + + +def ensure_unicode_string(value): + """ + Return the given ``value`` as unicode string. + + If the given ``value`` is not a unicode string, but a byte string, it is + decoded with the filesystem encoding (as in + :func:`sys.getfilesystemencoding()`). + """ + if not isinstance(value, six.text_type): + value = value.decode(sys.getfilesystemencoding()) + return value + + +def property_value_to_bytes(value): + """ + Return a byte string, which represents the given ``value`` in a way + suitable as raw value of an udev property. + + If ``value`` is a boolean object, it is converted to ``'1'`` or ``'0'``, + depending on whether ``value`` is ``True`` or ``False``. If ``value`` is a + byte string already, it is returned unchanged. Anything else is simply + converted to a unicode string, and then passed to + :func:`ensure_byte_string`. + """ + # udev represents boolean values as 1 or 0, therefore an explicit + # conversion to int is required for boolean values + if isinstance(value, bool): + value = int(value) + if isinstance(value, bytes): + return value + else: + return ensure_byte_string(six.text_type(value)) + + +def string_to_bool(value): + """ + Convert the given unicode string ``value`` to a boolean object. + + If ``value`` is ``'1'``, ``True`` is returned. If ``value`` is ``'0'``, + ``False`` is returned. Any other value raises a + :exc:`~exceptions.ValueError`. + """ + if value not in ('1', '0'): + raise ValueError('Not a boolean value: {0!r}'.format(value)) + return value == '1' + + +def udev_list_iterate(libudev, entry): + """ + Iteration helper for udev list entry objects. + + Yield a tuple ``(name, value)``. ``name`` and ``value`` are bytestrings + containing the name and the value of the list entry. The exact contents + depend on the list iterated over. + """ + while entry: + name = libudev.udev_list_entry_get_name(entry) + value = libudev.udev_list_entry_get_value(entry) + yield (name, value) + entry = libudev.udev_list_entry_get_next(entry) + + +def get_device_type(filename): + """ + Get the device type of a device file. + + ``filename`` is a string containing the path of a device file. + + Return ``'char'`` if ``filename`` is a character device, or ``'block'`` if + ``filename`` is a block device. Raise :exc:`~exceptions.ValueError` if + ``filename`` is no device file at all. Raise + :exc:`~exceptions.EnvironmentError` if ``filename`` does not exist or if + its metadata was inaccessible. + + .. versionadded:: 0.15 + """ + mode = os.stat(filename).st_mode + if stat.S_ISCHR(mode): + return 'char' + elif stat.S_ISBLK(mode): + return 'block' + else: + raise ValueError('not a device file: {0!r}'.format(filename)) + + +def eintr_retry_call(func, *args, **kwargs): + """ + Handle interruptions to an interruptible system call. + + Run an interruptible system call in a loop and retry if it raises EINTR. + The signal calls that may raise EINTR prior to Python 3.5 are listed in + PEP 0475. Any calls to these functions must be wrapped in eintr_retry_call + in order to handle EINTR returns in older versions of Python. + + This function is safe to use under Python 3.5 and newer since the wrapped + function will simply return without raising EINTR. + + This function is based on _eintr_retry_call in python's subprocess.py. + """ + + # select.error inherits from Exception instead of OSError in Python 2 + import select + + while True: + try: + return func(*args, **kwargs) + except (OSError, IOError, select.error) as err: + # If this is not an IOError or OSError, it's the old select.error + # type, which means that the errno is only accessible via subscript + if isinstance(err, (OSError, IOError)): + error_code = err.errno + else: + error_code = err.args[0] + + if error_code == errno.EINTR: + continue + raise + +def udev_version(): + """ + Get the version of the underlying udev library. + + udev doesn't use a standard major-minor versioning scheme, but instead + labels releases with a single consecutive number. Consequently, the + version number returned by this function is a single integer, and not a + tuple (like for instance the interpreter version in + :data:`sys.version_info`). + + As libudev itself does not provide a function to query the version number, + this function calls the ``udevadm`` utility, so be prepared to catch + :exc:`~exceptions.EnvironmentError` and + :exc:`~subprocess.CalledProcessError` if you call this function. + + Return the version number as single integer. Raise + :exc:`~exceptions.ValueError`, if the version number retrieved from udev + could not be converted to an integer. Raise + :exc:`~exceptions.EnvironmentError`, if ``udevadm`` was not found, or could + not be executed. Raise :exc:`subprocess.CalledProcessError`, if + ``udevadm`` returned a non-zero exit code. On Python 2.7 or newer, the + ``output`` attribute of this exception is correctly set. + + .. versionadded:: 0.8 + """ + output = ensure_unicode_string(check_output(['udevadm', '--version'])) + return int(output.strip()) diff --git a/scripts/pyudev/core.py b/scripts/pyudev/core.py new file mode 100644 index 0000000..9cb6dbe --- /dev/null +++ b/scripts/pyudev/core.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.core + =========== + + Core types and functions of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from .device import Devices +from ._errors import DeviceNotFoundAtPathError +from ._ctypeslib.libudev import ERROR_CHECKERS +from ._ctypeslib.libudev import SIGNATURES +from ._ctypeslib.utils import load_ctypes_library + +from ._util import ensure_byte_string +from ._util import ensure_unicode_string +from ._util import property_value_to_bytes +from ._util import udev_list_iterate + + +class Context(object): + """ + A device database connection. + + This class represents a connection to the udev device database, and is + really *the* central object to access udev. You need an instance of this + class for almost anything else in pyudev. + + This class itself gives access to various udev configuration data (e.g. + :attr:`sys_path`, :attr:`device_path`), and provides device enumeration + (:meth:`list_devices()`). + + Instances of this class can directly be given as ``udev *`` to functions + wrapped through :mod:`ctypes`. + """ + + def __init__(self): + """ + Create a new context. + """ + self._libudev = load_ctypes_library('udev', SIGNATURES, ERROR_CHECKERS) + self._as_parameter_ = self._libudev.udev_new() + + def __del__(self): + self._libudev.udev_unref(self) + + @property + def sys_path(self): + """ + The ``sysfs`` mount point defaulting to ``/sys'`` as unicode string. + """ + if hasattr(self._libudev, 'udev_get_sys_path'): + return ensure_unicode_string(self._libudev.udev_get_sys_path(self)) + else: + # Fixed path since udev 183 + return '/sys' + + @property + def device_path(self): + """ + The device directory path defaulting to ``/dev`` as unicode string. + """ + if hasattr(self._libudev, 'udev_get_dev_path'): + return ensure_unicode_string(self._libudev.udev_get_dev_path(self)) + else: + # Fixed path since udev 183 + return '/dev' + + @property + def run_path(self): + """ + The run runtime directory path defaulting to ``/run`` as unicode + string. + + .. udevversion:: 167 + + .. versionadded:: 0.10 + """ + if hasattr(self._libudev, 'udev_get_run_path'): + return ensure_unicode_string(self._libudev.udev_get_run_path(self)) + else: + return '/run/udev' + + @property + def log_priority(self): + """ + The logging priority of the interal logging facitility of udev as + integer with a standard :mod:`syslog` priority. Assign to this + property to change the logging priority. + + UDev uses the standard :mod:`syslog` priorities. Constants for these + priorities are defined in the :mod:`syslog` module in the standard + library: + + >>> import syslog + >>> context = pyudev.Context() + >>> context.log_priority = syslog.LOG_DEBUG + + .. versionadded:: 0.9 + """ + return self._libudev.udev_get_log_priority(self) + + @log_priority.setter + def log_priority(self, value): + """ + Set the log priority. + + :param int value: the log priority. + """ + self._libudev.udev_set_log_priority(self, value) + + def list_devices(self, **kwargs): + """ + List all available devices. + + The arguments of this method are the same as for + :meth:`Enumerator.match()`. In fact, the arguments are simply passed + straight to method :meth:`~Enumerator.match()`. + + This function creates and returns an :class:`Enumerator` object, + that can be used to filter the list of devices, and eventually + retrieve :class:`Device` objects representing matching devices. + + .. versionchanged:: 0.8 + Accept keyword arguments now for easy matching. + """ + return Enumerator(self).match(**kwargs) + + +class Enumerator(object): + """ + A filtered iterable of devices. + + To retrieve devices, simply iterate over an instance of this class. + This operation yields :class:`Device` objects representing the available + devices. + + Before iteration the device list can be filtered by subsystem or by + property values using :meth:`match_subsystem` and + :meth:`match_property`. Multiple subsystem (property) filters are + combined using a logical OR, filters of different types are combined + using a logical AND. The following filter for instance:: + + devices.match_subsystem('block').match_property( + 'ID_TYPE', 'disk').match_property('DEVTYPE', 'disk') + + means the following:: + + subsystem == 'block' and (ID_TYPE == 'disk' or DEVTYPE == 'disk') + + Once added, a filter cannot be removed anymore. Create a new object + instead. + + Instances of this class can directly be given as given ``udev_enumerate *`` + to functions wrapped through :mod:`ctypes`. + """ + + def __init__(self, context): + """ + Create a new enumerator with the given ``context`` (a + :class:`Context` instance). + + While you can create objects of this class directly, this is not + recommended. Call :method:`Context.list_devices()` instead. + """ + if not isinstance(context, Context): + raise TypeError('Invalid context object') + self.context = context + self._as_parameter_ = context._libudev.udev_enumerate_new(context) + self._libudev = context._libudev + + def __del__(self): + self._libudev.udev_enumerate_unref(self) + + def match(self, **kwargs): + """ + Include devices according to the rules defined by the keyword + arguments. These keyword arguments are interpreted as follows: + + - The value for the keyword argument ``subsystem`` is forwarded to + :meth:`match_subsystem()`. + - The value for the keyword argument ``sys_name`` is forwared to + :meth:`match_sys_name()`. + - The value for the keyword argument ``tag`` is forwared to + :meth:`match_tag()`. + - The value for the keyword argument ``parent`` is forwared to + :meth:`match_parent()`. + - All other keyword arguments are forwareded one by one to + :meth:`match_property()`. The keyword argument itself is interpreted + as property name, the value of the keyword argument as the property + value. + + All keyword arguments are optional, calling this method without no + arguments at all is simply a noop. + + Return the instance again. + + .. versionadded:: 0.8 + + .. versionchanged:: 0.13 + Add ``parent`` keyword. + """ + subsystem = kwargs.pop('subsystem', None) + if subsystem is not None: + self.match_subsystem(subsystem) + sys_name = kwargs.pop('sys_name', None) + if sys_name is not None: + self.match_sys_name(sys_name) + tag = kwargs.pop('tag', None) + if tag is not None: + self.match_tag(tag) + parent = kwargs.pop('parent', None) + if parent is not None: + self.match_parent(parent) + for prop, value in kwargs.items(): + self.match_property(prop, value) + return self + + def match_subsystem(self, subsystem, nomatch=False): + """ + Include all devices, which are part of the given ``subsystem``. + + ``subsystem`` is either a unicode string or a byte string, containing + the name of the subsystem. If ``nomatch`` is ``True`` (default is + ``False``), the match is inverted: A device is only included if it is + *not* part of the given ``subsystem``. + + Note that, if a device has no subsystem, it is not included either + with value of ``nomatch`` True or with value of ``nomatch`` False. + + Return the instance again. + """ + match = self._libudev.udev_enumerate_add_nomatch_subsystem \ + if nomatch else self._libudev.udev_enumerate_add_match_subsystem + match(self, ensure_byte_string(subsystem)) + return self + + def match_sys_name(self, sys_name): + """ + Include all devices with the given name. + + ``sys_name`` is a byte or unicode string containing the device name. + + Return the instance again. + + .. versionadded:: 0.8 + """ + self._libudev.udev_enumerate_add_match_sysname( + self, ensure_byte_string(sys_name)) + return self + + def match_property(self, prop, value): + """ + Include all devices, whose ``prop`` has the given ``value``. + + ``prop`` is either a unicode string or a byte string, containing + the name of the property to match. ``value`` is a property value, + being one of the following types: + + - :func:`int` + - :func:`bool` + - A byte string + - Anything convertable to a unicode string (including a unicode string + itself) + + Return the instance again. + """ + self._libudev.udev_enumerate_add_match_property( + self, ensure_byte_string(prop), property_value_to_bytes(value)) + return self + + def match_attribute(self, attribute, value, nomatch=False): + """ + Include all devices, whose ``attribute`` has the given ``value``. + + ``attribute`` is either a unicode string or a byte string, containing + the name of a sys attribute to match. ``value`` is an attribute value, + being one of the following types: + + - :func:`int`, + - :func:`bool` + - A byte string + - Anything convertable to a unicode string (including a unicode string + itself) + + If ``nomatch`` is ``True`` (default is ``False``), the match is + inverted: A device is include if the ``attribute`` does *not* match + the given ``value``. + + .. note:: + + If ``nomatch`` is ``True``, devices which do not have the given + ``attribute`` at all are also included. In other words, with + ``nomatch=True`` the given ``attribute`` is *not* guaranteed to + exist on all returned devices. + + Return the instance again. + """ + match = (self._libudev.udev_enumerate_add_match_sysattr + if not nomatch else + self._libudev.udev_enumerate_add_nomatch_sysattr) + match(self, ensure_byte_string(attribute), + property_value_to_bytes(value)) + return self + + def match_tag(self, tag): + """ + Include all devices, which have the given ``tag`` attached. + + ``tag`` is a byte or unicode string containing the tag name. + + Return the instance again. + + .. udevversion:: 154 + + .. versionadded:: 0.6 + """ + self._libudev.udev_enumerate_add_match_tag(self, ensure_byte_string(tag)) + return self + + def match_is_initialized(self): + """ + Include only devices, which are initialized. + + Initialized devices have properly set device node permissions and + context, and are (in case of network devices) fully renamed. + + Currently this will not affect devices which do not have device nodes + and are not network interfaces. + + Return the instance again. + + .. seealso:: :attr:`Device.is_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + self._libudev.udev_enumerate_add_match_is_initialized(self) + return self + + def match_parent(self, parent): + """ + Include all devices on the subtree of the given ``parent`` device. + + The ``parent`` device itself is also included. + + ``parent`` is a :class:`~pyudev.Device`. + + Return the instance again. + + .. udevversion:: 172 + + .. versionadded:: 0.13 + """ + self._libudev.udev_enumerate_add_match_parent(self, parent) + return self + + def __iter__(self): + """ + Iterate over all matching devices. + + Yield :class:`Device` objects. + """ + self._libudev.udev_enumerate_scan_devices(self) + entry = self._libudev.udev_enumerate_get_list_entry(self) + for name, _ in udev_list_iterate(self._libudev, entry): + try: + yield Devices.from_sys_path(self.context, name) + except DeviceNotFoundAtPathError: + continue diff --git a/scripts/pyudev/device/__init__.py b/scripts/pyudev/device/__init__.py new file mode 100644 index 0000000..14c9c63 --- /dev/null +++ b/scripts/pyudev/device/__init__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.device + ============= + + Device class implementation of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from ._device import Attributes +from ._device import Device +from ._device import Devices +from ._device import Tags diff --git a/scripts/pyudev/device/_device.py b/scripts/pyudev/device/_device.py new file mode 100644 index 0000000..94dd41c --- /dev/null +++ b/scripts/pyudev/device/_device.py @@ -0,0 +1,1282 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.device._device + ===================== + + Device class implementation of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import re +from collections import Container +from collections import Iterable +from collections import Mapping +from datetime import timedelta + +from .._errors import DeviceNotFoundAtPathError +from .._errors import DeviceNotFoundByFileError +from .._errors import DeviceNotFoundByInterfaceIndexError +from .._errors import DeviceNotFoundByKernelDeviceError +from .._errors import DeviceNotFoundByNameError +from .._errors import DeviceNotFoundByNumberError +from .._errors import DeviceNotFoundInEnvironmentError +from .._util import ensure_byte_string +from .._util import ensure_unicode_string +from .._util import get_device_type +from .._util import string_to_bool +from .._util import udev_list_iterate + +# pylint: disable=too-many-lines + +class Devices(object): + """ + Class for constructing :class:`Device` objects from various kinds of data. + """ + + @classmethod + def from_path(cls, context, path): + """ + Create a device from a device ``path``. The ``path`` may or may not + start with the ``sysfs`` mount point: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> Devices.from_path(context, '/devices/platform') + Device(u'/sys/devices/platform') + >>> Devices.from_path(context, '/sys/devices/platform') + Device(u'/sys/devices/platform') + + ``context`` is the :class:`Context` in which to search the device. + ``path`` is a device path as unicode or byte string. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundAtPathError`, if no device was found for ``path``. + + .. versionadded:: 0.18 + """ + if not path.startswith(context.sys_path): + path = os.path.join(context.sys_path, path.lstrip(os.sep)) + return cls.from_sys_path(context, path) + + @classmethod + def from_sys_path(cls, context, sys_path): + """ + Create a new device from a given ``sys_path``: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> Devices.from_sys_path(context, '/sys/devices/platform') + Device(u'/sys/devices/platform') + + ``context`` is the :class:`Context` in which to search the device. + ``sys_path`` is a unicode or byte string containing the path of the + device inside ``sysfs`` with the mount point included. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundAtPathError`, if no device was found for + ``sys_path``. + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_syspath( + context, ensure_byte_string(sys_path)) + if not device: + raise DeviceNotFoundAtPathError(sys_path) + return Device(context, device) + + @classmethod + def from_name(cls, context, subsystem, sys_name): + """ + Create a new device from a given ``subsystem`` and a given + ``sys_name``: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> sda = Devices.from_name(context, 'block', 'sda') + >>> sda + Device(u'/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda') + >>> sda == Devices.from_path(context, '/block/sda') + + ``context`` is the :class:`Context` in which to search the device. + ``subsystem`` and ``sys_name`` are byte or unicode strings, which + denote the subsystem and the name of the device to create. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundByNameError`, if no device was found with the given + name. + + .. versionadded:: 0.18 + """ + sys_name = sys_name.replace("/", "!") + device = context._libudev.udev_device_new_from_subsystem_sysname( + context, ensure_byte_string(subsystem), + ensure_byte_string(sys_name)) + if not device: + raise DeviceNotFoundByNameError(subsystem, sys_name) + return Device(context, device) + + @classmethod + def from_device_number(cls, context, typ, number): + """ + Create a new device from a device ``number`` with the given device + ``type``: + + >>> import os + >>> from pyudev import Context, Device + >>> ctx = Context() + >>> major, minor = 8, 0 + >>> device = Devices.from_device_number(context, 'block', + ... os.makedev(major, minor)) + >>> device + Device(u'/sys/devices/pci0000:00/0000:00:11.0/host0/target0:0:0/0:0:0:0/block/sda') + >>> os.major(device.device_number), os.minor(device.device_number) + (8, 0) + + Use :func:`os.makedev` to construct a device number from a major and a + minor device number, as shown in the example above. + + .. warning:: + + Device numbers are not unique across different device types. + Passing a correct number with a wrong type may silently yield a + wrong device object, so make sure to pass the correct device type. + + ``context`` is the :class:`Context`, in which to search the device. + ``type`` is either ``'char'`` or ``'block'``, according to whether the + device is a character or block device. ``number`` is the device number + as integer. + + Return a :class:`Device` object for the device with the given device + ``number``. Raise :exc:`DeviceNotFoundByNumberError`, if no device was + found with the given device type and number. + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_devnum( + context, ensure_byte_string(typ[0]), number) + if not device: + raise DeviceNotFoundByNumberError(typ, number) + return Device(context, device) + + @classmethod + def from_device_file(cls, context, filename): + """ + Create a new device from the given device file: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> device = Devices.from_device_file(context, '/dev/sda') + >>> device + Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda') + >>> device.device_node + u'/dev/sda' + + .. warning:: + + Though the example seems to suggest that ``device.device_node == + filename`` holds with ``device = Devices.from_device_file(context, + filename)``, this is only true in a majority of cases. There *can* + be devices, for which this relation is actually false! Thus, do + *not* expect :attr:`~Device.device_node` to be equal to the given + ``filename`` for the returned :class:`Device`. Especially, use + :attr:`~Device.device_node` if you need the device file of a + :class:`Device` created with this method afterwards. + + ``context`` is the :class:`Context` in which to search the device. + ``filename`` is a string containing the path of a device file. + + Return a :class:`Device` representing the given device file. Raise + :exc:`DeviceNotFoundByFileError` if ``filename`` is no device file + at all or if ``filename`` does not exist or if its metadata was + inaccessible. + + .. versionadded:: 0.18 + """ + try: + device_type = get_device_type(filename) + device_number = os.stat(filename).st_rdev + except (EnvironmentError, ValueError) as err: + raise DeviceNotFoundByFileError(err) + + return cls.from_device_number(context, device_type, device_number) + + + @classmethod + def from_interface_index(cls, context, ifindex): + """ + Locate a device based on the interface index. + + :param `Context` context: the libudev context + :param int ifindex: the interface index + :returns: the device corresponding to the interface index + :rtype: `Device` + + This method is only appropriate for network devices. + """ + network_devices = context.list_devices(subsystem='net') + dev = next( + (d for d in network_devices if \ + d.attributes.get('ifindex') == ifindex), + None + ) + if dev is not None: + return dev + else: + raise DeviceNotFoundByInterfaceIndexError(ifindex) + + + @classmethod + def from_kernel_device(cls, context, kernel_device): + """ + Locate a device based on the kernel device. + + :param `Context` context: the libudev context + :param str kernel_device: the kernel device + :returns: the device corresponding to ``kernel_device`` + :rtype: `Device` + """ + switch_char = kernel_device[0] + rest = kernel_device[1:] + if switch_char in ('b', 'c'): + number_re = re.compile(r'^(?P\d+):(?P\d+)$') + match = number_re.match(rest) + if match: + number = os.makedev( + int(match.group('major')), + int(match.group('minor')) + ) + return cls.from_device_number(context, switch_char, number) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + elif switch_char == 'n': + return cls.from_interface_index(context, rest) + elif switch_char == '+': + (subsystem, _, kernel_device_name) = rest.partition(':') + if kernel_device_name and subsystem: + return cls.from_name(context, subsystem, kernel_device_name) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + + + @classmethod + def from_environment(cls, context): + """ + Create a new device from the process environment (as in + :data:`os.environ`). + + This only works reliable, if the current process is called from an + udev rule, and is usually used for tools executed from ``IMPORT=`` + rules. Use this method to create device objects in Python scripts + called from udev rules. + + ``context`` is the library :class:`Context`. + + Return a :class:`Device` object constructed from the environment. + Raise :exc:`DeviceNotFoundInEnvironmentError`, if no device could be + created from the environment. + + .. udevversion:: 152 + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_environment(context) + if not device: + raise DeviceNotFoundInEnvironmentError() + return Device(context, device) + + @classmethod + def METHODS(cls): # pylint: disable=invalid-name + """ + Return methods that obtain a :class:`Device` from a variety of + different data. + + :return: a list of from_* methods. + :rtype: list of class methods + + .. versionadded:: 0.18 + """ + return [ #pragma: no cover + cls.from_device_file, + cls.from_device_number, + cls.from_name, + cls.from_path, + cls.from_sys_path + ] + + +class Device(Mapping): + # pylint: disable=too-many-public-methods + """ + A single device with attached attributes and properties. + + A device also has a set of udev-specific attributes like the path + inside ``sysfs``. + + :class:`Device` objects compare equal and unequal to other devices and + to strings (based on :attr:`device_path`). However, there is no + ordering on :class:`Device` objects, and the corresponding operators + ``>``, ``<``, ``<=`` and ``>=`` raise :exc:`~exceptions.TypeError`. + + .. warning:: + + Currently, Device extends Mapping. The mapping that it stores is that + of udev property names to udev property values. This use is deprecated + and Device will no longer extend Mapping in 1.0. To look up udev + properties, use the Device.properties property. + + .. warning:: + + **Never** use object identity (``is`` operator) to compare + :class:`Device` objects. :mod:`pyudev` may create multiple + :class:`Device` objects for the same device. Instead compare + devices by value using ``==`` or ``!=``. + + :class:`Device` objects are hashable and can therefore be used as keys + in dictionaries and sets. + + They can also be given directly as ``udev_device *`` to functions wrapped + through :mod:`ctypes`. + """ + + @classmethod + def from_path(cls, context, path): #pragma: no cover + """ + .. versionadded:: 0.4 + .. deprecated:: 0.18 + Use :class:`Devices.from_path` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_path(context, path) + + @classmethod + def from_sys_path(cls, context, sys_path): #pragma: no cover + """ + .. versionchanged:: 0.4 + Raise :exc:`NoSuchDeviceError` instead of returning ``None``, if + no device was found for ``sys_path``. + .. versionchanged:: 0.5 + Raise :exc:`DeviceNotFoundAtPathError` instead of + :exc:`NoSuchDeviceError`. + .. deprecated:: 0.18 + Use :class:`Devices.from_sys_path` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_sys_path(context, sys_path) + + @classmethod + def from_name(cls, context, subsystem, sys_name): #pragma: no cover + """ + .. versionadded:: 0.5 + .. deprecated:: 0.18 + Use :class:`Devices.from_name` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_name(context, subsystem, sys_name) + + @classmethod + def from_device_number(cls, context, typ, number): #pragma: no cover + """ + .. versionadded:: 0.11 + .. deprecated:: 0.18 + Use :class:`Devices.from_device_number` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_device_number(context, typ, number) + + @classmethod + def from_device_file(cls, context, filename): #pragma: no cover + """ + .. versionadded:: 0.15 + .. deprecated:: 0.18 + Use :class:`Devices.from_device_file` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_device_file(context, filename) + + @classmethod + def from_environment(cls, context): #pragma: no cover + """ + .. versionadded:: 0.6 + .. deprecated:: 0.18 + Use :class:`Devices.from_environment` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_environment(context) + + def __init__(self, context, _device): + self.context = context + self._as_parameter_ = _device + self._libudev = context._libudev + + def __del__(self): + self._libudev.udev_device_unref(self) + + def __repr__(self): + return 'Device({0.sys_path!r})'.format(self) + + @property + def parent(self): + """ + The parent :class:`Device` or ``None``, if there is no parent + device. + """ + parent = self._libudev.udev_device_get_parent(self) + if not parent: + return None + # the parent device is not referenced, thus forcibly acquire a + # reference + return Device(self.context, self._libudev.udev_device_ref(parent)) + + @property + def children(self): + """ + Yield all direct children of this device. + + .. note:: + + In udev, parent-child relationships are generally ambiguous, i.e. + a parent can have multiple children, *and* a child can have multiple + parents. Hence, `child.parent == parent` does generally *not* hold + for all `child` objects in `parent.children`. In other words, + the :attr:`parent` of a device in this property can be different + from this device! + + .. note:: + + As the underlying library does not provide any means to directly + query the children of a device, this property performs a linear + search through all devices. + + Return an iterable yielding a :class:`Device` object for each direct + child of this device. + + .. udevversion:: 172 + + .. versionchanged:: 0.13 + Requires udev version 172 now. + """ + for device in self.context.list_devices().match_parent(self): + if device != self: + yield device + + @property + def ancestors(self): + """ + Yield all ancestors of this device from bottom to top. + + Return an iterator yielding a :class:`Device` object for each + ancestor of this device from bottom to top. + + .. versionadded:: 0.16 + """ + parent = self.parent + while parent is not None: + yield parent + parent = parent.parent + + def find_parent(self, subsystem, device_type=None): + """ + Find the parent device with the given ``subsystem`` and + ``device_type``. + + ``subsystem`` is a byte or unicode string containing the name of the + subsystem, in which to search for the parent. ``device_type`` is a + byte or unicode string holding the expected device type of the parent. + It can be ``None`` (the default), which means, that no specific device + type is expected. + + Return a parent :class:`Device` within the given ``subsystem`` and, if + ``device_type`` is not ``None``, with the given ``device_type``, or + ``None``, if this device has no parent device matching these + constraints. + + .. versionadded:: 0.9 + """ + subsystem = ensure_byte_string(subsystem) + if device_type is not None: + device_type = ensure_byte_string(device_type) + parent = self._libudev.udev_device_get_parent_with_subsystem_devtype( + self, subsystem, device_type) + if not parent: + return None + # parent device is not referenced, thus forcibly acquire a reference + return Device(self.context, self._libudev.udev_device_ref(parent)) + + def traverse(self): + """ + Traverse all parent devices of this device from bottom to top. + + Return an iterable yielding all parent devices as :class:`Device` + objects, *not* including the current device. The last yielded + :class:`Device` is the top of the device hierarchy. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :attr:`ancestors` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.ancestors instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.ancestors + + @property + def sys_path(self): + """ + Absolute path of this device in ``sysfs`` including the ``sysfs`` + mount point as unicode string. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_syspath(self)) + + @property + def device_path(self): + """ + Kernel device path as unicode string. This path uniquely identifies + a single device. + + Unlike :attr:`sys_path`, this path does not contain the ``sysfs`` + mount point. However, the path is absolute and starts with a slash + ``'/'``. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_devpath(self)) + + @property + def subsystem(self): + """ + Name of the subsystem this device is part of as unicode string. + + :returns: name of subsystem if found, else None + :rtype: unicode string or NoneType + """ + subsys = self._libudev.udev_device_get_subsystem(self) + return None if subsys is None else ensure_unicode_string(subsys) + + @property + def sys_name(self): + """ + Device file name inside ``sysfs`` as unicode string. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_sysname(self)) + + @property + def sys_number(self): + """ + The trailing number of the :attr:`sys_name` as unicode string, or + ``None``, if the device has no trailing number in its name. + + .. note:: + + The number is returned as unicode string to preserve the exact + format of the number, especially any leading zeros: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> device = Devices.from_path(context, '/sys/devices/LNXSYSTM:00') + >>> device.sys_number + u'00' + + To work with numbers, explicitly convert them to ints: + + >>> int(device.sys_number) + 0 + + .. versionadded:: 0.11 + """ + number = self._libudev.udev_device_get_sysnum(self) + return ensure_unicode_string(number) if number is not None else None + + @property + def device_type(self): + """ + Device type as unicode string, or ``None``, if the device type is + unknown. + + >>> from pyudev import Context + >>> context = Context() + >>> for device in context.list_devices(subsystem='net'): + ... '{0} - {1}'.format(device.sys_name, device.device_type or 'ethernet') + ... + u'eth0 - ethernet' + u'wlan0 - wlan' + u'lo - ethernet' + u'vboxnet0 - ethernet' + + .. versionadded:: 0.10 + """ + device_type = self._libudev.udev_device_get_devtype(self) + if device_type is not None: + return ensure_unicode_string(device_type) + else: + return device_type + + @property + def driver(self): + """ + The driver name as unicode string, or ``None``, if there is no + driver for this device. + + .. versionadded:: 0.5 + """ + driver = self._libudev.udev_device_get_driver(self) + return ensure_unicode_string(driver) if driver is not None else None + + @property + def device_node(self): + """ + Absolute path to the device node of this device as unicode string or + ``None``, if this device doesn't have a device node. The path + includes the device directory (see :attr:`Context.device_path`). + + This path always points to the actual device node associated with + this device, and never to any symbolic links to this device node. + See :attr:`device_links` to get a list of symbolic links to this + device node. + + .. warning:: + + For devices created with :meth:`from_device_file()`, the value of + this property is not necessary equal to the ``filename`` given to + :meth:`from_device_file()`. + """ + node = self._libudev.udev_device_get_devnode(self) + return ensure_unicode_string(node) if node is not None else None + + @property + def device_number(self): + """ + The device number of the associated device as integer, or ``0``, if no + device number is associated. + + Use :func:`os.major` and :func:`os.minor` to decompose the device + number into its major and minor number: + + >>> import os + >>> from pyudev import Context, Device + >>> context = Context() + >>> sda = Devices.from_name(context, 'block', 'sda') + >>> sda.device_number + 2048L + >>> (os.major(sda.device_number), os.minor(sda.device_number)) + (8, 0) + + For devices with an associated :attr:`device_node`, this is the same as + the ``st_rdev`` field of the stat result of the :attr:`device_node`: + + >>> os.stat(sda.device_node).st_rdev + 2048 + + .. versionadded:: 0.11 + """ + return self._libudev.udev_device_get_devnum(self) + + @property + def is_initialized(self): + """ + ``True``, if the device is initialized, ``False`` otherwise. + + A device is initialized, if udev has already handled this device and + has set up device node permissions and context, or renamed a network + device. + + Consequently, this property is only implemented for devices with a + device node or for network devices. On all other devices this property + is always ``True``. + + It is *not* recommended, that you use uninitialized devices. + + .. seealso:: :attr:`time_since_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + return bool(self._libudev.udev_device_get_is_initialized(self)) + + @property + def time_since_initialized(self): + """ + The time elapsed since initialization as :class:`~datetime.timedelta`. + + This property is only implemented on devices, which need to store + properties in the udev database. On all other devices this property is + simply zero :class:`~datetime.timedelta`. + + .. seealso:: :attr:`is_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + microseconds = self._libudev.udev_device_get_usec_since_initialized( + self) + return timedelta(microseconds=microseconds) + + @property + def device_links(self): + """ + An iterator, which yields the absolute paths (including the device + directory, see :attr:`Context.device_path`) of all symbolic links + pointing to the :attr:`device_node` of this device. The paths are + unicode strings. + + UDev can create symlinks to the original device node (see + :attr:`device_node`) inside the device directory. This is often + used to assign a constant, fixed device node to devices like + removeable media, which technically do not have a constant device + node, or to map a single device into multiple device hierarchies. + The property provides access to all such symbolic links, which were + created by UDev for this device. + + .. warning:: + + Links are not necessarily resolved by + :meth:`Devices.from_device_file()`. Hence do *not* rely on + ``Devices.from_device_file(context, link).device_path == + device.device_path`` from any ``link`` in ``device.device_links``. + """ + devlinks = self._libudev.udev_device_get_devlinks_list_entry(self) + for name, _ in udev_list_iterate(self._libudev, devlinks): + yield ensure_unicode_string(name) + + @property + def action(self): + """ + The device event action as string, or ``None``, if this device was not + received from a :class:`Monitor`. + + Usual actions are: + + ``'add'`` + A device has been added (e.g. a USB device was plugged in) + ``'remove'`` + A device has been removed (e.g. a USB device was unplugged) + ``'change'`` + Something about the device changed (e.g. a device property) + ``'online'`` + The device is online now + ``'offline'`` + The device is offline now + + .. warning:: + + Though the actions listed above are the most common, this property + *may* return other values, too, so be prepared to handle unknown + actions! + + .. versionadded:: 0.16 + """ + action = self._libudev.udev_device_get_action(self) + return ensure_unicode_string(action) if action is not None else None + + @property + def sequence_number(self): + """ + The device event sequence number as integer, or ``0`` if this device + has no sequence number, i.e. was not received from a :class:`Monitor`. + + .. versionadded:: 0.16 + """ + return self._libudev.udev_device_get_seqnum(self) + + @property + def attributes(self): + """ + The system attributes of this device as read-only + :class:`Attributes` mapping. + + System attributes are basically normal files inside the device + directory. These files contain all sorts of information about the + device, which may not be reflected by properties. These attributes + are commonly used for matching in udev rules, and can be printed + using ``udevadm info --attribute-walk``. + + The values of these attributes are not always proper strings, and + can contain arbitrary bytes. + + :returns: an Attributes object, useful for reading attributes + :rtype: Attributes + + .. versionadded:: 0.5 + """ + # do *not* cache the created object in an attribute of this class. + # Doing so creates an uncollectable reference cycle between Device and + # Attributes, because Attributes refers to this object through + # Attributes.device. + return Attributes(self) + + @property + def properties(self): + """ + The udev properties of this object as read-only Properties mapping. + + .. versionadded:: 0.21 + """ + return Properties(self) + + @property + def tags(self): + """ + A :class:`Tags` object representing the tags attached to this device. + + The :class:`Tags` object supports a test for a single tag as well as + iteration over all tags: + + >>> from pyudev import Context + >>> context = Context() + >>> device = next(iter(context.list_devices(tag='systemd'))) + >>> 'systemd' in device.tags + True + >>> list(device.tags) + [u'seat', u'systemd', u'uaccess'] + + Tags are arbitrary classifiers that can be attached to devices by udev + scripts and daemons. For instance, systemd_ uses tags for multi-seat_ + support. + + .. _systemd: http://freedesktop.org/wiki/Software/systemd + .. _multi-seat: http://www.freedesktop.org/wiki/Software/systemd/multiseat + + .. udevversion:: 154 + + .. versionadded:: 0.6 + + .. versionchanged:: 0.13 + Return a :class:`Tags` object now. + """ + return Tags(self) + + def __iter__(self): + """ + Iterate over the names of all properties defined for this device. + + Return a generator yielding the names of all properties of this + device as unicode strings. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__iter__() + + def __len__(self): + """ + Return the amount of properties defined for this device as integer. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__len__() + + def __getitem__(self, prop): + """ + Get the given property from this device. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as unicode string, or raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__getitem__(prop) + + def asint(self, prop): + """ + Get the given property from this device as integer. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as integer. Raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device, or a :exc:`~exceptions.ValueError`, if the property + value cannot be converted to an integer. + + .. deprecated:: 0.21 + Will be removed in 1.0. Use Device.properties.asint() instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.properties.asint instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.asint(prop) + + def asbool(self, prop): + """ + Get the given property from this device as boolean. + + A boolean property has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return ``True``, if the property value is ``'1'`` and ``False``, if + the property value is ``'0'``. Any other value raises a + :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, + if the given property is not defined for this device. + + .. deprecated:: 0.21 + Will be removed in 1.0. Use Device.properties.asbool() instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.properties.asbool instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.asbool(prop) + + def __hash__(self): + return hash(self.device_path) + + def __eq__(self, other): + if isinstance(other, Device): + return self.device_path == other.device_path + else: + return self.device_path == other + + def __ne__(self, other): + if isinstance(other, Device): + return self.device_path != other.device_path + else: + return self.device_path != other + + def __gt__(self, other): + raise TypeError('Device not orderable') + + def __lt__(self, other): + raise TypeError('Device not orderable') + + def __le__(self, other): + raise TypeError('Device not orderable') + + def __ge__(self, other): + raise TypeError('Device not orderable') + + +class Properties(Mapping): + """ + udev properties :class:`Device` objects. + + .. versionadded:: 0.21 + """ + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + def __iter__(self): + """ + Iterate over the names of all properties defined for the device. + + Return a generator yielding the names of all properties of this + device as unicode strings. + """ + properties = \ + self._libudev.udev_device_get_properties_list_entry(self.device) + for name, _ in udev_list_iterate(self._libudev, properties): + yield ensure_unicode_string(name) + + def __len__(self): + """ + Return the amount of properties defined for this device as integer. + """ + properties = \ + self._libudev.udev_device_get_properties_list_entry(self.device) + return sum(1 for _ in udev_list_iterate(self._libudev, properties)) + + def __getitem__(self, prop): + """ + Get the given property from this device. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as unicode string, or raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device. + """ + value = self._libudev.udev_device_get_property_value( + self.device, + ensure_byte_string(prop) + ) + if value is None: + raise KeyError(prop) + return ensure_unicode_string(value) + + def asint(self, prop): + """ + Get the given property from this device as integer. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as integer. Raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device, or a :exc:`~exceptions.ValueError`, if the property + value cannot be converted to an integer. + """ + return int(self[prop]) + + def asbool(self, prop): + """ + Get the given property from this device as boolean. + + A boolean property has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return ``True``, if the property value is ``'1'`` and ``False``, if + the property value is ``'0'``. Any other value raises a + :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, + if the given property is not defined for this device. + """ + return string_to_bool(self[prop]) + + +class Attributes(object): + """ + udev attributes for :class:`Device` objects. + + .. versionadded:: 0.5 + """ + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + @property + def available_attributes(self): + """ + Yield the ``available`` attributes for the device. + + It is not guaranteed that a key in this list will have a value. + It is not guaranteed that a key not in this list will not have a value. + + It is guaranteed that the keys in this list are the keys that libudev + considers to be "available" attributes. + + If libudev version does not define udev_device_get_sysattr_list_entry() + yields nothing. + + See rhbz#1267584. + """ + if not hasattr(self._libudev, 'udev_device_get_sysattr_list_entry'): + return # pragma: no cover + attrs = self._libudev.udev_device_get_sysattr_list_entry(self.device) + for attribute, _ in udev_list_iterate(self._libudev, attrs): + yield ensure_unicode_string(attribute) + + def _get(self, attribute): + """ + Get the given system ``attribute`` for the device. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute`` + :rtype: an arbitrary sequence of bytes + :raises KeyError: if no value found + """ + value = self._libudev.udev_device_get_sysattr_value( + self.device, + ensure_byte_string(attribute) + ) + if value is None: + raise KeyError(attribute) + return value + + def get(self, attribute, default=None): + """ + Get the given system ``attribute`` for the device. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :param default: a default if no corresponding value found + :type default: a sequence of bytes + :returns: the value corresponding to ``attribute`` or ``default`` + :rtype: object + """ + try: + return self._get(attribute) + except KeyError: + return default + + def asstring(self, attribute): + """ + Get the given ``attribute`` for the device as unicode string. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as unicode + :rtype: unicode + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible + """ + return ensure_unicode_string(self._get(attribute)) + + def asint(self, attribute): + """ + Get the given ``attribute`` as an int. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as an int + :rtype: int + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible to unicode + :raises ValueError: if unicode value can not be converted to an int + """ + return int(self.asstring(attribute)) + + def asbool(self, attribute): + """ + Get the given ``attribute`` from this device as a bool. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as bool + :rtype: bool + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible to unicode + :raises ValueError: if unicode value can not be converted to a bool + + A boolean attribute has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + """ + return string_to_bool(self.asstring(attribute)) + + +class Tags(Iterable, Container): + """ + A iterable over :class:`Device` tags. + + Subclasses the ``Container`` and the ``Iterable`` ABC. + """ + + # pylint: disable=too-few-public-methods + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + def _has_tag(self, tag): + """ + Whether ``tag`` exists. + + :param tag: unicode string with name of tag + :rtype: bool + """ + if hasattr(self._libudev, 'udev_device_has_tag'): + return bool(self._libudev.udev_device_has_tag( + self.device, ensure_byte_string(tag))) + else: # pragma: no cover + return any(t == tag for t in self) + + def __contains__(self, tag): + """ + Check for existence of ``tag``. + + ``tag`` is a tag as unicode string. + + Return ``True``, if ``tag`` is attached to the device, ``False`` + otherwise. + """ + return self._has_tag(tag) + + def __iter__(self): + """ + Iterate over all tags. + + Yield each tag as unicode string. + """ + tags = self._libudev.udev_device_get_tags_list_entry(self.device) + for tag, _ in udev_list_iterate(self._libudev, tags): + yield ensure_unicode_string(tag) diff --git a/scripts/pyudev/discover.py b/scripts/pyudev/discover.py new file mode 100644 index 0000000..f2b4261 --- /dev/null +++ b/scripts/pyudev/discover.py @@ -0,0 +1,391 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.discover + =============== + + Tools to discover a device given limited information. + + .. moduleauthor:: mulhern +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import abc +import functools +import os +import re +import six + +from ._errors import DeviceNotFoundError + +from .device import Devices + + +def wrap_exception(func): + """ + Allow Device discovery methods to return None instead of raising an + exception. + """ + + @functools.wraps(func) + def the_func(*args, **kwargs): + """ + Returns result of calling ``func`` on ``args``, ``kwargs``. + Returns None if ``func`` raises :exc:`DeviceNotFoundError`. + """ + try: + return func(*args, **kwargs) + except DeviceNotFoundError: + return None + + return the_func + +@six.add_metaclass(abc.ABCMeta) +class Hypothesis(object): + """ + Represents a hypothesis about the meaning of the device identifier. + """ + + @classmethod + @abc.abstractmethod + def match(cls, value): # pragma: no cover + """ + Match the given string according to the hypothesis. + + The purpose of this method is to obtain a value corresponding to + ``value`` if that is possible. It may use a regular expression, but + in general it should just return ``value`` and let the lookup method + sort out the rest. + + :param str value: the string to inspect + :returns: the matched thing or None if unmatched + :rtype: the type of lookup's key parameter or NoneType + """ + raise NotImplementedError() + + @classmethod + @abc.abstractmethod + def lookup(cls, context, key): # pragma: no cover + """ + Lookup the given string according to the hypothesis. + + :param Context context: the pyudev context + :param key: a key with which to lookup the device + :type key: the type of match's return value if not None + :returns: a list of Devices obtained + :rtype: frozenset of :class:`Device` + """ + raise NotImplementedError() + + @classmethod + def setup(cls, context): + """ + A potentially expensive method that may allow an :class:`Hypothesis` + to find devices more rapidly or to find a device that it would + otherwise miss. + + :param Context context: the pyudev context + """ + pass + + @classmethod + def get_devices(cls, context, value): + """ + Get any devices that may correspond to the given string. + + :param Context context: the pyudev context + :param str value: the value to look for + :returns: a list of devices obtained + :rtype: set of :class:`Device` + """ + key = cls.match(value) + return cls.lookup(context, key) if key is not None else frozenset() + + +class DeviceNumberHypothesis(Hypothesis): + """ + Represents the hypothesis that the device is a device number. + + The device may be separated into major/minor number or a composite number. + """ + + @classmethod + def _match_major_minor(cls, value): + """ + Match the number under the assumption that it is a major,minor pair. + + :param str value: value to match + :returns: the device number or None + :rtype: int or NoneType + """ + major_minor_re = re.compile( + r'^(?P\d+)(\D+)(?P\d+)$' + ) + match = major_minor_re.match(value) + return match and os.makedev( + int(match.group('major')), + int(match.group('minor')) + ) + + @classmethod + def _match_number(cls, value): + """ + Match the number under the assumption that it is a single number. + + :param str value: value to match + :returns: the device number or None + :rtype: int or NoneType + """ + number_re = re.compile(r'^(?P\d+)$') + match = number_re.match(value) + return match and int(match.group('number')) + + @classmethod + def match(cls, value): + """ + Match the number under the assumption that it is a device number. + + :returns: the device number or None + :rtype: int or NoneType + """ + return cls._match_major_minor(value) or cls._match_number(value) + + @classmethod + def find_subsystems(cls, context): + """ + Find subsystems in /sys/dev. + + :param Context context: the context + :returns: a lis of available subsystems + :rtype: list of str + """ + sys_path = context.sys_path + return os.listdir(os.path.join(sys_path, 'dev')) + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the device number. + + :param Context context: the context + :param int key: the device number + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + func = wrap_exception(Devices.from_device_number) + res = (func(context, s, key) for s in cls.find_subsystems(context)) + return frozenset(r for r in res if r is not None) + + +class DevicePathHypothesis(Hypothesis): + """ + Discover the device assuming the identifier is a device path. + """ + + @classmethod + def match(cls, value): + """ + Match ``value`` under the assumption that it is a device path. + + :returns: the device path or None + :rtype: str or NoneType + """ + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the path. + + :param Context context: the context + :param str key: the device path + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + res = wrap_exception(Devices.from_path)(context, key) + return frozenset((res,)) if res is not None else frozenset() + + +class DeviceNameHypothesis(Hypothesis): + """ + Discover the device assuming the input is a device name. + + Try every available subsystem. + """ + + @classmethod + def find_subsystems(cls, context): + """ + Find all subsystems in sysfs. + + :param Context context: the context + :rtype: frozenset + :returns: subsystems in sysfs + """ + sys_path = context.sys_path + dirnames = ('bus', 'class', 'subsystem') + absnames = (os.path.join(sys_path, name) for name in dirnames) + realnames = (d for d in absnames if os.path.isdir(d)) + return frozenset(n for d in realnames for n in os.listdir(d)) + + @classmethod + def match(cls, value): + """ + Match ``value`` under the assumption that it is a device name. + + :returns: the device path or None + :rtype: str or NoneType + """ + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the path. + + :param Context context: the context + :param str key: the device path + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + func = wrap_exception(Devices.from_name) + res = (func(context, s, key) for s in cls.find_subsystems(context)) + return frozenset(r for r in res if r is not None) + + +class DeviceFileHypothesis(Hypothesis): + """ + Discover the device assuming the value is some portion of a device file. + + The device file may be a link to a device node. + """ + + _LINK_DIRS = [ + '/dev', + '/dev/disk/by-id', + '/dev/disk/by-label', + '/dev/disk/by-partlabel', + '/dev/disk/by-partuuid', + '/dev/disk/by-path', + '/dev/disk/by-uuid', + '/dev/input/by-path', + '/dev/mapper', + '/dev/md', + '/dev/vg' + ] + + @classmethod + def get_link_dirs(cls, context): + """ + Get all directories that may contain links to device nodes. + + This method checks the device links of every device, so it is very + expensive. + + :param Context context: the context + :returns: a sorted list of directories that contain device links + :rtype: list + """ + devices = context.list_devices() + devices_with_links = (d for d in devices if list(d.device_links)) + links = (l for d in devices_with_links for l in d.device_links) + return sorted(set(os.path.dirname(l) for l in links)) + + @classmethod + def setup(cls, context): + """ + Set the link directories to be used when discovering by file. + + Uses `get_link_dirs`, so is as expensive as it is. + + :param Context context: the context + """ + cls._LINK_DIRS = cls.get_link_dirs(context) + + @classmethod + def match(cls, value): + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup the device under the assumption that the key is part of + the name of a device file. + + :param Context context: the context + :param str key: a portion of the device file name + + It is assumed that either it is the whole name of the device file + or it is the basename. + + A device file may be a device node or a device link. + """ + func = wrap_exception(Devices.from_device_file) + if '/' in key: + device = func(context, key) + return frozenset((device,)) if device is not None else frozenset() + else: + files = (os.path.join(ld, key) for ld in cls._LINK_DIRS) + devices = (func(context, f) for f in files) + return frozenset(d for d in devices if d is not None) + + +class Discovery(object): + # pylint: disable=too-few-public-methods + """ + Provides discovery methods for devices. + """ + + _HYPOTHESES = [ + DeviceFileHypothesis, + DeviceNameHypothesis, + DeviceNumberHypothesis, + DevicePathHypothesis + ] + + def __init__(self): + self._hypotheses = self._HYPOTHESES + + def setup(self, context): + """ + Set up individual hypotheses. + + May be an expensive call. + + :param Context context: the context + """ + for hyp in self._hypotheses: + hyp.setup(context) + + def get_devices(self, context, value): + """ + Get the devices corresponding to value. + + :param Context context: the context + :param str value: some identifier of the device + :returns: a list of corresponding devices + :rtype: frozenset of :class:`Device` + """ + return frozenset( + d for h in self._hypotheses for d in h.get_devices(context, value) + ) diff --git a/scripts/pyudev/glib.py b/scripts/pyudev/glib.py new file mode 100644 index 0000000..4cb5c0e --- /dev/null +++ b/scripts/pyudev/glib.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""pyudev.glib + =========== + + Glib integration. + + :class:`MonitorObserver` integrates device monitoring into the Glib + mainloop by turing device events into Glib signals. + + :mod:`glib` and :mod:`gobject` from PyGObject_ must be available when + importing this module. PyGtk is not required. + + .. _PyGObject: http://www.pygtk.org/ + + .. moduleauthor:: Sebastian Wiesner + .. versionadded:: 0.7 + +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +# thanks to absolute imports, this really imports the glib binding and not this +# module again +import glib +import gobject + + +class _ObserverMixin(object): + """Mixin to provide observer behavior to the old and the new API.""" + # pylint: disable=too-few-public-methods + + def _setup_observer(self, monitor): + # pylint: disable=attribute-defined-outside-init + self.monitor = monitor + self.event_source = None + self.enabled = True + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. + + .. versionadded:: 0.14 + """ + return self.event_source is not None + + @enabled.setter + def enabled(self, value): + if value and self.event_source is None: + # pylint: disable=attribute-defined-outside-init + self.event_source = glib.io_add_watch( + self.monitor, glib.IO_IN, self._process_udev_event) + elif not value and self.event_source is not None: + glib.source_remove(self.event_source) + + def _process_udev_event(self, source, condition): + # pylint: disable=unused-argument + if condition == glib.IO_IN: + device = self.monitor.poll(timeout=0) + if device is not None: + self._emit_event(device) + return True + + def _emit_event(self, device): + self.emit('device-event', device) + + +class MonitorObserver(gobject.GObject, _ObserverMixin): + """ + An observer for device events integrating into the :mod:`glib` mainloop. + + This class inherits :class:`~gobject.GObject` to turn device events into + glib signals. + + >>> from pyudev import Context, Monitor + >>> from pyudev.glib import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(observer, device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.connect('device-event', device_event) + >>> monitor.start() + + This class is a child of :class:`gobject.GObject`. + """ + + __gsignals__ = { + # explicitly convert the signal to str, because glib expects the + # *native* string type of the corresponding python version as type of + # signal name, and str() is the name of the native string type of both + # python versions. We could also remove the "unicode_literals" import, + # but I don't want to make exceptions to the standard set of future + # imports used throughout pyudev for the sake of consistency. + str('device-event'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + } + + def __init__(self, monitor): + gobject.GObject.__init__(self) + self._setup_observer(monitor) + + +gobject.type_register(MonitorObserver) + + +class GUDevMonitorObserver(gobject.GObject, _ObserverMixin): + """ + An observer for device events integrating into the :mod:`glib` mainloop. + + .. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. + """ + + _action_signal_map = { + 'add': 'device-added', 'remove': 'device-removed', + 'change': 'device-changed', 'move': 'device-moved'} + + __gsignals__ = { + str('device-event'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)), + str('device-added'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-removed'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-changed'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-moved'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + } + + def __init__(self, monitor): + gobject.GObject.__init__(self) + self._setup_observer(monitor) + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.glib.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + self.emit('device-event', device.action, device) + signal = self._action_signal_map.get(device.action) + if signal is not None: + self.emit(signal, device) + + +gobject.type_register(GUDevMonitorObserver) diff --git a/scripts/pyudev/monitor.py b/scripts/pyudev/monitor.py new file mode 100644 index 0000000..ec3e7e4 --- /dev/null +++ b/scripts/pyudev/monitor.py @@ -0,0 +1,582 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.monitor + ============== + + Monitor implementation. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +import os +import errno +from threading import Thread +from functools import partial + +from .device import Device + +from ._util import eintr_retry_call +from ._util import ensure_byte_string + +from ._os import pipe +from ._os import poll + + +class Monitor(object): + """ + A synchronous device event monitor. + + A :class:`Monitor` objects connects to the udev daemon and listens for + changes to the device list. A monitor is created by connecting to the + kernel daemon through netlink (see :meth:`from_netlink`): + + >>> from pyudev import Context, Monitor + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + + Once the monitor is created, you can add a filter using :meth:`filter_by()` + or :meth:`filter_by_tag()` to drop incoming events in subsystems, which are + not of interest to the application: + + >>> monitor.filter_by('input') + + When the monitor is eventually set up, you can either poll for events + synchronously: + + >>> device = monitor.poll(timeout=3) + >>> if device: + ... print('{0.action}: {0}'.format(device)) + ... + + Or you can monitor events asynchronously with :class:`MonitorObserver`. + + To integrate into various event processing frameworks, the monitor provides + a :func:`selectable ` file description by :meth:`fileno()`. + However, do *not* read or write directly on this file descriptor. + + Instances of this class can directly be given as ``udev_monitor *`` to + functions wrapped through :mod:`ctypes`. + + .. versionchanged:: 0.16 + Remove :meth:`from_socket()` which is deprecated, and even removed in + recent udev versions. + """ + + def __init__(self, context, monitor_p): + self.context = context + self._as_parameter_ = monitor_p + self._libudev = context._libudev + self._started = False + + def __del__(self): + self._libudev.udev_monitor_unref(self) + + @classmethod + def from_netlink(cls, context, source='udev'): + """ + Create a monitor by connecting to the kernel daemon through netlink. + + ``context`` is the :class:`Context` to use. ``source`` is a string, + describing the event source. Two sources are available: + + ``'udev'`` (the default) + Events emitted after udev as registered and configured the device. + This is the absolutely recommended source for applications. + + ``'kernel'`` + Events emitted directly after the kernel has seen the device. The + device has not yet been configured by udev and might not be usable + at all. **Never** use this, unless you know what you are doing. + + Return a new :class:`Monitor` object, which is connected to the + given source. Raise :exc:`~exceptions.ValueError`, if an invalid + source has been specified. Raise + :exc:`~exceptions.EnvironmentError`, if the creation of the monitor + failed. + """ + if source not in ('kernel', 'udev'): + raise ValueError('Invalid source: {0!r}. Must be one of "udev" ' + 'or "kernel"'.format(source)) + monitor = context._libudev.udev_monitor_new_from_netlink( + context, ensure_byte_string(source)) + if not monitor: + raise EnvironmentError('Could not create udev monitor') + return cls(context, monitor) + + @property + def started(self): + """ + ``True``, if this monitor was started, ``False`` otherwise. Readonly. + + .. seealso:: :meth:`start()` + .. versionadded:: 0.16 + """ + return self._started + + def fileno(self): + # pylint: disable=anomalous-backslash-in-string + """ + Return the file description associated with this monitor as integer. + + This is really a real file descriptor ;), which can be watched and + :func:`select.select`\ ed. + """ + return self._libudev.udev_monitor_get_fd(self) + + def filter_by(self, subsystem, device_type=None): + """ + Filter incoming events. + + ``subsystem`` is a byte or unicode string with the name of a + subsystem (e.g. ``'input'``). Only events originating from the + given subsystem pass the filter and are handed to the caller. + + If given, ``device_type`` is a byte or unicode string specifying the + device type. Only devices with the given device type are propagated + to the caller. If ``device_type`` is not given, no additional + filter for a specific device type is installed. + + These filters are executed inside the kernel, and client processes + will usually not be woken up for device, that do not match these + filters. + + .. versionchanged:: 0.15 + This method can also be after :meth:`start()` now. + """ + subsystem = ensure_byte_string(subsystem) + if device_type is not None: + device_type = ensure_byte_string(device_type) + self._libudev.udev_monitor_filter_add_match_subsystem_devtype( + self, subsystem, device_type) + self._libudev.udev_monitor_filter_update(self) + + def filter_by_tag(self, tag): + """ + Filter incoming events by the given ``tag``. + + ``tag`` is a byte or unicode string with the name of a tag. Only + events for devices which have this tag attached pass the filter and are + handed to the caller. + + Like with :meth:`filter_by` this filter is also executed inside the + kernel, so that client processes are usually not woken up for devices + without the given ``tag``. + + .. udevversion:: 154 + + .. versionadded:: 0.9 + + .. versionchanged:: 0.15 + This method can also be after :meth:`start()` now. + """ + self._libudev.udev_monitor_filter_add_match_tag( + self, ensure_byte_string(tag)) + self._libudev.udev_monitor_filter_update(self) + + def remove_filter(self): + """ + Remove any filters installed with :meth:`filter_by()` or + :meth:`filter_by_tag()` from this monitor. + + .. warning:: + + Up to udev 181 (and possibly even later versions) the underlying + ``udev_monitor_filter_remove()`` seems to be broken. If used with + affected versions this method always raises + :exc:`~exceptions.ValueError`. + + Raise :exc:`~exceptions.EnvironmentError` if removal of installed + filters failed. + + .. versionadded:: 0.15 + """ + self._libudev.udev_monitor_filter_remove(self) + self._libudev.udev_monitor_filter_update(self) + + def enable_receiving(self): + """ + Switch the monitor into listing mode. + + Connect to the event source and receive incoming events. Only after + calling this method, the monitor listens for incoming events. + + .. note:: + + This method is implicitly called by :meth:`__iter__`. You don't + need to call it explicitly, if you are iterating over the + monitor. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :meth:`start()` instead. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use Monitor.start() instead.', + DeprecationWarning) + self.start() + + def start(self): + """ + Start this monitor. + + The monitor will not receive events until this method is called. This + method does nothing if called on an already started :class:`Monitor`. + + .. note:: + + Typically you don't need to call this method. It is implicitly + called by :meth:`poll()` and :meth:`__iter__()`. + + .. seealso:: :attr:`started` + .. versionchanged:: 0.16 + This method does nothing if the :class:`Monitor` was already + started. + """ + if not self._started: + self._libudev.udev_monitor_enable_receiving(self) + # Force monitor FD into non-blocking mode + pipe.set_fd_status_flag(self, os.O_NONBLOCK) + self._started = True + + def set_receive_buffer_size(self, size): + """ + Set the receive buffer ``size``. + + ``size`` is the requested buffer size in bytes, as integer. + + .. note:: + + The CAP_NET_ADMIN capability must be contained in the effective + capability set of the caller for this method to succeed. Otherwise + :exc:`~exceptions.EnvironmentError` will be raised, with ``errno`` + set to :data:`~errno.EPERM`. Unprivileged processes typically lack + this capability. You can check the capabilities of the current + process with the python-prctl_ module: + + >>> import prctl + >>> prctl.cap_effective.net_admin + + Raise :exc:`~exceptions.EnvironmentError`, if the buffer size could not + bet set. + + .. versionadded:: 0.13 + + .. _python-prctl: http://packages.python.org/python-prctl + """ + self._libudev.udev_monitor_set_receive_buffer_size(self, size) + + def _receive_device(self): + """Receive a single device from the monitor. + + Return the received :class:`Device`, or ``None`` if no device could be + received. + + """ + while True: + try: + device_p = self._libudev.udev_monitor_receive_device(self) + return Device(self.context, device_p) if device_p else None + except EnvironmentError as error: + if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): + # No data available + return None + elif error.errno == errno.EINTR: + # Try again if our system call was interrupted + continue + else: + raise + + def poll(self, timeout=None): + """ + Poll for a device event. + + You can use this method together with :func:`iter()` to synchronously + monitor events in the current thread:: + + for device in iter(monitor.poll, None): + print('{0.action} on {0.device_path}'.format(device)) + + Since this method will never return ``None`` if no ``timeout`` is + specified, this is effectively an endless loop. With + :func:`functools.partial()` you can also create a loop that only waits + for a specified time:: + + for device in iter(partial(monitor.poll, 3), None): + print('{0.action} on {0.device_path}'.format(device)) + + This loop will only wait three seconds for a new device event. If no + device event occurred after three seconds, the loop will exit. + + ``timeout`` is a floating point number that specifies a time-out in + seconds. If omitted or ``None``, this method blocks until a device + event is available. If ``0``, this method just polls and will never + block. + + .. note:: + + This method implicitly calls :meth:`start()`. + + Return the received :class:`Device`, or ``None`` if a timeout + occurred. Raise :exc:`~exceptions.EnvironmentError` if event retrieval + failed. + + .. seealso:: + + :attr:`Device.action` + The action that created this event. + + :attr:`Device.sequence_number` + The sequence number of this event. + + .. versionadded:: 0.16 + """ + if timeout is not None and timeout > 0: + # .poll() takes timeout in milliseconds + timeout = int(timeout * 1000) + self.start() + if eintr_retry_call(poll.Poll.for_events((self, 'r')).poll, timeout): + return self._receive_device() + else: + return None + + def receive_device(self): + """ + Receive a single device from the monitor. + + .. warning:: + + You *must* call :meth:`start()` before calling this method. + + The caller must make sure, that there are events available in the + event queue. The call blocks, until a device is available. + + If a device was available, return ``(action, device)``. ``device`` + is the :class:`Device` object describing the device. ``action`` is + a string describing the action. Usual actions are: + + ``'add'`` + A device has been added (e.g. a USB device was plugged in) + ``'remove'`` + A device has been removed (e.g. a USB device was unplugged) + ``'change'`` + Something about the device changed (e.g. a device property) + ``'online'`` + The device is online now + ``'offline'`` + The device is offline now + + Raise :exc:`~exceptions.EnvironmentError`, if no device could be + read. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :meth:`Monitor.poll()` instead. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use Monitor.poll() instead.', + DeprecationWarning) + device = self.poll() + return device.action, device + + def __iter__(self): + """ + Wait for incoming events and receive them upon arrival. + + This methods implicitly calls :meth:`start()`, and starts polling the + :meth:`fileno` of this monitor. If a event comes in, it receives the + corresponding device and yields it to the caller. + + The returned iterator is endless, and continues receiving devices + without ever stopping. + + Yields ``(action, device)`` (see :meth:`receive_device` for a + description). + + .. deprecated:: 0.16 + Will be removed in 1.0. Use an explicit loop over :meth:`poll()` + instead, or monitor asynchronously with :class:`MonitorObserver`. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use an explicit loop over ' + '"poll()" instead, or monitor asynchronously with ' + '"MonitorObserver".', DeprecationWarning) + self.start() + while True: + device = self.poll() + if device is not None: + yield device.action, device + + +class MonitorObserver(Thread): + """ + An asynchronous observer for device events. + + This class subclasses :class:`~threading.Thread` class to asynchronously + observe a :class:`Monitor` in a background thread: + + >>> from pyudev import Context, Monitor, MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> def print_device_event(device): + ... print('background event {0.action}: {0.device_path}'.format(device)) + >>> observer = MonitorObserver(monitor, callback=print_device_event, name='monitor-observer') + >>> observer.daemon + True + >>> observer.start() + + In the above example, input device events will be printed in background, + until :meth:`stop()` is called on ``observer``. + + .. note:: + + Instances of this class are always created as daemon thread. If you do + not want to use daemon threads for monitoring, you need explicitly set + :attr:`~threading.Thread.daemon` to ``False`` before invoking + :meth:`~threading.Thread.start()`. + + .. seealso:: + + :attr:`Device.action` + The action that created this event. + + :attr:`Device.sequence_number` + The sequence number of this event. + + .. versionadded:: 0.14 + + .. versionchanged:: 0.15 + :meth:`Monitor.start()` is implicitly called when the thread is started. + """ + + def __init__(self, monitor, event_handler=None, callback=None, *args, + **kwargs): + """ + Create a new observer for the given ``monitor``. + + ``monitor`` is the :class:`Monitor` to observe. ``callback`` is the + callable to invoke on events, with the signature ``callback(device)`` + where ``device`` is the :class:`Device` that caused the event. + + .. warning:: + + ``callback`` is invoked in the observer thread, hence the observer + is blocked while callback executes. + + ``args`` and ``kwargs`` are passed unchanged to the constructor of + :class:`~threading.Thread`. + + .. deprecated:: 0.16 + The ``event_handler`` argument will be removed in 1.0. Use + the ``callback`` argument instead. + .. versionchanged:: 0.16 + Add ``callback`` argument. + """ + if callback is None and event_handler is None: + raise ValueError('callback missing') + elif callback is not None and event_handler is not None: + raise ValueError('Use either callback or event handler') + + Thread.__init__(self, *args, **kwargs) + self.monitor = monitor + # observer threads should not keep the interpreter alive + self.daemon = True + self._stop_event = None + if event_handler is not None: + import warnings + warnings.warn('"event_handler" argument will be removed in 1.0. ' + 'Use Monitor.poll() instead.', DeprecationWarning) + callback = lambda d: event_handler(d.action, d) + self._callback = callback + + def start(self): + """Start the observer thread.""" + if not self.is_alive(): + self._stop_event = pipe.Pipe.open() + Thread.start(self) + + def run(self): + self.monitor.start() + notifier = poll.Poll.for_events( + (self.monitor, 'r'), (self._stop_event.source, 'r')) + while True: + for file_descriptor, event in eintr_retry_call(notifier.poll): + if file_descriptor == self._stop_event.source.fileno(): + # in case of a stop event, close our pipe side, and + # return from the thread + self._stop_event.source.close() + return + elif file_descriptor == self.monitor.fileno() and event == 'r': + read_device = partial(eintr_retry_call, self.monitor.poll, timeout=0) + for device in iter(read_device, None): + self._callback(device) + else: + raise EnvironmentError('Observed monitor hung up') + + def send_stop(self): + """ + Send a stop signal to the background thread. + + The background thread will eventually exit, but it may still be running + when this method returns. This method is essentially the asynchronous + equivalent to :meth:`stop()`. + + .. note:: + + The underlying :attr:`monitor` is *not* stopped. + """ + if self._stop_event is None: + return + with self._stop_event.sink: + # emit a stop event to the thread + eintr_retry_call(self._stop_event.sink.write, b'\x01') + self._stop_event.sink.flush() + + def stop(self): + """ + Synchronously stop the background thread. + + .. note:: + + This method can safely be called from the observer thread. In this + case it is equivalent to :meth:`send_stop()`. + + Send a stop signal to the backgroud (see :meth:`send_stop`), and waits + for the background thread to exit (see :meth:`~threading.Thread.join`) + if the current thread is *not* the observer thread. + + After this method returns in a thread *that is not the observer + thread*, the ``callback`` is guaranteed to not be invoked again + anymore. + + .. note:: + + The underlying :attr:`monitor` is *not* stopped. + + .. versionchanged:: 0.16 + This method can be called from the observer thread. + """ + self.send_stop() + try: + self.join() + except RuntimeError: + pass diff --git a/scripts/pyudev/pyqt4.py b/scripts/pyudev/pyqt4.py new file mode 100644 index 0000000..dc1d56b --- /dev/null +++ b/scripts/pyudev/pyqt4.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +""" + pyudev.pyqt4 + ============ + + PyQt4 integration. + + :class:`MonitorObserver` integrates device monitoring into the PyQt4\_ + mainloop by turning device events into Qt signals. + + :mod:`PyQt4.QtCore` from PyQt4\_ must be available when importing this + module. + + .. _PyQt4: http://riverbankcomputing.co.uk/software/pyqt/intro + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PyQt4 import QtCore + +from ._qt_base import MonitorObserverGenerator +from ._qt_base import QUDevMonitorObserverGenerator + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) + + +""" +.. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. +""" +QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) diff --git a/scripts/pyudev/pyqt5.py b/scripts/pyudev/pyqt5.py new file mode 100644 index 0000000..73ab057 --- /dev/null +++ b/scripts/pyudev/pyqt5.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.pyqt5 + ============ + + PyQt5 integration. + + :class:`MonitorObserver` integrates device monitoring into the PyQt5_ + mainloop by turning device events into Qt signals. + + :mod:`PyQt5.QtCore` from PyQt5_ must be available when importing this + module. + + .. _gPyQt5: http://riverbankcomputing.co.uk/software/pyqt/intro + + .. moduleauthor:: Tobias Gehring +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PyQt5 import QtCore + +from ._qt_base import MonitorObserverGenerator + + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) diff --git a/scripts/pyudev/pyside.py b/scripts/pyudev/pyside.py new file mode 100644 index 0000000..b090fa8 --- /dev/null +++ b/scripts/pyudev/pyside.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +""" + pyudev.pyside + ============= + + PySide integration. + + :class:`QUDevMonitorObserver` integrates device monitoring into the PySide\_ + mainloop by turing device events into Qt signals. + + :mod:`PySide.QtCore` from PySide\_ must be available when importing this + module. + + .. _PySide: http://www.pyside.org + + .. moduleauthor:: Sebastian Wiesner + .. versionadded:: 0.6 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PySide import QtCore + +from ._qt_base import MonitorObserverGenerator +from ._qt_base import QUDevMonitorObserverGenerator + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.Signal, + QtCore.QSocketNotifier +) + +""" +.. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. +""" +QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.Signal, + QtCore.QSocketNotifier +) diff --git a/scripts/pyudev/version.py b/scripts/pyudev/version.py new file mode 100644 index 0000000..43c1e96 --- /dev/null +++ b/scripts/pyudev/version.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.version + ============== + + Version information. + + .. moduleauthor:: mulhern +""" + +__version_info__ = (0, 21, 0, '') +__version__ = "%s%s" % \ + ( + ".".join(str(x) for x in __version_info__[:3]), + "".join(str(x) for x in __version_info__[3:]) + ) diff --git a/scripts/pyudev/wx.py b/scripts/pyudev/wx.py new file mode 100644 index 0000000..8279dd4 --- /dev/null +++ b/scripts/pyudev/wx.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +"""pyudev.wx + ========= + + Wx integration. + + :class:`MonitorObserver` integrates device monitoring into the wxPython\_ + mainloop by turing device events into wx events. + + :mod:`wx` from wxPython\_ must be available when importing this module. + + .. _wxPython: http://wxpython.org/ + + .. moduleauthor:: Tobias Eberle + .. versionadded:: 0.14 + +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from wx import EvtHandler, PostEvent +from wx.lib.newevent import NewEvent + +import pyudev + + +DeviceEvent, EVT_DEVICE_EVENT = NewEvent() + + +class MonitorObserver(EvtHandler): + """ + An observer for device events integrating into the :mod:`wx` mainloop. + + This class inherits :class:`~wx.EvtHandler` to turn device events into + wx events: + + >>> from pyudev import Context, Monitor + >>> from pyudev.wx import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(event): + ... print('action {0} on device {1}'.format(event.device.action, event.device)) + >>> observer.Bind(EVT_DEVICE_EVENT, device_event) + >>> monitor.start() + + This class is a child of :class:`wx.EvtHandler`. + + .. versionadded:: 0.17 + """ + + def __init__(self, monitor): + EvtHandler.__init__(self) + self.monitor = monitor + self._observer_thread = None + self.start() + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. + """ + return self._observer_thread is not None + + @enabled.setter + def enabled(self, value): + if value: + self.start() + else: + self.stop() + + def start(self): + """ + Enable this observer. + + Do nothing, if the observer is already enabled. + """ + if self._observer_thread is not None: + return + self._observer_thread = pyudev.MonitorObserver( + self.monitor, callback=self._emit_event, + name='wx-observer-thread') + self._observer_thread.start() + + def stop(self): + """ + Disable this observer. + + Do nothing, if the observer is already disabled. + """ + if self._observer_thread is None: + return + self._observer_thread.stop() + + def _emit_event(self, device): + PostEvent(self, DeviceEvent(device=device)) + + +DeviceAddedEvent, EVT_DEVICE_ADDED = NewEvent() +DeviceRemovedEvent, EVT_DEVICE_REMOVED = NewEvent() +DeviceChangedEvent, EVT_DEVICE_CHANGED = NewEvent() +DeviceMovedEvent, EVT_DEVICE_MOVED = NewEvent() + + +class WxUDevMonitorObserver(MonitorObserver): + """An observer for device events integrating into the :mod:`wx` mainloop. + + .. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. + """ + + _action_event_map = { + 'add': DeviceAddedEvent, + 'remove': DeviceRemovedEvent, + 'change': DeviceChangedEvent, + 'move': DeviceMovedEvent + } + + def __init__(self, monitor): + MonitorObserver.__init__(self, monitor) + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.wx.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + PostEvent(self, DeviceEvent(action=device.action, device=device)) + event_class = self._action_event_map.get(device.action) + if event_class is not None: + PostEvent(self, event_class(device=device)) diff --git a/scripts/qemu.py b/scripts/qemu.py new file mode 100644 index 0000000..2d6b85e --- /dev/null +++ b/scripts/qemu.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Name: qemu.py +# Purpose: Module to boot ISO and USB disks using QEMU. +# Depends: QEMU must be installed under Linux for availing this feature. For windows, QEMU package is shipped +# along with executable file +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import subprocess +import platform +from .admin import adminCmd +from PyQt5 import QtWidgets +from .gui.ui_multibootusb import Ui_Dialog +from .gen import * + + +class Qemu(QtWidgets.QDialog, Ui_Dialog): + """ + ISO and USB booting using QEMU. + """ + def on_Qemu_Browse_iso_Click(self): + """ + Browse and choose an ISO. + :return: + """ + self.ui.lineEdit_2.clear() + + qemu = self.check_qemu_exist() + + if not qemu is None: + + qemu_iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', "", "ISO Files (*.iso)")[0] + else: + print("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu package to avail this feature.') + qemu_iso_link = None + + if not qemu_iso_link is None: + self.ui.lineEdit_2.insert(qemu_iso_link) + else: + print ("File not selected.") + + def on_Qemu_Boot_iso_Click(self): + """ + Main function to boot a selected ISO. + :return: + """ + if not self.ui.lineEdit_2.text(): + QtWidgets.QMessageBox.information(self, 'No ISO...', 'No ISO selected.\n\nPlease choose an iso and click Boot ISO.') + else: + qemu = self.check_qemu_exist() + qemu_iso_link = str(self.ui.lineEdit_2.text()) + if qemu is None: + print("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to avail this feature.') + else: + ram = self.qemu_iso_ram() + if not ram is None: + self.ui.lineEdit_2.clear() + if platform.system() == "Windows": + try: + print("Executing ==> " + qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram) + subprocess.Popen(qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Unable to start QEMU.') + else: + print(qemu + ' -m ' + ram + ' -cdrom ' + str(qemu_iso_link) + ' -boot d') + try: + print("Executing ==> " + qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram) + subprocess.Popen(qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting ISO\n' + 'Unable to start QEMU.') + else: + QtWidgets.QMessageBox.information(self, 'No ram...', 'No ram selected.\n\nPlease choose any ram value and click Boot ISO.') + + + def on_Qemu_Boot_usb_Click(self, usb_disk): + """ + Main function to boot a selected USB disk. + :param usb_disk: Path to usb disk. + :return: + """ + qemu = self.check_qemu_exist() + + if qemu is None: + print("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to avail this feature.') + else: + ram = self.qemu_usb_ram() + if ram is None: + QtWidgets.QMessageBox.information(self, 'No ram...', 'No ram selected.\n\nPlease choose any ram value and click Boot USB.') + else: + if platform.system() == "Windows": + disk_number = self.get_physical_disk_number(usb_disk) + parent_dir = os.getcwd() + os.chdir(resource_path(os.path.join("data", "tools", "qemu"))) + try: + print("Executing ==> " + qemu + " -L . -boot c -m " + ram + " -hda //./PhysicalDrive" + disk_number) + subprocess.Popen("qemu-system-x86_64.exe -L . -boot c -m " + ram + " -hda //./PhysicalDrive" + disk_number, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n' + 'Unable to start QEMU.') + os.chdir(parent_dir) + elif platform.system() == "Linux": + try: + qemu_cmd = qemu + ' -hda ' + usb_disk[:-1] + ' -m ' + ram + ' -vga std' + print('Executing ==>', qemu_cmd) + # adminCmd([qemu, '-hda', usb_disk[:-1], '-m', ram, '-vga std'], gui=True) + subprocess.Popen(qemu_cmd, shell=True) + # adminCmd(qemu_cmd, gui=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n\nUnable to start QEMU.') + + def qemu_iso_ram(self): + """ + Choose a ram size for ISO booting. + :return: Ram size as string. + """ + if self.ui.ram_iso_256.isChecked(): + return str(256) + elif self.ui.ram_iso_512.isChecked(): + return str(512) + elif self.ui.ram_iso_768.isChecked(): + return str(768) + elif self.ui.ram_iso_1024.isChecked(): + return str(1024) + elif self.ui.ram_iso_2048.isChecked(): + return str(2048) + else: + return None + + def qemu_usb_ram(self): + """ + Choose a ram size for USB booting. + :return: Ram size as string. + """ + if self.ui.ram_usb_256.isChecked(): + return str(256) + if self.ui.ram_usb_512.isChecked(): + return str(512) + if self.ui.ram_usb_768.isChecked(): + return str(768) + if self.ui.ram_usb_1024.isChecked(): + return str(1024) + if self.ui.ram_usb_2048.isChecked(): + return str(2048) + else: + return None + + def check_qemu_exist(self): + """ + Check if QEMU is available on host system. + :return: path to QEMU program or None otherwise. + """ + if platform.system() == "Linux": + if subprocess.call('which qemu-system-x86_64', shell=True) == 0: + print("qemu-system-x86_64 exists...") + qemu = "qemu-system-x86_64" + elif subprocess.call('which qemu', shell=True) == 0: + print("qemu exists") + qemu = "qemu" + else: + qemu = None + + if qemu: + return qemu + else: + return None + elif platform.system() == "Windows": + print(resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe"))) + return resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe")) + + def get_physical_disk_number(self, usb_disk): + """ + Get the physical disk number as detected ny Windows. + :param usb_disk: USB disk (Like F:) + :return: Disk number. + """ + import wmi + c = wmi.WMI () + for physical_disk in c.Win32_DiskDrive (): + for partition in physical_disk.associators ("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators ("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == usb_disk: + """ + print physical_disk.Caption + print partition.Caption + print logical_disk.Caption + """ + print("Physical Device Number is " + partition.Caption[6:-14]) + return str(partition.Caption[6:-14]) diff --git a/scripts/syslinux.py b/scripts/syslinux.py new file mode 100644 index 0000000..abfc3e3 --- /dev/null +++ b/scripts/syslinux.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Name: syslinux.py +# Purpose: Module to install syslinux and extlinux on selected USB disk. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import sys +import subprocess +import platform +from .gen import * +from . import usb +from .iso import * +from . import config + +extlinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux4") +syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4") +extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"] +syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"] +mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin")) + + +def set_boot_flag(usb_disk): + if platform.system() == "Linux": + print("\nChecking boot flag on " + usb_disk[:-1], '\n') + cmd_out = subprocess.check_output("parted -m -s " + usb_disk[:-1] + " print", shell=True) + if b'boot' in cmd_out: + print("\nDisk " + usb_disk[:-1] + " already has boot flag.\n") + return True + else: + print("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 boot on", '\n') + if subprocess.call("parted " + usb_disk[:-1] + " set 1 boot on", shell=True) == 0: + print("\nBoot flag set to bootable " + usb_disk[:-1], '\n') + return True + else: + print("\nUnable to set boot flag on " + usb_disk[:-1], '\n') + return False + + +def syslinux_default(usb_disk, version=4): + """ + Install Syslinux of a selected drive + :param usb_disk: '/dev/sdx' on linux and 'E:' on Windows + :version: Default version is 4. Change it if you wish. But necessary files needs to be copied accordingly + :return: Bootable USB disk :-) + """ + usb_details = usb.details(usb_disk) + usb_fs = usb_details['file_system'] + usb_mount = usb_details['mount_point'] + mbr_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin + ' of=' + usb_disk[:-1] + print(usb_fs) + if usb_fs in extlinux_fs: + extlinu_cmd = extlinux_path + ' --install ' + os.path.join(usb_mount, 'multibootusb') + if os.access(extlinux_path, os.X_OK) is False: + subprocess.call('chmod +x ' + extlinux_path, shell=True) + print("\nExecuting ==> " + extlinu_cmd) + if subprocess.call(extlinu_cmd, shell=True) == 0: + print("\nDefault Extlinux install is success...\n") + print('\nExecuting ==> ' + mbr_install_cmd) + if subprocess.call(mbr_install_cmd, shell=True) == 0: + print("\nmbr install is success...\n") + if set_boot_flag(usb_disk) is True: + return True + + elif usb_fs in syslinux_fs: + + if platform.system() == "Linux": + syslinux_cmd = syslinux_path + ' -i -d multibootusb ' + usb_disk + if os.access(syslinux_path, os.X_OK) is False: + subprocess.call('chmod +x ' + syslinux_path, shell=True) + print("\nExecuting ==> " + syslinux_cmd + "\n") + if subprocess.call(syslinux_cmd, shell=True) == 0: + print("\nDefault syslinux install is success...\n") + if subprocess.call(mbr_install_cmd, shell=True) == 0: + print("\nmbr install is success...\n") + if set_boot_flag(usb_disk) is True: + return True + else: + print("\nFailed to install default syslinux...\n") + return False + + elif platform.system() == "Windows": + syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe")) + print('Executing ==>', syslinux + ' -maf -d multibootusb ' + usb_disk) + if subprocess.call(syslinux + ' -maf -d multibootusb ' + usb_disk, shell=True) == 0: + print("\nDefault syslinux install is success...\n") + return True + else: + print("\nFailed to install default syslinux...\n") + return False + + +def syslinux_distro_dir(usb_disk, iso_link, distro): + """ + Install syslinux/extlinux on distro specific isolinux directory. + :param usb_disk: '/dev/sdx' on linux and 'E:' on Windows + :param iso_link: Path to ISO file + :return: + """ + usb_details = usb.details(usb_disk) + usb_fs = usb_details['file_system'] + usb_mount = usb_details['mount_point'] + isolinux_bin_dir(iso_link) + if isolinux_bin_exist(iso_link) is False: + print('Distro does not use isolinux for booting ISO.') + else: + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(iso_link)[1:]) + iso_linux_bin_dir = isolinux_bin_dir(iso_link) + config.syslinux_version = isolinux_version(isolinux_path) + + if distro == "generic" or distro == "alpine": + install_dir = usb_mount + distro_syslinux_install_dir = os.path.join(usb_mount, iso_linux_bin_dir[1:].strip("/")).replace(usb_mount, "") + distro_sys_install_bs = os.path.join(install_dir, iso_linux_bin_dir[1:].strip("/"), distro + '.bs') + else: + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + distro_syslinux_install_dir = os.path.join(install_dir, iso_linux_bin_dir[1:].strip("/")).replace(usb_mount, "") + distro_sys_install_bs = os.path.join(install_dir, iso_linux_bin_dir[1:].strip("/"), distro + '.bs') + print(distro_sys_install_bs) + print(distro_syslinux_install_dir) + + if usb_fs in syslinux_fs: + if config.syslinux_version == str(3): + if distro == "generic" and iso_linux_bin_dir == "/": + option = "" + else: + option = " -d " + else: + if distro == "generic" and iso_linux_bin_dir == "/": + option = " -i " + else: + option = " -i -d " + + if platform.system() == "Linux": + syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux") + config.syslinux_version + if os.access(syslinux_path, os.X_OK) is False: + subprocess.call('chmod +x ' + syslinux_path, shell=True) == 0 + sys_cmd = syslinux_path + option + quote(distro_syslinux_install_dir) + ' ' + usb_disk + dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + quote(distro_sys_install_bs) + ' count=1' + print("Executing ==> ", sys_cmd) + if subprocess.call(sys_cmd, shell=True) == 0: + print("\nSyslinux install on distro directory is success...\n") + print('Executing ==> ', dd_cmd, '\n') + if subprocess.call(dd_cmd, shell=True) == 0: + print("\nBootsector copy is success...\n") + else: + print("\nFailed to copy boot sector...\n") + else: + print("\nFailed to install syslinux on distro directory...\n") + elif platform.system() == "Windows": + syslinux_path = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin")) + \ + "\syslinux" + config.syslinux_version + ".exe" + distro_syslinux_install_dir = "/" + distro_syslinux_install_dir.replace("\\", "/") + distro_sys_install_bs = distro_sys_install_bs.replace("/", "\\") + sys_cmd = syslinux_path + option + distro_syslinux_install_dir + ' ' + usb_disk + ' ' + \ + distro_sys_install_bs + print("\nExecuting ==> ", sys_cmd, '\n') + if subprocess.call(sys_cmd, shell=True) == 0: + print("\nSyslinux install was successful on distro directory...\n") + else: + print("\nFailed to install syslinux on distro directory...\n") + elif usb_fs in extlinux_fs: + if platform.system() == "Linux": + distro_syslinux_install_dir = os.path.join(install_dir, iso_linux_bin_dir.strip("/")) + syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux") + config.syslinux_version + ext_cmd = syslinux_path + " --install " + distro_syslinux_install_dir + dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + usb_mount + quote(distro_sys_install_bs) + ' count=1' + if os.access(syslinux_path, os.X_OK) is False: + subprocess.call('chmod +x ' + syslinux_path, shell=True) == 0 + print("Executing ==> ", ext_cmd) + if subprocess.call(ext_cmd, shell=True) == 0: + print("\nSyslinux install on distro directory is success...\n") + print('Executing ==> ', dd_cmd, '\n') + if subprocess.call(dd_cmd, shell=True) == 0: + print("\nBootsector copy is success...\n") + else: + print("\nFailed to install syslinux on distro directory...\n") + +if __name__ == '__main__': + if os.geteuid() != 0: + print('Please running this script with sudo/root/admin privilage.') + exit(1) + else: + syslinux_distro_dir('/dev/sdb1', '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso', 'debian') + syslinux_default('/dev/sdb1') diff --git a/scripts/uninstall_distro.py b/scripts/uninstall_distro.py new file mode 100644 index 0000000..985dda8 --- /dev/null +++ b/scripts/uninstall_distro.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Name: uninstall_distro.py +# Purpose: Module to uninstall distros installed by multibootusb +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import re +import shutil +import threading +import platform +from .usb import * +from . import config + + +def install_distro_list(): + """ + List all distro names installed by previous install + :return: List of distro names as list + """ + usb_details = details(config.usb_disk) + config.usb_mount = usb_details['mount_point'] + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + + if os.path.exists(sys_cfg_file): + distro_list = [] + for line in open(sys_cfg_file): + if "#start " in line: + distro_list.append(line[7:]) + return distro_list + else: + return None + + +def unin_distro(): + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + config.uninstall_distro_dir_name = config.uninstall_distro_dir_name.replace('\n', '') + print(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")) + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg"), "r") as multibootusb_cfg: + config.distro = multibootusb_cfg.read().replace('\n', '') + if config.distro: + uninstall_distro() + else: + return "" + + +def delete_frm_file_list(): + """ + Generic way to remove files from USB disk. + :param config.usb_disk: + :param iso_file_list: List of files installed in the USB disk + :param config.uninstall_distro_dir_name: Directory where the distro is installed + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + if config.iso_file_list is not None: + for f in config.iso_file_list: + if platform.system() == "Windows": + f = f.replace('\n', '').strip("/").replace("/", "\\") + else: + f = f.replace('\n', '').strip("/") + if os.path.exists(os.path.join(usb_mount, "ldlinux.sys")): + os.chmod(os.path.join(usb_mount, "ldlinux.sys"), 0o777) + os.unlink(os.path.join(usb_mount, "ldlinux.sys")) + + if os.path.exists(os.path.join(usb_mount, f)): + print("Removing " + (os.path.join(usb_mount, f))) + if os.path.isfile(os.path.join(usb_mount, f)): + os.remove(os.path.join(usb_mount, f)) + elif os.path.isdir(os.path.join(usb_mount, f)): + shutil.rmtree(os.path.join(usb_mount, f)) + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "generic.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "generic.cfg"), "r") as generic_cfg: + if platform.system() == "Windows": + generic = generic_cfg.read().replace('\n', '').replace("/", "\\") + else: + generic = generic_cfg.read().replace('\n', '') + if os.path.exists(os.path.join(usb_mount, generic.strip("/"))): + os.remove(os.path.join(usb_mount, generic.strip("/"))) + if platform.system() == 'Linux': + print('Removed files from', config.uninstall_distro_dir_name) + print('Syncing....') + os.system('sync') + + + + +def uninstall_distro(): + """ + Uninstall selected distro from selected USB disk. + :param config.usb_disk: Path of the USB disk + :param config.uninstall_distro_dir_name: Directory where the distro is installed + :param _distro: Generic name applied to distro install by multibootusb + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + + if platform.system() == 'Linux': + os.system('sync') + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "iso_file_list.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "iso_file_list.cfg"), "r") as f: + config.iso_file_list = f.readlines() + # print iso_file_list + + for path, subdirs, files in os.walk(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)): + for name in files: + if name.endswith('ldlinux.sys') or name.endswith('ldlinux.c32'): + os.chmod(os.path.join(path, name), 0o777) + os.unlink(os.path.join(path, name)) + if config.distro == "opensuse": + if os.path.exists(os.path.join(usb_mount, config.uninstall_distro_dir_name + ".iso")): + os.remove(os.path.join(usb_mount, config.uninstall_distro_dir_name + ".iso")) + elif config.distro == "windows" or config.distro == "alpine" or config.distro == "generic": + delete_frm_file_list() + if config.distro == "ipfire": + files = os.listdir(usb_mount) + for f in files: + if f.endswith('.tlz'): + os.remove(os.path.join(usb_mount, f)) + if os.path.exists(os.path.join(usb_mount, "distro.img")): + os.remove(os.path.join(usb_mount, "distro.img")) + elif config.distro == "trinity-rescue": + shutil.rmtree(os.path.join(usb_mount, "trk3")) + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)): + if platform.system() == 'Linux': + os.system('sync') + shutil.rmtree(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)) + + delete_frm_file_list() + + update_sys_cfg_file() + + +def update_sys_cfg_file(): + """ + Main function to remove uninstall distro specific operations. + :return: + """ + if platform.system() == 'Linux': + os.system('sync') + + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + if not os.path.exists(sys_cfg_file): + print("syslinux.cfg file not found for updating changes.") + else: + print("Updating syslinux.cfg file...") + string = open(sys_cfg_file).read() + string = re.sub(r'#start ' + config.uninstall_distro_dir_name + '.*?' + '#end ' + config.uninstall_distro_dir_name + '\s*', '', string, flags=re.DOTALL) + config_file = open(sys_cfg_file, "w") + config_file.write(string) + config_file.close() + + +def uninstall_progress(): + """ + Calculate uninstall progress percentage. + :return: + """ + from . import progressbar + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + if platform.system() == 'Linux': + os.system('sync') + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg"), + "r") as multibootusb_cfg: + config.distro = multibootusb_cfg.read().replace('\n', '') + else: + config.distro = "" + print("Installed distro type is", config.distro) + + if config.distro == "opensuse": + if os.path.exists(os.path.join(usb_mount, config.uninstall_distro_dir_name) + ".iso"): + folder_size_to_remove = os.path.getsize(os.path.join(usb_mount, config.uninstall_distro_dir_name) + ".iso") + else: + folder_size_to_remove = 0 + folder_size_to_remove += disk_usage(str(usb_mount) + "/multibootusb/" + config.uninstall_distro_dir_name).used + elif config.distro == "windows" or config.distro == "Windows": + folder_size_to_remove = disk_usage(str(usb_mount) + "/SOURCES").used + elif config.distro == "ipfire": + folder_size_to_remove = disk_usage(str(usb_mount) + "/multibootusb/" + config.uninstall_distro_dir_name).used + files = os.listdir(os.path.join(str(usb_mount))) + for f in files: + if f.endswith('.tlz'): + folder_size_to_remove += os.path.getsize(os.path.join(config.usb_mount, f)) + elif config.distro == "trinity-rescue": + folder_size_to_remove = disk_usage(os.path.join(usb_mount, "trk3")).used + folder_size_to_remove += disk_usage(usb_mount + "/multibootusb/" + config.uninstall_distro_dir_name).used + else: + + folder_size_to_remove = disk_usage(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)).used + + thrd = threading.Thread(target=unin_distro, name="uninstall_progress") + initial_usb_size = disk_usage(usb_mount).used + thrd.start() + config.status_text = "Uninstalling " + config.uninstall_distro_dir_name + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while thrd.is_alive(): + current_size = disk_usage(usb_mount).used + diff_size = int(initial_usb_size - current_size) + config.percentage = round(float(diff_size) / folder_size_to_remove * 100) + if config.percentage > 100: + config.percentage = 100 + + pbar.update(config.percentage) + + if not thrd.is_alive(): + config.persistence = 0 + config.status_text = "" diff --git a/scripts/update_cfg_file.py b/scripts/update_cfg_file.py new file mode 100644 index 0000000..cc70088 --- /dev/null +++ b/scripts/update_cfg_file.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: update_cfg_file.py +# Purpose: Module to manipulate distro specific and main config files. +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import re +import shutil +from .usb import * +from .gen import * +from .iso import * +from . import config + + +def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0): + """ + Main function to modify/update distro specific strings on distro config files. + :return: + """ + usb_details = details(usb_disk) + usb_mount = usb_details['mount_point'] + usb_uuid = usb_details['uuid'] + usb_label = usb_details['label'] + patch = None + iso_cfg_ext_dir = iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + if isolinux_bin_exist(config.iso_link): + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(iso_link)[1:]) + config.status_text = "Updating config files..." + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + print('Updating distro specific config files...') + for dirpath, dirnames, filenames in os.walk(install_dir): + for f in filenames: + if f.endswith(".cfg") or f.endswith('.CFG') or f.endswith('.lst'): + cfg_file = os.path.join(dirpath, f) + try: + string = open(cfg_file, errors='ignore').read() + except IOError: + print("Unable to read ", cfg_file) + else: + if not distro == "generic": + replace_text = r'\1/multibootusb/' + iso_basename(iso_link) + '/' + string = re.sub(r'([ \t =,])/', replace_text, string) + if distro == "ubuntu": + string = re.sub(r'boot=casper', + 'boot=casper cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ' + 'ignore_bootid root=UUID=' + usb_uuid + ' live-media-path=/multibootusb/' + + iso_basename(iso_link) + '/casper', string) + string = re.sub(r'ui gfxboot', '#ui gfxboot', string) + if not persistence == 0: + string = re.sub(r'boot=casper', 'boot=casper persistent persistent-path=/multibootusb/' + + iso_basename(iso_link) + "/", string) + + elif distro == "debian" or distro == "debian-install": + string = re.sub(r'boot=live', 'boot=live ignore_bootid live-media-path=/multibootusb/' + + iso_basename(iso_link) + '/live', string) + if not persistence == 0: + string = re.sub(r'boot=live', 'boot=live persistent persistent-path=/multibootusb/' + + iso_basename(iso_link) + "/", string) + + elif distro == "ubuntu-server": + string = re.sub(r'file', + 'cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ignore_bootid root=UUID=' + + usb_uuid + ' file', string) + elif distro == "fedora": + string = re.sub(r'root=\S*', 'root=UUID=' + usb_uuid, string) + if re.search(r'liveimg', string, re.I): + string = re.sub(r'liveimg', 'liveimg live_dir=/multibootusb/' + + iso_basename(iso_link) + '/LiveOS', string) + elif re.search(r'rd.live.image', string, re.I): + string = re.sub(r'rd.live.image', 'rd.live.image rd.live.dir=/multibootusb/' + + iso_basename(iso_link) + '/LiveOS', string) + if not persistence == 0: + if re.search(r'liveimg', string, re.I): + string = re.sub(r'liveimg', + 'liveimg overlay=UUID=' + usb_uuid, string) + elif re.search(r'rd.live.image', string, re.I): + string = re.sub(r'rd.live.image', 'rd.live.image rd.live.overlay=UUID=' + usb_uuid, string) + string = re.sub(r' ro ', ' rw ', string) + elif distro == 'kaspersky': + if not os.path.exists(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')): + shutil.copyfile(resource_path(os.path.join('data', 'multibootusb', 'syslinux.cfg')), + os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')) + config_string = kaspersky_config('kaspersky') + config_string = config_string.replace('$INSTALL_DIR', '/multibootusb/' + iso_basename(iso_link)) + config_string = re.sub(r'root=live:UUID=', 'root=live:UUID=' + usb_uuid, config_string) + with open(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg'), "a") as f: + f.write(config_string) + elif distro == "parted-magic": + if re.search(r'append', string, re.I): + string = re.sub(r'append', 'append directory=/multibootusb/' + iso_basename(iso_link), string, + flags=re.I) + string = re.sub(r'initrd=', 'directory=/multibootusb/' + iso_basename(iso_link) + '/ initrd=', + string) + elif distro == "ubcd": + string = re.sub(r'iso_filename=\S*', 'directory=/multibootusb/' + iso_basename(iso_link), + string, flags=re.I) + elif distro == 'f4ubcd': + if not 'multibootusb' in string: + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', string) + if not 'multibootusb' in string: + string = re.sub(r'/F4UBCD', '/multibootusb/' + iso_basename(iso_link) + '/F4UBCD', string) + elif distro == "ipcop": + string = re.sub(r'ipcopboot=cdrom\S*', 'ipcopboot=usb', string) + elif distro == "puppy": + string = re.sub(r'pmedia=cd\S*', + 'pmedia=usbflash psubok=TRUE psubdir=/multibootusb/' + iso_basename(iso_link) + '/', + string) + elif distro == "slax": + string = re.sub(r'initrd=', + r'from=/multibootusb/' + iso_basename(iso_link) + '/slax fromusb initrd=', string) + elif distro == "knoppix": + string = re.sub(r'(append)', + r'\1 knoppix_dir=/multibootusb/' + iso_basename(iso_link) + '/KNOPPIX', + string) + elif distro == "gentoo": + string = re.sub(r'append ', + 'append real_root=' + usb_disk + ' slowusb subdir=/multibootusb/' + + iso_basename(iso_link) + '/ ', string, flags=re.I) + elif distro == "systemrescuecd": + rows = [] + subdir = '/multibootusb/' + iso_basename(iso_link) + '/' + for line in string.splitlines(True): + addline = True + if re.match(r'append.*--.*', line, flags=re.I): + line = re.sub(r'(append)(.*)--(.*)', r'\1\2subdir=' + subdir + r' --\3 subdir=' + subdir, + line, flags=re.I) + elif re.match(r'append', line, flags=re.I): + line = re.sub(r'(append)', r'\1 subdir=' + subdir, line, flags=re.I) + elif re.match(r'label rescue(32|64)_1', line, flags=re.I): + rows.append(line) + rows.append('append subdir=%s\n' % (subdir,)) + addline = False + + if addline: + rows.append(line) + + string = ''.join(rows) + elif distro == "arch" or distro == "chakra": + string = re.sub(r'isolabel=\S*', + 'isodevice=/dev/disk/by-uuid/' + usb_uuid, string, + flags=re.I) + string = re.sub(r'isobasedir=', + 'isobasedir=/multibootusb/' + iso_basename(iso_link) + '/', string, + flags=re.I) + elif distro == "suse" or distro == "opensuse": + if re.search(r'opensuse_12', string, re.I): + string = re.sub(r'append', + 'append loader=syslinux isofrom_system=/dev/disk/by-uuid/' + usb_uuid + ":/" + + iso_name(iso_link), string, flags=re.I) + else: + string = re.sub(r'append', + 'append loader=syslinux isofrom_device=/dev/disk/by-uuid/' + usb_uuid + + ' isofrom_system=/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link), + string, flags=re.I) + elif distro == "pclinuxos": + string = re.sub(r'livecd=', + 'fromusb livecd=' + '/multibootusb/' + iso_basename(iso_link) + '/', + string) + string = re.sub(r'prompt', '#prompt', string) + string = re.sub(r'ui gfxboot.com', '#ui gfxboot.com', string) + string = re.sub(r'timeout', '#timeout', string) + elif distro == "porteus" or distro == "wifislax": + string = re.sub(r'initrd=', + 'from=' + '/multibootusb/' + iso_basename(iso_link) + ' initrd=', string) + elif distro == "hbcd": + if not 'multibootusb' in string: + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', string) + elif distro == "zenwalk": + string = re.sub(r'initrd=', + 'from=/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link) + ' initrd=', + string) + elif distro == "mageialive": + string = re.sub(r'LABEL=\S*', 'LABEL=' + usb_label, string) + elif distro == "antix": + string = re.sub(r'APPEND', 'image_dir=/multibootusb/' + iso_basename(iso_link), string) + elif distro == "solydx": + string = re.sub(r'live-media-path=', 'live-media-path=/multibootusb/' + iso_basename(iso_link), + string) + elif distro == "salix-live": + string = re.sub(r'iso_path', '/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link), + string) + + config_file = open(cfg_file, "w") + config_file.write(string) + config_file.close() + + update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro) + + +def update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro): + """ + Update main multibootusb suslinux.cfg file after distro is installed. + :return: + """ + print('Updating multibootusb config file...') + sys_cfg_file = os.path.join(usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + if os.path.exists(sys_cfg_file): + + if distro == "hbcd": + if os.path.exists(os.path.join(usb_mount, "multibootusb", "menu.lst")): + _config_file = os.path.join(usb_mount, "multibootusb", "menu.lst") + config_file = open(_config_file,"w") + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', _config_file) + config_file.write(string) + config_file.close() + + if distro == "Windows": + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("KERNEL chain.c32 hd0 1 ntldr=/bootmgr" + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + if distro == 'f4ubcd': + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("KERNEL grub.exe" + "\n") + config_file.write('APPEND --config-file=/multibootusb/' + iso_basename(config.iso_link) + '/menu.lst' + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + if distro == 'kaspersky': + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("CONFIG " + '/multibootusb/' + iso_basename(config.iso_link) + '/kaspersky.cfg' + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + else: + # admin.adminCmd(["mount", "-o", "remount,rw", config.usb_disk]) + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + if distro == "salix-live": + config_file.write( + "LINUX " + '/multibootusb/' + iso_basename(iso_link) + '/boot/grub2-linux.img' + "\n") + elif distro == "pclinuxos": + config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/isolinux/vmlinuz' + "\n") + config_file.write("append livecd=livecd root=/dev/rd/3 acpi=on vga=788 keyb=us vmalloc=256M nokmsboot " + "fromusb root=UUID=" + usb_uuid + " bootfromiso=/multibootusb/" + + iso_basename(iso_link) + "/" + iso_name(iso_link) + " initrd=/multibootusb/" + + iso_basename(iso_link) + '/isolinux/initrd.gz' + "\n") + elif distro == "mentest": + config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/BOOT/MEMTEST.IMG\n') + else: + if distro == "generic": + distro_syslinux_install_dir = isolinux_bin_dir(iso_link) + if not isolinux_bin_dir(iso_link) == "/": + distro_sys_install_bs = os.path.join(usb_mount, isolinux_bin_dir(iso_link)) + '/' + distro + '.bs' + else: + distro_sys_install_bs = '/' + distro + '.bs' + else: + distro_syslinux_install_dir = install_dir + distro_syslinux_install_dir = distro_syslinux_install_dir.replace(usb_mount, '') + distro_sys_install_bs = distro_syslinux_install_dir + isolinux_bin_dir(iso_link) + '/' + distro + '.bs' + + distro_sys_install_bs = "/" + distro_sys_install_bs.replace("\\", "/") # Windows path issue. + config_file.write("BOOT " + distro_sys_install_bs.replace("//", "/") + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + + for dirpath, dirnames, filenames in os.walk(install_dir): + for f in filenames: + if f.endswith("isolinux.cfg") or f.endswith("ISOLINUX.CFG"): + shutil.copy2(os.path.join(dirpath, f), os.path.join(dirpath, "syslinux.cfg")) + + ''' + else: + if distro == "ubuntu" and config.sys_version == "6": + config_file.write("CONFIG " + "/multibootusb/" + iso_basename(iso_link) + + "/isolinux/isolinux.cfg" + "\n") + config_file.write("APPEND " + "/multibootusb/" + iso_basename(iso_link) + + "/isolinux" + "\n") + ''' + +def kaspersky_config(distro): + if distro == 'kaspersky': + return """ +menu label Kaspersky Rescue Disk + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ looptype=squashfs rootfstype=auto vga=791 init=/linuxrc loop=$INSTALL_DIR/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz lang=en udev liveimg splash quiet doscsi nomodeset +label text + menu label Kaspersky Rescue Disk - Text Mode + kernel $INSTALL_DIR/boot/rescue + append root=UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ rootfstype=auto vga=791 init=/linuxrc loop=/multiboot/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz SLUG_lang=en udev liveimg quiet nox shell noresume doscsi nomodeset +label hwinfo + menu label Kaspersky Hardware Info + kernel $INSTALL_DIR/boot/rescue + append root=UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ rootfstype=auto vga=791 init=/linuxrc loop=$INSTALL_DIR/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz SLUG_lang=en udev liveimg quiet softlevel=boot nox hwinfo noresume doscsi nomodeset """ \ No newline at end of file diff --git a/scripts/usb.py b/scripts/usb.py new file mode 100644 index 0000000..f50518c --- /dev/null +++ b/scripts/usb.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: usb.py +# Purpose: Module to list USB devices and get details under Linux and Windows +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import sys +import platform +import os +import shutil +import collections +import ctypes + + +def is_block(usb_disk): + """ + Function to detect if the USB is block device + :param usb_disk: USB disk path + :return: True is devie is block device else False + """ + import stat + if platform.system() == 'Linux': + if len(usb_disk) != 9: + return False + elif platform.system() == 'Windows': + if len(usb_disk) != 2: + return False + else: + return True + try: + mode = os.stat(usb_disk).st_mode + print(mode) + print(stat.S_ISBLK(mode)) + except: + return False + + return stat.S_ISBLK(mode) + + +def disk_usage(mount_path): + """ + Return disk usage statistics about the given path as a (total, used, free) + namedtuple. Values are expressed in bytes. + """ + # Author: Giampaolo Rodola' + # License: MIT + _ntuple_diskusage = collections.namedtuple('usage', 'total used free') + + if platform.system() == "Linux": + st = os.statvfs(mount_path) + free = st.f_bavail * st.f_frsize + total = st.f_blocks * st.f_frsize + used = (st.f_blocks - st.f_bfree) * st.f_frsize + + return _ntuple_diskusage(total, used, free) + + elif platform.system() == "Windows": + + _, total, free = ctypes.c_ulonglong(), ctypes.c_ulonglong(), \ + ctypes.c_ulonglong() + if sys.version_info >= (3,) or isinstance(mount_path, unicode): + fun = ctypes.windll.kernel32.GetDiskFreeSpaceExW + else: + fun = ctypes.windll.kernel32.GetDiskFreeSpaceExA + ret = fun(mount_path, ctypes.byref(_), ctypes.byref(total), ctypes.byref(free)) + if ret == 0: + raise ctypes.WinError() + used = total.value - free.value + + return _ntuple_diskusage(total.value, used, free.value) + else: + raise NotImplementedError("Platform not supported.") + + +def list(partition=1): + """ + List inserted USB devices. + :return: USB devices as list. + """ + devices = [] + if platform.system() == "Linux": + from . import pyudev + import dbus + + + try: + # pyudev is good enough to detect USB devices on modern Linux + # machines. + print("Using pyudev for detecting USB drives...") + context = pyudev.Context() + for device in context.list_devices(subsystem='block', DEVTYPE='partition', + ID_FS_USAGE="filesystem", ID_TYPE="disk", + ID_BUS="usb"): + if device['ID_BUS'] == "usb" and device['DEVTYPE'] == "partition": + # print(device['DEVNAME']) + devices.append(str(device['DEVNAME'])) + except: + bus = dbus.SystemBus() + try: + # You should come here only if your system does'nt have udev installed. + # We will use udiskd2 for now. + print("Falling back to Udisks2..") + ud_manager_obj = bus.get_object( + 'org.freedesktop.UDisks2', '/org/freedesktop/UDisks2') + ud_manager = dbus.Interface( + ud_manager_obj, 'org.freedesktop.DBus.ObjectManager') + for k, v in ud_manager.GetManagedObjects().iteritems(): + drive_info = v.get('org.freedesktop.UDisks2.Block', {}) + if drive_info.get('IdUsage') == "filesystem" and not drive_info.get( + 'HintSystem') and not drive_info.get('ReadOnly'): + device = drive_info.get('Device') + device = bytearray(device).replace( + b'\x00', b'').decode('utf-8') + devices.append(device) + except: + try: + # You must be using really old distro. Otherwise, the code + # should not reach here. + print("Falling back to Udisks1...") + ud_manager_obj = bus.get_object( + "org.freedesktop.UDisks", "/org/freedesktop/UDisks") + ud_manager = dbus.Interface( + ud_manager_obj, 'org.freedesktop.UDisks') + for dev in ud_manager.EnumerateDevices(): + device_obj = bus.get_object( + "org.freedesktop.UDisks", dev) + device_props = dbus.Interface( + device_obj, dbus.PROPERTIES_IFACE) + if device_props.Get('org.freedesktop.UDisks.Device', + "DriveConnectionInterface") == "usb" and device_props.Get( + 'org.freedesktop.UDisks.Device', "DeviceIsPartition"): + if device_props.Get('org.freedesktop.UDisks.Device', "DeviceIsMounted"): + device_file = device_props.Get( + 'org.freedesktop.UDisks.Device', "DeviceFile") + devices.append(device_file) + except: + print("No USB device found...") + + elif platform.system() == "Windows": + import win32com.client + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + oDrives = oFS.Drives + for drive in oDrives: + if drive.DriveType == 1 and drive.IsReady: + devices.append(drive) + + if devices: + return devices + else: + print("No USB device found...") + return None + + +def details_udev(usb_disk_part): + """ + Get details of USB partition using udev + """ + if platform.system() == "Linux": + from . import pyudev + """ + Try with PyUdev to get the details of USB disks. + This is the easiest and reliable method to find USB details. + Also, it is a standalone package and no dependencies are required. + """ + # print "Using PyUdev for detecting USB details..." + context = pyudev.Context() + for device in context.list_devices(subsystem='block', DEVTYPE='partition', + ID_FS_USAGE="filesystem", ID_TYPE="disk", + ID_BUS="usb"): + if device['ID_BUS'] == "usb" and device['DEVTYPE'] == "partition": + if (device['DEVNAME']) == usb_disk_part: + uuid = str(device['ID_FS_UUID']) + file_system = str(device['ID_FS_TYPE']) + try: + label = str(device['ID_FS_LABEL']) + except: + label = "No_Label" + mount_point = os.popen('findmnt -nr -o target -S %s' + % usb_disk_part).read().strip() + # Convert the hex string of space to empty space. + mount_point = mount_point.replace('\\x20', ' ') + try: + vendor = str(device['ID_VENDOR']) + except: + vendor = str('No_Vendor') + try: + model = str(device['ID_MODEL']) + except: + model = str('No_Model') + + if not mount_point == '': + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + + else: + size_total = str('No_Mount') + size_used = str('No_Mount') + size_free = str('No_Mount') + mount_point = str('No_Mount') + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def details_udisks2(usb_disk_part): + """ + Get details of USB disk detail. + usb_disk_part: It is the partition of an USB removable disk. + """ + import dbus + bus = dbus.SystemBus() + bd = bus.get_object('org.freedesktop.UDisks2', '/org/freedesktop/UDisks2/block_devices%s'%usb_disk_part[4:]) + device = bd.Get('org.freedesktop.UDisks2.Block', 'Device', dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + uuid = bd.Get('org.freedesktop.UDisks2.Block', 'IdUUID', dbus_interface='org.freedesktop.DBus.Properties') + file_system = bd.Get('org.freedesktop.UDisks2.Block', 'IdType', dbus_interface='org.freedesktop.DBus.Properties') + mount_point = bd.Get('org.freedesktop.UDisks2.Filesystem', 'MountPoints', dbus_interface='org.freedesktop.DBus.Properties') + if mount_point: + mount_point = str(bytearray(mount_point[0]).decode('utf-8').replace(b'\x00', b'')) + else: + mount_point = "No_Mount" + try: + label = bd.Get('org.freedesktop.UDisks2.Block', 'IdLabel', dbus_interface='org.freedesktop.DBus.Properties') + except: + label = "No_Label" + usb_drive_id = (bd.Get('org.freedesktop.UDisks2.Block', 'Drive', dbus_interface='org.freedesktop.DBus.Properties')) + bd1 = bus.get_object('org.freedesktop.UDisks2', usb_drive_id) + try: + vendor = bd1.Get('org.freedesktop.UDisks2.Drive', 'Vendor', dbus_interface='org.freedesktop.DBus.Properties') + except: + vendor = str('No_Vendor') + try: + model = bd1.Get('org.freedesktop.UDisks2.Drive', 'Model', dbus_interface='org.freedesktop.DBus.Properties') + except: + model = str('No_Model') + if not mount_point == "No_Mount": + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + else: + size_total = str('No_Mount') + size_used = str('No_Mount') + size_free = str('No_Mount') + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def bytes2human(n): + """ + Convert the size to human readable format + Authored by 'Giampaolo Rodolà' and original link is:- + http://code.activestate.com/recipes/577972-disk-usage/ + """ + try: + n = int(n) + except: + return 'Unknown' + symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + prefix = {} + for i, s in enumerate(symbols): + prefix[s] = 1 << (i + 1) * 10 + for s in reversed(symbols): + if n >= prefix[s]: + value = float(n) / prefix[s] + return '%.1f%s' % (value, s) + return "%sB" % n + + +def win_disk_details(disk_drive): + """ + Populate and get details of an USB disk under windows. Minimum required windows version is Vista. + :param disk_drive: USB disk like 'G:' + :return: See the details(usb_disk_part) function for return values. + """ + import win32com.client + import wmi + import pythoncom + pythoncom.CoInitialize() + vendor = 'Not_Found' + model = 'Not_Found' + c = wmi.WMI() + selected_usb_part = str(disk_drive) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part))) + selected_usb_device = d.DriveLetter + label = (d.VolumeName).strip() + if not label.strip(): + label = "No_label" + mount_point = selected_usb_device + ":\\" + serno = "%X" % (int(d.SerialNumber) & 0xFFFFFFFF) + uuid = serno[:4] + '-' + serno[4:] + file_system = (d.FileSystem).strip() + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + for physical_disk in c.Win32_DiskDrive(InterfaceType="USB"): + for partition in physical_disk.associators("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == disk_drive: + vendor = (physical_disk.PNPDeviceID.split('&VEN_'))[1].split('&PROD_')[0] + model = (physical_disk.PNPDeviceID.split('&PROD_'))[1].split('&REV_')[0] + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def details(usb_disk_part): + """ + Populate and get details of an USB disk. + :param usb_disk_part: USB disk. Example.. "/dev/sdb1" on Linux and "D:\" on Windows. + :return: label == > returns name/label of an inserted USB device. + mount_point == > returns mount path of an inserted USB device. + uuid == > returns uuid of an inserted USB device. + file_system == > returns type of filesystem of an inserted USB device. + device == > returns device path of an inserted USB device. + size_total == > returns total size in MB/GB of an inserted USB device. + size_free == > returns free size in MB/GB of an inserted USB device. + size_used == > returns used size in MB/GB of an inserted USB device. + vendor == > returns the name of the manufacturer. + model == > returns the model name of the USB. + """ + if platform.system() == 'Linux': + try: + udev = details_udev(usb_disk_part) + uuid = udev['uuid'] + file_system = udev['file_system'] + label = udev['label'] + mount_point = udev['mount_point'] + size_total = udev['size_total'] + size_used = udev['size_used'] + size_free = udev['size_free'] + vendor = udev['vendor'] + model = udev['model'] + except: + udisks2 = details_udisks2(usb_disk_part) + uuid = udisks2['uuid'] + file_system = udisks2['file_system'] + label = udisks2['label'] + mount_point = udisks2['mount_point'] + size_total = udisks2['size_total'] + size_used = udisks2['size_used'] + size_free = udisks2['size_free'] + vendor = udisks2['vendor'] + model = udisks2['model'] + elif platform.system() == 'Windows': + win_details = win_disk_details(usb_disk_part) + uuid = win_details['uuid'] + file_system = win_details['file_system'] + label = win_details['label'] + mount_point = win_details['mount_point'] + size_total = win_details['size_total'] + size_used = win_details['size_used'] + size_free = win_details['size_free'] + vendor = win_details['vendor'] + model = win_details['model'] + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + +if __name__ == '__main__': + usb_devices = list() + if usb_devices is not None: + for dev in usb_devices: + print(details(dev)) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6bb51d5 --- /dev/null +++ b/setup.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Name: setup.py +# Purpose: Module to create packages or install multibootusb package from source +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +from distutils.core import setup +#from setuptools import setup, find_packages +import os +import sys +from scripts.gen import mbusb_version + +Version = mbusb_version() +print(Version) +setup( + name='multibootusb', + version=Version, + packages=['scripts', 'scripts.pyudev', 'scripts.pyudev.device', 'scripts.pyudev._ctypeslib', 'scripts.pyudev._os', + 'scripts.gui', 'scripts.progressbar'], + #packages=find_packages(), + scripts=['multibootusb', 'multibootusb-pkexec'], + platforms=['Linux'], + url='http://multibootusb.org/', + license='General Public License (GPL)', + author='Sundar', + author_email='feedback.multibootusb@gmail.com', + description='Create multi boot live Linux on a USB disk...', + long_description='multibootusb is an advanced cross-platform application for installing/uninstalling Linux operating systems on to a single USB flash drives.', + data_files=[("/usr/share/applications", ["data/multibootusb.desktop"]), + ('/usr/share/pixmaps', ["data/tools/multibootusb.png"]), + ('/usr/share/polkit-1/actions/', ['org.debian.pkexec.run-multibootusb.policy']), + ('/usr/share/multibootusb/data/tools', ["data/tools/mbr.bin"]), + ('/usr/share/multibootusb/data', ["data/version.txt"]), + ('/usr/share/multibootusb/data/tools', ["data/tools/multibootusb.png"]), + ('/usr/share/multibootusb/data/tools/dd', ["data/tools/dd/dd.exe"]), + ('/usr/share/multibootusb/data/tools/dd', ["data/tools/dd/diskio.dll"]), + ('/usr/share/multibootusb/data/tools/mkfs', ["data/tools/mkfs/mke2fs.exe"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/chain.c32"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/bg.png"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/extlinux.cfg"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/grub.exe"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/memdisk"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/menu.c32"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/menu.lst"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/syslinux.cfg"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/vesamenu.c32"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_modules.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_linux.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_linux_64.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_windows.zip"])] +) diff --git a/uninstall.py b/uninstall.py new file mode 100755 index 0000000..a3905a5 --- /dev/null +++ b/uninstall.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: uninstall.py +# Purpose: Script to uninstall multibootusb from source, previously installed by install.py +# Authors: Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License, v.2 or above + +import os +import sys +import shutil + +if not os.getuid() == 0: + print("You must run this file with admin privilege.") + print("Try sudo ./uninstall.py") + sys.exit(0) +else: + if os.path.exists("./.install_files.txt"): + with open("./.install_files.txt", "r") as f: + file_list = f.readlines() + for f in file_list: + print("Removing " + f.replace('\n', '')) + if os.path.isfile(f.replace('\n', '')): + os.remove(f.replace('\n', '')) + elif os.path.isdir(f.replace('\n', '')): + shutil.rmtree(f.replace('\n', '')) + if os.path.exists('/usr/share/multibootusb'): + shutil.rmtree('/usr/share/multibootusb') + + print("multibootusb is successfully unistalled...") + else: + print("Unable to find install file list.") + print("This script works only if you have installed multibootusb using install.py script.")