commit c1554f4c87d767e4c99c408f89f483c7f03c1845 Author: Martchus Date: Mon Jan 25 00:24:31 2021 +0100 Initial import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6c10d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# C++ objects and libs + +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es + +/.qmake.cache +/.qmake.stash +*.pro.user +*.txt.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +qrc_*.cpp +ui_*.h +Makefile* +*-build-* + +# QtCreator + +*.autosave + +#QtCtreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# Dolphin +.directory + +# documentation +/doc + +# clang-format +.clang-format + +# node modules (only essential JavaScript and CSS files are force-added) +/srv/static/package-lock.json +/srv/static/node_modules/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2d52962 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/tabulate"] + path = 3rdparty/tabulate + url = git@github.com:p-ranav/tabulate.git diff --git a/3rdparty/tabulate b/3rdparty/tabulate new file mode 160000 index 0000000..67b010d --- /dev/null +++ b/3rdparty/tabulate @@ -0,0 +1 @@ +Subproject commit 67b010d095e866b651582dbfa0208010cde5afa0 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fa5d086 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +# metadata +set(META_PROJECT_NAME repomgr) +set(META_PROJECT_TYPE application) +set(META_APP_AUTHOR "Martchus") +set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") +set(META_APP_DESCRIPTION "Repository manager and package builder for Arch Linux") +set(META_APP_CATEGORIES "System;Utility;Network;FileTransfer") +set(META_VERSION_MAJOR 0) +set(META_VERSION_MINOR 0) +set(META_VERSION_PATCH 1) +set(META_VERSION_EXACT_SONAME ON) +set(META_CXX_STANDARD 20) + +project(${META_PROJECT_NAME}) + +enable_testing() + +# add subdirectories +add_subdirectory(3rdparty/tabulate) +add_subdirectory(libpkg) +link_directories(${LIBPKG_BINARY_DIR}) +add_subdirectory(librepomgr) +link_directories(${LIBREPOMGR_BINARY_DIR}) +add_subdirectory(srv) +add_subdirectory(cli) +add_subdirectory(pacfind) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8cdb845 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {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. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..1523d7d --- /dev/null +++ b/README.md @@ -0,0 +1,322 @@ +# Repository manager and build tool for Arch Linux + +This **experimental** project contains **inofficial** tools to manage custom Arch Linux +repositories. It is built on top of the official tools provided by the `pacman` and +`devtools` packages. + +So far it consists of: + +* libpkg: C++ library to parse Arch Linux packages and databases + * parse pacman config + * parse databases + * extract and parse binary packages (.PKGINFO, dependencies on soname-level) + * parse package source infos (.SRCINFO) +* librepomgr: C++ library for managing custom Arch Linux repositories +* srv: Daemon and web application for building Arch Linux packages and managing custom Arch + Linux repositories + * build packages via `makechrootpkg` and add them to a custom repository via `repo-add` + * check AUR for package updates + * build from locally stored PKGBUILDs and from the AUR + * remove packages from a repository + * move packages from one repository to another + * check for repository issues (missing packages, package misses rebuild, …) + * provides an HTTP API and a web UI with live streaming +* pacfind: Tool to find the package containing a certain file + * requires `*.files`-databases to be present + * does not need the full file path and supports regex (in contrast to `pacman -F`) +* cli: Command line tool to interact with srv + * search for packages and show package details + * TODO: show and submit build actions +* distri: Tool to distribute applications from the packages in a repository + * TODO: bundle an application with its dependencies similar to `linuxdeployqt` + and `windeployqt` + +## Setup server +An example config files can be found in this repository, see the `srv/doc` +directory. + +### Setting up a working directory +The server needs a place to temporarily store PKGBUILDs, cache files and other stuff. +Just create a directory at any place with enough disk space and set the permissions so the +user you start the server with can read and write there. This directory goes under +`working_directory` in the settings file. Relative paths within the configuration file (used +for other locations) will be treated relative to that directory. + +### Setting up a database/repository directories +The databases for the official repositories will be synced from the upstream repositories and +stored in a local directory. The actual packages might be found in the pacman cache directory. +Example configuration: + +``` +[database/core] +arch = x86_64 +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +``` + +Own repositories are not synced from a mirror and all packages are expected to be present in +a dedicated local directory. It makes most sense to store packages and databases in the same +directory. Example configuration: + +``` +[database/ownstuff] +path = $own_regular_db_path +files = $own_files_db_path +arch = x86_64 +depends = core extra community multilib +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch +``` + +The placeholder/variable values come from the previous "definitions" block, e.g.: + +``` +[definitions] +sync_db_path = sync-dbs +local_db_path = /run/media/repo/arch +own_sync_db_path = $local_db_path/$repo/os/$arch +own_regular_db_path = $local_db_path/$repo/os/$arch/$repo.db.tar.xz +own_files_db_path = $local_db_path/$repo/os/$arch/$repo.files.tar.xz +default_mirror = http://mirror.23media.de/archlinux +local_mirror = file://$local_db_path +``` + +The server obviously needs write permissions to add packages to repositories. In my +setup I've just add it as group and set permissions accordingly: + +``` +sudo chown martchus:buildservice $local_db_path +find $local_db_path -type d -exec chmod g+rwx {} \+ +find $local_db_path -type f -exec chmod g+rw {} \+ +``` + +### Setting up the chroot for the build +The overall reasoning and procedure is already outlined +[in the Wiki](https://wiki.archlinux.org/index.php/DeveloperWiki:Building_in_a_clean_chroot). + +I also like to be able to build for other architectures than x86_64 so the server +expects a directory layout like this: + +``` +/the/chroot/directory +├── arch-i686 +│   └── root +├── arch-x86_64 +│   └── root +├── config-i686 +│   ├── makepkg.conf +│   ├── pacman.conf +├── config-x86_64 +│   ├── makepkg.conf +│   └── pacman.conf +``` + +So there's a "root" chroot for every architecture and config files for that architecture +in dedicated directories. To achieve this one just has to call `mkarchroot` for each architecture. +It is invoked like this: + +``` +mkarchroot -C /path/to/pacman.conf -M /path/to/makepkg.conf /directory/to/store/the/tree base-devel +``` + +Note that `makechrootpkg` will not use the "root" chroot directories directly. It will create a +working copy using `rsync -a --delete -q -W -x "$chrootdir/root/" "$copydir"` (when using btrfs +it creates a subvolume instead of using rsync). + +When following the procedure the "root" chroot is owned by `root` and the working copies by the +user you start the buildservice/makechrootpkg with. + +Until I find a better solution the buildservice updates the config files within the "root" chroot +to configure the databases used during the build. It relies on the configuration files stored within +the `config-*` directories for that and needs permissions to update them: + +``` +sudo chown buildservice:buildservice /the/chroot/directory/arch-x86_64/root/etc/{pacman,makepkg}.conf +``` + +### sudo configuration +`makechrootpkg` seems to use `sudo` internally so it is required that the user one starts the +server with is allowed to use `sudo`. Currently it is not possible to supply the password +automatically so one has to allow access without password like this: + +``` +buildservice ALL=(ALL) NOPASSWD:ALL +``` + +### Sample NGINX config +To make the server publicy accessible one should use a reverse proxy. NGINX example: + +``` +proxy_http_version 1.1; # this is essential for chunked responses to work +proxy_read_timeout 1d; # prevent timeouts when serving live stream + +location /buildservice/api/ { + proxy_buffering off; # for live streaming of logfiles + proxy_pass http://localhost:8090/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; +} +location ~ ^(?!/buildservice/api/)/buildservice/(.*)$ { + alias /run/media/devel/projects/c++/cmake/auto-makepkg/buildservice/static/$1; +} + +``` + +### Sample GPG config +This minimal GPG config allows `makepkg` to validate signatures which might be present +in some PKGBUILDs: +``` +~/.gnupg/gpg.conf +--- +utf8-strings +keyring /etc/pacman.d/gnupg/pubring.gpg +keyserver-options auto-key-retrieve +keyserver hkp://keys.gnupg.net +``` + +#### Notes + +* Adding the pacman keyring is actually not very useful because we need to check signatures + of any upstream project and not just arch devs. +* If "auto-key-retrieve" does not work, use `gpg --recv-key ` as a workaround +* Also see http://allanmcrae.com/2015/01/two-pgp-keyrings-for-package-management-in-arch-linux/ + +### ccache configuration +To use ccache one can set the ccache directory in the config (`ccache_dir`) and install the +package `ccache` (and `mingw-w64-ccache` for MinGW packages) into the chroot. Make sure the user +you start the server with has permissions to read and write there. Otherwise the resulting +configure errors can be confusing. Internally the server is mounting that directory like +described in [the wiki](https://wiki.archlinux.org/index.php/Ccache#makechrootpkg). + +Note that ccache *slows* down the initial compilation. It is only useful to enable it if rebuilds +with only slight changes are expected. Currently it is *not* possible to enable/disable ccache usage +per package (e.g. via a regex). + +## Workflow + +### General +* Use the "Reload database" build action to reload one or more databases after databases have been + changes by build actions or manually. So far databases are *not* automatically reloaded. +* Use the "Reload configuration" build action to apply configuration changes without restarting the + service. + +### Building packages +0. Ensure the databases are up to date via the "Reload databases" build action. +1. Start the "Prepare building" build action. + * Specify the names of packages to be built. + * If a package already exists the `pkgrel` or `epoch` is bumped as needed so the rebuilt + packages are considered as newer by pacman. A warning is logged when bumping is done. + * Missing dependencies are pulled into the build automatically. Whether dependencies are + considered "missing" or "given" depends on the specified databases (see next points). + * Packages will be splitted into batches. The first batch contains all packages which can be + built immediately. The second batch contains all packages which only depend on packages in + the first batch. The third batch contains all packages which only depend on packages in the + second batch and so on. That means packages do not need to be specified in the correct order. + However, the order in which packages are specified is preserved within each batch so it makes + still sense to specify packages in the order you would prefer to build them. + * Cyclic dependencies can not be added to a batch. A list of cyclic "leftovers" is emitted + if those exist and that is considered a failure. If this is the case you need to add a + bootstrap package to break the cycle. The build system is not clever enough to pull a bootstrap + package automatically into the build so it must be specified explicitely. E.g. to build + `mingw-w64-freetype2` and `mingw-w64-harfbuzz` one needs to add `mingw-w64-freetype2-bootstrap` + explicitely to the list of packages to be built. + * Specify exactly one destination database. The built packages are added to this database. + * Specify source databases. + * All packages contained by the source databases are considered as "given" and not pulled + into the build as dependency. + * If no source databases are specified the destination database and all databases the + destination database is based on are used as source databases. + * Source databases must be the destination database or a database the destination database + is based on. + * Specify a "directory" to store meta-data, logs, PKGBUILDs, sources and packages. + * Check "Clean source directories" when previously prepared sources which are still present in + the specified "directory" should be overridden. + * When the build action has finished, have a look at the results to check whether they are as + expected. If not, just restart the build action with adjusted parameters. The "directory" + can generally be re-used. Use "Clean source directories" as needed. To override only a single + source directory, simply delete it manually before restarting the build action. + * You can amend PKGBUILDs created in the "directory" as needed. +2. Start the "Conduct building" build action. + * Specify the same "directory" as in 1. + * Do *not* specify any databases. The databases as specified in 1. will be used. + * One can optionally specify package names to build only a subset of the initially prepared + build. + * When starting the build, the following steps are performed: + 1. All sources of all packages are downloaded. The build is stopped when not all sources + can be downloaded. + 2. Packages will now be built in the same order as computed when preparing. Building of + the next batch is stopped when a failure happend within the previous batch. + * When using "Build as far as possible" the build is not stopped as previously explained. + * When using "Save chroot of failures" the chroot directory is renamed when a build failure + happens and therefore not overridden by the next build. This is useful to investigate the + failure later without starting the build from scratch. + * When the build has been stopped due to failures one can simply restart the build action. Packages + which have already been built will be skipped. + * It is useful to modify `build-progress.json` in the "directory". + * Set `finished` back to `"1"` and `addedToRepo` back to `false` to let the build system think + a package has not been built yet. This is useful to build a package again after a non-fatal + packaging mistake. + * Set `addedToRepo` to `true` let the build system think a package has already been built. + This effectively skips a package completely. + * Set `hasSources` back to `false` to recreate the source package and download sources + again as needed. This will also copy over contents of the `src` directory to the `pkg` + directory again. +3. Amend a PKGBUILD file to fix a failure within a "big rebuild". + * Fix the package at the source, e.g. the AUR or the local package directory, then + 1. Clean the `src` directory within "directory" for the package(s) or use "Clean source directory" if + all packages need fixing. + 2. Restart step 1. Existing sources are reused (except for the removed `src` directories) so it shouldn't + take long. The build progress (`hasSources` in particular) of the affected package(s) should be resetted. + 3. Restart step 2. + * or: Amend the PKGBUILD within the `src` directory. + 1. Set `hasSources` back to `false` as mentioned under 2. to retrigger building the source directory. + 2. Optionally restart step 1. to reevaluate possibly adjusted versions, split packages and dependencies. + Be sure *not* to check "Clean source directory" as this would override your amendment. + 2. Restart step 2. + +#### Further notes +* One can keep using the same "directory" for different builds. Since the `pkg` dirs will not be cleaned + up (existing files are only updated/overridden) previously downloaded source files can be reused (useful if + only `pkgrel` changes or when building from VCS sources or when some sources just remain the same). + +## TODOs and further ideas for improvement +[ ] Add generic locking so build actions don't interfere with each other, e.g. when accessing a database directory +[ ] CLI client +[ ] More advanced search options +[ ] Allow running tasks automatically/periodically +[ ] Refresh build action details automatically while an action is pending +[ ] Fix permission error when stopping a process +[ ] Keep the possibility for a "soft stop" where the build action would finish the current item +[ ] Show statistics like CPU and RAM usage about ongoing build processes +[ ] Stop a build process which doesn't produce output after a certain time +[ ] Don't keep always *all* packages in memory for better scalability +[ ] Find out why the web service sometimes gets stuck + * Weirdly, restarting the client (browser) helps in these cases + * Add "stress" test for live-streaming + * Start process producing lots of output very fast + * Let different clients connect and disconnect fast +[ ] Improve test coverage +[ ] Include `xterm.js` properly + +## Build instructions and dependencies +For a PKGBUILD checkout my [PKGBUILDs repository](https://github.com/Martchus/PKGBUILDs). + +### C++ stuff +The application depends on [c++utilities](https://github.com/Martchus/cpp-utilities), +[reflective-rapidjson](https://github.com/Martchus/reflective-rapidjson) and some boost modules. + +For basic instructions checkout the README file of [c++utilities](https://github.com/Martchus/cpp-utilities). + +### Web stuff +The only dependency is `xterm.js` which is bundled. However, only the JavaScript and CSS files are +bundled. For development the full checkout might be useful (e.g. for TypeScript mapping). It can be +retrieved using npm: + +``` +cd srv/static/node_modules +npm install xterm +``` diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..9d5f822 --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +# add project files +set(HEADER_FILES) +set(SRC_FILES main.cpp) +set(TEST_HEADER_FILES) +set(TEST_SRC_FILES tests/cppunit.cpp) + +# meta data +set(META_PROJECT_NAME repomgr) +set(META_PROJECT_TYPE application) +set(META_PROJECT_VARNAME BUILD_SERVICE_CLIENT) +set(META_APP_NAME "CLI for repo manager") +set(META_APP_DESCRIPTION "CLI tool to use the repository manager for Arch Linux") + +# find c++utilities +set(CONFIGURATION_PACKAGE_SUFFIX + "" + CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities") +find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED) +use_cpp_utilities() + +# find backend libraries +find_package(libpkg ${META_APP_VERSION} REQUIRED) +use_libpkg() + +# find backend libraries +find_package(librepomgr ${META_APP_VERSION} REQUIRED) +use_librepomgr() + +# use bundled tabulate +list(APPEND PRIVATE_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/tabulate/include/") + +# include modules to apply configuration +include(BasicConfig) +include(WindowsResources) +include(AppTarget) +include(TestTarget) +include(ShellCompletion) +include(ConfigHeader) + +# add install for config file template +install( + FILES doc/client-config-example.conf + DESTINATION "${CMAKE_INSTALL_PREFIX}/${META_DATA_DIR}/skel" + COMPONENT config) diff --git a/cli/doc/client-config-example.conf b/cli/doc/client-config-example.conf new file mode 100644 index 0000000..aed7624 --- /dev/null +++ b/cli/doc/client-config-example.conf @@ -0,0 +1,14 @@ +[instance/local] +url = http://127.0.0.1:8090 +user = martchus + +[instance/server] +url = https://martchus.no-ip.biz/buildservice +user = martchus + +[instance/bogus] +url = http://martchus.no-ip.biz/foo +user = martchus + +[user/martchus] +password = test diff --git a/cli/main.cpp b/cli/main.cpp new file mode 100644 index 0000000..bd58c7c --- /dev/null +++ b/cli/main.cpp @@ -0,0 +1,322 @@ +#include "resources/config.h" + +#include "../librepomgr/json.h" +#include "../librepomgr/webapi/params.h" +#include "../librepomgr/webclient/session.h" + +#include "../libpkg/data/database.h" +#include "../libpkg/data/package.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; +using namespace std; + +struct ClientConfig { + void parse(const Argument &configFileArg, const Argument &instanceArg); + + const char *path = nullptr; + std::string instance; + std::string url; + std::string userName; + std::string password; +}; + +void ClientConfig::parse(const Argument &configFileArg, const Argument &instanceArg) +{ + // parse connfig file + path = configFileArg.firstValue(); + if (!path || !*path) { + path = "/etc/buildservice" PROJECT_CONFIG_SUFFIX "/client.conf"; + } + auto configFile = NativeFileStream(); + configFile.exceptions(std::ios_base::badbit | std::ios_base::failbit); + configFile.open(path, std::ios_base::in); + auto configIni = AdvancedIniFile(); + configIni.parse(configFile); + configFile.close(); + + // read innstance + if (instanceArg.isPresent()) { + instance = instanceArg.values().front(); + } + for (const auto §ion : configIni.sections) { + if (!section.name.starts_with("instance/")) { + continue; + } + if (!instance.empty() && instance != std::string_view(section.name.data() + 9, section.name.size() - 9)) { + continue; + } + instance = section.name; + if (const auto url = section.findField("url"); url != section.fieldEnd()) { + this->url = std::move(url->value); + } else { + throw std::runtime_error("Config is invalid: No \"url\" specified within \"" % section.name + "\"."); + } + if (const auto user = section.findField("user"); user != section.fieldEnd()) { + this->userName = std::move(user->value); + } + break; + } + if (url.empty()) { + throw std::runtime_error("Config is invalid: Instance configuration insufficient."); + } + + // read user data + if (userName.empty()) { + return; + } + if (const auto userSection = configIni.findSection("user/" + userName); userSection != configIni.sectionEnd()) { + if (const auto password = userSection->findField("password"); password != userSection->fieldEnd()) { + this->password = std::move(password->value); + } else { + throw std::runtime_error("Config is invalid: No \"password\" specified within \"" % userSection->name + "\"."); + } + } else { + throw std::runtime_error("Config is invalid: User \"" % userName + "\" referenced in instance configuration not found."); + } +} + +void configureColumnWidths(tabulate::Table &table) +{ + const auto terminalSize = determineTerminalSize(); + if (!terminalSize.columns) { + return; + } + struct ColumnStats { + std::size_t maxSize = 0; + std::size_t totalSize = 0; + std::size_t rows = 0; + double averageSize = 0.0; + double averagePercentage = 0.0; + std::size_t width = 0; + }; + auto columnStats = std::vector(); + for (const auto &row : table) { + const auto columnCount = row.size(); + if (columnStats.size() < columnCount) { + columnStats.resize(columnCount); + } + auto column = columnStats.begin(); + for (const auto &cell : row.cells()) { + const auto size = cell->size(); + column->maxSize = std::max(column->maxSize, size); + column->totalSize += std::max(10, size); + column->rows += 1; + ++column; + } + } + auto totalAverageSize = 0.0; + for (auto &column : columnStats) { + totalAverageSize += (column.averageSize = static_cast(column.totalSize) / column.rows); + } + for (auto &column : columnStats) { + column.averagePercentage = column.averageSize / totalAverageSize; + column.width = std::max(terminalSize.columns * column.averagePercentage, std::min(column.maxSize, 10)); + } + for (std::size_t columnIndex = 0; columnIndex != columnStats.size(); ++columnIndex) { + table.column(columnIndex).format().width(columnStats[columnIndex].width); + } +} + +void printPackageSearchResults(const LibRepoMgr::WebClient::Response::body_type::value_type &jsonData) +{ + const auto packages = ReflectiveRapidJSON::JsonReflector::fromJson>(jsonData.data(), jsonData.size()); + tabulate::Table t; + t.format().hide_border(); + t.add_row({ "Arch", "Repo", "Name", "Version", "Description", "Build date" }); + for (const auto &[db, package] : packages) { + const auto &dbInfo = std::get(db); + t.add_row( + { package->packageInfo ? package->packageInfo->arch : dbInfo.arch, dbInfo.name, package->name, package->version, package->description, + package->packageInfo && !package->packageInfo->buildDate.isNull() ? package->packageInfo->buildDate.toString() : "?" }); + } + t.row(0).format().font_align(tabulate::FontAlign::center).font_style({ tabulate::FontStyle::bold }); + configureColumnWidths(t); + std::cout << t << std::endl; +} + +template inline std::string formatList(const List &list) +{ + return joinStrings(list, ", "); +} + +std::string formatDependencies(const std::vector &deps) +{ + auto asStrings = std::vector(); + asStrings.reserve(deps.size()); + for (const auto &dep : deps) { + asStrings.emplace_back(dep.toString()); + } + return formatList(asStrings); +} + +void printPackageDetails(const LibRepoMgr::WebClient::Response::body_type::value_type &jsonData) +{ + const auto packages = ReflectiveRapidJSON::JsonReflector::fromJson>(jsonData.data(), jsonData.size()); + for (const auto &package : packages) { + const auto *const pkg = &package; + std::cout << TextAttribute::Bold << pkg->name << ' ' << pkg->version << TextAttribute::Reset << '\n'; + tabulate::Table t; + t.format().hide_border(); + if (pkg->packageInfo) { + t.add_row({ "Arch", pkg->packageInfo->arch }); + } else if (pkg->sourceInfo) { + t.add_row({ "Archs", formatList(pkg->sourceInfo->archs) }); + } + t.add_row({ "Description", pkg->description }); + t.add_row({ "Upstream URL", pkg->upstreamUrl }); + t.add_row({ "License(s)", formatList(pkg->licenses) }); + t.add_row({ "Groups", formatList(pkg->groups) }); + if (pkg->packageInfo && pkg->packageInfo->size) { + t.add_row({ "Package size", dataSizeToString(pkg->packageInfo->size, true) }); + } + if (pkg->installInfo) { + t.add_row({ "Installed size", dataSizeToString(pkg->installInfo->installedSize, true) }); + } + if (pkg->packageInfo) { + if (!pkg->packageInfo->packager.empty()) { + t.add_row({ "Packager", pkg->packageInfo->packager }); + } + if (!pkg->packageInfo->buildDate.isNull()) { + t.add_row({ "Packager", pkg->packageInfo->buildDate.toString() }); + } + } + t.add_row({ "Dependencies", formatDependencies(pkg->dependencies) }); + t.add_row({ "Optional dependencies", formatDependencies(pkg->optionalDependencies) }); + if (pkg->sourceInfo) { + t.add_row({ "Make dependencies", formatDependencies(pkg->sourceInfo->makeDependencies) }); + t.add_row({ "Check dependencies", formatDependencies(pkg->sourceInfo->checkDependencies) }); + } + t.add_row({ "Provides", formatDependencies(pkg->provides) }); + t.add_row({ "Replaces", formatDependencies(pkg->replaces) }); + t.add_row({ "Conflicts", formatDependencies(pkg->conflicts) }); + t.add_row({ "Contained libraries", formatList(pkg->libprovides) }); + t.add_row({ "Needed libraries", formatList(pkg->libdepends) }); + t.column(0).format().font_align(tabulate::FontAlign::right); + configureColumnWidths(t); + std::cout << t << '\n'; + } + std::cout.flush(); +} + +void printRawData(const LibRepoMgr::WebClient::Response::body_type::value_type &rawData) +{ + if (!rawData.empty()) { + std::cerr << Phrases::InfoMessage << "Server replied:" << Phrases::End << rawData << '\n'; + } +} + +void handleResponse(const std::string &url, const LibRepoMgr::WebClient::SessionData &data, const LibRepoMgr::WebClient::HttpClientError &error, + void (*printer)(const LibRepoMgr::WebClient::Response::body_type::value_type &jsonData), int &returnCode) +{ + const auto &response = std::get(data.response); + const auto &body = response.body(); + if (error.errorCode != boost::beast::errc::success && error.errorCode != boost::asio::ssl::error::stream_truncated) { + std::cerr << Phrases::ErrorMessage << "Unable to connect: " << error.what() << Phrases::End; + std::cerr << Phrases::InfoMessage << "URL was: " << url << Phrases::End; + printRawData(body); + return; + } + if (response.result() != boost::beast::http::status::ok) { + std::cerr << Phrases::ErrorMessage << "HTTP request not successful: " << error.what() << Phrases::End; + std::cerr << Phrases::InfoMessage << "URL was: " << url << Phrases::End; + printRawData(body); + return; + } + try { + std::invoke(printer, body); + } catch (const RAPIDJSON_NAMESPACE::ParseResult &e) { + std::cerr << Phrases::ErrorMessage << "Unable to parse responnse: " << tupleToString(LibRepoMgr::serializeParseError(e)) << Phrases::End; + std::cerr << Phrases::InfoMessage << "URL was: " << url << std::endl; + returnCode = 11; + } catch (const std::runtime_error &e) { + std::cerr << Phrases::ErrorMessage << "Unable to display response: " << e.what() << Phrases::End; + std::cerr << Phrases::InfoMessage << "URL was: " << url << std::endl; + returnCode = 12; + } +} + +int main(int argc, const char *argv[]) +{ + // define command-specific parameters + std::string path; + void (*printer)(const LibRepoMgr::WebClient::Response::body_type::value_type &jsonData) = nullptr; + + // read CLI args + ArgumentParser parser; + ConfigValueArgument configFileArg("config-file", 'c', "specifies the path of the config file", { "path" }); + configFileArg.setEnvironmentVariable(PROJECT_VARNAME_UPPER "_CONFIG_FILE"); + ConfigValueArgument instanceArg("instance", 'i', "specifies the instance to connect to", { "instance" }); + OperationArgument searchArg("search", 's', "searches packages"); + ConfigValueArgument searchTermArg("term", 't', "specifies the search term", { "term" }); + searchTermArg.setImplicit(true); + searchTermArg.setRequired(true); + ConfigValueArgument searchModeArg("mode", 'm', "specifies the mode", { "name/name-contains/regex/provides/depends/libprovides/libdepends" }); + searchModeArg.setPreDefinedCompletionValues("name name-contains regex provides depends libprovides libdepends"); + searchArg.setSubArguments({ &searchTermArg, &searchModeArg }); + searchArg.setCallback([&path, &printer, &searchTermArg, &searchModeArg](const ArgumentOccurrence &) { + path = "/api/v0/packages?mode=" % LibRepoMgr::WebAPI::Url::encodeValue(searchModeArg.firstValueOr("name-contains")) % "&name=" + + LibRepoMgr::WebAPI::Url::encodeValue(searchTermArg.values().front()); + printer = printPackageSearchResults; + }); + OperationArgument packageArg("package", 'p', "shows detauls about a package"); + ConfigValueArgument packageNameArg("name", 'n', "specifies the package name", { "name" }); + packageNameArg.setImplicit(true); + packageNameArg.setRequired(true); + packageArg.setSubArguments({ &packageNameArg }); + packageArg.setCallback([&path, &printer, &packageNameArg](const ArgumentOccurrence &) { + path = "/api/v0/packages?mode=name&details=1&name=" + LibRepoMgr::WebAPI::Url::encodeValue(packageNameArg.values().front()); + printer = printPackageDetails; + }); + HelpArgument helpArg(parser); + NoColorArgument noColorArg; + parser.setMainArguments({ &searchArg, &packageArg, &instanceArg, &configFileArg, &noColorArg, &helpArg }); + parser.parseArgs(argc, argv); + + // return early if no operation specified + if (!printer) { + if (!helpArg.isPresent()) { + std::cerr << "No command specified; use --help to list available commands.\n"; + } + return 0; + } + + // parse config + auto config = ClientConfig(); + try { + config.parse(configFileArg, instanceArg); + } catch (const std::runtime_error &e) { + std::cerr << Phrases::ErrorMessage << "Unable to parse config: " << e.what() << Phrases::End; + std::cerr << Phrases::InfoMessage << "Path of config file was: " << (config.path ? config.path : "[none]") << Phrases::End; + return 10; + } + + // make HTTP request and show response + const auto url = config.url + path; + auto ioContext = boost::asio::io_context(); + auto sslContext = boost::asio::ssl::context{ boost::asio::ssl::context::sslv23_client }; + auto returnCode = 0; + sslContext.set_verify_mode(boost::asio::ssl::verify_peer); + sslContext.set_default_verify_paths(); + LibRepoMgr::WebClient::runSessionFromUrl(ioContext, sslContext, url, + std::bind(&handleResponse, std::ref(url), std::placeholders::_1, std::placeholders::_2, printer, std::ref(returnCode)), std::string(), + config.userName, config.password); + ioContext.run(); + + return 0; +} diff --git a/cli/tests/cppunit.cpp b/cli/tests/cppunit.cpp new file mode 100644 index 0000000..67aaee6 --- /dev/null +++ b/cli/tests/cppunit.cpp @@ -0,0 +1 @@ +#include diff --git a/libpkg/CMakeLists.txt b/libpkg/CMakeLists.txt new file mode 100644 index 0000000..4fd69a6 --- /dev/null +++ b/libpkg/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +# add project files +set(HEADER_FILES + data/package.h + data/database.h + data/config.h + data/lockable.h + data/siglevel.h + parser/aur.h + parser/package.h + parser/database.h + parser/config.h + parser/utils.h + parser/binary.h) +set(SRC_FILES + data/package.cpp + data/database.cpp + data/config.cpp + data/lockable.cpp + algo/search.cpp + algo/buildorder.cpp + algo/licenses.cpp + parser/aur.cpp + parser/package.cpp + parser/database.cpp + parser/config.cpp + parser/utils.cpp + parser/binary.cpp + parser/siglevel.cpp) +set(TEST_HEADER_FILES tests/parser_helper.h) +set(TEST_SRC_FILES tests/cppunit.cpp tests/parser.cpp tests/parser_binary.cpp tests/parser_helper.cpp tests/data.cpp + tests/utils.cpp) + +# meta data +set(META_PROJECT_NAME libpkg) +set(META_PROJECT_TYPE library) +set(META_PROJECT_VARNAME LIBPKG) +set(META_APP_NAME "Inofficial Arch Linux package library") +set(META_APP_DESCRIPTION "C++ library to parse Arch Linux packages and databases") +set(META_VERSION_MAJOR 0) +set(META_VERSION_MINOR 0) +set(META_VERSION_PATCH 1) + +# find c++utilities +set(CONFIGURATION_PACKAGE_SUFFIX + "" + CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities") +find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.2.0 REQUIRED) +use_cpp_utilities(VISIBILITY PUBLIC) + +# use std::filesystem +include(3rdParty) +use_standard_filesystem(VISIBILITY PUBLIC) + +# find reflective-rapidjson +find_package(reflective_rapidjson${CONFIGURATION_PACKAGE_SUFFIX} REQUIRED) +use_reflective_rapidjson(VISIBILITY PUBLIC) + +# find 3rd party libraries zlib +use_zlib() +# libarchive +find_package(LibArchive) +if (NOT LibArchive_FOUND) + message(FATAL_ERROR "Unable to find libarchive.") +endif () +add_library(libarchive UNKNOWN IMPORTED) +set_property(TARGET libarchive PROPERTY IMPORTED_LOCATION "${LibArchive_LIBRARIES}") +set_property(TARGET libarchive PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${LibArchive_INCLUDE_DIRS}") +use_target(TARGET_NAME libarchive) + +# apply basic configuration +include(BasicConfig) + +# trigger code generator for tests because the tests already contain structs to be (de)serialized +include(ReflectionGenerator) +add_reflection_generator_invocation( + INPUT_FILES + data/database.h + data/package.h + data/config.h + data/siglevel.h + parser/aur.h + CLANG_OPTIONS_FROM_TARGETS + "${META_TARGET_NAME}" + CLANG_OPTIONS_FROM_DEPENDENCIES + "${PUBLIC_LIBRARIES};${PRIVATE_LIBRARIES}" + GENERATORS + json + binary + OUTPUT_LISTS + SRC_FILES + JSON_VISIBILITY + ${META_PROJECT_VARNAME}_EXPORT + BINARY_VISBILITY + ${META_PROJECT_VARNAME}_EXPORT) + +# include modules to apply configuration +include(WindowsResources) +include(LibraryTarget) +include(TestTarget) +include(Doxygen) +include(ConfigHeader) diff --git a/libpkg/algo/buildorder.cpp b/libpkg/algo/buildorder.cpp new file mode 100644 index 0000000..4a93985 --- /dev/null +++ b/libpkg/algo/buildorder.cpp @@ -0,0 +1,157 @@ +#include "../data/config.h" + +using namespace std; + +namespace LibPkg { + +/// \cond +struct TopoSortItem { + explicit TopoSortItem(PackageSearchResult &&package, bool onlyDependency) + : package(package) + , onlyDependency(onlyDependency) + , finished(false) + { + } + + PackageSearchResult package; + bool onlyDependency; + bool finished; +}; +/// \endcond + +/*! + * \brief Adds \a dependency and its dependencies in topological order to \a finishedItems. + * + * Also populates \a allItems in arbitrary order which owns the items. Unknown \a dependencies (no matching package found in any of the databases) are + * added to \a ignored and ignored. + * + * The variable \a cycleTracking is supposed to be empty in the beginning and internally used to keep track of the current chain. In case a cycle is detected, + * \a cycleTracking is set to contain the cyclic dependency path. On normal exit it just contains the most recently processed dependency chain (not really + * interesting). + * + * \remarks Dependency cycles are ignored if there are already binary packages of the involved dependencies. It is assumed that in this case it is possible to + * install the existing binaries for building new ones (e.g. just use the current GCC to build the new one instead of taking additional bootstrapping + * effort). We also ignore cycles affecting only indirect dependencies as long as there are binaries for them available (e.g. to build a package requiring + * mesa we just install mesa and don't care about its cyclic dependency with libglvnd). + * \return Returns true if \a dependency could be added to \a finishedItems, has already been present or has been ignored. Returns false if a cycle has been detected. + * \deprecated This function can likely be removed. The build preparation task has its own implementation (to compute batches) which is more useful. + */ +bool Config::addDepsRecursivelyInTopoOrder(vector> &allItems, vector &finishedItems, + std::vector &ignored, std::vector &cycleTracking, const Dependency &dependency, BuildOrderOptions options, + bool onlyDependency) +{ + // skip if dependency is already present + for (const auto &item : finishedItems) { + if (item->package.pkg->providesDependency(dependency)) { + // ensure the already finished dependency is not treated as dependency + if (!onlyDependency) { + item->onlyDependency = false; + } + return true; + } + } + + // search for a package providing the specified dependency (just using the first matching package for now) + auto packageSearchResult = findPackage(dependency); + auto &pkg = packageSearchResult.pkg; + + // ignore the dependency if we can't find a package which provides it + if (!pkg) { + ignored.emplace_back(dependency.toString()); + return true; + } + + // check whether a topo sort item for the current dependency is already pending to detect cycles + for (auto &item : allItems) { + if (pkg != item->package.pkg) { + continue; + } + + // skip package if it is only a dependency and there's already a binary package + if (onlyDependency && pkg->packageInfo) { + return true; + } + + // report error: remove so far "healy" path in the current chain so it contains only the cyclic path anymore + for (auto i = cycleTracking.begin(), end = cycleTracking.end(); i != end; ++i) { + if (pkg == i->pkg) { + cycleTracking.erase(cycleTracking.begin(), i); + break; + } + } + return false; + } + + // add package to the "current chain" (used to comprehend the path in case a cycle occured) + cycleTracking.emplace_back(packageSearchResult); + + // add topo sort item for dependency + allItems.emplace_back(make_unique(move(packageSearchResult), onlyDependency)); + auto *const currentItem = allItems.back().get(); + + // add the dependencies of this dependency first + const auto addBuildDependencies = (options & BuildOrderOptions::ConsiderBuildDependencies) && pkg->sourceInfo; + const auto *const runtimeDependencies = &pkg->dependencies; + const auto *const makeDependencies = addBuildDependencies ? &pkg->sourceInfo->makeDependencies : nullptr; + const auto *const checkDependencies = addBuildDependencies ? &pkg->sourceInfo->checkDependencies : nullptr; + for (const auto *dependencies : { runtimeDependencies, makeDependencies, checkDependencies }) { + if (!dependencies) { + continue; + } + for (const auto &dependency : *dependencies) { + // skip dependencies provided by the current package itself (FIXME: right now python 3.n depends on python<3.(n+1) which should be fixed) + if (pkg->providesDependency(dependency)) { + continue; + } + + if (!addDepsRecursivelyInTopoOrder(allItems, finishedItems, ignored, cycleTracking, dependency, options, true)) { + // skip if a cycle has been detected + return false; + } + } + } + + // remove the package from the "current chain" again + cycleTracking.pop_back(); + + // mark the current item as visited and add it to finished items + finishedItems.emplace_back(currentItem); + return currentItem->finished = true; +} + +BuildOrderResult Config::computeBuildOrder(const std::vector &dependencyDenotations, BuildOrderOptions options) +{ + // setup variables to store results + BuildOrderResult result; + vector> allTopoSortItems; + vector finishedTopoSortItems; + allTopoSortItems.reserve(dependencyDenotations.size()); + + // add dependencies + for (const auto &dependency : dependencyDenotations) { + // continue adding dependencies as long as no cycles have been detected + if (addDepsRecursivelyInTopoOrder(allTopoSortItems, finishedTopoSortItems, result.ignored, result.cycle, + Dependency(dependency.data(), dependency.size()), options, false)) { + result.cycle.clear(); + continue; + } + + // stop on the first cycle + break; + } + + // add finished items to result (even if we detected a cycle) + result.order.reserve(allTopoSortItems.size()); + for (auto &item : finishedTopoSortItems) { + if (!(options & BuildOrderOptions::IncludeAllDependencies) + && (item->onlyDependency && ((options & BuildOrderOptions::IncludeSourceOnlyDependencies) || item->package.pkg->packageInfo))) { + continue; + } + result.order.emplace_back(move(item->package)); + } + + result.success = result.cycle.empty() && result.ignored.empty(); + return result; +} + +} // namespace LibPkg diff --git a/libpkg/algo/licenses.cpp b/libpkg/algo/licenses.cpp new file mode 100644 index 0000000..ddcc766 --- /dev/null +++ b/libpkg/algo/licenses.cpp @@ -0,0 +1,335 @@ +#include "../data/config.h" + +#include + +#include +#include + +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibPkg { + +bool Config::addLicenseInfo(LicenseResult &result, const Dependency &dependency) +{ + // find the referenced package + auto searchResult = findPackage(dependency); + const auto &package = searchResult.pkg; + if (!package) { + result.success = false; + result.notes.emplace_back("Unable to locate " + dependency.toString()); + return false; + } + + auto packageID = addLicenseInfo(result, searchResult, package); + if (packageID.empty()) { + result.ignoredPackages.emplace_back(package->name % '-' + package->version); + return false; + } + + result.consideredPackages.emplace_back(package->name % '-' + package->version); + if (result.mainProject.empty()) { + result.mainProject = move(packageID); + } else { + result.dependendProjects.emplace(move(packageID)); + } + return true; +} + +std::string Config::addLicenseInfo(LicenseResult &result, PackageSearchResult &searchResult, const std::shared_ptr &package) +{ + // make up some identifier to refer to the package in the license summary + // * use the "regular" package name (e.g. gcc instead of mingw-w64-gcc) + // * include only the upstream version (but not epoch and pkgrel) + // * the licensing files for the whole MinGW-w64 project is contained by the mingw-w64-headers package + // * the licensing files for GCC is contained by the gcc-libs package + // * the licensing files for all Qt modules are contained within qt5-base; don't distinguish the modules for our purposes + // * use the real project name if known + const auto upstreamVersion = PackageVersion::fromString(package->version).upstream; + auto regularPackageName = package->computeRegularPackageName(); + auto packageID = regularPackageName.empty() ? package->name : regularPackageName; + static const auto displayNames = unordered_map{ + { "mingw-w64-headers", "MinGW-w64" }, + { "gcc", "GCC" }, + { "gcc-libs", "GCC" }, + { "freetype2", "FreeType" }, + { "harfbuzz", "HarfBuzz" }, + { "graphite", "Graphite" }, + { "openssl", "OpenSSL" }, + { "pcre", "PCRE" }, + { "pcre2", "PCRE2" }, + { "glib2", "GLib" }, + { "numix-icon-theme", "Numix icon theme" }, + { "breeze-icons", "Breeze icons (from KDE)" }, + { "go", "Go" }, + { "syncthing", "Syncthing" }, + { "syncthingtray", "Syncthing Tray" }, + { "tageditor", "Tag Editor" }, + { "passwordmanager", "Password Manager" }, + }; + if (endsWith(packageID, "-git") || endsWith(packageID, "-svn")) { + packageID.resize(packageID.size() - 4); + } else if (endsWith(packageID, "-hg")) { + packageID.resize(packageID.size() - 3); + } + if (const auto displayName = displayNames.find(packageID); displayName != displayNames.cend()) { + packageID = displayName->second; + } else if (startsWith(packageID, "qt5-")) { + packageID = "Qt 5"; + regularPackageName = "qt5-base"; + } + + // skip package if custom license has already been added + if (const auto customLicenses = result.customLicences.find(packageID); + customLicenses != result.customLicences.end() && !customLicenses->second.empty()) { + return packageID; + } + + // check whether the package has a standard license and/or a custom license + bool hasCustomLicense = package->licenses.empty(); + if (packageID == "Qt 5") { + // consider Qt's licenses custom as it has special variants of the standard licenses FDL, GPL and LGPL + hasCustomLicense = true; + } else { + // read the package's license field (see https://wiki.archlinux.org/index.php/PKGBUILD#license) + for (const auto &license : package->licenses) { + // check for custom licenses and licenses which contain project specific references and are therefore considered custom as well + if (startsWith(license, "custom") || license == "BSD" || license == "ISC" || license == "MIT" || license == "ZLIB" + || license == "Python") { + hasCustomLicense = true; + continue; + } + + // map Arch Linux generic way to say e.g. "GPL2 and above" to a concrete license e.g. "GPL2" + auto concreteLicense = license; + if (license == "GPL") { + concreteLicense = "GPL2"; + } else if (license == "LGPL" || license == "LGPL2") { + concreteLicense = "LGPL2.1"; + } else if (license == "FDL") { + concreteLicense = "FDL1.2"; + } else if (license == "AGPL") { + concreteLicense = "AGPL3"; + } else if (license == "APACHE") { + concreteLicense = "Apache"; + } + + result.commonLicenses[concreteLicense].relevantPackages.emplace(packageID); + } + } + + if (!hasCustomLicense) { + return packageID; + } + + // read custom license + if (!regularPackageName.empty()) { + const auto regularPackageSearchResult = findPackage(Dependency(regularPackageName, package->version)); + if (regularPackageSearchResult.pkg) { + const auto regularUpstreamVersion = PackageVersion::fromString(regularPackageSearchResult.pkg->version).upstream; + if (upstreamVersion != regularUpstreamVersion) { + result.success = false; // likely the license hasn't change; let's continue but don't consider it a successful run + result.notes.emplace_back( + "Regular package for " % searchResult.pkg->name % '-' % searchResult.pkg->version % " has different upstream version " + + regularUpstreamVersion); + } + searchResult.db = regularPackageSearchResult.db; + searchResult.pkg = regularPackageSearchResult.pkg; + } + } + // -> locate package + const auto *const db = std::get(searchResult.db); + if (!db) { + packageID.clear(); + return packageID; + } + const auto &packageInfo = searchResult.pkg->packageInfo; + if (!packageInfo) { + packageID.clear(); + return packageID; + } + const auto path = db->localPkgDir % '/' + packageInfo->fileName; + try { + auto directories = extractFiles(path, &Package::isLicense); + auto &licenses = result.customLicences[packageID]; + for (auto &dir : directories) { + for (auto &file : dir.second) { + if (file.type != ArchiveFileType::Regular) { + // skip anything but regular files (symlinks might point to other packages so resolving them is tricky) + continue; + } + licenses.emplace_back(dir.first % '/' + file.name, move(file.content)); + } + } + if (licenses.empty()) { + const auto &packageName = searchResult.pkg->name; + string fallback; + if (startsWith(packageName, "qt5-")) { + fallback = "qt5-base"; // the qt5-base package contains licenses for the other qt5-* packages + } else if (packageName == "gcc") { + fallback = "gcc-libs"; // the gcc-libs packages contains licenses for the gcc package + } + if (!fallback.empty() && fallback != packageName) { + result.notes.emplace_back("No license file contained in " % path % ", falling back to " + fallback); + if (!addLicenseInfo(result, Dependency(fallback, searchResult.pkg->version))) { + packageID.clear(); + } + return packageID; + } + + result.success = false; + result.notes.emplace_back("No license file contained in " + path); + packageID.clear(); + return packageID; + } + } catch (const runtime_error &e) { + result.success = false; + result.notes.emplace_back(e.what()); + packageID.clear(); + return packageID; + } + + return packageID; +} + +static void printQuoted(ostream &os, const string &str) +{ + bool needQuote = true; + for (auto c : str) { + if (needQuote) { + os << "> "; + needQuote = false; + } + os << c; + if (c == '\n') { + needQuote = true; + } + } +} + +LicenseResult Config::computeLicenseInfo(const std::vector &dependencyDenotations) +{ + LicenseResult result; + + // find "licenses" package containing common licenses + const auto commonLicensePackageSearch = findPackage(Dependency("licenses")); + const auto &licensesPackage = commonLicensePackageSearch.pkg; + const auto *const db = std::get(commonLicensePackageSearch.db); + if (!db || !licensesPackage || !licensesPackage->packageInfo) { + result.success = false; + result.notes.emplace_back("Unable to find licenses package."); + return result; + } + + // extract common licenses + const auto path = db->localPkgDir % '/' + licensesPackage->packageInfo->fileName; + decltype(extractFiles(path, &Package::isLicense)) licensesDirs; + try { + licensesDirs = extractFiles(path, &Package::isLicense); + if (licensesDirs.empty()) { + result.success = false; + result.notes.emplace_back("No relevant files found in licenses package."); + return result; + } + } catch (const runtime_error &e) { + result.success = false; + result.notes.emplace_back(e.what()); + return result; + } + + // add required common licenses and custom licenses + for (const auto &dependencyDenotation : dependencyDenotations) { + addLicenseInfo(result, Dependency(dependencyDenotation.data(), dependencyDenotation.size())); + } + + // find relevant common licenses in licenses package + for (auto &commonLicense : result.commonLicenses) { + const auto &licenseName = commonLicense.first; + auto &license = commonLicense.second; + const auto dir = licensesDirs.find("usr/share/licenses/common/" + licenseName); + if (dir == licensesDirs.end() || dir->second.empty()) { + result.success = false; + result.notes.emplace_back("Unable to find license dir for common license " + licenseName); + continue; + } + for (auto &file : dir->second) { + license.files.emplace_back(move(file.name), move(file.content)); + } + } + + // move "unique" common licenses to project specific licenses + for (auto i = result.commonLicenses.begin(); i != result.commonLicenses.end();) { + auto &license = i->second; + if (license.relevantPackages.size() != 1) { + ++i; + continue; + } + auto &customLicenses = result.customLicences[*license.relevantPackages.cbegin()]; + customLicenses.insert(customLicenses.begin(), license.files.begin(), license.files.end()); + result.commonLicenses.erase(i++); + } + + if (result.dependendProjects.empty()) { + return result; + } + + // start summary + stringstream summary; + summary << "This file contains licensing information for `" << result.mainProject << "` and libraries distributed with it:\n"; + for (const auto &dependency : result.dependendProjects) { + summary << " * `" << dependency << "`\n"; + } + + // add common licenses to summary + for (const auto &commonLicense : result.commonLicenses) { + const auto &licenseName = commonLicense.first; + auto &license = commonLicense.second; + if (summary.tellp()) { + summary << "\n---\n\n"; + } + summary << "License"; + if (license.files.size() != 1) { + summary << 's'; + } + summary << " `" << licenseName << "` of " << joinStrings(license.relevantPackages, ", ", false, "`", "`") << ":\n\n"; + for (const auto &licenseFile : license.files) { + if (license.files.size() > 1) { + summary << '`' << licenseFile.filename << "`: \n"; + } + printQuoted(summary, licenseFile.content); + } + } + + // add custom licenses to summary + for (const auto &customLicense : result.customLicences) { + if (customLicense.second.empty()) { + continue; + } + if (summary.tellp()) { + summary << "\n---\n\n"; + } + summary << "License"; + if (customLicense.second.size() != 1) { + summary << 's'; + } + summary << " of `" << customLicense.first << "`:\n\n"; + for (const auto &license : customLicense.second) { + if (customLicense.second.size() > 1) { + summary << '`' << license.filename << "`: \n"; + } + printQuoted(summary, license.content); + } + } + + result.licenseSummary = summary.str(); + + // print the summary for debugging (FIXME: remove debug code) + fstream("license-summary.md", ios_base::out | ios_base::trunc) << result.licenseSummary; + + // add project specific licenses + return result; +} + +} // namespace LibPkg diff --git a/libpkg/algo/search.cpp b/libpkg/algo/search.cpp new file mode 100644 index 0000000..3dd20a4 --- /dev/null +++ b/libpkg/algo/search.cpp @@ -0,0 +1,355 @@ +#include "../data/config.h" + +#include +#include + +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibPkg { + +/*! + * \brief Returns the database with the specified \a name and \a architecture or nullptr if it doesn't exist. + */ +Database *Config::findDatabase(std::string_view name, std::string_view architecture) +{ + for (auto &db : databases) { + if (db.name == name && (architecture.empty() || db.arch == architecture)) { + return &db; + } + } + return nullptr; +} + +/*! + * \brief Returns the database with the specified \a databaseDenotation or nullptr if it doesn't exist. + * \sa parseDatabaseDenotation() for the format of \a databaseDenotation + */ +Database *Config::findDatabaseFromDenotation(std::string_view databaseDenotation) +{ + const auto dbInfo = parseDatabaseDenotation(databaseDenotation); + return findDatabase(dbInfo.first, dbInfo.second); +} + +/*! + * \brief Returns the database with the specified \a name and \a architecture or creates a new one if it doesn't exist. + * \remarks Resets the database's configuration. You'll end up with a blank database in any case. + */ +Database *Config::findOrCreateDatabase(std::string &&name, std::string_view architecture) +{ + auto *db = findDatabase(name, architecture); + if (db) { + db->resetConfiguration(); + } else { + db = &databases.emplace_back(move(name)); + } + if (!architecture.empty()) { + db->arch = architecture; + } + return db; +} + +/*! + * \brief Returns the database with the specified \a name and \a architecture or creates a new one if it doesn't exist. + * \remarks Resets the database's configuration. You'll end up with a blank database in any case. + */ +Database *Config::findOrCreateDatabase(std::string_view name, std::string_view architecture) +{ + auto *db = findDatabase(name, architecture); + if (db) { + db->resetConfiguration(); + } else { + db = &databases.emplace_back(std::string(name)); + } + if (!architecture.empty()) { + db->arch = architecture; + } + return db; +} + +/*! + * \brief Returns the database with the specified \a databaseDenotation or creates a new one if it doesn't exist. + * \remarks Resets the database's configuration. You'll end up with a blank database in any case. + * \sa parseDatabaseDenotation() for the format of \a databaseDenotation + */ +Database *Config::findOrCreateDatabaseFromDenotation(std::string_view databaseDenotation) +{ + const auto dbInfo = parseDatabaseDenotation(databaseDenotation); + return findOrCreateDatabase(dbInfo.first, dbInfo.second); +} + +/*! + * \brief Runs \a processNextPackage for each package of each database using as many threads as CPU cores available. + * + * Databases and packages are iterated in order. \a processNextDatabase is called when "reaching" the next database. + * + * \a processNextDatabase and \a processNextPackage are supposed to return an empty string on success and an error message + * on failure. If \a processNextDatabase fails, the whole database is skipped. + * + * \a processNextDatabase is not ran in parallel and therefore expected to be fast. + * + * \returns Returns the error messages returned by \a processNextDatabase and \a processNextPackage. + * \remarks Not used anymore. Possibly still useful at some point? + */ +std::list Config::forEachPackage(const std::function &processNextDatabase, + const std::function &pkg, std::mutex &dbMutex)> &processNextPackage) +{ + // define mutex to sync getting the next package + std::mutex getNextPathMutex, submitFailureMutex, dbMutex; + std::list errorMessages; + + // define and initialize iterators + auto dbIterator = databases.begin(), dbEnd = databases.end(); + auto error = processNextDatabase(&*dbIterator); + while (dbIterator != dbEnd) { + if (error.empty()) { + break; + } + errorMessages.emplace_back(move(error)); + error = processNextDatabase(&*++dbIterator); + } + if (dbIterator == dbEnd) { + return errorMessages; + } + auto pkgIterator = dbIterator->packages.begin(), pkgEnd = dbIterator->packages.end(); + + // get the first database + Database *currentDb = &*dbIterator; + ++dbIterator; + + const auto recordError = [&](auto &&error) { + lock_guard lock(submitFailureMutex); + cerr << Phrases::SubError << error << Phrases::End; + errorMessages.emplace_back(error); + }; + + const auto processPackages = [&] { + for (;;) { + // get next package + shared_ptr currentPackage; + { + lock_guard lock(getNextPathMutex); + for (;;) { + if (pkgIterator != pkgEnd) { + currentPackage = pkgIterator->second; + ++pkgIterator; + break; + } else if (dbIterator != dbEnd) { + // process next database + auto error = processNextDatabase(&*dbIterator); + if (!error.empty()) { + if (error != "skip") { + recordError(move(error)); + } + ++dbIterator; + continue; + } + currentDb = &*dbIterator; + pkgIterator = dbIterator->packages.begin(); + pkgEnd = dbIterator->packages.end(); + ++dbIterator; + continue; + } else { + return; + } + } + } + + // process next package + try { + auto error = processNextPackage(currentDb, currentPackage, dbMutex); + if (!error.empty()) { + recordError(move(error)); + } + } catch (const runtime_error &e) { + recordError(argsToString(currentPackage->name, ':', ' ', e.what())); + continue; + } + } + }; + + vector threads(thread::hardware_concurrency() + 2); // FIXME: make this thread count configurable? + for (thread &t : threads) { + t = thread(processPackages); + } + processPackages(); + for (thread &t : threads) { + t.join(); + } + + return errorMessages; +} + +/*! + * \brief Returns all packages with the specified database name, database architecture and package name. + */ +std::vector Config::findPackages(std::tuple dbAndPackageName) +{ + vector pkgs; + const auto &[dbName, dbArch, packageName] = dbAndPackageName; + + // don't allow to get a list of all packages + if (packageName.empty()) { + return pkgs; + } + + const auto name = std::string(packageName); + for (auto &db : databases) { + if ((!dbName.empty() && dbName != db.name) || (!dbArch.empty() && dbArch != db.arch)) { + continue; + } + if (const auto i = db.packages.find(name); i != db.packages.end()) { + pkgs.emplace_back(db, i->second); + } + } + return pkgs; +} + +/*! + * \brief Returns the first package satisfying \a dependency. + * \remarks Packages where the name itself matches are preferred. + */ +PackageSearchResult Config::findPackage(const Dependency &dependency) +{ + PackageSearchResult result; + for (auto &db : databases) { + for (auto range = db.providedDeps.equal_range(dependency.name); range.first != range.second; ++range.first) { + const auto &providedDependency = range.first->second; + if (!Dependency::matches(dependency.mode, dependency.version, providedDependency.version)) { + continue; + } + const auto pkgs = providedDependency.relevantPackages; + for (const auto &pkg : pkgs) { + if (!result.pkg) { + result.db = &db; + result.pkg = pkg; + } + // prefer package where the name matches exactly; so if we found one no need to look further + if (dependency.name == pkg->name) { + result.db = &db; + result.pkg = pkg; + return result; + } + } + } + } + return result; +} + +/*! + * \brief Returns all packages satisfying \a dependency or - if \a reverse is true - all packages requiring \a dependency. + */ +std::vector Config::findPackages(const Dependency &dependency, bool reverse) +{ + const auto dependencySet = reverse ? &Database::requiredDeps : &Database::providedDeps; + vector results; + for (auto &db : databases) { + for (auto range = (db.*dependencySet).equal_range(dependency.name); range.first != range.second; ++range.first) { + const auto &providedDependency = range.first->second; + if (!Dependency::matches(dependency.mode, dependency.version, providedDependency.version)) { + continue; + } + for (const auto &pkg : providedDependency.relevantPackages) { + if (std::find_if(results.begin(), results.end(), [&pkg](const auto &res) { return res.pkg == pkg; }) == results.end()) { + results.emplace_back(db, pkg); + } + } + } + } + return results; +} + +/*! + * \brief Returns all packages providing \a library or - if \a reverse is true - all packages requiring \a library. + */ +std::vector Config::findPackagesProvidingLibrary(const string &library, bool reverse) +{ + const auto packagesByLibraryName = reverse ? &Database::requiredLibs : &Database::providedLibs; + vector results; + for (auto &db : databases) { + for (auto range = (db.*packagesByLibraryName).equal_range(library); range.first != range.second; ++range.first) { + for (const auto &pkg : range.first->second) { + if (std::find_if(results.begin(), results.end(), [&pkg](const auto &res) { return res.pkg == pkg; }) == results.end()) { + results.emplace_back(db, pkg); + } + } + } + } + return results; +} + +/*! + * \brief Returns all packages which names matches \a regex. + */ +std::vector Config::findPackages(const regex ®ex) +{ + vector pkgs; + for (auto &db : databases) { + for (const auto &pkg : db.packages) { + if (regex_match(pkg.second->name, regex)) { + pkgs.emplace_back(db, pkg.second); + } + } + } + return pkgs; +} + +/*! + * \brief Returns all packages considered "the same" as \a package. + * \remarks See Package::isSame(). + */ +std::vector Config::findPackages(const Package &package) +{ + vector pkgs; + for (auto &db : databases) { + for (const auto &pkg : db.packages) { + if (pkg.second->isSame(package)) { + pkgs.emplace_back(db, pkg.second); + break; + } + } + } + return pkgs; +} + +/*! + * \brief Returns all packages \a packagePred returns true for from all databases \a databasePred returns true for. + */ +std::vector Config::findPackages( + const std::function &databasePred, const std::function &packagePred) +{ + std::vector pkgs; + for (auto &db : databases) { + if (!databasePred(db)) { + continue; + } + for (const auto &pkg : db.packages) { + if (packagePred(db, *pkg.second)) { + pkgs.emplace_back(db, pkg.second); + } + } + } + return pkgs; +} + +/*! + * \brief Returns all packages \a pred returns true for. + */ +std::vector Config::findPackages(const std::function &pred) +{ + std::vector pkgs; + for (auto &db : databases) { + for (const auto &pkg : db.packages) { + if (pred(db, *pkg.second)) { + pkgs.emplace_back(db, pkg.second); + } + } + } + return pkgs; +} + +} // namespace LibPkg diff --git a/libpkg/data/config.cpp b/libpkg/data/config.cpp new file mode 100644 index 0000000..2a33908 --- /dev/null +++ b/libpkg/data/config.cpp @@ -0,0 +1,160 @@ +#include "./config.h" +#include "./global.h" + +#include + +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibPkg { + +Status::Status(const Config &config) + : architectures(config.architectures) + , pacmanDatabasePath(config.pacmanDatabasePath) + , packageCacheDirs(config.packageCacheDirs) +{ + dbStats.reserve(config.databases.size() + 1); + for (const auto &db : config.databases) { + dbStats.emplace_back(db); + } + dbStats.emplace_back(config.aur); +} + +static const std::string &firstNonLocalMirror(const std::vector &mirrors) +{ + for (const auto &mirror : mirrors) { + if (!mirror.empty() && !startsWith(mirror, "file:")) { + return mirror; + } + } + static const auto emptyMirror = std::string(); + return emptyMirror; +} + +DatabaseStatistics::DatabaseStatistics(const Database &db) + : name(db.name) + , packageCount(db.packages.size()) + , arch(db.arch) + , lastUpdate(db.lastUpdate) + , localPkgDir(db.localPkgDir) + , mainMirror(firstNonLocalMirror(db.mirrors)) + , syncFromMirror(db.syncFromMirror) +{ +} + +static std::string addDatabaseDependencies( + Config &config, Database &database, std::vector &result, std::unordered_map &visited) +{ + // abort if ... + const auto insertion = visited.emplace(make_pair(&database, false)); + if (insertion.first->second) { + return string(); // ... the database is already dealt with + } + if (!insertion.second) { + return argsToString("cycle at ", database.name); // ... a cycle has been detected + } + + // add the dependencies first + for (const auto &dependency : database.dependencies) { + auto *const requiredDb = config.findDatabase(dependency, database.arch); + if (!requiredDb) { + return argsToString( + "database \"", dependency, "\" required by \"", database.name, "\" does not exist (architecture ", database.arch, ')'); + } + if (auto error = addDatabaseDependencies(config, *requiredDb, result, visited); !error.empty()) { + return error; + } + } + + // add database itself + result.emplace_back(&database); + + // consider this db done; if something else depends on it is o.k. and not a cycle + visited[&database] = true; + + return string(); +} + +std::variant, std::string> Config::computeDatabaseDependencyOrder(Database &database) +{ + std::vector result; + std::unordered_map visited; + result.reserve(database.dependencies.size()); + auto error = addDatabaseDependencies(*this, database, result, visited); + if (!error.empty()) { + return move(error); + } + return move(result); +} + +static void addDatabasesRequiringDatabase( + Config &config, Database ¤tDatabase, std::vector &result, std::unordered_set &visited) +{ + // abort if the database is already dealt with or being processed (not caring about cycles here) + if (!visited.emplace(¤tDatabase).second) { + return; + } + + // add the current database + result.emplace_back(¤tDatabase); + + // add all configured databases depending on the current database + for (auto &configuredDb : config.databases) { + if (&configuredDb == ¤tDatabase || configuredDb.arch != currentDatabase.arch) { + continue; + } + if (std::find(configuredDb.dependencies.begin(), configuredDb.dependencies.end(), currentDatabase.name) != configuredDb.dependencies.end()) { + addDatabasesRequiringDatabase(config, configuredDb, result, visited); + } + } +} + +std::vector Config::computeDatabasesRequiringDatabase(Database &database) +{ + std::vector result; + std::unordered_set visited; + result.reserve(databases.size()); + addDatabasesRequiringDatabase(*this, database, result, visited); + return result; +} + +void Config::pullDependentPackages(const std::vector &dependencies, const std::shared_ptr &relevantPackage, + const std::unordered_set &relevantDbs, std::unordered_set &runtimeDependencies, DependencySet &missingDependencies) +{ + for (const auto &dependency : dependencies) { + const auto results = findPackages(dependency); + auto found = false; + for (const auto &result : results) { + if (relevantDbs.find(std::get(result.db)) != relevantDbs.end()) { + found = true; + if (runtimeDependencies.emplace(result.pkg.get()).second) { + pullDependentPackages(result.pkg, relevantDbs, runtimeDependencies, missingDependencies); + } + } + } + if (!found) { + missingDependencies.add(dependency, relevantPackage); + } + } +} + +void Config::pullDependentPackages(const std::shared_ptr &package, const std::unordered_set &relevantDbs, + std::unordered_set &runtimeDependencies, DependencySet &missingDependencies) +{ + pullDependentPackages(package->dependencies, package, relevantDbs, runtimeDependencies, missingDependencies); + pullDependentPackages(package->optionalDependencies, package, relevantDbs, runtimeDependencies, missingDependencies); +} + +void Config::markAllDatabasesToBeDiscarded() +{ + for_each(databases.begin(), databases.end(), [](auto &db) { db.toBeDiscarded = true; }); +} + +void Config::discardDatabases() +{ + databases.erase(remove_if(databases.begin(), databases.end(), [](const auto &db) { return db.toBeDiscarded; }), databases.end()); +} + +} // namespace LibPkg diff --git a/libpkg/data/config.h b/libpkg/data/config.h new file mode 100644 index 0000000..43a6c58 --- /dev/null +++ b/libpkg/data/config.h @@ -0,0 +1,185 @@ +#ifndef LIBPKG_DATA_CONFIG_H +#define LIBPKG_DATA_CONFIG_H + +#include "./database.h" +#include "./lockable.h" +#include "./siglevel.h" + +#include "../global.h" + +#include +#include + +#include +#include +#include +#include + +namespace LibPkg { + +/*! + * \brief The SigStatus enum specifies PGP signature verification status return codes. + */ +enum class SignatureStatus { Valid, KeyExpired, SigExpired, KeyUnknown, KeyDisabled, InvalidId }; + +struct Config; +struct Database; + +struct LIBPKG_EXPORT DatabaseStatistics : public ReflectiveRapidJSON::JsonSerializable { + DatabaseStatistics(const Database &config); + + const std::string &name; + const std::size_t packageCount; + const std::string &arch; + const CppUtilities::DateTime lastUpdate; + const std::string &localPkgDir; + const std::string &mainMirror; + const bool syncFromMirror; +}; + +struct LIBPKG_EXPORT Status : public ReflectiveRapidJSON::JsonSerializable { + Status(const Config &config); + + std::vector dbStats; + const std::set &architectures; + const std::string &pacmanDatabasePath; + const std::vector &packageCacheDirs; +}; + +struct TopoSortItem; + +struct LIBPKG_EXPORT BuildOrderResult : public ReflectiveRapidJSON::JsonSerializable { + std::vector order; + std::vector cycle; + std::vector ignored; + bool success = false; +}; + +enum BuildOrderOptions { + None = 0x0, /**< none of the other options enabled */ + IncludeSourceOnlyDependencies = 0x2, /**< whether source-only dependencies should be added the list of resulting packages */ + IncludeAllDependencies + = 0x3, /**< whether *all* dependencies should be added the list of resulting packages (implies IncludeSourceOnlyDependencies) */ + ConsiderBuildDependencies = 0x4, /**< whether build dependencies should be taken into account for the topo sort */ +}; + +struct LicenseFile : public ReflectiveRapidJSON::JsonSerializable, public ReflectiveRapidJSON::BinarySerializable { + LicenseFile() = default; + LicenseFile(std::string &&filename, std::string &&content) + : filename(filename) + , content(content) + { + } + std::string filename; + std::string content; +}; + +struct LIBPKG_EXPORT CommonLicense : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::set relevantPackages; + std::vector files; +}; + +struct LIBPKG_EXPORT LicenseResult : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::map commonLicenses; + std::map> customLicences; + std::vector consideredPackages; + std::vector ignoredPackages; + std::vector notes; + std::string mainProject; + std::set dependendProjects; + std::string licenseSummary; + bool success = true; +}; + +constexpr BuildOrderOptions operator|(BuildOrderOptions lhs, BuildOrderOptions rhs) +{ + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +constexpr bool operator&(BuildOrderOptions lhs, BuildOrderOptions rhs) +{ + return (static_cast(lhs) & static_cast(rhs)) != 0; +} + +struct LIBPKG_EXPORT Config : public Lockable, public ReflectiveRapidJSON::BinarySerializable { + // load config and packages + void loadPacmanConfig(const char *pacmanConfigPath); + void loadAllPackages(bool withFiles); + + // caching + std::uint64_t restoreFromCache(); + std::uint64_t dumpCacheFile(); + void markAllDatabasesToBeDiscarded(); + void discardDatabases(); + + // computions + Status computeStatus() const; + BuildOrderResult computeBuildOrder(const std::vector &dependencyDenotations, BuildOrderOptions options); + LicenseResult computeLicenseInfo(const std::vector &dependencyDenotations); + std::variant, std::string> computeDatabaseDependencyOrder(Database &database); + std::vector computeDatabasesRequiringDatabase(Database &database); + void pullDependentPackages(const std::vector &dependencies, const std::shared_ptr &relevantPackage, + const std::unordered_set &relevantDbs, std::unordered_set &runtimeDependencies, + DependencySet &missingDependencies); + void pullDependentPackages(const std::shared_ptr &package, const std::unordered_set &relevantDbs, + std::unordered_set &runtimeDependencies, DependencySet &missingDependencies); + + // search for packages + static std::pair parseDatabaseDenotation(std::string_view databaseDenotation); + Database *findDatabase(std::string_view name, std::string_view architecture); + Database *findDatabaseFromDenotation(std::string_view databaseDenotation); + Database *findOrCreateDatabase(std::string &&name, std::string_view architecture); + Database *findOrCreateDatabase(std::string_view name, std::string_view architecture); + Database *findOrCreateDatabaseFromDenotation(std::string_view databaseDenotation); + static std::tuple parsePackageDenotation(std::string_view packageDenotation); + std::vector findPackages(std::string_view packageDenotation); + std::vector findPackages(std::string_view dbName, std::string_view dbArch, std::string_view packageName); + std::vector findPackages(std::tuple dbAndPackageName); + PackageSearchResult findPackage(const Dependency &dependency); + std::vector findPackages(const Dependency &dependency, bool reverse = false); + std::vector findPackagesProvidingLibrary(const std::string &library, bool reverse = false); + std::vector findPackages(const std::regex ®ex); + std::vector findPackages(const Package &package); + std::vector findPackages( + const std::function &databasePred, const std::function &packagePred); + std::vector findPackages(const std::function &pred); + + // utilities + std::list forEachPackage(const std::function &processNextDatabase, + const std::function &pkg, std::mutex &dbMutex)> &processNextPackage); + + std::vector databases; + Database aur = Database("aur"); + std::set architectures; + std::string pacmanDatabasePath; + std::vector packageCacheDirs; + SignatureLevelConfig signatureLevel; + +private: + bool addDepsRecursivelyInTopoOrder(std::vector> &allItems, std::vector &items, + std::vector &ignored, std::vector &cycleTracking, const Dependency &dependency, BuildOrderOptions options, + bool onlyDependency); + bool addLicenseInfo(LicenseResult &result, const Dependency &dependency); + std::string addLicenseInfo(LicenseResult &result, PackageSearchResult &searchResult, const std::shared_ptr &package); +}; + +inline Status Config::computeStatus() const +{ + return Status(*this); +} + +inline std::vector Config::findPackages(std::string_view dbName, std::string_view dbArch, std::string_view packageName) +{ + return findPackages(std::make_tuple(dbName, dbArch, packageName)); +} + +inline std::vector Config::findPackages(std::string_view packageDenotation) +{ + return findPackages(parsePackageDenotation(packageDenotation)); +} + +} // namespace LibPkg + +#endif // LIBPKG_DATA_CONFIG_H diff --git a/libpkg/data/database.cpp b/libpkg/data/database.cpp new file mode 100644 index 0000000..4aa449f --- /dev/null +++ b/libpkg/data/database.cpp @@ -0,0 +1,492 @@ +#include "./database.h" +#include "./config.h" + +#include "reflection/database.h" + +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibPkg { + +void LibPkg::Database::deducePathsFromLocalDirs() +{ + if (localDbDir.empty()) { + return; + } + if (path.empty()) { + path = localDbDir % '/' % name + ".db"; + } + if (filesPath.empty()) { + filesPath = localDbDir % '/' % name + ".files"; + } +} + +void Database::resetConfiguration() +{ + path.clear(); + filesPath.clear(); + mirrors.clear(); + usage = DatabaseUsage::None; + signatureLevel = SignatureLevel::Default; + arch = "x86_64"; + dependencies.clear(); + localPkgDir.clear(); + localDbDir.clear(); + syncFromMirror = false; +} + +void Database::clearPackages() +{ + packages.clear(); + providedLibs.clear(); + requiredLibs.clear(); + providedDeps.clear(); + requiredDeps.clear(); + lastUpdate = CppUtilities::DateTime::gmtNow(); +} + +std::vector> Database::findPackages(const std::function &pred) +{ + std::vector> pkgs; + for (const auto &pkg : packages) { + if (pred(*this, *pkg.second)) { + pkgs.emplace_back(pkg.second); + } + } + return pkgs; +} + +void Database::removePackageDependencies(PackageMap::const_iterator packageIterator) +{ + const auto &package = packageIterator->second; + providedDeps.remove(Dependency(package->name, package->version), package); + for (const auto &dep : package->provides) { + providedDeps.remove(dep, package); + } + for (const auto &dep : package->dependencies) { + requiredDeps.remove(dep, package); + } + for (const auto &dep : package->optionalDependencies) { + requiredDeps.remove(dep, package); + } + for (const auto &lib : package->libprovides) { + const auto iterator(providedLibs.find(lib)); + if (iterator == providedLibs.end()) { + continue; + } + auto &relevantPackages(iterator->second); + relevantPackages.erase(remove(relevantPackages.begin(), relevantPackages.end(), package), relevantPackages.end()); + if (relevantPackages.empty()) { + providedLibs.erase(iterator); + } + } + for (const auto &lib : package->libdepends) { + const auto iterator(requiredLibs.find(lib)); + if (iterator == requiredLibs.end()) { + continue; + } + auto &relevantPackages(iterator->second); + relevantPackages.erase(remove(relevantPackages.begin(), relevantPackages.end(), package), relevantPackages.end()); + if (relevantPackages.empty()) { + requiredLibs.erase(iterator); + } + } +} + +void Database::addPackageDependencies(const std::shared_ptr &package) +{ + providedDeps.add(Dependency(package->name, package->version), package); + for (const auto &dep : package->provides) { + providedDeps.add(dep, package); + } + for (const auto &dep : package->dependencies) { + requiredDeps.add(dep, package); + } + for (const auto &dep : package->optionalDependencies) { + requiredDeps.add(dep, package); + } + for (const auto &lib : package->libprovides) { + providedLibs[lib].emplace_back(package); + } + for (const auto &lib : package->libdepends) { + requiredLibs[lib].emplace_back(package); + } +} + +void Database::removePackage(const std::string &packageName) +{ + const auto packageIterator = packages.find(packageName); + if (packageIterator == packages.end()) { + return; + } + removePackage(packageIterator); +} + +void LibPkg::Database::removePackage(PackageMap::const_iterator packageIterator) +{ + removePackageDependencies(packageIterator); + packages.erase(packageIterator); +} + +void Database::updatePackage(const std::shared_ptr &package) +{ + // check whether the package already exists + const auto packageIterator = packages.find(package->name); + if (packageIterator != packages.end()) { + const auto &existingPackage = packageIterator->second; + if (package == existingPackage) { + return; + } + // retain certain information obtained from package contents if this is actually the same package as before + package->addDepsAndProvidesFromOtherPackage(*existingPackage); + // remove the existing package + removePackage(packageIterator); + } + + // add the new package + addPackageDependencies(package); + packages.emplace(package->name, package); +} + +void Database::forceUpdatePackage(const std::shared_ptr &package) +{ + // check whether the package already exists + const auto packageIterator = packages.find(package->name); + auto differentPackage = true; + if (packageIterator != packages.end()) { + const auto &existingPackage = packageIterator->second; + if ((differentPackage = package != existingPackage)) { + // retain certain information obtained from package contents if this is actually the same package as before + package->addDepsAndProvidesFromOtherPackage(*existingPackage); + // remove the existing package + removePackage(packageIterator); + } + } + + // add the new package + addPackageDependencies(package); + if (differentPackage) { + packages.emplace(package->name, package); + } +} + +void Database::replacePackages(const std::vector> &newPackages, DateTime lastModified) +{ + // retain certain information obtained from package contents + for (auto &package : newPackages) { + const auto packageIterator = packages.find(package->name); + if (packageIterator == packages.end()) { + continue; + } + package->addDepsAndProvidesFromOtherPackage(*packageIterator->second); + } + // clear current packages and add new ones + clearPackages(); + for (auto &package : newPackages) { + updatePackage(package); + } + lastUpdate = lastModified; +} + +/*! + * \brief Determines which packages are unresolvable. + * \param config The configuration supposed to contain database dependencies. + * \param newPackages Packages which should be added to the database. + * \param removedProvides Provides which will be removed from the database. + * \remarks "Resolvable" means here (so far) just that all dependencies are present. It does not mean "installable". + */ +std::unordered_map, UnresolvedDependencies> Database::detectUnresolvedPackages( + Config &config, const std::vector> &newPackages, const DependencySet &removedProvides) +{ + std::unordered_map, UnresolvedDependencies> unresolvedPackages; + + // determine new provides + DependencySet newProvides; + set newLibProvides; + for (const auto &newPackage : newPackages) { + newProvides.add(Dependency(newPackage->name, newPackage->version), newPackage); + for (const auto &newProvide : newPackage->provides) { + newProvides.add(newProvide, newPackage); + } + for (const auto &newLibProvide : newPackage->libprovides) { + newLibProvides.emplace(newLibProvide); + } + } + + // check whether all required dependencies are still provided + for (const auto &requiredDep : requiredDeps) { + const auto &[dependencyName, dependencyDetail] = requiredDep; + const auto &affectedPackages = dependencyDetail.relevantPackages; + + // skip if new packages provide dependency + if (newProvides.provides(dependencyName, dependencyDetail)) { + continue; + } + + // skip if db provides dependency + if (!removedProvides.provides(dependencyName, dependencyDetail) && providedDeps.provides(dependencyName, dependencyDetail)) { + continue; + } + + // skip if dependency is provided by a database this database depends on + const auto checkDb = [this, &config, &requiredDep](const std::string &dbName) { + if (const auto *const db = config.findDatabase(dbName, arch)) { + if (db->providedDeps.provides(requiredDep.first, requiredDep.second)) { + return true; + } + } + return false; + }; + auto providedByAnotherDb = false; + for (const auto &dbName : dependencies) { + if ((providedByAnotherDb = checkDb(dbName))) { + break; + } + } + if (providedByAnotherDb) { + continue; + } + + // skip if dependency is provided by the protected version of this db + if (checkDb(name + "-protected")) { + continue; + } + + // add packages to list of unresolved packages + for (const auto &affectedPackage : affectedPackages) { + unresolvedPackages[affectedPackage].deps.emplace_back(Dependency(dependencyName, dependencyDetail.version, dependencyDetail.mode)); + } + } + + // check whether all required libraries are still provided + for (const auto &[requiredLib, affectedPackages] : requiredLibs) { + + // skip if new packages provide dependency + if (newLibProvides.count(requiredLib)) { + continue; + } + + // skip if db provides dependency + if (providedLibs.count(requiredLib)) { + continue; + } + + // skip if dependency is provided by a database this database depends on + const auto checkDb = [this, &config, &requiredLib = requiredLib](const std::string &dbName) { + if (const auto *const db = config.findDatabase(dbName, arch)) { + if (db->providedLibs.count(requiredLib)) { + return true; + } + } + return false; + }; + auto providedByAnotherDb = false; + for (const auto &dbName : dependencies) { + if ((providedByAnotherDb = checkDb(dbName))) { + break; + } + } + if (providedByAnotherDb) { + continue; + } + + // skip if library is provided by the protected version of this db + if (checkDb(name + "-protected")) { + continue; + } + + // add packages to list of unresolved packages + for (const auto &affectedPackage : affectedPackages) { + unresolvedPackages[affectedPackage].libs.emplace_back(requiredLib); + } + } + + return unresolvedPackages; +} + +LibPkg::PackageUpdates LibPkg::Database::checkForUpdates(const std::vector &updateSources) +{ + PackageUpdates results; + for (const auto &myPackageIterator : packages) { + const auto &myPackage = myPackageIterator.second; + auto foundPackage = false; + for (auto *updateSource : updateSources) { + const auto updatePackageIterator = updateSource->packages.find(myPackageIterator.first); + if (updatePackageIterator == updateSource->packages.cend()) { + continue; + } + foundPackage = true; + const auto &updatePackage = updatePackageIterator->second; + const auto versionDiff = myPackage->compareVersion(*updatePackage); + std::vector *list = nullptr; + switch (versionDiff) { + case PackageVersionComparison::SoftwareUpgrade: + list = &results.versionUpdates; + break; + case PackageVersionComparison::PackageUpgradeOnly: + list = &results.packageUpdates; + break; + case PackageVersionComparison::NewerThanSyncVersion: + list = &results.downgrades; + break; + default:; + } + if (list) { + list->emplace_back(PackageSearchResult(*this, myPackage), PackageSearchResult(*updateSource, updatePackage)); + } + } + if (!foundPackage) { + results.orphans.emplace_back(PackageSearchResult(*this, myPackage)); + } + } + return results; +} + +PackageLocation Database::locatePackage(const string &packageName) const +{ + PackageLocation res; + if (packageName.empty()) { + return res; + } + res.pathWithinRepo = localPkgDir % '/' + packageName; + try { + switch (std::filesystem::symlink_status(res.pathWithinRepo).type()) { + case std::filesystem::file_type::regular: + res.exists = true; + break; + case std::filesystem::file_type::symlink: + res.storageLocation = std::filesystem::read_symlink(res.pathWithinRepo); + if (res.storageLocation.is_absolute()) { + res.exists = std::filesystem::is_regular_file(res.storageLocation); + break; + } + res.storageLocation = argsToString(localPkgDir, '/', res.storageLocation); + if ((res.exists = std::filesystem::is_regular_file(res.storageLocation))) { + res.storageLocation = std::filesystem::canonical(res.storageLocation); + } + break; + default: + break; + } + } catch (std::filesystem::filesystem_error &e) { + res.error = move(e); + } + return res; +} + +std::string Database::filesPathFromRegularPath() const +{ + if (path.empty()) { + return std::string(); + } + const auto ext = path.rfind(".db"); + return ext == std::string::npos ? path : argsToString(std::string_view(path.data(), ext), ".files"); +} + +} // namespace LibPkg + +namespace ReflectiveRapidJSON { + +namespace JsonReflector { + +template <> +LIBPKG_EXPORT void push( + const LibPkg::PackageSearchResult &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) +{ + // customize serialization of PackageSearchResult to render as if it was pkg itself with an additional db property + value.SetObject(); + auto obj = value.GetObject(); + auto &pkg = reflectable.pkg; + push(pkg->name, "name", obj, allocator); + push(pkg->origin, "origin", obj, allocator); + push(pkg->timestamp, "timestamp", obj, allocator); + push(pkg->version, "version", obj, allocator); + push(pkg->description, "description", obj, allocator); + if (const auto &pkgInfo = pkg->packageInfo) { + push(pkgInfo->arch, "arch", obj, allocator); + push(pkgInfo->buildDate, "buildDate", obj, allocator); + } + if (const auto &srcInfo = pkg->sourceInfo) { + push(srcInfo->archs, "archs", obj, allocator); + } + if (const auto *const dbInfo = std::get_if(&reflectable.db)) { + if (!dbInfo->name.empty()) { + push(dbInfo->name, "db", obj, allocator); + } + if (!dbInfo->arch.empty()) { + push(dbInfo->arch, "dbArch", obj, allocator); + } + } else if (const auto *const db = std::get(reflectable.db)) { + push(db->name, "db", obj, allocator); + if (!db->arch.empty()) { + push(db->arch, "dbArch", obj, allocator); + } + } +} + +template <> +LIBPKG_EXPORT void pull(LibPkg::PackageSearchResult &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (!value.IsObject()) { + if (errors) { + errors->reportTypeMismatch(value.GetType()); + } + return; + } + + auto obj = value.GetObject(); + auto &pkg = reflectable.pkg; + if (!pkg) { + pkg = make_shared(); + } + ReflectiveRapidJSON::JsonReflector::pull(pkg->name, "name", obj, errors); + ReflectiveRapidJSON::JsonReflector::pull(pkg->origin, "origin", obj, errors); + ReflectiveRapidJSON::JsonReflector::pull(pkg->timestamp, "timestamp", obj, errors); + ReflectiveRapidJSON::JsonReflector::pull(pkg->version, "version", obj, errors); + ReflectiveRapidJSON::JsonReflector::pull(pkg->description, "description", obj, errors); + auto &pkgInfo = pkg->packageInfo; + if (!pkgInfo) { + pkgInfo = make_unique(); + } + ReflectiveRapidJSON::JsonReflector::pull(pkgInfo->arch, "arch", obj, errors); + ReflectiveRapidJSON::JsonReflector::pull(pkgInfo->buildDate, "buildDate", obj, errors); + auto &srcInfo = pkg->sourceInfo; + if (!srcInfo) { + srcInfo = make_shared(); + } + ReflectiveRapidJSON::JsonReflector::pull(srcInfo->archs, "archs", obj, errors); + auto &dbInfo = reflectable.db.emplace(); + ReflectiveRapidJSON::JsonReflector::pull(dbInfo.name, "db", obj, errors); + ReflectiveRapidJSON::JsonReflector::pull(dbInfo.arch, "dbArch", obj, errors); +} + +} // namespace JsonReflector + +namespace BinaryReflector { + +template <> +LIBPKG_EXPORT void writeCustomType(BinarySerializer &serializer, const LibPkg::PackageSearchResult &packageSearchResult) +{ + if (const auto *const dbInfo = std::get_if(&packageSearchResult.db)) { + serializer.write(dbInfo->name); + } else if (const auto *const db = std::get(packageSearchResult.db)) { + serializer.write(db->name); + } else { + serializer.write(std::string()); + } + serializer.write(packageSearchResult.pkg); +} + +template <> +LIBPKG_EXPORT void readCustomType(BinaryDeserializer &deserializer, LibPkg::PackageSearchResult &packageSearchResult) +{ + deserializer.read(packageSearchResult.db.emplace().name); + deserializer.read(packageSearchResult.pkg); +} + +} // namespace BinaryReflector + +} // namespace ReflectiveRapidJSON diff --git a/libpkg/data/database.h b/libpkg/data/database.h new file mode 100644 index 0000000..52eb412 --- /dev/null +++ b/libpkg/data/database.h @@ -0,0 +1,217 @@ +#ifndef LIBPKG_DATA_DATABASE_H +#define LIBPKG_DATA_DATABASE_H + +#include "./package.h" +#include "./siglevel.h" + +#include "../global.h" + +#include +#include + +#include +#include +#include + +namespace LibPkg { + +struct Config; +struct Database; + +struct LIBPKG_EXPORT DatabaseInfo { + std::string name; + std::string arch; +}; + +struct LIBPKG_EXPORT PackageSearchResult { + PackageSearchResult(); + PackageSearchResult(Database &database, const std::shared_ptr &package); + bool operator==(const PackageSearchResult &other) const; + + /// \brief The related database. + /// \remarks + /// - The find functions always use Database* and it is guaranteed to be never nullptr. + /// - The deserialization functions always use DatabaseInfo and the values might be empty if the source was empty. + /// - The serialization functions can cope with both alternatives. + std::variant db; + std::shared_ptr pkg; +}; + +/*! + * \brief The DatabaseUsage enum specifies the usage of a database within pacman. + */ +enum class DatabaseUsage { + None = 0, + Sync = 1, /*! The database is used when synchronizing. */ + Search = (1 << 1), /*! The database is used when searching. */ + Install = (1 << 2), /*! The database is used to install packages. */ + Upgrade = (1 << 3), /*! The database is used to upgrade packages. */ + All = (1 << 4) - 1, /*! The database is used for everything. */ +}; + +} // namespace LibPkg + +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibPkg, LibPkg::DatabaseUsage) + +namespace LibPkg { + +struct LIBPKG_EXPORT PackageUpdate : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + PackageUpdate(PackageSearchResult &&oldVersion = PackageSearchResult(), PackageSearchResult &&newVersion = PackageSearchResult()) + : oldVersion(oldVersion) + , newVersion(newVersion) + { + } + PackageSearchResult oldVersion; + PackageSearchResult newVersion; +}; + +struct LIBPKG_EXPORT PackageUpdates : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::vector versionUpdates; + std::vector packageUpdates; + std::vector downgrades; + std::vector orphans; +}; + +struct LIBPKG_EXPORT PackageLocation { + std::filesystem::path pathWithinRepo; + std::filesystem::path storageLocation; + std::optional error; + bool exists = false; +}; + +struct LIBPKG_EXPORT UnresolvedDependencies : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::vector deps; + std::vector libs; +}; + +struct LIBPKG_EXPORT Database : public ReflectiveRapidJSON::JsonSerializable, public ReflectiveRapidJSON::BinarySerializable { + using PackageMap = std::unordered_map>; + + Database(const std::string &name = std::string(), const std::string &path = std::string()); + Database(std::string &&name, std::string &&path); + void deducePathsFromLocalDirs(); + void resetConfiguration(); + void clearPackages(); + void loadPackages(bool withFiles = false); + void loadPackages(const std::string &databaseData, CppUtilities::DateTime lastModified); + void loadPackages(FileMap &&databaseFiles, CppUtilities::DateTime lastModified); + static bool isFileRelevant(const char *filePath, const char *fileName, mode_t); + std::vector> findPackages(const std::function &pred); + void removePackageDependencies(typename PackageMap::const_iterator packageIterator); + void addPackageDependencies(const std::shared_ptr &package); + void removePackage(const std::string &packageName); + void removePackage(typename PackageMap::const_iterator packageIterator); + void updatePackage(const std::shared_ptr &package); + void forceUpdatePackage(const std::shared_ptr &package); + void replacePackages(const std::vector> &newPackages, CppUtilities::DateTime lastModified); + std::unordered_map, UnresolvedDependencies> detectUnresolvedPackages( + Config &config, const std::vector> &newPackages, const DependencySet &removedPackages); + PackageUpdates checkForUpdates(const std::vector &updateSources); + PackageLocation locatePackage(const std::string &packageName) const; + std::string filesPathFromRegularPath() const; + + std::string name; + std::string path; + std::string filesPath; + std::vector mirrors; + PackageMap packages; + DatabaseUsage usage = DatabaseUsage::None; + SignatureLevel signatureLevel = SignatureLevel::Default; + std::string arch = "x86_64"; + std::vector dependencies; + DependencySet providedDeps; + DependencySet requiredDeps; + std::unordered_map>> providedLibs; + std::unordered_map>> requiredLibs; + std::string localPkgDir; + std::string localDbDir; + CppUtilities::DateTime lastUpdate; + bool syncFromMirror = false; + bool toBeDiscarded = false; + + // FIXME: The member variables packages, providedDeps, requiredDeps, providedLibs and requiredLibs should + // not be updated directly/individually; better make them private and provide a getter to a const ref. +}; + +inline Database::Database(const std::string &name, const std::string &path) + : name(name) + , path(path) +{ +} + +inline Database::Database(std::string &&name, std::string &&path) + : name(std::move(name)) + , path(std::move(path)) +{ +} + +inline PackageSearchResult::PackageSearchResult() + : db(nullptr) +{ +} + +inline PackageSearchResult::PackageSearchResult(Database &database, const std::shared_ptr &package) + : db(&database) + , pkg(package) +{ +} + +inline bool PackageSearchResult::operator==(const PackageSearchResult &other) const +{ + const auto *const *const db1 = std::get_if(&db); + const auto *const *const db2 = std::get_if(&other.db); + if (!db1 || !db2) { + return false; + } + return ((!*db1 && !*db2) || (*db1 && *db2 && (**db1).name == (**db2).name)) && pkg == other.pkg; +} + +} // namespace LibPkg + +namespace std { + +template <> struct hash { + std::size_t operator()(const LibPkg::PackageSearchResult &res) const + { + using std::hash; + const std::string *dbName = nullptr; + if (const auto *const dbInfo = std::get_if(&res.db)) { + dbName = &dbInfo->name; + } else if (const auto *const db = std::get(res.db)) { + dbName = &db->name; + } + return ((hash()(dbName ? *dbName : string()) ^ (hash>()(res.pkg) << 1)) >> 1); + } +}; + +} // namespace std + +namespace ReflectiveRapidJSON { + +namespace JsonReflector { + +// declare custom (de)serialization for PackageSearchResult +template <> +LIBPKG_EXPORT void push( + const LibPkg::PackageSearchResult &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); +template <> +LIBPKG_EXPORT void pull(LibPkg::PackageSearchResult &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors); + +} // namespace JsonReflector + +namespace BinaryReflector { + +template <> +LIBPKG_EXPORT void writeCustomType(BinarySerializer &serializer, const LibPkg::PackageSearchResult &packageSearchResult); +template <> +LIBPKG_EXPORT void readCustomType(BinaryDeserializer &deserializer, LibPkg::PackageSearchResult &packageSearchResult); + +} // namespace BinaryReflector + +} // namespace ReflectiveRapidJSON + +#endif // LIBPKG_DATA_DATABASE_H diff --git a/libpkg/data/lockable.cpp b/libpkg/data/lockable.cpp new file mode 100644 index 0000000..858f412 --- /dev/null +++ b/libpkg/data/lockable.cpp @@ -0,0 +1,7 @@ +#include "./lockable.h" + +using namespace std; + +namespace LibPkg { + +} // namespace LibPkg diff --git a/libpkg/data/lockable.h b/libpkg/data/lockable.h new file mode 100644 index 0000000..27e4e30 --- /dev/null +++ b/libpkg/data/lockable.h @@ -0,0 +1,36 @@ +#ifndef LIBPKG_DATA_LOCKABLE_H +#define LIBPKG_DATA_LOCKABLE_H + +#include +#include + +namespace LibPkg { + +struct Lockable { + [[nodiscard]] std::shared_lock lockToRead() const; + [[nodiscard]] std::unique_lock lockToWrite(); + [[nodiscard]] std::unique_lock lockToWrite(std::shared_lock &readLock); + +private: + mutable std::shared_mutex m_mutex; +}; + +inline std::shared_lock Lockable::lockToRead() const +{ + return std::shared_lock(m_mutex); +} + +inline std::unique_lock Lockable::lockToWrite() +{ + return std::unique_lock(m_mutex); +} + +inline std::unique_lock Lockable::lockToWrite(std::shared_lock &readLock) +{ + readLock.unlock(); + return std::unique_lock(m_mutex); +} + +} // namespace LibPkg + +#endif // LIBPKG_DATA_LOCKABLE_H diff --git a/libpkg/data/package.cpp b/libpkg/data/package.cpp new file mode 100644 index 0000000..29426df --- /dev/null +++ b/libpkg/data/package.cpp @@ -0,0 +1,523 @@ +#include "./package.h" + +#include "../parser/utils.h" + +#include "reflection/package.h" + +#include + +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibPkg { + +bool Dependency::matches(const DependencyMode mode, const string &version1, const string &version2) +{ + switch (mode) { + case DependencyMode::Any: + return true; + case DependencyMode::Equal: + switch (PackageVersion::compareParts(version2, version1, true)) { + case PackageVersionPartComparison::Equal: + return true; + default:; + } + break; + case DependencyMode::GreatherEqual: + switch (PackageVersion::compareParts(version2, version1, true)) { + case PackageVersionPartComparison::Equal: + case PackageVersionPartComparison::Newer: + return true; + default:; + } + break; + case DependencyMode::GreatherThan: + switch (PackageVersion::compareParts(version2, version1, true)) { + case PackageVersionPartComparison::Newer: + return true; + default:; + } + break; + case DependencyMode::LessEqual: + switch (PackageVersion::compareParts(version2, version1, true)) { + case PackageVersionPartComparison::Equal: + case PackageVersionPartComparison::Older: + return true; + default:; + } + break; + case DependencyMode::LessThan: + switch (PackageVersion::compareParts(version2, version1, true)) { + case PackageVersionPartComparison::Older: + return true; + default:; + } + break; + } + return false; +} + +ostream &operator<<(ostream &o, const DependencyMode &mode) +{ + switch (mode) { + case DependencyMode::Any: + break; + case DependencyMode::Equal: + o << '='; + break; + case DependencyMode::GreatherEqual: + o << '>' << '='; + break; + case DependencyMode::GreatherThan: + o << '>'; + break; + case DependencyMode::LessEqual: + o << '<' << '='; + break; + case DependencyMode::LessThan: + o << '<'; + break; + } + return o; +} + +ostream &operator<<(ostream &o, const Dependency &dependency) +{ + o << dependency.name; + if (dependency.mode != DependencyMode::Any) { + o << dependency.mode << dependency.version; + } + if (!dependency.description.empty()) { + o << ':' << ' ' << dependency.description; + } + return o; +} + +ostream &operator<<(ostream &o, const std::vector &dependencies) +{ + for (const auto &dependency : dependencies) { + o << dependency << '\n'; + } + return o; +} + +bool Package::providesDependency(const Dependency &dependency) const +{ + if (name == dependency.name) { + if (Dependency::matches(dependency.mode, dependency.version, version)) { + return true; + } + } + for (const Dependency &provide : provides) { + if (provide.matches(dependency)) { + return true; + } + } + return false; +} + +void Package::exportProvides( + const std::shared_ptr &package, DependencySet &destinationProvides, std::unordered_set &destinationLibProvides) +{ + destinationProvides.add(LibPkg::Dependency(package->name, package->version), package); + for (const auto &provide : package->provides) { + destinationProvides.add(provide, package); + } + destinationLibProvides.insert(package->libprovides.begin(), package->libprovides.end()); +} + +ostream &operator<<(ostream &o, const PackageVersionComparison &res) +{ + switch (res) { + case PackageVersionComparison::Equal: + o << "equal"; + break; + case PackageVersionComparison::SoftwareUpgrade: + o << "software upgrade"; + break; + case PackageVersionComparison::PackageUpgradeOnly: + o << "package upgrade"; + break; + case PackageVersionComparison::NewerThanSyncVersion: + o << "newer than sync version"; + break; + } + return o; +} + +ostream &operator<<(ostream &o, const PackageVersionPartComparison &res) +{ + switch (res) { + case PackageVersionPartComparison::Equal: + o << "equal"; + break; + case PackageVersionPartComparison::Newer: + o << "newer"; + break; + case PackageVersionPartComparison::Older: + o << "older"; + break; + } + return o; +} + +PackageVersionComparison Package::compareVersion(const Package &other) const +{ + return PackageVersion::fromString(version).compare(PackageVersion::fromString(other.version)); +} + +/*! + * \brief Compares the specified version segments which are supposed to only contain alphanumeric characters. + */ +PackageVersionPartComparison compareSegments(const char *segment1, const char *segment1End, const char *segment2, const char *segment2End) +{ + // trim leading zeros + for (; segment1 != segment1End && *segment1 == '0'; ++segment1) + ; + for (; segment2 != segment2End && *segment2 == '0'; ++segment2) + ; + // calculate segment sizes + const auto segment1Size = segment1End - segment1; + const auto segment2Size = segment2End - segment2; + if (segment1Size > segment2Size) { + // segment 1 has more digits and hence is newer + return PackageVersionPartComparison::Newer; + } else if (segment2Size > segment1Size) { + // segment 2 has more digits and hence is newer + return PackageVersionPartComparison::Older; + } else { + for (; segment1 != segment1End; ++segment1, ++segment2) { + if (*segment1 > *segment2) { + // segment 1 digit has higher value and hence is newer + return PackageVersionPartComparison::Newer; + } else if (*segment2 > *segment1) { + // segment 2 digit has higher value and hence is newer + return PackageVersionPartComparison::Older; + } + } + } + return PackageVersionPartComparison::Equal; +} + +/*! + * \brief Compares the specified version parts (which are either epoch, pkgver or pkgrel). + * \returns Returns whether \a part1 is newer or older than \a part2 or whether both are equal. + * \remarks \a part1 and \a part2 are considered null-terminated. So processing them is stopped + * when the first null-byte occurs even if std::string::size() denotes that string is longer. + */ +PackageVersionPartComparison PackageVersion::compareParts(const string &part1, const string &part2, bool allowImplicitPkgRel) +{ + const char *part1Pos = part1.data(), *part2Pos = part2.data(); + const char *part1End = part1.data() + part1.size(), *part2End = part2.data() + part2.size(); + for (;;) { + // determine current segments + part1End = firstNonAlphanumericCharacter(part1Pos, part1End); + part2End = firstNonAlphanumericCharacter(part2Pos, part2End); + // compare segments + const auto segmentComparsion = compareSegments(part1Pos, part1End, part2Pos, part2End); + if (segmentComparsion != PackageVersionPartComparison::Equal) { + return segmentComparsion; + } + // the segments are equal, check next segment + if (*part1End && *part2End) { + // there is another segment in both parts + part1Pos = part1End + 1; + part2Pos = part2End + 1; + } else if (*part1End) { + // only part 1 has another segment -> it is more specific and hence considered newer + if (allowImplicitPkgRel) { + // check whether the only further segment in part 1 is pkgrel + // -> check for pkgrel separation which is always '-' + if (*part1End == '-') { + part1Pos = part1End + 1; + part1End = firstNonAlphanumericCharacter(part1Pos, part1End); + if (!*part1End) { + // consider both parts equal if part 2 doesn't have explicit pkgrel and part 1 does + return PackageVersionPartComparison::Equal; + } + } + } + return PackageVersionPartComparison::Newer; + } else if (*part2End) { + // only part 2 has another segment -> it is more specific and hence considered newer + return PackageVersionPartComparison::Older; + } else { + // none of the parts has another segment -> parts are equal + return PackageVersionPartComparison::Equal; + } + } +} + +string PackageVersion::trimPackageVersion(const string &versionString) +{ + const auto lastDash = versionString.rfind('-'); + if (lastDash == string::npos) { + return versionString; + } + return versionString.substr(0, lastDash); +} + +/*! + * \brief Compares the current instance with another version. + * \returns Returns whether the current instance is newer or older than \a other or whether both are equal. + */ +PackageVersionComparison PackageVersion::compare(const PackageVersion &other) const +{ + // check whether epoch differs + if (!epoch.empty() || !other.epoch.empty()) { + switch (compareParts(other.epoch, epoch)) { + case PackageVersionPartComparison::Newer: + return PackageVersionComparison::SoftwareUpgrade; + case PackageVersionPartComparison::Older: + return PackageVersionComparison::NewerThanSyncVersion; + case PackageVersionPartComparison::Equal:; + } + } + // check whether upstream version differs + switch (compareParts(other.upstream, upstream)) { + case PackageVersionPartComparison::Newer: + return PackageVersionComparison::SoftwareUpgrade; + case PackageVersionPartComparison::Older: + return PackageVersionComparison::NewerThanSyncVersion; + case PackageVersionPartComparison::Equal:; + } + // check whether package version differs + if (!package.empty() && !other.package.empty()) { + // only consider package release if both versions specify it (otherwise consider packages equal) + switch (compareParts(other.package, package)) { + case PackageVersionPartComparison::Newer: + return PackageVersionComparison::PackageUpgradeOnly; + case PackageVersionPartComparison::Older: + return PackageVersionComparison::NewerThanSyncVersion; + case PackageVersionPartComparison::Equal:; + } + } + // no difference -> equal + return PackageVersionComparison::Equal; +} + +std::string PackageVersion::toString() const +{ + return epoch.empty() ? upstream % '-' + package : epoch % ':' % upstream % '-' + package; +} + +/*! + * \brief Returns the file of the binary package file. + * \remarks This value is computed from name, version and arch if unknown. + * \param extension Specifies the extension to be used when computing the value. + */ +string LibPkg::Package::computeFileName(const char *extension) const +{ + if (!packageInfo) { + return string(); + } + if (!packageInfo->fileName.empty()) { + return packageInfo->fileName; + } + return argsToString(name, '-', version, '-', packageInfo->arch, '.', extension); +} + +string LibPkg::Package::computeRegularPackageName() const +{ + if (name == "mingw-w64-headers" || name == "mingw-w64-crt") { + return string(); + } + if (startsWith(name, "lib32-")) { + return name.substr(strlen("lib32-")); + } else if (startsWith(name, "mingw-w64-")) { + return name.substr(strlen("mingw-w64-")); + } else if (startsWith(name, "android-aarch64-")) { + return name.substr(strlen("android-aarch64-")); + } else if (startsWith(name, "android-x86-64-")) { + return name.substr(strlen("android-x86-64-")); + } else if (startsWith(name, "android-x86-")) { + return name.substr(strlen("android-x86-")); + } else if (startsWith(name, "android-armv7a-eabi-")) { + return name.substr(strlen("android-armv7a-eabi-")); + } + return string(); +} + +bool Package::addDepsAndProvidesFromOtherPackage(const Package &otherPackage, bool force) +{ + if (&otherPackage == this) { + return false; + } + + // skip if otherPackage does not match the current instance (only version and build date are considered) or if otherPackage has no info from package contents + if (!force + && ((otherPackage.origin != PackageOrigin::PackageContents && otherPackage.origin != PackageOrigin::CustomSource) + || version != otherPackage.version + || !(!packageInfo || packageInfo->buildDate.isNull() + || (otherPackage.packageInfo && packageInfo->buildDate == otherPackage.packageInfo->buildDate)))) { + return false; + } + + // add package info from other package if this package has none at all + if (!packageInfo && otherPackage.packageInfo) { + packageInfo = make_unique(*(otherPackage.packageInfo)); + } + // add source info from other package; at least add missing make and check dependencies + if (!sourceInfo) { + sourceInfo = otherPackage.sourceInfo; + } else if (otherPackage.sourceInfo) { + if (sourceInfo->checkDependencies.empty()) { + sourceInfo->checkDependencies = otherPackage.sourceInfo->checkDependencies; + } + if (sourceInfo->makeDependencies.empty()) { + sourceInfo->makeDependencies = otherPackage.sourceInfo->makeDependencies; + } + } + + // retain libdepends and libprovides added in Package::addDepsAndProvidesFromContents() + libdepends = otherPackage.libdepends; + libprovides = otherPackage.libprovides; + + // replace python/perl dependencies with ones added in Package::addDepsAndProvidesFromContents() + const static std::unordered_set relevantDeps{ "python", "python2", "perl" }; + std::unordered_multimap foundDeps; + for (const auto &dependency : otherPackage.dependencies) { + const auto relevantDep = relevantDeps.find(dependency.name); + if (relevantDep != relevantDeps.end()) { + foundDeps.insert(std::make_pair(*relevantDep, &dependency)); + } + } + dependencies.erase(std::remove_if(dependencies.begin(), dependencies.end(), + [&foundDeps](const Dependency &dependency) { return foundDeps.find(dependency.name) != foundDeps.end(); }), + dependencies.end()); + const auto requiredCapacity = dependencies.size() + foundDeps.size(); + if (dependencies.capacity() < requiredCapacity) { + dependencies.reserve(requiredCapacity); + } + for (const auto &[dependencyName, dependency] : foundDeps) { + dependencies.emplace_back(*dependency); + } + + // consider this package as good as if Package::addDepsAndProvidesFromContents() was called + origin = PackageOrigin::PackageContents; + if (otherPackage.timestamp > timestamp) { + timestamp = otherPackage.timestamp; + } + return true; +} + +DependencySetBase::iterator DependencySet::find(const Dependency &dependency) +{ + for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) { + if (Dependency::matches(dependency.mode, dependency.version, range.first->second.version)) { + return range.first; + } + } + return end(); +} + +// FIXME: maybe remove again +DependencySetBase::iterator DependencySet::find(const std::string &dependencyName, const DependencyDetail &dependencyDetail) +{ + for (auto range = equal_range(dependencyName); range.first != range.second; ++range.first) { + if (Dependency::matches(dependencyDetail.mode, dependencyDetail.version, range.first->second.version)) { + return range.first; + } + } + return end(); +} + +DependencySetBase::iterator DependencySet::findExact(const Dependency &dependency) +{ + for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) { + if (dependency.version == range.first->second.version) { + return range.first; + } + } + return end(); +} + +// FIXME: maybe remove again +DependencySetBase::iterator DependencySet::findExact(const std::string &dependencyName, const DependencyDetail &dependencyDetail) +{ + for (auto range = equal_range(dependencyName); range.first != range.second; ++range.first) { + if (dependencyDetail.version == range.first->second.version) { + return range.first; + } + } + return end(); +} + +bool DependencySet::provides(const Dependency &dependency) const +{ + for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) { + if (Dependency::matches(dependency.mode, dependency.version, range.first->second.version)) { + return true; + } + } + return false; +} + +bool DependencySet::provides(const string &dependencyName, const DependencyDetail &dependencyDetail) const +{ + for (auto range = equal_range(dependencyName); range.first != range.second; ++range.first) { + if (Dependency::matches(dependencyDetail.mode, dependencyDetail.version, range.first->second.version)) { + return true; + } + } + return false; +} + +DependencySetBase::iterator DependencySet::add(const Dependency &dependency, const std::shared_ptr &relevantPackage) +{ + auto iterator = findExact(dependency); + if (iterator == end()) { + iterator = insert(make_pair(dependency.name, DependencyDetail(dependency.version, dependency.mode, { relevantPackage }))); + } else { + iterator->second.relevantPackages.emplace(relevantPackage); + } + return iterator; +} + +// FIXME: maybe remove again +DependencySetBase::iterator DependencySet::add( + const string &dependencyName, const DependencyDetail &dependencyDetail, const std::shared_ptr &relevantPackage) +{ + auto iterator = findExact(dependencyName, dependencyDetail); + if (iterator == end()) { + iterator = insert(make_pair(dependencyName, DependencyDetail{ dependencyDetail.version, dependencyDetail.mode, { relevantPackage } })); + } else { + iterator->second.relevantPackages.emplace(relevantPackage); + } + return iterator; +} + +DependencySetBase::iterator DependencySet::add(string &&dependencyName, DependencyDetail &&dependencyDetail) +{ + auto iterator = find(dependencyName, dependencyDetail); + if (iterator == end() || iterator->second.version != dependencyDetail.version) { + iterator = insert(std::pair(std::move(dependencyName), std::move(dependencyDetail))); + } + return iterator; +} + +void DependencySet::remove(const Dependency &dependency, const std::shared_ptr &relevantPackage) +{ + for (auto range = equal_range(dependency.name); range.first != range.second;) { + auto &relevantPackages = range.first->second.relevantPackages; + relevantPackages.erase(relevantPackage); + if (relevantPackages.empty()) { + erase(range.first); + range = equal_range(dependency.name); + } else { + ++range.first; + } + } +} + +void DependencySet::remove(const string &name) +{ + for (auto range = equal_range(name); range.first != range.second;) { + erase(range.first); + range = equal_range(name); + } +} + +} // namespace LibPkg diff --git a/libpkg/data/package.h b/libpkg/data/package.h new file mode 100644 index 0000000..59b8612 --- /dev/null +++ b/libpkg/data/package.h @@ -0,0 +1,417 @@ +#ifndef LIBPKG_DATA_PACKAGE_H +#define LIBPKG_DATA_PACKAGE_H + +#include "../global.h" + +#include "../parser/utils.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace LibPkg { + +/*! + * \brief The InstallStatus enum specifies whether a package has been installed explicitely or as dependency. + */ +enum class InstallStatus { Explicit = 0, AsDependency = 1, Unknown = 20 }; + +/*! + * \brief Either unknown, true or false. + */ +enum class UnknownTrueFalse : unsigned char { Unknown, True, False }; + +/*! + * \brief The PackageValidation enum specifies methods used to validate a package. + * \remarks ALMP type: alpm_pkgvalidation_t + */ +enum class PackageValidation { Unknown = 0, None = (1 << 0), Md5Sum = (1 << 1), Sha256Sum = (1 << 2), PgpSignature = (1 << 3) }; + +constexpr PackageValidation operator|(PackageValidation lhs, PackageValidation rhs) +{ + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +constexpr bool operator&(PackageValidation lhs, PackageValidation rhs) +{ + return (static_cast(lhs) & static_cast(rhs)) != 0; +} + +constexpr int operator~(PackageValidation lhs) +{ + return ~static_cast(lhs); +} + +inline PackageValidation &operator|=(PackageValidation &lhs, PackageValidation rhs) +{ + lhs = static_cast(static_cast(lhs) | static_cast(rhs)); + return lhs; +} + +inline PackageValidation &operator&=(PackageValidation &lhs, int rhs) +{ + lhs = static_cast(static_cast(lhs) & rhs); + return lhs; +} + +/*! + * \brief The DependencyMode enum specifies the version constraints in dependency specs. + * \remarks ALPM: alpm_depmod_t + */ +enum class DependencyMode { + Any = 1, /*! No version constraint */ + Equal, /*! Test version equality (package=x.y.z) */ + GreatherEqual, /*! Test for at least a version (package>=x.y.z) */ + LessEqual, /*! Test for at most a version (package<=x.y.z) */ + GreatherThan, /*! Test for greater than some version (package>x.y.z) */ + LessThan /*! Test for less than some version (package, + public ReflectiveRapidJSON::BinarySerializable { + explicit Dependency() = default; + explicit Dependency(const std::string &name, const std::string &version = std::string(), DependencyMode mode = DependencyMode::Any, + const std::string &description = std::string()); + explicit Dependency(const char *denotation, std::size_t denotationSize = std::numeric_limits::max()); + explicit Dependency(std::string_view denotation); + bool operator==(const Dependency &other) const; + bool matches(const Dependency &other) const; + static bool matches(const DependencyMode mode, const std::string &version1, const std::string &version2); + + static Dependency fromString(const char *dependency, std::size_t dependencySize = std::numeric_limits::max()); + static Dependency fromString(std::string_view dependency); + std::string toString() const; + + std::string name; + std::string version; + std::string description; + DependencyMode mode = DependencyMode::Any; +}; + +inline Dependency::Dependency(const std::string &name, const std::string &version, DependencyMode mode, const std::string &description) + : name(name) + , version(version) + , description(description) + , mode(mode) +{ +} + +inline Dependency::Dependency(std::string_view denotation) + : Dependency(denotation.data(), denotation.size()) +{ +} + +inline bool Dependency::operator==(const Dependency &other) const +{ + return name == other.name && version == other.version && description == other.description && mode == other.mode; +} + +inline bool Dependency::matches(const Dependency &other) const +{ + return name == other.name && matches(other.mode, other.version, version); +} + +inline Dependency Dependency::fromString(const char *denotation, size_t denotationSize) +{ + return Dependency(denotation, denotationSize); +} + +inline Dependency Dependency::fromString(std::string_view dependency) +{ + return Dependency(dependency); +} + +} // namespace LibPkg + +namespace std { + +template <> struct hash { + std::size_t operator()(const LibPkg::Dependency &dep) const + { + using std::hash; + using DepModeType = typename std::underlying_type::type; + return ((hash()(dep.name) ^ (hash()(dep.version) << 1)) >> 1) + ^ ((hash()(dep.description) << 4) ^ (hash()(static_cast(dep.mode)) << 1) >> 1); + } +}; + +} // namespace std + +namespace LibPkg { + +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const DependencyMode &mode); +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const Dependency &dependency); +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const std::vector &dependencies); + +struct SourceFile : public ReflectiveRapidJSON::JsonSerializable, public ReflectiveRapidJSON::BinarySerializable { + std::string path; + std::string contents; +}; + +struct LIBPKG_EXPORT SourceInfo : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::string name; + std::vector archs; + std::vector makeDependencies; + std::vector checkDependencies; + std::string maintainer; + std::int64_t id = 0; + std::int64_t category = 0; + std::int64_t votes = 0; + CppUtilities::DateTime outOfDate; + CppUtilities::DateTime firstSubmitted; + CppUtilities::DateTime lastModified; + std::string url; + std::vector sources; + std::string directory; +}; + +struct LIBPKG_EXPORT PackageInfo : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::string fileName; + std::vector files; + CppUtilities::DateTime buildDate; + std::string packager; + std::string md5; + std::string sha256; + std::string pgpSignature; + std::string arch; + std::uint32_t size = 0; +}; + +struct LIBPKG_EXPORT InstallInfo : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + CppUtilities::DateTime installDate; + std::uint32_t installedSize = 0; + std::vector backupFiles; + InstallStatus installStatus = InstallStatus::Unknown; + PackageValidation validationMethods = PackageValidation::None; +}; + +/*! + * \brief The PackageVersionComparison enum defines possible results of packages version comparison. + */ +enum class PackageVersionComparison { + Equal, /*!< The version of this package is the same as the version of the package from the sync db. */ + SoftwareUpgrade, /*!< The software version of the package from the sync db is newer. */ + PackageUpgradeOnly, /*!< The package release number of the package from the sync db is newer. */ + NewerThanSyncVersion /*!< The version of this package is NEWER then the version of the package from the sync db. */ +}; + +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const PackageVersionComparison &res); + +/*! + * \brief The ComparsionResult enum defines possible results of the package version part comparsion + * provided by PackageVersion::compareParts(). + */ +enum class PackageVersionPartComparison { + Equal, /*!< Both parts are equal. */ + Newer, /*!< Part 1 is newer then part 2. */ + Older /*!< Part 2 is newer then part 1. */ +}; + +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const PackageVersionPartComparison &res); + +struct LIBPKG_EXPORT PackageVersion : public ReflectiveRapidJSON::JsonSerializable { + static PackageVersion fromString(const char *versionString, std::size_t versionStringSize); + static PackageVersion fromString(const std::string &versionString); + static PackageVersionPartComparison compareParts(const std::string &part1, const std::string &part2, bool allowImplicitPkgRel = false); + static std::string trimPackageVersion(const std::string &versionString); + PackageVersionComparison compare(const PackageVersion &other) const; + static PackageVersionComparison compare(const std::string &versionString1, const std::string &versionString2); + static bool isNewer(PackageVersionComparison comparision); + std::string toString() const; + + std::string epoch; + std::string upstream; + std::string package; +}; + +inline PackageVersion PackageVersion::fromString(const std::string &versionString) +{ + return fromString(versionString.data(), versionString.size()); +} + +inline PackageVersionComparison PackageVersion::compare(const std::string &versionString1, const std::string &versionString2) +{ + return fromString(versionString1).compare(fromString(versionString2)); +} + +inline bool PackageVersion::isNewer(PackageVersionComparison comparision) +{ + return comparision == PackageVersionComparison::SoftwareUpgrade || comparision == PackageVersionComparison::PackageUpgradeOnly; +} + +/*! + * \brief The PackageOrigin enum specifies where the information contained by a Package object come from. + * + * This is useful to know because the different origins provide a different information. For instance, a Package + * which has only been created from a file name (via Package::fromPkgFileName()) will not contain any dependencies. + */ +enum class PackageOrigin { + Default, /*!< The package has been default-constructed. That means the package instance is empty. */ + PackageFileName, /*!< A package file name has been parsed via Package::fromPkgFileName(). */ + SourceInfo, /*!< A .SRCINFO file has been parsed via Package::fromInfo(). */ + PackageInfo, /*!< A .PKGINFO file has been parsed via Package::fromInfo(). */ + Database, /*!< A description file from a database has been parsed via Package::fromDescription(). */ + PackageContents, /*!< A binary package has been parsed via Package::fromPkgFile(). */ + AurRpcInfo, /*!< A JSON document from the AUR RPC (info, multiinfo) has been parsed via Package::fromAurRpcJson(). */ + CustomSource, /*! The package info has been populated from a custom source. */ + AurRpcSearch, /*!< A JSON document from the AUR RPC (search) has been parsed via Package::fromAurRpcJson(). */ +}; + +struct LIBPKG_EXPORT PackageNameData { + std::string compose() const; + std::string variant() const; + bool isVcsPackage() const; + static PackageNameData decompose(std::string_view packageName); + + std::string_view actualName; + std::string_view targetPrefix; + std::string_view vcsSuffix; +}; + +struct DependencySet; + +struct LIBPKG_EXPORT Package : public ReflectiveRapidJSON::JsonSerializable, public ReflectiveRapidJSON::BinarySerializable { + Package() = default; + Package(const Package &other); + Package(Package &&other) = default; + bool providesDependency(const Dependency &dependency) const; + static void exportProvides( + const std::shared_ptr &package, DependencySet &destinationProvides, std::unordered_set &destinationLibProvides); + bool isSame(const Package &other) const; + PackageVersionComparison compareVersion(const Package &other) const; + std::string computeFileName(const char *extension = "pkg.tar.zst") const; + std::string computeRegularPackageName() const; + PackageNameData decomposeName() const; + void addInfoFromPkgInfoFile(const std::string &info); + void addDepsAndProvidesFromContainedDirectory(const std::string &directoryPath); + void addDepsAndProvidesFromContainedFile(const ArchiveFile &file, std::set &dllsReferencedByImportLibs); + void addDepsAndProvidesFromContents(const FileMap &contents); + void processDllsReferencedByImportLibs(std::set &&dllsReferencedByImportLibs); + bool addDepsAndProvidesFromOtherPackage(const Package &otherPackage, bool force = false); + + static bool isPkgInfoFileOrBinary(const char *filePath, const char *fileName, mode_t mode); + static bool isLicense(const char *filePath, const char *fileName, mode_t mode); + + static std::vector> fromInfo(const std::string &info, bool isPackageInfo = false); + static std::shared_ptr fromDescription(const std::vector &descriptionParts); + static std::vector> fromDatabaseFile(FileMap &&databaseFile); + static std::shared_ptr fromPkgFile(const std::string &path); + static std::tuple fileNameComponents(std::string_view fileName); + static std::shared_ptr fromPkgFileName(std::string_view fileName); + static std::vector> fromAurRpcJson( + const char *jsonData, std::size_t jsonSize, PackageOrigin origin = PackageOrigin::AurRpcInfo); + + PackageOrigin origin = PackageOrigin::Default; + CppUtilities::DateTime timestamp; + std::string name; + std::string version; + std::string description; + std::string upstreamUrl; + std::vector licenses; + std::vector groups; + std::vector dependencies; + std::vector optionalDependencies; + std::vector conflicts; + std::vector provides; + std::vector replaces; + std::set libprovides; + std::set libdepends; + std::shared_ptr sourceInfo; + std::unique_ptr packageInfo; + std::unique_ptr installInfo; +}; + +inline Package::Package(const Package &other) + : origin(other.origin) + , timestamp(other.timestamp) + , name(other.name) + , version(other.version) + , description(other.description) + , upstreamUrl(other.upstreamUrl) + , licenses(other.licenses) + , groups(other.groups) + , dependencies(other.dependencies) + , optionalDependencies(other.optionalDependencies) + , conflicts(other.conflicts) + , provides(other.provides) + , replaces(other.replaces) + , sourceInfo(other.sourceInfo) + , packageInfo() + , installInfo() +{ +} + +inline bool Package::isSame(const Package &other) const +{ + return name == other.name && version == other.version; +} + +inline PackageNameData Package::decomposeName() const +{ + return PackageNameData::decompose(name); +} + +struct LIBPKG_EXPORT DependencyDetail : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + DependencyDetail(const std::string &version = std::string(), DependencyMode mode = DependencyMode::Any, + const std::unordered_set> &relevantPackages = std::unordered_set>()); + DependencyDetail(std::string &&version, DependencyMode mode); + + std::string version; + DependencyMode mode = DependencyMode::Any; + std::unordered_set> relevantPackages; +}; + +inline DependencyDetail::DependencyDetail( + const std::string &version, DependencyMode mode, const std::unordered_set> &relevantPackages) + : version(version) + , mode(mode) + , relevantPackages(relevantPackages) +{ +} + +inline DependencyDetail::DependencyDetail(std::string &&version, DependencyMode mode) + : version(move(version)) + , mode(mode) +{ +} + +using DependencySetBase = std::unordered_multimap; + +struct LIBPKG_EXPORT DependencySet : public DependencySetBase { + DependencySetBase::iterator find(const Dependency &dependency); + DependencySetBase::iterator find(const std::string &dependencyName, const DependencyDetail &dependencyDetail); + DependencySetBase::iterator findExact(const Dependency &dependency); + DependencySetBase::iterator findExact(const std::string &dependencyName, const DependencyDetail &dependencyDetail); + bool provides(const Dependency &dependency) const; + bool provides(const std::string &dependencyName, const DependencyDetail &dependencyDetail) const; + DependencySetBase::iterator add(const Dependency &dependency, const std::shared_ptr &relevantPackage); + DependencySetBase::iterator add( + const std::string &dependencyName, const DependencyDetail &dependencyDetail, const std::shared_ptr &relevantPackage); + DependencySetBase::iterator add(std::string &&dependencyName, DependencyDetail &&dependencyDetail); + void remove(const Dependency &dependency, const std::shared_ptr &relevantPackage); + void remove(const std::string &name); +}; + +} // namespace LibPkg + +namespace ReflectiveRapidJSON { + +REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH(LibPkg::DependencySet); +} + +#endif // LIBPKG_DATA_PACKAGE_H diff --git a/libpkg/data/siglevel.h b/libpkg/data/siglevel.h new file mode 100644 index 0000000..c5fb8ac --- /dev/null +++ b/libpkg/data/siglevel.h @@ -0,0 +1,93 @@ +#ifndef LIBPKG_DATA_SIGLEVEL_H +#define LIBPKG_DATA_SIGLEVEL_H + +#include "../global.h" + +#include +#include + +#include + +#include +#include + +namespace LibPkg { + +enum class SignatueScope { + Package, + Database, +}; + +/*! + * \brief The DatabaseSignatureLevel enum represents a database's "SigLevel". + * \sa https://www.archlinux.org/pacman/pacman.conf.5.html#SC + */ +enum class SignatureLevel { + Invalid = 0, /*! Indicates that the signature level could not be parsed by signatureLevelToString(). */ + Never = (1 << 0), /*! All signature checking is suppressed, even if signatures are present. */ + Optional = (1 + << 1), /*! Signatures are checked if present; absence of a signature is not an error. An invalid signature is a fatal error, as is a signature from a key not in the keyring. */ + Required = (1 + << 2), /*! Signatures are required; absence of a signature or an invalid signature is a fatal error, as is a signature from a key not in the keyring. */ + TrustedOnly = (1 << 3), /*! If a signature is checked, it must be in the keyring and fully trusted; marginal trust does not meet this criteria. */ + TrustAll = (1 + << 4), /*! If a signature is checked, it must be in the keyring, but is not required to be assigned a trust level (e.g., unknown or marginal trust). */ + Default = SignatureLevel::Optional | SignatureLevel::TrustedOnly, /*! The default signature level. */ +}; + +LIBPKG_EXPORT std::string signatureLevelToString(SignatureLevel sigLevel, std::string_view prefix = std::string_view()); + +} // namespace LibPkg + +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibPkg, LibPkg::SignatureLevel) + +namespace LibPkg { + +struct LIBPKG_EXPORT SignatureLevelConfig : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + explicit SignatureLevelConfig(); + explicit SignatureLevelConfig(SignatureLevel levelForAllScopes); + explicit SignatureLevelConfig(SignatureLevel levelForDbScope, SignatureLevel levelForPackageScope); + static SignatureLevelConfig fromString(std::string_view str); + std::string toString() const; + bool isValid() const; + bool operator==(const SignatureLevelConfig &other) const; + + SignatureLevel databaseScope = SignatureLevel::Default; + SignatureLevel packageScope = SignatureLevel::Default; +}; + +inline SignatureLevelConfig::SignatureLevelConfig() +{ +} + +inline SignatureLevelConfig::SignatureLevelConfig(SignatureLevel levelForAllScopes) + : databaseScope(levelForAllScopes) + , packageScope(levelForAllScopes) +{ +} + +inline SignatureLevelConfig::SignatureLevelConfig(SignatureLevel levelForDbScope, SignatureLevel levelForPackageScope) + : databaseScope(levelForDbScope) + , packageScope(levelForPackageScope) +{ +} + +inline bool SignatureLevelConfig::isValid() const +{ + return databaseScope != SignatureLevel::Invalid && packageScope != SignatureLevel::Invalid; +} + +inline bool SignatureLevelConfig::operator==(const SignatureLevelConfig &other) const +{ + return databaseScope == other.databaseScope && packageScope == other.packageScope; +} + +inline std::ostream &operator<<(std::ostream &o, const SignatureLevelConfig &signatureLevelConfig) +{ + return o << signatureLevelConfig.toString(); +} + +} // namespace LibPkg + +#endif // LIBPKG_DATA_LOCKABLE_H diff --git a/libpkg/global.h b/libpkg/global.h new file mode 100644 index 0000000..15a4a87 --- /dev/null +++ b/libpkg/global.h @@ -0,0 +1,27 @@ +// Created via CMake from template global.h.in +// WARNING! Any changes to this file will be overwritten by the next CMake run! + +#ifndef LIBPKG_GLOBAL +#define LIBPKG_GLOBAL + +#include + +#ifdef LIBPKG_STATIC +#define LIBPKG_EXPORT +#define LIBPKG_IMPORT +#else +#define LIBPKG_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT +#define LIBPKG_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT +#endif + +/*! + * \def LIBPKG_EXPORT + * \brief Marks the symbol to be exported by the libpkg library. + */ + +/*! + * \def LIBPKG_IMPORT + * \brief Marks the symbol to be imported from the libpkg library. + */ + +#endif // LIBPKG_GLOBAL diff --git a/libpkg/parser/aur.cpp b/libpkg/parser/aur.cpp new file mode 100644 index 0000000..ffa3422 --- /dev/null +++ b/libpkg/parser/aur.cpp @@ -0,0 +1,7 @@ +#include "./aur.h" + +namespace LibPkg { + +} // namespace LibPkg + +#include "reflection/aur.h" diff --git a/libpkg/parser/aur.h b/libpkg/parser/aur.h new file mode 100644 index 0000000..e749f9c --- /dev/null +++ b/libpkg/parser/aur.h @@ -0,0 +1,48 @@ +#ifndef LIBPKG_PARSER_AUR_H +#define LIBPKG_PARSER_AUR_H + +#include "../global.h" + +#include + +#include +#include +#include +#include + +namespace LibPkg { + +struct LIBPKG_EXPORT AurRpcResult : public ReflectiveRapidJSON::JsonSerializable { + std::int64_t ID; + std::string Name; + std::int64_t PackageBaseID; + std::string PackageBase; + std::string Version; + std::string Description; + std::string URL; + std::int64_t NumVotes; + double Popularity = 0.0; + std::unique_ptr OutOfDate; + std::string Maintainer; + std::int64_t FirstSubmitted = 0; + std::int64_t LastModified = 0; + std::string URLPath; + std::vector Depends; + std::vector MakeDepends; + std::vector CheckDepends; + std::vector OptDepends; + std::vector License; + std::vector Groups; + std::vector Keywords; +}; + +struct LIBPKG_EXPORT AurRpcMultiInfo : public ReflectiveRapidJSON::JsonSerializable { + std::int64_t version = -1; + std::int64_t resultcount = -1; + std::string type; + std::vector results; +}; + +} // namespace LibPkg + +#endif // LIBPKG_PARSER_CONFIG_H diff --git a/libpkg/parser/binary.cpp b/libpkg/parser/binary.cpp new file mode 100644 index 0000000..a84e7f9 --- /dev/null +++ b/libpkg/parser/binary.cpp @@ -0,0 +1,736 @@ +#include "./binary.h" + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibPkg { + +namespace BinarySectionTypes { +enum KnownValues : std::uint32_t { + Null, + ProgramData, + SymbolTable, + StringTable, + RelocationEntriesWithAddends, + SymbolHashTable, + DynamicLinkingInfo, + Notes, + ProgramSpace, + RelocationEntries, + Reserved, + DynamicLinkerSymbolTable, + ArrayOfConstructors = 0xE, + ArrayOfDestructors, + SectionGroup, + ExtendedSectionIndeces, + NumberOfDefinedTypes, + OsSpecific = 0x60000000, +}; +} + +namespace BinaryDynamicTags { +enum KnownValues : std::uint32_t { + Null, + Needed, + PltRelocationEntriesSize, + PltOrGotAddress, + SymbolHashTableAddress, + StringTableAddress, + SymbolTableAddress, + RelativeRelocationTableAddress, + RelativeRelocationTableSize, + RelativeRelocationTableEntrySize, + StringTableSize, + SymbolTableEntrySize, + InitializationFunctionAddress, + TerminationFunctionAddress, + Soname, + RPath, + Symbolic, + RelRelocationTableAddress, + RelocationTableSize, + RelocationTableEntrySize, + Pltrel, + Debug, + TextRel, + JmpRel, + BindNow, + InitArray, + FinitArray, + InitArraySize, + FinitArraySize, + RunPath, + Flags +}; +} + +namespace ProgramHeaderTypes { +enum KnownValues : std::uint32_t { + Null, + Load, + Dynamic, + Interpreter, + Note, + Shlib, + Phdr, + Loos = 0x60000000, + Hios = 0x6fffffff, + Loproc = 0x70000000, + Hiproc = 0x7fffffff +}; +} + +std::uint64_t VirtualAddressMapping::virtualAddressToFileOffset(std::uint64_t virtualAddress) const +{ + for (const VirtualAddressMappingEntry &entry : *this) { + if (entry.isVirtualAddressInRange(virtualAddress)) { + return entry.virtualAddressToFileOffset(virtualAddress); + } + } + return 0; +} + +std::uint64_t VirtualAddressMapping::fileOffsetToVirtualAddress(std::uint64_t fileOffset) const +{ + for (const VirtualAddressMappingEntry &entry : *this) { + if (entry.isFileOffsetInRange(fileOffset)) { + return entry.fileOffsetToVirtualAddress(fileOffset); + } + } + return 0; +} + +ostream &operator<<(ostream &o, const BinaryType &mode) +{ + switch (mode) { + case BinaryType::Invalid: + o << "invalid"; + break; + case BinaryType::Elf: + o << "ELF"; + break; + case BinaryType::Pe: + o << "PE"; + break; + case BinaryType::Ar: + o << "Ar"; + break; + } + return o; +} + +ostream &operator<<(ostream &o, const BinarySubType &mode) +{ + switch (mode) { + case BinarySubType::None: + o << "none"; + break; + case BinarySubType::Relocatable: + o << "relocatable"; + break; + case BinarySubType::Executable: + o << "executable"; + break; + case BinarySubType::SharedObject: + o << "shared object"; + break; + case BinarySubType::Core: + o << "core"; + break; + case BinarySubType::WindowsImportLibrary: + o << "Windows import library"; + break; + case BinarySubType::LoProc: + o << "lo proc"; + break; + case BinarySubType::HiProc: + o << "hi proc"; + break; + } + return o; +} + +ostream &operator<<(ostream &o, const BinaryClass &mode) +{ + switch (mode) { + case BinaryClass::Invalid: + o << "invalid"; + break; + case BinaryClass::Class32Bit: + o << "32-bit"; + break; + case BinaryClass::Class64Bit: + o << "64-bit"; + break; + } + return o; +} + +void Binary::load(const char *filePath) +{ + ifstream file; + file.exceptions(ios_base::failbit | ios_base::badbit); + file.open(filePath, ios_base::in | ios_base::binary); + parse(file); + switch (type) { + case BinaryType::Pe: + name = fileName(filePath); + break; + default:; + } +} + +void Binary::load(const string &fileContent, const string &fileName) +{ + stringstream fileStream(ios_base::in | ios_base::out | ios_base::binary); + fileStream.exceptions(ios_base::failbit | ios_base::badbit); + fileStream.rdbuf()->pubsetbuf(const_cast(fileContent.data()), static_cast(fileContent.size())); + parse(fileStream, &fileContent); + switch (type) { + case BinaryType::Pe: + name = fileName; + break; + default:; + } +} + +static constexpr unsigned char toLower(const unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +static std::string toLower(std::string str) +{ + for (auto &c : str) { + c = toLower(c); + } + return str; +} + +std::string Binary::addPrefix(const std::string &dependencyName) const +{ + switch (type) { + case BinaryType::Elf: + return argsToString("elf-", architecture, ':', ':', dependencyName); + case BinaryType::Pe: + return argsToString("pe-", architecture, ':', ':', toLower(dependencyName)); + case BinaryType::Ar: + switch (subType) { + case BinarySubType::WindowsImportLibrary: + return argsToString("pe-", architecture, ':', ':', toLower(dependencyName)); + default:; + } + default: + return "unknown::" + dependencyName; + } +} + +void Binary::parse(istream &stream, const string *fileContent) +{ + type = BinaryType::Invalid; + + BinaryReader reader(&stream); + const auto magic = reader.readUInt32BE(); + if (magic == 0x7f454c46) { + type = BinaryType::Elf; + parseElf(reader, fileContent); + return; + } + + if ((magic & 0xffff0000) == 0x4d5a0000) { + stream.seekg(0x3C, ios_base::beg); + stream.seekg(reader.readUInt32LE(), ios_base::beg); + if (reader.readUInt32BE() == 0x50450000) { + type = BinaryType::Pe; + parsePe(reader); + } + return; + } + + if (magic == 0x213C6172 && reader.readUInt32BE() == 0x63683E0A) { + type = BinaryType::Ar; + parseAr(reader); + } +} + +void Binary::parseElf(BinaryReader &reader, const string *fileContent) +{ + istream &stream = *reader.stream(); + + // read class + switch (reader.readByte()) { + case 1: + binaryClass = BinaryClass::Class32Bit; + break; + case 2: + binaryClass = BinaryClass::Class64Bit; + break; + default: + binaryClass = BinaryClass::Invalid; + throw runtime_error("invalid ELF class"); + } + + // read byte-order + const auto endianness = reader.readByte(); + if (endianness != 1 && endianness != 2) { + throw runtime_error("invalid endianness"); + } + isBigEndian = endianness == 2; + + // check version + if (reader.readByte() != 1) { + throw runtime_error("invalid ELF version"); + } + + // skip padding + stream.seekg(9, ios_base::cur); + + // read sub type + const std::uint16_t subType = reader.readUInt16LE(); + if (subType < 5 || subType == static_cast(BinarySubType::LoProc) || subType == static_cast(BinarySubType::HiProc)) { + this->subType = static_cast(subType); + } else { + throw runtime_error("invalid sub type"); + } + + // read machine/architecture + switch (reader.readUInt16LE()) { + case 0x02: + architecture = "sparc"; + break; + case 0x03: + architecture = "i386"; + break; + case 0x08: + architecture = "mips"; + break; + case 0x14: + architecture = "powerpc"; + break; + case 0x16: + architecture = "s390"; + break; + case 0x28: + architecture = "arm"; + break; + case 0x32: + architecture = "ia64"; + break; + case 0x3E: + architecture = "x86_64"; + break; + case 0xB7: + architecture = "aarch64"; + break; + default: + architecture.clear(); + } + + // check version + if (reader.readUInt32LE() != 1) { + throw runtime_error("invalid section ELF version"); + } + + // skip entry point + //const uint64 entryPoint = readElfAddress(reader); + stream.seekg(binaryClass == BinaryClass::Class64Bit ? 8 : 4, ios_base::cur); + // read offsets + const std::uint64_t programHeaderOffset = readElfAddress(reader); + const std::uint64_t sectionTableOffset = readElfAddress(reader); + // skip flags + stream.seekg(4, ios_base::cur); + // read sizes + const std::uint16_t elfHeaderSize = readElfInt16(reader); + const std::uint16_t programHeaderEntrySize = readElfInt16(reader); + const std::uint16_t programHeaderEntryCount = readElfInt16(reader); + const std::uint16_t sectionHeaderSize = readElfInt16(reader); + const std::uint16_t sectionHeaderCount = readElfInt16(reader); + const std::uint16_t nameTableIndex = readElfInt16(reader); + + // read program header + stream.seekg(static_cast(programHeaderOffset)); + virtualAddressMapping.reserve(2); + for (std::uint16_t programHeaderIndex = 0; programHeaderIndex != programHeaderEntryCount; ++programHeaderIndex) { + std::uint64_t fileOffset, virtualAddr, physicalAddr, fileSize, virtualSize, flags, align; + const std::uint32_t type = readElfInt32(reader); + if (binaryClass == BinaryClass::Class32Bit) { + fileOffset = readElfInt32(reader); + virtualAddr = readElfInt32(reader); + physicalAddr = readElfInt32(reader); + fileSize = readElfInt32(reader); + virtualSize = readElfInt32(reader); + flags = readElfInt32(reader); + align = readElfInt32(reader); + } else { + flags = readElfInt32(reader); + fileOffset = readElfAddress(reader); + virtualAddr = readElfAddress(reader); + physicalAddr = readElfAddress(reader); + fileSize = readElfAddress(reader); + virtualSize = readElfAddress(reader); + align = readElfAddress(reader); + } + switch (type) { + case ProgramHeaderTypes::Load: + virtualAddressMapping.emplace_back(fileOffset, fileSize, virtualAddr, virtualSize); + break; + } + } + + // read section header + std::uint64_t stringTableOffset = 0, stringTableSize = 0; + stream.seekg(static_cast(sectionTableOffset)); + for (std::uint16_t sectionHeaderIndex = 0; sectionHeaderIndex != sectionHeaderCount; ++sectionHeaderIndex) { + const std::uint32_t nameOffset = readElfInt32(reader); + const std::uint32_t type = readElfInt32(reader); + const std::uint64_t attributes = readElfAddress(reader); + const std::uint64_t virtualMemoryAddress = readElfAddress(reader); + const std::uint64_t offset = readElfAddress(reader); + const std::uint64_t size = readElfAddress(reader); + const std::uint32_t indexInAssociatedSection = readElfInt32(reader); + const std::uint32_t extraInfo = readElfInt32(reader); + const std::uint64_t requiredAlignment = readElfAddress(reader); + const std::uint64_t entrySize = readElfAddress(reader); + const auto nextSectionHeaderOffset = stream.tellg(); + + // read section + switch (type) { + case BinarySectionTypes::StringTable: { + stringTableOffset = offset; + stringTableSize = size; + break; + } + case BinarySectionTypes::DynamicLinkingInfo: { + // read string address of properties + stream.seekg(static_cast(offset)); + std::uint64_t dynamicStringTableAddress = 0, dynamicStringTableSize = 0, sonameAddr = 0, rpathAddr = 0; + vector neededLibs; + for (std::uint64_t read = 0; read < size; read += (binaryClass == BinaryClass::Class64Bit ? 16 : 8)) { + const std::uint64_t tag = readElfAddress(reader); + const std::uint64_t value = readElfAddress(reader); + switch (tag) { + case BinaryDynamicTags::StringTableAddress: + dynamicStringTableAddress = value; + break; + case BinaryDynamicTags::StringTableSize: + dynamicStringTableSize = value; + break; + case BinaryDynamicTags::Soname: + sonameAddr = value; + break; + case BinaryDynamicTags::RPath: + rpathAddr = value; + break; + case BinaryDynamicTags::RunPath: + rpathAddr = value; + break; + case BinaryDynamicTags::Needed: + neededLibs.push_back(value); + break; + default:; + } + } + + // lookup string address in string table to get actual strings + if (dynamicStringTableAddress && dynamicStringTableSize) { + if (sonameAddr) { + name = readElfString(stream, fileContent, dynamicStringTableAddress, dynamicStringTableSize, sonameAddr); + } + if (rpathAddr) { + rpath = readElfString(stream, fileContent, dynamicStringTableAddress, dynamicStringTableSize, rpathAddr); + } + for (const std::uint64_t neededLibStrAddr : neededLibs) { + requiredLibs.emplace(readElfString(stream, fileContent, dynamicStringTableAddress, dynamicStringTableSize, neededLibStrAddr)); + } + } + break; + } + default:; // section not relevant + } + // read next section header + stream.seekg(nextSectionHeaderOffset); + } +} + +struct PeSectionData { + char name[8]; + std::uint32_t virtualSize; + std::uint32_t virtualAddress; + std::uint32_t fileSize; + std::uint32_t fileOffset; + std::uint32_t relocationPtr; + std::uint32_t lineNumbersPtr; + std::uint16_t relocationCount; + std::uint16_t lineNumbersCount; + std::uint32_t characteristics; + + void read(BinaryReader &reader) + { + reader.read(name, 8); + virtualSize = reader.readUInt32LE(); + virtualAddress = reader.readUInt32LE(); + fileSize = reader.readUInt32LE(); + fileOffset = reader.readUInt32LE(); + relocationPtr = reader.readUInt32LE(); + lineNumbersPtr = reader.readUInt32LE(); + relocationCount = reader.readUInt16LE(); + lineNumbersCount = reader.readUInt16LE(); + characteristics = reader.readUInt32LE(); + } +}; + +struct PeImportTableEntry { + std::uint32_t originalFirstThunk; + std::uint32_t timeDateStamp; + std::uint32_t forwarderChain; + std::uint32_t nameVirtualAddress; + std::uint32_t firstThunk; + + void read(BinaryReader &reader) + { + originalFirstThunk = reader.readUInt32LE(); + timeDateStamp = reader.readUInt32LE(); + forwarderChain = reader.readUInt32LE(); + nameVirtualAddress = reader.readUInt32LE(); + firstThunk = reader.readUInt32LE(); + } + + constexpr bool isEmpty() const + { + return !originalFirstThunk && !timeDateStamp && !forwarderChain && !nameVirtualAddress && !firstThunk; + } +}; + +void Binary::parsePe(BinaryReader &reader, iostream::off_type baseFileOffset) +{ + istream &stream = *reader.stream(); + + // read machine/architecture + switch (reader.readUInt16LE()) { + case 0x14c: + architecture = "i386"; + break; + case 0x8664: + architecture = "x86_64"; + break; + case 0x162: + case 0x168: + case 0x266: + architecture = "mips"; + break; + case 0x1c0: + case 0x1c2: + case 0x1c4: + architecture = "arm"; + break; + case 0x32: + architecture = "ia64"; + break; + default: + architecture.clear(); + } + + // read rest of COFF header + const auto numberOfSections = reader.readUInt16LE(); + const auto timeDateStamp = reader.readUInt32LE(); + const auto symbolTableOffset = reader.readUInt32LE(); + const auto symbolTableSize = reader.readUInt32LE(); + const auto optionHeaderSize = reader.readUInt16LE(); + const auto characteristics = reader.readUInt16LE(); + + // read PE optional header + int64_t exportDirVirtualAddress = -1, exportDirSize = -1, importDirVirtualAddress = -1, importDirSize = -1; + if (optionHeaderSize) { + const auto optionHeaderStart = static_cast(stream.tellg()); + unsigned char minPeHeaderSize; + uint64_t imageBase; + switch (reader.readUInt16LE()) { + case 0x020b: + binaryClass = BinaryClass::Class64Bit; + stream.seekg(optionHeaderStart + 24, ios_base::beg); + imageBase = reader.readUInt64LE(); + minPeHeaderSize = 112; + break; + case 0x010b: + binaryClass = BinaryClass::Class32Bit; + stream.seekg(optionHeaderStart + 28, ios_base::beg); + imageBase = reader.readUInt32LE(); + minPeHeaderSize = 96; + break; + // case 0x0107: ROM image, not relevant + default: + return; + } + if (optionHeaderSize < minPeHeaderSize) { + throw runtime_error("PE optional header is truncated"); + } + // read virtual addresses of directories + stream.seekg(optionHeaderStart + minPeHeaderSize - 4); + const auto numberOfDirs = reader.readUInt32LE(); + if (numberOfDirs < 16) { + throw runtime_error("expected at least 16 directories in PE file"); + } + exportDirVirtualAddress = reader.readUInt32LE(); + exportDirSize = reader.readUInt32LE(); + importDirVirtualAddress = reader.readUInt32LE(); + importDirSize = reader.readUInt32LE(); + // skip remaining dirs (not relevant here) + stream.seekg(optionHeaderStart + optionHeaderSize, ios_base::beg); + } + + // read section table for mapping virtual addresses to file offsets + PeSectionData importDataSection; + std::int64_t dllNameOffset = -1, dllNameSize = -1; + for (auto sectionsLeft = numberOfSections; sectionsLeft; --sectionsLeft) { + importDataSection.read(reader); + if (!strncmp(importDataSection.name, ".idata$7", 8)) { + dllNameOffset = importDataSection.fileOffset; + dllNameSize = importDataSection.fileSize; + } + virtualAddressMapping.emplace_back( + importDataSection.fileOffset, importDataSection.fileSize, importDataSection.virtualAddress, importDataSection.virtualSize); + } + + // read import dir to get dependencies + if (importDirVirtualAddress >= 0) { + const auto importDirFileAddress + = static_cast(virtualAddressMapping.virtualAddressToFileOffset(static_cast(importDirVirtualAddress))); + if (!importDirFileAddress) { + throw runtime_error("unable to map virtual address of import directory to its file offset"); + } + stream.seekg(baseFileOffset + importDirFileAddress, ios_base::beg); + PeImportTableEntry importEntry; + vector dllNameOffsets; + for (;;) { + importEntry.read(reader); + if (importEntry.isEmpty()) { + break; + } + const auto nameFileAddress + = static_cast(virtualAddressMapping.virtualAddressToFileOffset(importEntry.nameVirtualAddress)); + if (!nameFileAddress) { + throw runtime_error("unable to map virtual address of import DLL name to its file offset"); + } + dllNameOffsets.emplace_back(nameFileAddress); + } + for (const auto dllNameOffset : dllNameOffsets) { + stream.seekg(dllNameOffset, ios_base::beg); + requiredLibs.emplace(reader.readTerminatedString()); + } + } + + // read import library name + if (dllNameOffset >= 0) { + stream.seekg(baseFileOffset + dllNameOffset, ios_base::beg); + name = reader.readTerminatedString(static_cast(dllNameSize), 0); + } +} + +void Binary::parseAr(BinaryReader &reader) +{ + for (auto remainingSize = reader.readStreamsize(); remainingSize >= 60;) { + // read file header + char fileName[17] = { 0 }; + reader.read(fileName, sizeof(fileName) - 1); + reader.stream()->seekg(12 + 6 + 6 + 8, ios_base::cur); // skip file modification timestamp, owner, group and file mode + char fileSizeStr[11] = { 0 }; + reader.read(fileSizeStr, sizeof(fileSizeStr) - 1); + if (reader.readUInt16BE() != 0x600A) { + throw runtime_error("ending characters not present"); + } + + // make file name and file size zero-terminated + for (auto &c : fileName) { + if (c == ' ' || c == '/') { + c = '\0'; + break; + } + } + for (auto &c : fileSizeStr) { + if (c == ' ') { + c = '\0'; + break; + } + } + + const auto fileOffset = reader.stream()->tellg(); + const auto fileSize = stringToNumber(fileSizeStr); + const auto nextFileOffset = fileOffset + fileSize; + if (endsWith(string_view(fileName), ".o")) { + const auto magic = reader.readUInt32BE(); + if (magic == 0x7f454c46u) { + return; // we're not interested in static libraries containing ELF files + } + + reader.stream()->seekg(-4, ios_base::cur); + parsePe(reader, fileOffset); + if (!name.empty()) { + subType = BinarySubType::WindowsImportLibrary; + return; // stop if the DLL name has been determined + } + } + + // parse the next file + remainingSize -= fileSize; + reader.stream()->seekg(nextFileOffset); + } +} + +std::uint64_t Binary::readElfAddress(BinaryReader &reader) +{ + switch (binaryClass) { + case BinaryClass::Class64Bit: + return isBigEndian ? reader.readUInt64BE() : reader.readUInt64LE(); + case BinaryClass::Class32Bit: + return isBigEndian ? reader.readUInt32BE() : reader.readUInt32LE(); + default: + throw runtime_error("Inavlid binary class"); + } +} + +std::uint32_t Binary::readElfInt32(BinaryReader &reader) +{ + return isBigEndian ? reader.readUInt32BE() : reader.readUInt32LE(); +} + +std::uint16_t Binary::readElfInt16(BinaryReader &reader) +{ + return isBigEndian ? reader.readUInt16BE() : reader.readUInt16LE(); +} + +string Binary::readElfString( + istream &stream, const string *fileContent, std::uint64_t stringTableAddress, std::uint64_t stringTableSize, std::uint64_t relativeStringAddress) +{ + // check bounds + if (relativeStringAddress >= stringTableSize) { + throw runtime_error( + argsToString("string address ", relativeStringAddress, " exceeds size of string table (", stringTableSize, ") at ", stringTableAddress)); + } + + // take shortcut when file content has already been buffered + if (fileContent) { + return string(fileContent->data() + virtualAddressMapping.virtualAddressToFileOffset(stringTableAddress + relativeStringAddress)); + } + + // read string from stream + const auto stringOffset + = static_cast(virtualAddressMapping.virtualAddressToFileOffset(stringTableAddress + relativeStringAddress)); + stream.seekg(stringOffset); + size_t strSize = 0, bytesAvailable = stringTableSize - relativeStringAddress; + for (; stream.get() && bytesAvailable; ++strSize, --bytesAvailable) + ; + stream.seekg(stringOffset); + auto buffer = make_unique(strSize); + stream.read(buffer.get(), static_cast(strSize)); + return string(buffer.get(), strSize); +} +} // namespace LibPkg diff --git a/libpkg/parser/binary.h b/libpkg/parser/binary.h new file mode 100644 index 0000000..4465c38 --- /dev/null +++ b/libpkg/parser/binary.h @@ -0,0 +1,115 @@ +#ifndef LIBPKG_PARSER_BINARY_H +#define LIBPKG_PARSER_BINARY_H + +#include "../global.h" + +#include +#include +#include +#include + +namespace CppUtilities { +class BinaryReader; +} + +namespace LibPkg { + +enum class BinaryType { Invalid, Elf, Pe, Ar }; + +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const BinaryType &mode); + +enum class BinarySubType { None, Relocatable, Executable, SharedObject, Core, WindowsImportLibrary, LoProc = 0xff00, HiProc = 0xffff }; + +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const BinarySubType &mode); + +enum class BinaryClass { Invalid, Class32Bit, Class64Bit }; + +LIBPKG_EXPORT std::ostream &operator<<(std::ostream &o, const BinaryClass &mode); + +struct LIBPKG_EXPORT VirtualAddressMappingEntry { + constexpr VirtualAddressMappingEntry(std::uint64_t fileOffset, std::uint64_t fileSize, std::uint64_t virtualAddress, std::uint64_t virtualSize); + + constexpr bool isVirtualAddressInRange(std::uint64_t virtualAddress) const; + constexpr std::uint64_t virtualAddressToFileOffset(std::uint64_t virtualAddress) const; + constexpr bool isFileOffsetInRange(std::uint64_t fileOffset) const; + constexpr std::uint64_t fileOffsetToVirtualAddress(std::uint64_t fileOffset) const; + + const std::uint64_t fileStartOffset; + const std::uint64_t fileEndOffset; + const std::uint64_t virtualStartAddress; + const std::uint64_t virtualEndAddress; +}; + +constexpr VirtualAddressMappingEntry::VirtualAddressMappingEntry( + std::uint64_t fileOffset, std::uint64_t fileSize, std::uint64_t virtualAddress, std::uint64_t virtualSize) + : fileStartOffset(fileOffset) + , fileEndOffset(fileOffset + fileSize) + , virtualStartAddress(virtualAddress) + , virtualEndAddress(virtualAddress + virtualSize) +{ +} + +constexpr bool VirtualAddressMappingEntry::isVirtualAddressInRange(std::uint64_t virtualAddress) const +{ + return virtualAddress >= virtualStartAddress && virtualAddress < virtualEndAddress; +} + +constexpr std::uint64_t VirtualAddressMappingEntry::virtualAddressToFileOffset(std::uint64_t virtualAddress) const +{ + return virtualStartAddress > fileStartOffset ? (virtualAddress - (virtualStartAddress - fileStartOffset)) + : (virtualAddress + (fileStartOffset - virtualStartAddress)); +} + +constexpr bool VirtualAddressMappingEntry::isFileOffsetInRange(std::uint64_t fileOffset) const +{ + return fileOffset >= fileStartOffset && fileOffset < fileEndOffset; +} + +constexpr std::uint64_t VirtualAddressMappingEntry::fileOffsetToVirtualAddress(std::uint64_t fileOffset) const +{ + return virtualStartAddress > fileStartOffset ? (fileOffset + (virtualStartAddress - fileStartOffset)) + : (fileOffset - (fileStartOffset - virtualStartAddress)); +} + +struct LIBPKG_EXPORT VirtualAddressMapping : public std::vector { + VirtualAddressMapping(); + std::uint64_t virtualAddressToFileOffset(std::uint64_t virtualAddress) const; + std::uint64_t fileOffsetToVirtualAddress(std::uint64_t fileOffset) const; +}; + +inline VirtualAddressMapping::VirtualAddressMapping() +{ +} + +struct LIBPKG_EXPORT Binary { + void load(const char *filePath); + void load(const std::string &fileContent, const std::string &fileName); + std::string addPrefix(const std::string &dependencyName) const; + + BinaryType type = BinaryType::Invalid; + BinarySubType subType = BinarySubType::None; + std::string name; + BinaryClass binaryClass = BinaryClass::Invalid; + bool isBigEndian = false; + std::string architecture; + std::set symbols; + std::set requiredLibs; + std::string rpath; + VirtualAddressMapping virtualAddressMapping; + +private: + void parse(std::istream &stream, const std::string *fileContent = nullptr); + void parseElf(CppUtilities::BinaryReader &reader, const std::string *fileContent = nullptr); + void parsePe(CppUtilities::BinaryReader &reader, typename std::iostream::off_type baseFileOffset = 0); + void parseAr(CppUtilities::BinaryReader &reader); + void parseCoff(CppUtilities::BinaryReader &reader); + + std::uint64_t readElfAddress(CppUtilities::BinaryReader &reader); + std::uint32_t readElfInt32(CppUtilities::BinaryReader &reader); + std::uint16_t readElfInt16(CppUtilities::BinaryReader &reader); + std::string readElfString(std::istream &stream, const std::string *fileContent, std::uint64_t stringTableOffset, std::uint64_t stringTableSize, + std::uint64_t stringOffset); +}; +} // namespace LibPkg + +#endif // LIBPKG_PARSER_BINARY_H diff --git a/libpkg/parser/config.cpp b/libpkg/parser/config.cpp new file mode 100644 index 0000000..f23ec0e --- /dev/null +++ b/libpkg/parser/config.cpp @@ -0,0 +1,204 @@ +#include "./config.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include // for uname + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibPkg { + +static void moveLastValue(string &target, multimap &multimap, const string &key) +{ + const auto i = find_if(multimap.rbegin(), multimap.rend(), [&key](const pair &i) { return i.first == key; }); + if (i != multimap.rend()) { + target = move(i->second); + } +} + +static void moveValues(vector &target, multimap &multimap, const string &key) +{ + for (auto range = multimap.equal_range(key); range.first != range.second; ++range.first) { + target.emplace_back(move(range.first->second)); + } +} + +void Config::loadPacmanConfig(const char *pacmanConfigPath) +{ + // open and parse ini + IniFile configIni; + unordered_map includedInis; + { + ifstream configFile; + configFile.exceptions(ios_base::failbit | ios_base::badbit); + configFile.open(pacmanConfigPath, ios_base::in); + configIni.parse(configFile); + } + auto &configData = configIni.data(); + auto architecture = std::string_view{}; + + // read options and create Database object for each db + for (auto &scope : configData) { + if (scope.first == "options") { + // read global options or assume defaults + auto &options = scope.second; + for (auto range = options.equal_range("Architecture"); range.first != range.second; ++range.first) { + if (range.first->second != "auto") { + architecture = *architectures.emplace(move(range.first->second)).first; + } else { + struct utsname un; + uname(&un); + architecture = *architectures.emplace(un.machine).first; + } + } + moveLastValue(pacmanDatabasePath, options, "DBPath"); + if (pacmanDatabasePath.empty()) { + pacmanDatabasePath = "/var/lib/pacman/"; + } + moveValues(packageCacheDirs, options, "CacheDir"); + if (packageCacheDirs.empty()) { + packageCacheDirs.emplace_back("/var/cache/pacman/pkg/"); + } + string sigLevel; + moveLastValue(sigLevel, options, "SigLevel"); + signatureLevel = SignatureLevelConfig::fromString(sigLevel); + if (!signatureLevel.isValid()) { + signatureLevel = SignatureLevelConfig(); + cerr << Phrases::WarningMessage << "The global/default signature level \"" << sigLevel << "\" is invalid and will be ignored." + << Phrases::End << Phrases::SubWarning << "Assuming default \"" << signatureLevel.toString() << "\" instead" + << Phrases::EndFlush; + } + } else { + // read sync database + auto *const db = findOrCreateDatabase(move(scope.first), architecture); + // read sig level + string sigLevel; + moveLastValue(sigLevel, scope.second, "SigLevel"); + const auto dbSpecificSignatureLevelConfig = SignatureLevelConfig::fromString(sigLevel); + if (dbSpecificSignatureLevelConfig.databaseScope != SignatureLevel::Invalid) { + db->signatureLevel = dbSpecificSignatureLevelConfig.databaseScope; + } else { + cerr << Phrases::WarningMessage << "The signature level \"" << sigLevel << "\" specified for DB \"" << db->name + << "\" is invalid and will be ignored." << Phrases::End << Phrases::SubWarning << "Assuming global default \"" + << signatureLevelToString(signatureLevel.databaseScope) << "\" instead" << Phrases::EndFlush; + db->signatureLevel = signatureLevel.databaseScope; + } + // add mirrors + for (auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) { + for (const auto &arch : architectures) { + string url = range.first->second; + findAndReplace(url, "$repo", db->name); + findAndReplace(url, "$arch", arch); + db->mirrors.emplace_back(move(url)); + } + } + // add included mirrors + for (auto range = scope.second.equal_range("Include"); range.first != range.second; ++range.first) { + const auto &path = range.first->second; + auto &includedIni = includedInis[path]; + if (includedIni.data().empty()) { + try { + ifstream includedFile; + includedFile.exceptions(ios_base::failbit | ios_base::badbit); + includedFile.open(path, ios_base::in); + includedIni.parse(includedFile); + } catch (const ios_base::failure &) { + cerr << Phrases::WarningMessage << "An IO error occured when parsing the included file \"" << path << "\"." + << Phrases::EndFlush; + } + } + for (auto &nestedScope : includedIni.data()) { + if (!nestedScope.first.empty()) { + continue; + } + for (auto range = nestedScope.second.equal_range("Server"); range.first != range.second; ++range.first) { + for (const auto &arch : architectures) { + string url = range.first->second; + findAndReplace(url, "$repo", db->name); + findAndReplace(url, "$arch", arch); + db->mirrors.emplace_back(move(url)); + } + } + } + } + // set database file paths + if (db->localDbDir.empty()) { + db->localDbDir = pacmanDatabasePath + "sync"; + } + if (db->localPkgDir.empty()) { + db->localPkgDir = packageCacheDirs.front(); + } + // ensure the database is not being discarded + db->toBeDiscarded = false; + } + } +} + +void Config::loadAllPackages(bool withFiles) +{ + for (Database &db : databases) { + try { + db.loadPackages(withFiles); + } catch (const runtime_error &e) { + cerr << Phrases::ErrorMessage << "Unable to load database \"" << db.name << "\": " << e.what() << Phrases::EndFlush; + } + } +} + +std::uint64_t Config::restoreFromCache() +{ + fstream cacheFile; + cacheFile.exceptions(ios_base::failbit | ios_base::badbit); + cacheFile.open("cache.bin", ios_base::in | ios_base::binary); + restoreFromBinary(cacheFile); + return static_cast(cacheFile.tellg()); +} + +std::uint64_t Config::dumpCacheFile() +{ + fstream cacheFile; + cacheFile.exceptions(ios_base::failbit | ios_base::badbit); + cacheFile.open("cache.bin", ios_base::out | ios_base::trunc | ios_base::binary); + toBinary(cacheFile); + const auto size = static_cast(cacheFile.tellp()); + cacheFile.close(); + return size; +} + +std::pair Config::parseDatabaseDenotation(std::string_view databaseDenotation) +{ + const auto archStart = databaseDenotation.rfind('@'); + if (archStart == std::string_view::npos) { + return std::make_pair(databaseDenotation, "x86_64"); + } else { + return std::make_pair(databaseDenotation.substr(0, archStart), databaseDenotation.substr(archStart + 1)); + } +} + +std::tuple Config::parsePackageDenotation(std::string_view packageDenotation) +{ + const char *const end = packageDenotation.data() + packageDenotation.size(); + const char *packageName = packageDenotation.data(); + for (; packageName != end && *packageName != '/'; ++packageName) + ; + if (packageName == end) { + return std::make_tuple(std::string_view(), std::string_view(), packageDenotation); + } else { + const auto &[dbName, dbArch] + = parseDatabaseDenotation(std::string_view(packageDenotation.data(), static_cast(packageName - packageDenotation.data()))); + return std::make_tuple(dbName, dbArch, std::string_view(packageName + 1, static_cast(end - packageName - 1))); + } +} + +} // namespace LibPkg + +#include "reflection/config.h" diff --git a/libpkg/parser/config.h b/libpkg/parser/config.h new file mode 100644 index 0000000..3a2118a --- /dev/null +++ b/libpkg/parser/config.h @@ -0,0 +1,12 @@ +#ifndef LIBPKG_PARSER_CONFIG_H +#define LIBPKG_PARSER_CONFIG_H + +#include "../data/config.h" + +namespace LibPkg { + +// TODO: parseDatabaseUsage() +// TODO: parseSignatureLevel() +} + +#endif // LIBPKG_PARSER_CONFIG_H diff --git a/libpkg/parser/database.cpp b/libpkg/parser/database.cpp new file mode 100644 index 0000000..f81ba0a --- /dev/null +++ b/libpkg/parser/database.cpp @@ -0,0 +1,55 @@ +#include "./database.h" +#include "./utils.h" + +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibPkg { + +bool Database::isFileRelevant(const char *filePath, const char *fileName, mode_t) +{ + CPP_UTILITIES_UNUSED(filePath) + return !std::strcmp(fileName, "desc") || !std::strcmp(fileName, "depends") || !std::strcmp(fileName, "files"); +} + +void Database::loadPackages(bool withFiles) +{ + const auto &dbPath = withFiles && !filesPath.empty() ? filesPath : path; + if (dbPath.empty()) { + throw runtime_error("local path not configured"); + } + loadPackages(extractFiles(dbPath, &isFileRelevant), lastModified(dbPath)); +} + +void LibPkg::Database::loadPackages(const string &databaseData, DateTime lastModified) +{ + loadPackages(extractFilesFromBuffer(databaseData, name + " db file", &isFileRelevant), lastModified); +} + +void Database::loadPackages(FileMap &&databaseFiles, DateTime lastModified) +{ + lastUpdate = lastModified; + for (auto &dir : databaseFiles) { + if (dir.first.find('/') != std::string::npos) { + cerr << Phrases::WarningMessage << "Database \"" << name << "\" contains unexpected sub directory: " << dir.first << Phrases::EndFlush; + continue; + } + vector descriptionParts; + descriptionParts.reserve(dir.second.size()); + for (auto &file : dir.second) { + descriptionParts.emplace_back(move(file.content)); + } + updatePackage(Package::fromDescription(descriptionParts)); + } +} + +} // namespace LibPkg diff --git a/libpkg/parser/database.h b/libpkg/parser/database.h new file mode 100644 index 0000000..7db8e84 --- /dev/null +++ b/libpkg/parser/database.h @@ -0,0 +1,10 @@ +#ifndef LIBPKG_PARSER_DATABASE_H +#define LIBPKG_PARSER_DATABASE_H + +#include "../data/database.h" + +namespace LibPkg { + +} + +#endif // LIBPKG_PARSER_DATABASE_H diff --git a/libpkg/parser/package.cpp b/libpkg/parser/package.cpp new file mode 100644 index 0000000..b436381 --- /dev/null +++ b/libpkg/parser/package.cpp @@ -0,0 +1,996 @@ +#include "./package.h" +#include "./aur.h" +#include "./binary.h" +#include "./utils.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace CppUtilities; +using namespace EscapeCodes; + +namespace LibPkg { + +Dependency::Dependency(const char *denotation, std::size_t denotationSize) + : Dependency() +{ + if (denotationSize == std::numeric_limits::max()) { + denotationSize = std::strlen(denotation); + } + + const char *version = nullptr, *description = nullptr; + size_t versionSize = 0, descriptionSize = 0, nameSize = 0; + bool hasEpoch = false; + for (const char *c = denotation, *end = denotation + denotationSize; c != end; ++c) { + if (description) { + if (!descriptionSize && *c == ' ') { + ++description; + } else { + ++descriptionSize; + } + continue; + } + switch (mode) { + case DependencyMode::Any: + switch (*c) { + case '<': + mode = DependencyMode::LessThan; + version = c + 1; + break; + case '>': + mode = DependencyMode::GreatherThan; + version = c + 1; + break; + case '=': + mode = DependencyMode::Equal; + version = c + 1; + break; + case ':': + description = c + 1; + break; + default: + ++nameSize; + } + break; + case DependencyMode::LessThan: + switch (*c) { + case '=': + mode = DependencyMode::LessEqual; + ++version; + break; + case ':': + if (hasEpoch) { + description = c + 1; + break; + } + hasEpoch = true; + [[fallthrough]]; + default: + ++versionSize; + } + break; + case DependencyMode::GreatherThan: + switch (*c) { + case '=': + mode = DependencyMode::GreatherEqual; + ++version; + break; + case ':': + if (hasEpoch) { + description = c + 1; + break; + } + hasEpoch = true; + [[fallthrough]]; + default: + ++versionSize; + } + break; + default: + switch (*c) { + case ':': + if (hasEpoch) { + description = c + 1; + break; + } + hasEpoch = true; + [[fallthrough]]; + default: + ++versionSize; + } + } + } + + // assign values + name.assign(denotation, nameSize); + if (version) { + this->version.assign(version, versionSize); + } + if (description) { + this->description.assign(description, descriptionSize); + } +} + +std::string Dependency::toString() const +{ + // check for empty desc any any mode -> only name relevant + if (description.empty() && mode == DependencyMode::Any) { + return name; + } + + // convert mode to string + const char *modeStr = nullptr; + switch (mode) { + case DependencyMode::Equal: + modeStr = "="; + break; + case DependencyMode::GreatherEqual: + modeStr = ">="; + break; + case DependencyMode::LessEqual: + modeStr = "<="; + break; + case DependencyMode::GreatherThan: + modeStr = ">"; + break; + case DependencyMode::LessThan: + modeStr = "<"; + break; + default: + // mode is any, but a desc is present + return argsToString(name, ':', ' ', description); + } + + // no desc but mode + if (description.empty()) { + return argsToString(name, modeStr, version); + } + // all parts present + return argsToString(name, modeStr, version, ':', ' ', description); +} + +PackageVersion PackageVersion::fromString(const char *versionString, size_t versionStringSize) +{ + PackageVersion version; + // find epoch + const char *const firstAlphanumeric = firstNonAlphanumericCharacter(versionString, versionString + versionStringSize); + if (*firstAlphanumeric == ':') { + version.epoch.assign(versionString, static_cast(firstAlphanumeric - versionString)); + versionStringSize -= static_cast(firstAlphanumeric - versionString) + 1; + versionString = firstAlphanumeric + 1; + } + // find pkgrel + const char *const end = versionString + versionStringSize; + for (const char *i = end - 1; i >= versionString; --i) { + if (*i == '-') { + const auto pkgrelSize = end - i - 1; + if (pkgrelSize > 0) { + version.package.assign(i + 1, static_cast(pkgrelSize)); + } + versionStringSize -= static_cast(end - i); + break; + } + } + // pkgver remains + version.upstream.assign(versionString, versionStringSize); + return version; +} + +#define if_field(x) if (!strncmp(field, x, fieldSize)) +#define else_if_field(x) else if (!strncmp(field, x, fieldSize)) +#define valueString string(value, valueSize) +#define ensure_pkg_info \ + if (!package.packageInfo) \ + package.packageInfo = make_unique() +#define ensure_install_info \ + if (!package.installInfo) \ + package.installInfo = make_unique() + +void addPackageInfo( + Package &package, PackageVersion &version, const char *field, size_t fieldSize, const char *value, size_t valueSize, bool isPackageInfo) +{ + if_field("pkgbase") + { + package.sourceInfo->name = valueString; + } + else_if_field("pkgname") + { + package.name = valueString; + } + else_if_field("epoch") + { + version.epoch = valueString; + } + else_if_field("pkgver") + { + version.upstream = valueString; + } + else_if_field("pkgrel") + { + version.package = valueString; + } + else_if_field("pkgdesc") + { + package.description = valueString; + } + else_if_field("url") + { + package.upstreamUrl = valueString; + } + else_if_field("arch") + { + if (isPackageInfo) { + ensure_pkg_info; + package.packageInfo->arch = valueString; + } else { + package.sourceInfo->archs.emplace_back(value, valueSize); + } + } + else_if_field("license") + { + package.licenses.emplace_back(value, valueSize); + } + else_if_field("depends") + { + package.dependencies.emplace_back(Dependency::fromString(value, valueSize)); + } + else_if_field("makedepends") + { + package.sourceInfo->makeDependencies.emplace_back(Dependency::fromString(value, valueSize)); + } + else_if_field("checkdepends") + { + package.sourceInfo->checkDependencies.emplace_back(Dependency::fromString(value, valueSize)); + } + else_if_field("optdepends") + { + package.optionalDependencies.emplace_back(Dependency::fromString(value, valueSize)); + } + else_if_field("conflicts") + { + package.conflicts.emplace_back(Dependency::fromString(value, valueSize)); + } + else_if_field("provides") + { + package.provides.emplace_back(Dependency::fromString(value, valueSize)); + } + else_if_field("replaces") + { + package.replaces.emplace_back(Dependency::fromString(value, valueSize)); + //} else_if_field("source") { + // currently not interesting + //package.sourceInfo->sources.emplace_back(value, valueSize); + //} else_if_field("size") { + // + } + else_if_field("builddate") + { + ensure_pkg_info; + package.packageInfo->buildDate = DateTime::fromTimeStampGmt(stringToNumber(valueString)); + } + else_if_field("packager") + { + ensure_pkg_info; + package.packageInfo->packager = valueString; + } +} + +void addPackageDescription(Package &package, const char *field, size_t fieldSize, const char *value, size_t valueSize) +{ + if_field("BASE") + { + package.sourceInfo->name = valueString; + } + else_if_field("NAME") + { + package.name = valueString; + } + else_if_field("VERSION") + { + package.version = valueString; + } + else_if_field("DESC") + { + package.description = valueString; + } + else_if_field("URL") + { + package.upstreamUrl = valueString; + } + else_if_field("ARCH") + { + package.packageInfo->arch = valueString; + } + else_if_field("LICENSE") + { + package.licenses.emplace_back(value, valueSize); + } + else_if_field("DEPENDS") + { + package.dependencies.emplace_back(value, valueSize); + } + else_if_field("MAKEDEPENDS") + { + package.sourceInfo->makeDependencies.emplace_back(value, valueSize); + } + else_if_field("CHECKDEPENDS") + { + package.sourceInfo->checkDependencies.emplace_back(value, valueSize); + } + else_if_field("OPTDEPENDS") + { + package.optionalDependencies.emplace_back(value, valueSize); + } + else_if_field("CONFLICTS") + { + package.conflicts.emplace_back(value, valueSize); + } + else_if_field("PROVIDES") + { + package.provides.emplace_back(value, valueSize); + } + else_if_field("REPLACES") + { + package.replaces.emplace_back(value, valueSize); + } + else_if_field("BUILDDATE") + { + package.packageInfo->buildDate = DateTime::fromTimeStampGmt(stringToNumber(valueString)); + } + else_if_field("INSTALLDATE") + { + ensure_install_info; + package.installInfo->installDate = DateTime::fromTimeStampGmt(stringToNumber(valueString)); + } + else_if_field("ISIZE") + { + ensure_install_info; + package.installInfo->installedSize = stringToNumber(valueString); + } + else_if_field("SIZE") + { + ensure_install_info; + package.installInfo->installedSize = stringToNumber(valueString); + } + else_if_field("CSIZE") + { + package.packageInfo->size = stringToNumber(valueString); + } + else_if_field("PACKAGER") + { + package.packageInfo->packager = valueString; + } + else_if_field("MD5SUM") + { + package.packageInfo->md5 = valueString; + } + else_if_field("SHA256SUM") + { + package.packageInfo->sha256 = valueString; + } + else_if_field("PGPSIG") + { + package.packageInfo->pgpSignature = valueString; + } + else_if_field("FILES") + { + package.packageInfo->files.emplace_back(value, valueSize); + } + else_if_field("REASON") + { + ensure_install_info; + package.installInfo->installStatus = (valueSize == 1 && *value == '1') ? InstallStatus::AsDependency : InstallStatus::Explicit; + } + else_if_field("VALIDATION") + { + if (!strncmp(value, "md5", valueSize)) { + package.installInfo->validationMethods |= PackageValidation::Md5Sum; + } else if (!strncmp(value, "sha256", valueSize)) { + package.installInfo->validationMethods |= PackageValidation::Sha256Sum; + } else if (!strncmp(value, "pgp", valueSize)) { + package.installInfo->validationMethods |= PackageValidation::PgpSignature; + } else { + throw ConversionException("invalid validation " + string(value, valueSize)); + } + } + else_if_field("GROUPS") + { + package.groups.emplace_back(value, valueSize); + } + else_if_field("FILENAME") + { + package.packageInfo->fileName = valueString; + } +} + +#undef if_field +#undef else_if_field +#undef valueString +#undef ensure_pkg_info + +void addVersionInfo(Package &package, PackageVersion &version, bool isPackageInfo) +{ + if (isPackageInfo) { + // when parsing .PKGINFO pkgver specifies the complete version which is + // treated in addPackageInfo always as upstream version + package.version = version.upstream; + } else { + // when parsing .SRCINFO pkgver only contains upstream version; epoch and pkgrel are + // specified separately + if (version.package.empty()) { + version.package = '1'; + } + if (version.epoch.empty()) { + package.version = version.upstream % '-' + version.package; + } else { + package.version = version.epoch % ':' % version.upstream % '-' + version.package; + } + } +} + +void parsePkgInfo(const std::string &info, const std::function &nextPackage, bool isPackageInfo) +{ + // define variables to store intermediate results while still parsing package base + PackageVersion version; + Package basePackage; + basePackage.origin = isPackageInfo ? PackageOrigin::PackageInfo : PackageOrigin::SourceInfo; + basePackage.sourceInfo = make_shared(); + std::string &packageBase = basePackage.sourceInfo->name; + + // states + enum { + FieldName, // reading field name (initial state) + EquationSign, // expecting equation sign + Pad, // expecting padding + FieldValue, // reading field value + Comment // reading comment + } state + = FieldName; + + // variables for current field + const char *currentFieldName = nullptr; + size_t currentFieldNameSize = 0; + const char *currentFieldValue = nullptr; + size_t currentFieldValueSize = 0; + + // do actual parsing via state machine + Package *currentPackage = nullptr; + for (const char *i = info.data(); *i; ++i) { + const char c = *i; + switch (state) { + case FieldName: + switch (c) { + case '#': + // discard truncated line + currentFieldName = nullptr; + currentFieldNameSize = 0; + state = Comment; + [[fallthrough]]; + case ' ': + // field name complete, expect equation sign + if (currentFieldName) { + state = EquationSign; + } + break; + case '\n': + case '\r': + case '\t': + // discard truncated line + currentFieldName = nullptr; + currentFieldNameSize = 0; + break; + default: + if (!currentFieldName) { + currentFieldName = i; + } + ++currentFieldNameSize; + } + break; + case EquationSign: + switch (c) { + case '=': + state = Pad; + break; + case '\n': + case '\r': + case '\t': + // unexpected new line -> discard truncated line + state = FieldName; + currentFieldName = nullptr; + currentFieldNameSize = 0; + break; + default:; // ignore unexpected characters + } + break; + case Pad: + switch (c) { + case ' ': + state = FieldValue; + break; + case '\n': + case '\r': + case '\t': + // unexpected new line -> discard truncated line + state = FieldName; + currentFieldName = nullptr; + currentFieldNameSize = 0; + break; + default:; // ignore unexpected characters + } + break; + case FieldValue: + switch (c) { + case '\n': + case '\r': + // field concluded + // -> expect next field name + state = FieldName; + // -> handle pkgbase/pkgname + if (currentFieldName && !strncmp(currentFieldName, "pkgbase", currentFieldNameSize)) { + packageBase = string(currentFieldValue, currentFieldValueSize); + } else if (currentFieldName && !strncmp(currentFieldName, "pkgname", currentFieldNameSize)) { + // next package + if (packageBase.empty()) { + // no pkgbase specified so far -> use the first pkgname as pkgbase + packageBase = string(currentFieldValue, currentFieldValueSize); + } + if (currentPackage) { + addVersionInfo(*currentPackage, version, isPackageInfo); + currentPackage->provides.emplace_back(currentPackage->name, currentPackage->version); + } + // find next package + currentPackage = nextPackage(basePackage); + } + // -> add field to ... + try { + if (currentPackage) { + // ... concrete package info if there's already a concrete package + addPackageInfo(*currentPackage, version, currentFieldName, currentFieldNameSize, currentFieldValue, currentFieldValueSize, + isPackageInfo); + } else { + // ... base info if still parsing general info + addPackageInfo( + basePackage, version, currentFieldName, currentFieldNameSize, currentFieldValue, currentFieldValueSize, isPackageInfo); + } + } catch (const ConversionException &) { + // FIXME: error handling + } + + currentFieldName = currentFieldValue = nullptr; + currentFieldNameSize = currentFieldValueSize = 0; + break; + default: + if (!currentFieldValue) { + currentFieldValue = i; + } + ++currentFieldValueSize; + } + break; + case Comment: + switch (c) { + case '\n': + case '\r': + case '\t': + state = FieldName; + break; + default:; // ignore outcommented characters + } + break; + } + } + if (currentPackage) { + addVersionInfo(*currentPackage, version, isPackageInfo); + currentPackage->provides.emplace_back(currentPackage->name, currentPackage->version); + } +} + +std::vector> Package::fromInfo(const std::string &info, bool isPackageInfo) +{ + vector> packages; + const auto nextPackage = [&](Package &basePackage) { return packages.emplace_back(make_shared(basePackage)).get(); }; + parsePkgInfo(info, nextPackage, isPackageInfo); + return packages; +} + +shared_ptr Package::fromDescription(const std::vector &descriptionParts) +{ + auto package = make_shared(); + package->origin = PackageOrigin::Database; + package->sourceInfo = make_shared(); + package->packageInfo = make_unique(); + for (const string &desc : descriptionParts) { + // states + enum { + FieldName, // reading field name + NewLine, // expecting new line (after field name) + Next, // start reading next field value / next field name (initial state) + FieldValue, // reading field value + } state + = Next; + + // variables for current field + const char *currentFieldName = nullptr; + size_t currentFieldNameSize = 0; + const char *currentFieldValue = nullptr; + size_t currentFieldValueSize = 0; + + // do actual parsing via state machine + for (const char *i = desc.data(); *i; ++i) { + const char c = *i; + switch (state) { + case FieldName: + switch (c) { + case '%': + state = NewLine; + break; + default: + if (!currentFieldName) { + currentFieldName = i; + } + ++currentFieldNameSize; + } + break; + case NewLine: + switch (c) { + case '\n': + case '\r': + state = Next; + break; + default:; // ignore unexpected characters + } + break; + case Next: + switch (c) { + case '\n': + case '\r': + case '\t': + case ' ': + break; + case '%': + state = FieldName; + currentFieldName = nullptr; + currentFieldNameSize = 0; + break; + default: + state = FieldValue; + if (!currentFieldValue) { + currentFieldValue = i; + } + ++currentFieldValueSize; + } + break; + case FieldValue: + switch (c) { + case '\n': + case '\r': + state = Next; + try { + addPackageDescription(*package, currentFieldName, currentFieldNameSize, currentFieldValue, currentFieldValueSize); + } catch (const ConversionException &) { + // FIXME: error handling + } + currentFieldValue = nullptr; + currentFieldValueSize = 0; + break; + default: + if (!currentFieldValue) { + currentFieldValue = i; + } + ++currentFieldValueSize; + } + } + } + if (currentFieldName && currentFieldValue) { + addPackageDescription(*package, currentFieldName, currentFieldNameSize, currentFieldValue, currentFieldValueSize); + } + } + package->provides.emplace_back(package->name, package->version); + return package; +} + +std::vector> Package::fromDatabaseFile(FileMap &&databaseFile) +{ + std::vector> packages; + packages.reserve(databaseFile.size()); + for (auto &dir : databaseFile) { + vector descriptionParts; + descriptionParts.reserve(dir.second.size()); + for (auto &file : dir.second) { + descriptionParts.emplace_back(move(file.content)); + } + packages.emplace_back(Package::fromDescription(descriptionParts)); + } + return packages; +} + +bool Package::isPkgInfoFileOrBinary(const char *filePath, const char *fileName, mode_t mode) +{ + return !strcmp(fileName, ".PKGINFO") || mode == S_IXUSR || strstr(filePath, "usr/bin") == filePath || strstr(filePath, "usr/lib") == filePath + || strstr(fileName, ".so") > fileName || strstr(fileName, ".dll") > fileName || strstr(fileName, ".a"); +} + +bool LibPkg::Package::isLicense(const char *filePath, const char *fileName, mode_t mode) +{ + CPP_UTILITIES_UNUSED(mode) + return strstr(fileName, "LICENSE") == fileName || strstr(filePath, "usr/share/licenses") == filePath; +} + +void Package::addInfoFromPkgInfoFile(const string &info) +{ + parsePkgInfo( + info, [this](Package &) { return this; }, true); +} + +static const regex pythonVersionRegex("usr/lib/python(2|3)\\.([0-9]*)(\\..*)?/site-packages"); +static const regex perlVersionRegex("usr/lib/perl5/5\\.([0-9]*)(\\..*)?/vendor_perl"); + +void Package::addDepsAndProvidesFromContainedDirectory(const string &directoryPath) +{ + // check for Python modules + thread_local smatch match; + if (regex_match(directoryPath, match, pythonVersionRegex)) { + const auto majorVersion = match[1].str(); + const auto minorVersion = match[2].str(); + const char *const pythonPackage(majorVersion == "3" ? "python" : "python2"); + auto currentVersion = argsToString(majorVersion, '.', minorVersion); + auto nextVersion = argsToString(majorVersion, '.', stringToNumber(minorVersion) + 1); + dependencies.emplace_back(pythonPackage, std::move(currentVersion), DependencyMode::GreatherEqual); + dependencies.emplace_back(pythonPackage, std::move(nextVersion), DependencyMode::LessThan); + } + + // check for Perl modules + if (regex_match(directoryPath, match, perlVersionRegex)) { + const auto minorVersion = match[1].str(); + auto currentVersion = "5." + minorVersion; + auto nextVersion = "5." + numberToString(stringToNumber(minorVersion) + 1); + dependencies.emplace_back("perl", std::move(currentVersion), DependencyMode::GreatherEqual); + dependencies.emplace_back("perl", std::move(nextVersion), DependencyMode::LessThan); + } +} + +void Package::addDepsAndProvidesFromContainedFile(const ArchiveFile &file, std::set &dllsReferencedByImportLibs) +{ + try { + Binary binary; + binary.load(file.content, file.name); + if (!binary.name.empty()) { + if (binary.type == BinaryType::Ar && binary.subType == BinarySubType::WindowsImportLibrary) { + dllsReferencedByImportLibs.emplace(binary.addPrefix(binary.name)); + } else { + libprovides.emplace(binary.addPrefix(binary.name)); + } + } + for (const auto &require : binary.requiredLibs) { + libdepends.emplace(binary.addPrefix(require)); + } + } catch (const std::ios_base::failure &) { + // TODO: handle IO error + } catch (const std::runtime_error &) { + // TODO: handle parsing error + } +} + +void Package::processDllsReferencedByImportLibs(std::set &&dllsReferencedByImportLibs) +{ + // check whether all DLLs referenced by import libraries are actually part of the package + if (dllsReferencedByImportLibs.empty()) { + return; + } else if (name == "mingw-w64-crt") { + // assume the CRT references DLLs provided by Windows itself + libprovides = move(dllsReferencedByImportLibs); + } + for (const auto &referencedDLL : dllsReferencedByImportLibs) { + // TODO: report these errors in a better way + if (libprovides.find(referencedDLL) == libprovides.end()) { + cerr << Phrases::SubMessage << "DLL " << referencedDLL << " is missing in " << name << Phrases::End; + } + } +} + +void Package::addDepsAndProvidesFromContents(const FileMap &contents) +{ + std::set dllsReferencedByImportLibs; + for (const auto &[directoryPath, files] : contents) { + addDepsAndProvidesFromContainedDirectory(directoryPath); + for (const auto &file : files) { + addDepsAndProvidesFromContainedFile(file, dllsReferencedByImportLibs); + } + } + processDllsReferencedByImportLibs(std::move(dllsReferencedByImportLibs)); +} + +std::shared_ptr Package::fromPkgFile(const string &path) +{ + std::set dllsReferencedByImportLibs; + Package tmpPackageForLibraryDeps; + shared_ptr package; + LibPkg::walkThroughArchive( + path, &LibPkg::Package::isPkgInfoFileOrBinary, + [&package, &tmpPackageForLibraryDeps, &dllsReferencedByImportLibs](std::string &&directoryPath, LibPkg::ArchiveFile &&file) { + if (directoryPath.empty() && file.name == ".PKGINFO") { + if (package) { + return; // only consider one .PKGINFO file (multiple ones are likely not possible in any supported archive formats anyways) + } + auto packages = fromInfo(file.content, true); + if (!packages.empty()) { + package = std::move(packages.front()); + } + return; + } + tmpPackageForLibraryDeps.addDepsAndProvidesFromContainedFile(file, dllsReferencedByImportLibs); + }, + [&tmpPackageForLibraryDeps](std::string &&directoryPath) { + if (directoryPath.empty()) { + return; + } + tmpPackageForLibraryDeps.addDepsAndProvidesFromContainedDirectory(directoryPath); + }); + if (!package) { + throw runtime_error("Package " % path + " does not contain a valid .PKGINFO"); + } + if (!package->packageInfo) { + package->packageInfo = make_unique(); + } + package->packageInfo->fileName = fileName(path); + package->addDepsAndProvidesFromOtherPackage(tmpPackageForLibraryDeps, true); + package->processDllsReferencedByImportLibs(std::move(dllsReferencedByImportLibs)); + return package; +} + +std::tuple Package::fileNameComponents(std::string_view fileName) +{ + auto extBegin = fileName.rfind(".pkg"); + auto isSourcePackage = false; + if (extBegin == string::npos) { + extBegin = fileName.rfind(".src"); + isSourcePackage = true; + } + if (extBegin == string::npos) { + throw runtime_error("File name " % fileName + " does not have .pkg or .src extension."); + } + const auto archBegin = isSourcePackage ? (extBegin) : fileName.rfind('-', extBegin - 1); + if (archBegin == string::npos) { + throw runtime_error("File name " % fileName + " does not contain architecture."); + } + auto pkgrelBegin = fileName.rfind('-', archBegin - 1); + if (pkgrelBegin == string::npos) { + throw runtime_error("File name " % fileName + " does not contain pkgrel."); + } + const auto pkgverBegin = fileName.rfind('-', pkgrelBegin - 1); + if (pkgverBegin == string::npos) { + throw runtime_error("File name " % fileName + " does not contain pkgver."); + } + return make_tuple(fileName.substr(0, pkgverBegin), fileName.substr(pkgverBegin + 1, archBegin - pkgverBegin - 1), + isSourcePackage ? "src" : fileName.substr(archBegin + 1, extBegin - archBegin - 1)); +} + +std::shared_ptr Package::fromPkgFileName(std::string_view fileName) +{ + const auto [name, version, arch] = fileNameComponents(fileName); + auto pkg = make_shared(); + pkg->origin = PackageOrigin::PackageFileName; + pkg->name = name; + pkg->version = version; + pkg->provides.emplace_back(pkg->name, pkg->version); + pkg->packageInfo = make_unique(); + pkg->packageInfo->fileName = fileName; + pkg->packageInfo->arch = arch; + return pkg; +} + +std::vector> Package::fromAurRpcJson(const char *jsonData, std::size_t jsonSize, PackageOrigin origin) +{ + ReflectiveRapidJSON::JsonDeserializationErrors errors; + auto rpcMultiInfo = AurRpcMultiInfo::fromJson(jsonData, jsonSize, &errors); + + std::vector> packages; + packages.reserve(rpcMultiInfo.results.size()); + + for (auto &result : rpcMultiInfo.results) { + auto package = make_shared(); + auto sourceInfo = make_shared(); + package->origin = origin; + package->name = move(result.Name); + package->version = move(result.Version); + package->description = move(result.Description); + package->upstreamUrl = move(result.URL); + package->licenses = move(result.License); + package->groups = move(result.Groups); + for (auto &dependencyName : result.Depends) { + package->dependencies.emplace_back(dependencyName.data(), dependencyName.size()); + } + for (auto &dependencyName : result.OptDepends) { + package->optionalDependencies.emplace_back(dependencyName.data(), dependencyName.size()); + } + for (auto &dependencyName : result.MakeDepends) { + sourceInfo->makeDependencies.emplace_back(dependencyName.data(), dependencyName.size()); + } + for (auto &dependencyName : result.CheckDepends) { + sourceInfo->checkDependencies.emplace_back(dependencyName.data(), dependencyName.size()); + } + sourceInfo->name = move(result.PackageBase); + sourceInfo->maintainer = move(result.Maintainer); + sourceInfo->id = move(result.ID); + sourceInfo->votes = move(result.NumVotes); + if (result.OutOfDate) { + sourceInfo->outOfDate = DateTime::fromTimeStampGmt(*result.OutOfDate); + } + sourceInfo->firstSubmitted = DateTime::fromTimeStampGmt(result.FirstSubmitted); + sourceInfo->lastModified = DateTime::fromTimeStampGmt(result.LastModified); + sourceInfo->url = move(result.URLPath); + package->sourceInfo = move(sourceInfo); + packages.emplace_back(move(package)); + } + return packages; +} + +string PackageNameData::compose() const +{ + string res; + res.reserve(targetPrefix.size() + vcsSuffix.size() + actualName.size() + 2); + if (!targetPrefix.empty()) { + res += targetPrefix; + res += '-'; + } + res += actualName; + if (!vcsSuffix.empty()) { + res += '-'; + res += vcsSuffix; + } + return res; +} + +string PackageNameData::variant() const +{ + if (targetPrefix.empty() && vcsSuffix.empty()) { + return "default"; + } + if (targetPrefix.empty()) { + return string(vcsSuffix); + } + if (vcsSuffix.empty()) { + return string(targetPrefix); + } + return targetPrefix % '-' + vcsSuffix; +} + +bool PackageNameData::isVcsPackage() const +{ + if (vcsSuffix.empty()) { + return false; + } + static const std::unordered_set vcsSuffixes = { "cvs", "svn", "hg", "darcs", "bzr", "git" }; + const auto lastDash = vcsSuffix.rfind('-'); + return vcsSuffixes.find(lastDash == std::string_view::npos ? vcsSuffix : vcsSuffix.substr(lastDash + 1)) != vcsSuffixes.end(); +} + +PackageNameData PackageNameData::decompose(std::string_view packageName) +{ + static const std::regex packageNameRegex( + "((lib32|mingw-w64|android-aarch64|android-x86-64|android-x86|android-armv7a-eabi|arm-none-eabi|aarch64-linux-" + "gnu|riscv64-linux|avr|psp)-)?(.*?)((-(cvs|svn|hg|darcs|bzr|git|custom|compat|static|qt\\d+|doc|cli|gui))*)"); + auto data = PackageNameData{}; + auto match = std::cmatch{}; + if (!regex_match(packageName.cbegin(), packageName.cend(), match, packageNameRegex)) { + return data; + } + static constexpr auto matchToStringView = [](auto match, std::size_t offset = 0) { + return std::string_view(match.first + offset, static_cast(match.length() - offset)); + }; + data.targetPrefix = matchToStringView(match[2]); + data.actualName = matchToStringView(match[3]); + data.vcsSuffix = match[4].length() ? matchToStringView(match[4], 1) : std::string_view{}; + return data; +} + +} // namespace LibPkg diff --git a/libpkg/parser/package.h b/libpkg/parser/package.h new file mode 100644 index 0000000..e3273fe --- /dev/null +++ b/libpkg/parser/package.h @@ -0,0 +1,10 @@ +#ifndef LIBPKG_PARSER_PACKAGE_H +#define LIBPKG_PARSER_PACKAGE_H + +#include "../data/package.h" + +namespace LibPkg { + +} // namespace LibPkg + +#endif // LIBPKG_PARSER_PACKAGE_H diff --git a/libpkg/parser/siglevel.cpp b/libpkg/parser/siglevel.cpp new file mode 100644 index 0000000..16a5650 --- /dev/null +++ b/libpkg/parser/siglevel.cpp @@ -0,0 +1,115 @@ +#include "../data/siglevel.h" + +#include +#include + +#include + +using namespace CppUtilities; + +namespace LibPkg { + +SignatureLevelConfig SignatureLevelConfig::fromString(std::string_view str) +{ + auto parts = splitStringSimple>(str, std::string_view(" ")); + auto whatToCheckPkg = SignatureLevel::Optional; + auto whatIsAllowedPkg = SignatureLevel::TrustedOnly; + auto whatToCheckDb = SignatureLevel::Optional; + auto whatIsAllowedDb = SignatureLevel::TrustedOnly; + for (auto &part : parts) { + if (part.empty()) { + continue; + } + enum SignatureScope { + All, + Package, + Database, + } scope + = All; + if (startsWith(part, "Package")) { + part = part.substr(7); + scope = Package; + } else if (startsWith(part, "Database")) { + part = part.substr(8); + scope = All; + } + SignatureLevel whatToCheck = SignatureLevel::Invalid, whatIsAllowed = SignatureLevel::Invalid; + if (part == "Never") { + whatToCheck = SignatureLevel::Never; + } else if (part == "Optional") { + whatToCheck = SignatureLevel::Optional; + } else if (part == "Required") { + whatToCheck = SignatureLevel::Required; + } else if (part == "TrustedOnly") { + whatIsAllowed = SignatureLevel::TrustedOnly; + } else if (part == "TrustAll") { + whatIsAllowed = SignatureLevel::TrustAll; + } else { + return SignatureLevelConfig(SignatureLevel::Invalid); + } + switch (scope) { + case All: + if (whatToCheck != SignatureLevel::Invalid) { + whatToCheckDb = whatToCheckPkg = whatToCheck; + } else if (whatIsAllowed != SignatureLevel::Invalid) { + whatIsAllowedDb = whatIsAllowedPkg = whatIsAllowed; + } + break; + case Package: + if (whatToCheck != SignatureLevel::Invalid) { + whatToCheckPkg = whatToCheck; + } else if (whatIsAllowed != SignatureLevel::Invalid) { + whatIsAllowedPkg = whatIsAllowed; + } + break; + case Database: + if (whatToCheck != SignatureLevel::Invalid) { + whatToCheckDb = whatToCheck; + } else if (whatIsAllowed != SignatureLevel::Invalid) { + whatIsAllowedDb = whatIsAllowed; + } + break; + } + } + return SignatureLevelConfig(whatToCheckDb | whatIsAllowedDb, whatToCheckPkg | whatIsAllowedPkg); +} + +std::string signatureLevelToString(SignatureLevel sigLevel, std::string_view prefix) +{ + if (sigLevel == SignatureLevel::Invalid) { + return std::string(); + } + const char *whatToCheck = "Optional"; + const char *whatIsAllowed = "TrustedOnly"; + if (sigLevel & SignatureLevel::Required) { + whatToCheck = "Required"; + } else if (sigLevel & SignatureLevel::Optional) { + whatToCheck = "Optional"; + } else if (sigLevel & SignatureLevel::Never) { + whatToCheck = "Never"; + } + if (sigLevel & SignatureLevel::TrustedOnly) { + whatIsAllowed = "TrustedOnly"; + } else if (sigLevel & SignatureLevel::TrustAll) { + whatIsAllowed = "TrustAll"; + } + return argsToString(prefix, whatToCheck, ' ', prefix, whatIsAllowed); +} + +std::string SignatureLevelConfig::toString() const +{ + if (databaseScope == SignatureLevel::Invalid && packageScope == SignatureLevel::Invalid) { + return std::string(); + } else if (databaseScope == packageScope || packageScope == SignatureLevel::Invalid) { + return signatureLevelToString(databaseScope); + } else if (databaseScope == SignatureLevel::Invalid) { + return signatureLevelToString(packageScope); + } else { + return argsToString(signatureLevelToString(databaseScope, std::string_view("Database")), ' ', + signatureLevelToString(packageScope, std::string_view("Package"))); + } +} + +} // namespace LibPkg + +#include "reflection/siglevel.h" diff --git a/libpkg/parser/utils.cpp b/libpkg/parser/utils.cpp new file mode 100644 index 0000000..dd37834 --- /dev/null +++ b/libpkg/parser/utils.cpp @@ -0,0 +1,291 @@ +#include "./utils.h" + +#include "./data/package.h" + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibPkg { + +struct AddDirectoryToFileMap { + void operator()(std::string &&path) + { + fileMap[std::move(path)]; + } + FileMap &fileMap; +}; + +struct AddFileToFileMap { + void operator()(std::string &&directoryPath, ArchiveFile &&file) + { + fileMap[std::move(directoryPath)].emplace_back(std::move(file)); + } + FileMap &fileMap; +}; + +void walkThroughArchiveInternal(struct archive *ar, const string &archiveName, const FilePredicate &isFileRelevant, FileHandler &&fileHandler, + DirectoryHandler &&directoryHandler) +{ + // iterate through all archive entries + struct archive_entry *entry; + while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) { + // check entry type (only dirs, files and symlinks relevant here) + const auto entryType(archive_entry_filetype(entry)); + if (entryType != AE_IFDIR && entryType != AE_IFREG && entryType != AE_IFLNK) { + continue; + } + + // get file path + const char *filePath = archive_entry_pathname_utf8(entry); + if (!filePath) { + filePath = archive_entry_pathname(entry); + } + if (!filePath) { + continue; + } + + // get permissions + const mode_t perm = archive_entry_perm(entry); + + // add directories explicitely to get the entire tree though skipping irrelevant files + if (entryType == AE_IFDIR) { + // remove trailing slashes + const char *dirEnd = filePath; + for (const char *i = filePath; *i; ++i) { + if (*i != '/') { + dirEnd = i + 1; + } + } + + directoryHandler(string(filePath, dirEnd)); + continue; + } + + // split the path into dir and fileName + const char *fileName = filePath, *dirEnd = filePath; + for (const char *i = filePath; *i; ++i) { + if (*i == '/') { + fileName = i + 1; + dirEnd = i; + } + } + + // prevent looking into irrelevant files + if (isFileRelevant && !isFileRelevant(filePath, fileName, perm)) { + continue; + } + + // read timestamps + const auto creationTime = DateTime::fromTimeStampGmt(archive_entry_ctime(entry)); + const auto modificationTime = DateTime::fromTimeStampGmt(archive_entry_mtime(entry)); + + // read symlink + if (entryType == AE_IFLNK) { + fileHandler(string(filePath, static_cast(dirEnd - filePath)), + ArchiveFile(fileName, string(archive_entry_symlink_utf8(entry)), ArchiveFileType::Link, creationTime, modificationTime)); + continue; + } + + // determine file size to pre-allocate buffer for file content + const la_int64_t fileSize = archive_entry_size(entry); + string fileContent; + if (fileSize > 0) { + fileContent.reserve(static_cast(fileSize)); + } + + // read file content + const char *buff; + size_t size; + la_int64_t offset; + for (;;) { + int returnCode = archive_read_data_block(ar, reinterpret_cast(&buff), &size, &offset); + if (returnCode == ARCHIVE_EOF || returnCode < ARCHIVE_OK) { + break; + } + fileContent.append(buff, size); + } + + // move it to results + fileHandler(string(filePath, static_cast(dirEnd - filePath)), + ArchiveFile(fileName, move(fileContent), ArchiveFileType::Regular, creationTime, modificationTime)); + } + + // free resources used by libarchive + int returnCode = archive_read_free(ar); + if (returnCode != ARCHIVE_OK) { + throw runtime_error("Unable to free archive: " + archiveName); + } +} + +void walkThroughArchiveFromBuffer(const string &archiveData, const string &archiveName, const FilePredicate &isFileRelevant, + FileHandler &&fileHandler, DirectoryHandler &&directoryHandler) +{ + // open archive buffer using libarchive + struct archive *ar = archive_read_new(); + archive_read_support_filter_all(ar); + archive_read_support_format_all(ar); + int returnCode = archive_read_open_memory(ar, archiveData.data(), archiveData.size()); + if (returnCode != ARCHIVE_OK) { + if (const char *const error = archive_error_string(ar)) { + throw runtime_error("Unable to open/read archive: " % archiveName % "; " + error); + } else { + throw runtime_error("Unable to open/read archive: " + archiveName); + } + } + walkThroughArchiveInternal(ar, archiveName, isFileRelevant, std::move(fileHandler), std::move(directoryHandler)); +} + +FileMap extractFilesFromBuffer(const string &archiveData, const string &archiveName, const FilePredicate &isFileRelevant) +{ + FileMap results; + walkThroughArchiveFromBuffer(archiveData, archiveName, isFileRelevant, AddFileToFileMap{ results }, AddDirectoryToFileMap{ results }); + return results; +} + +void walkThroughArchive( + const string &archivePath, const FilePredicate &isFileRelevant, FileHandler &&fileHandler, DirectoryHandler &&directoryHandler) +{ + // open archive file using libarchive + if (archivePath.empty()) { + throw std::runtime_error("Unable to open archive: no path specificed"); + } + struct archive *ar = archive_read_new(); + archive_read_support_filter_all(ar); + archive_read_support_format_all(ar); + const auto returnCode = archive_read_open_filename(ar, archivePath.data(), 10240); + if (returnCode != ARCHIVE_OK) { + throw std::runtime_error("Unable to open/read archive: " + archivePath); + } + walkThroughArchiveInternal(ar, archivePath, isFileRelevant, std::move(fileHandler), std::move(directoryHandler)); +} + +FileMap extractFiles(const string &archivePath, const FilePredicate &isFileRelevant) +{ + FileMap results; + walkThroughArchive(archivePath, isFileRelevant, AddFileToFileMap{ results }, AddDirectoryToFileMap{ results }); + return results; +} + +/*! + * \brief Returns the first non-alphanumeric character \a str. + * \remarks The \a end is returned if \a str only contains alphanumeric characters. + * \todo Care about ä, ö, ü, ß in version numbers? + */ +const char *firstNonAlphanumericCharacter(const char *str, const char *end) +{ + for (; str != end; ++str) { + const char c = *str; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) { + return str; + } + } + return str; +} + +/*! + * \brief Determines when the file with the specified \a path has been modified the last time. + * \fixme Make no assumptions on the internal resolution so the code is portable (seems not possible with C++17). + */ +CppUtilities::DateTime lastModified(const string &path) +{ + try { + return CppUtilities::DateTime::fromChronoTimePointGmt( + chrono::time_point{ filesystem::last_write_time(path).time_since_epoch() } + + chrono::seconds{ 6437664000 }); + } catch (const runtime_error &) { + return CppUtilities::DateTime(); + } +} + +/*! + * \brief Sets when the file with the specified \a path has been modified the last time. + * \fixme Use the std::filesystem library once the time point can be constructed in a portable way. + */ +bool setLastModified(const string &path, DateTime lastModified) +{ + timeval tv[2]; + tv[0].tv_usec = UTIME_OMIT; + tv[1].tv_sec = lastModified.toTimeStamp(); + tv[1].tv_usec = lastModified.nanosecond(); + return utimes(path.data(), tv) == 0; +} + +/*! + * \brief Override an overridden variable assignment (to ensure the configured default value is actually used and not overridden). + */ +static void overrideOverriddenVariableAssignment(string &pkgbuildContents, string_view variableName, const string *configuredDefaultValue) +{ + if (!configuredDefaultValue || pkgbuildContents.find(variableName) == string::npos) { + return; + } + pkgbuildContents.append(argsToString( + pkgbuildContents.empty() || pkgbuildContents.back() == '\n' ? "" : "\n", variableName, '=', '\'', *configuredDefaultValue, '\'', '\n')); +} + +/*! + * \brief Amends the PKGBUILD with the specified \a path. + * \throws Throws std::ios_base::failure when an IO error occurs. + */ +AmendedVersions amendPkgbuild(const string &path, const PackageVersion &existingVersion, const PackageAmendment &amendment) +{ + AmendedVersions amendedVersions; + if (amendment.isEmpty()) { + return amendedVersions; + } + + auto pkgbuildContents = readFile(path, 0x10000); + + // set upstream version + if (amendment.setUpstreamVersion) { + static const auto pkgverRegex = regex{ "\npkgver=[^\n]*", regex::extended }; + pkgbuildContents = regex_replace(pkgbuildContents, pkgverRegex, "\npkgver=" + existingVersion.upstream); + } + + // bump downstream version + switch (amendment.bumpDownstreamVersion) { + case PackageAmendment::VersionBump::None: + break; + case PackageAmendment::VersionBump::Epoch: + amendedVersions.newEpoch = numberToString(stringToNumber(existingVersion.epoch) + 1); + amendedVersions.newPkgRel = "1"; + break; + case PackageAmendment::VersionBump::PackageVersion: + amendedVersions.newPkgRel = numberToString(stringToNumber(existingVersion.package) + 0.1); + break; + } + if (!amendedVersions.newEpoch.empty()) { + if (pkgbuildContents.find("\nepoch=") != string::npos) { + static const auto epochRegex = regex{ "\nepoch=[^\n]*", regex::extended }; + pkgbuildContents = regex_replace(pkgbuildContents, epochRegex, "\nepoch=" + amendedVersions.newEpoch); + } else { + static const auto epochRegex = regex{ "\npkgver=", regex::extended }; + pkgbuildContents = regex_replace(pkgbuildContents, epochRegex, "\nepoch=" % amendedVersions.newEpoch + "\npkgver="); + } + } + if (!amendedVersions.newPkgRel.empty()) { + static const auto pkgrelRegex = regex{ "\npkgrel=[^\n]*", regex::extended }; + pkgbuildContents = regex_replace(pkgbuildContents, pkgrelRegex, "\npkgrel=" + amendedVersions.newPkgRel); + } + + // override overrides for SRCEXT/PKGEXT to ensure defaults from makepkg.conf are used + overrideOverriddenVariableAssignment(pkgbuildContents, "SRCEXT", amendment.ensureSourceExtension); + overrideOverriddenVariableAssignment(pkgbuildContents, "PKGEXT", amendment.ensurePackageExtension); + + writeFile(path, pkgbuildContents); + return amendedVersions; +} + +} // namespace LibPkg diff --git a/libpkg/parser/utils.h b/libpkg/parser/utils.h new file mode 100644 index 0000000..b3b4fbb --- /dev/null +++ b/libpkg/parser/utils.h @@ -0,0 +1,100 @@ +#ifndef LIBPKG_PARSER_UTILS_H +#define LIBPKG_PARSER_UTILS_H + +#include "../global.h" + +#include + +#include +#include +#include +#include + +namespace LibPkg { + +/* + * Helper to extract archives with libarchive + */ + +enum class ArchiveFileType { Regular, Link }; + +struct LIBPKG_EXPORT ArchiveFile { + ArchiveFile( + std::string &&name, std::string &&content, ArchiveFileType type, CppUtilities::DateTime creationTime, CppUtilities::DateTime modificationTime) + : name(name) + , content(content) + , creationTime(creationTime) + , modificationTime(modificationTime) + , type(type) + { + } + std::string name; + std::string content; + CppUtilities::DateTime creationTime; + CppUtilities::DateTime modificationTime; + ArchiveFileType type; +}; + +using FileMap = std::map>; +using FilePredicate = std::function; +using DirectoryHandler = std::function; +using FileHandler = std::function; + +LIBPKG_EXPORT FileMap extractFiles(const std::string &archivePath, const FilePredicate &isFileRelevant = FilePredicate()); +LIBPKG_EXPORT void walkThroughArchive(const std::string &archivePath, const FilePredicate &isFileRelevant = FilePredicate(), + FileHandler &&fileHandler = FileHandler(), DirectoryHandler &&directoryHandler = DirectoryHandler()); +LIBPKG_EXPORT FileMap extractFilesFromBuffer( + const std::string &archiveData, const std::string &archiveName, const FilePredicate &isFileRelevant = FilePredicate()); +LIBPKG_EXPORT void walkThroughArchiveFromBuffer(const std::string &archiveData, const std::string &archiveName, + const FilePredicate &isFileRelevant = FilePredicate(), FileHandler &&fileHandler = FileHandler(), + DirectoryHandler &&directoryHandler = DirectoryHandler()); + +/* + * PKGBUILD amendment + */ + +struct PackageVersion; + +struct LIBPKG_EXPORT PackageAmendment { + enum class VersionBump { + None, /*!< Don't increment the version. */ + PackageVersion, /*!< Increments the package version of the specified existing version by 0.1 and sets that as pkgrel within the PKGBUILD. */ + Epoch, /*!< Increments the epoch of the specified existing version by 1 and sets that as epoch within the PKGBUILD. */ + }; + /// \brief Ensures that "PKGEXT" is *not* overridden. If it appears to be overridden, the specified "PKGEXT" is enforced by appending an + /// explicit assignment. + const std::string *ensurePackageExtension = nullptr; + /// \brief Ensures that "SRCEXT" is *not* overridden. If it appears to be overridden, the specified "SRCEXT" is enforced by appending an + /// explicit assignment. + const std::string *ensureSourceExtension = nullptr; + /// \brief Increments epoch/pkgrel with respect to the specified existing version. + VersionBump bumpDownstreamVersion = VersionBump::None; + /// \brief Sets pkgver to the existing version. + bool setUpstreamVersion = false; + + bool isEmpty() const; +}; + +struct LIBPKG_EXPORT AmendedVersions { + std::string newPkgRel; + std::string newEpoch; +}; + +inline bool PackageAmendment::isEmpty() const +{ + return bumpDownstreamVersion == PackageAmendment::VersionBump::None && !setUpstreamVersion; +} + +LIBPKG_EXPORT AmendedVersions amendPkgbuild(const std::string &path, const PackageVersion &existingVersion, const PackageAmendment &amendment); + +/* + * Misc helper + */ + +LIBPKG_EXPORT const char *firstNonAlphanumericCharacter(const char *str, const char *end); +LIBPKG_EXPORT CppUtilities::DateTime lastModified(const std::string &path); +LIBPKG_EXPORT bool setLastModified(const std::string &path, CppUtilities::DateTime lastModified); + +} // namespace LibPkg + +#endif // LIBPKG_PARSER_UTILS_H diff --git a/libpkg/testfiles/c++utilities/PKGBUILD b/libpkg/testfiles/c++utilities/PKGBUILD new file mode 100644 index 0000000..45cf5c8 --- /dev/null +++ b/libpkg/testfiles/c++utilities/PKGBUILD @@ -0,0 +1,38 @@ +_reponame=cpp-utilities +pkgname=c++utilities +pkgver=5.0.1 +pkgrel=2 +arch=('i686' 'x86_64' 'armv6h' 'armv7h' 'aarch64') +pkgdesc='Common C++ classes and routines such as argument parser, IO and conversion utilities' +license=('GPL') +depends=() +optdepends=("$pkgname-doc: API documentation") +makedepends=('cmake') +checkdepends=('cppunit') +url="https://github.com/Martchus/${_reponame}" +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/Martchus/${_reponame}/archive/v${pkgver}.tar.gz") +sha256sums=('1e10abf567930614178f3c8674d16ae6711758cf61b8e6fdd2fbbe5629cf6ebf') + +prepare() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" +} + +build() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + cmake \ + -DCMAKE_BUILD_TYPE:STRING='Release' \ + -DCMAKE_INSTALL_PREFIX:PATH='/usr' \ + -DBUILD_SHARED_LIBS:BOOL=ON \ + . + make +} + +check() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make check +} + +package() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make DESTDIR="${pkgdir}" install +} diff --git a/libpkg/testfiles/c++utilities/PKGBUILD.newepoch b/libpkg/testfiles/c++utilities/PKGBUILD.newepoch new file mode 100644 index 0000000..d125a1c --- /dev/null +++ b/libpkg/testfiles/c++utilities/PKGBUILD.newepoch @@ -0,0 +1,39 @@ +_reponame=cpp-utilities +pkgname=c++utilities +epoch=1 +pkgver=5.0.1 +pkgrel=1 +arch=('i686' 'x86_64' 'armv6h' 'armv7h' 'aarch64') +pkgdesc='Common C++ classes and routines such as argument parser, IO and conversion utilities' +license=('GPL') +depends=() +optdepends=("$pkgname-doc: API documentation") +makedepends=('cmake') +checkdepends=('cppunit') +url="https://github.com/Martchus/${_reponame}" +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/Martchus/${_reponame}/archive/v${pkgver}.tar.gz") +sha256sums=('1e10abf567930614178f3c8674d16ae6711758cf61b8e6fdd2fbbe5629cf6ebf') + +prepare() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" +} + +build() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + cmake \ + -DCMAKE_BUILD_TYPE:STRING='Release' \ + -DCMAKE_INSTALL_PREFIX:PATH='/usr' \ + -DBUILD_SHARED_LIBS:BOOL=ON \ + . + make +} + +check() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make check +} + +package() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make DESTDIR="${pkgdir}" install +} diff --git a/libpkg/testfiles/c++utilities/PKGBUILD.newpkgrel b/libpkg/testfiles/c++utilities/PKGBUILD.newpkgrel new file mode 100644 index 0000000..66e8e9b --- /dev/null +++ b/libpkg/testfiles/c++utilities/PKGBUILD.newpkgrel @@ -0,0 +1,38 @@ +_reponame=cpp-utilities +pkgname=c++utilities +pkgver=5.0.1 +pkgrel=3.1 +arch=('i686' 'x86_64' 'armv6h' 'armv7h' 'aarch64') +pkgdesc='Common C++ classes and routines such as argument parser, IO and conversion utilities' +license=('GPL') +depends=() +optdepends=("$pkgname-doc: API documentation") +makedepends=('cmake') +checkdepends=('cppunit') +url="https://github.com/Martchus/${_reponame}" +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/Martchus/${_reponame}/archive/v${pkgver}.tar.gz") +sha256sums=('1e10abf567930614178f3c8674d16ae6711758cf61b8e6fdd2fbbe5629cf6ebf') + +prepare() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" +} + +build() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + cmake \ + -DCMAKE_BUILD_TYPE:STRING='Release' \ + -DCMAKE_INSTALL_PREFIX:PATH='/usr' \ + -DBUILD_SHARED_LIBS:BOOL=ON \ + . + make +} + +check() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make check +} + +package() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make DESTDIR="${pkgdir}" install +} diff --git a/libpkg/testfiles/c++utilities/PKGBUILD.newpkgver b/libpkg/testfiles/c++utilities/PKGBUILD.newpkgver new file mode 100644 index 0000000..49cdbc0 --- /dev/null +++ b/libpkg/testfiles/c++utilities/PKGBUILD.newpkgver @@ -0,0 +1,39 @@ +_reponame=cpp-utilities +pkgname=c++utilities +epoch=1 +pkgver=5.0.2 +pkgrel=1 +arch=('i686' 'x86_64' 'armv6h' 'armv7h' 'aarch64') +pkgdesc='Common C++ classes and routines such as argument parser, IO and conversion utilities' +license=('GPL') +depends=() +optdepends=("$pkgname-doc: API documentation") +makedepends=('cmake') +checkdepends=('cppunit') +url="https://github.com/Martchus/${_reponame}" +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/Martchus/${_reponame}/archive/v${pkgver}.tar.gz") +sha256sums=('1e10abf567930614178f3c8674d16ae6711758cf61b8e6fdd2fbbe5629cf6ebf') + +prepare() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" +} + +build() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + cmake \ + -DCMAKE_BUILD_TYPE:STRING='Release' \ + -DCMAKE_INSTALL_PREFIX:PATH='/usr' \ + -DBUILD_SHARED_LIBS:BOOL=ON \ + . + make +} + +check() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make check +} + +package() { + cd "$srcdir/${PROJECT_DIR_NAME:-$_reponame-$pkgver}" + make DESTDIR="${pkgdir}" install +} diff --git a/libpkg/testfiles/c++utilities/SRCINFO b/libpkg/testfiles/c++utilities/SRCINFO new file mode 100644 index 0000000..9d98c58 --- /dev/null +++ b/libpkg/testfiles/c++utilities/SRCINFO @@ -0,0 +1,18 @@ +# Generated by mksrcinfo v8 +# Mon Jan 30 22:06:20 UTC 2017 +pkgbase = c++utilities + pkgdesc = Common C++ classes and routines such as argument parser, IO and conversion utilities + pkgver = 4.5.0 + pkgrel = 1 + url = https://github.com/Martchus/cpp-utilities + arch = i686 + arch = x86_64 + license = GPL + checkdepends = cppunit + makedepends = cmake>=3.0 + optdepends = c++utilities-doc: API documentation + source = c++utilities-4.5.0.tar.gz::https://github.com/Martchus/cpp-utilities/archive/v4.5.0.tar.gz + sha256sums = fc9d99a13db8584f25787e3f531fc4b2e71a2febc2fce5f78e04be0698d1b0db + +pkgname = c++utilities + diff --git a/libpkg/testfiles/c++utilities/c++utilities.dll b/libpkg/testfiles/c++utilities/c++utilities.dll new file mode 100755 index 0000000..dd5f81e Binary files /dev/null and b/libpkg/testfiles/c++utilities/c++utilities.dll differ diff --git a/libpkg/testfiles/c++utilities/desc b/libpkg/testfiles/c++utilities/desc new file mode 100644 index 0000000..b6485cb --- /dev/null +++ b/libpkg/testfiles/c++utilities/desc @@ -0,0 +1,48 @@ +%FILENAME% +c++utilities-4.5.0-1-x86_64.pkg.tar.xz + +%NAME% +c++utilities + +%VERSION% +4.5.0-1 + +%DESC% +Common C++ classes and routines such as argument parser, IO and conversion utilities + +%CSIZE% +103652 + +%ISIZE% +486400 + +%MD5SUM% +a19ae0cda6ac51a22d7f3dd5827cf4c1 + +%SHA256SUM% +87b8bc41d537c452ec81a59cc68940f45c26f845ae2a76acf7b44064794f54e2 + +%URL% +https://github.com/Martchus/cpp-utilities + +%LICENSE% +GPL + +%ARCH% +x86_64 + +%BUILDDATE% +1485811117 + +%PACKAGER% +Martchus <@.net> + +%OPTDEPENDS% +c++utilities-doc: API documentation + +%MAKEDEPENDS% +cmake + +%CHECKDEPENDS% +cppunit + diff --git a/libpkg/testfiles/c++utilities/libc++utilities.so.4.3.0 b/libpkg/testfiles/c++utilities/libc++utilities.so.4.3.0 new file mode 100755 index 0000000..12c7d3a Binary files /dev/null and b/libpkg/testfiles/c++utilities/libc++utilities.so.4.3.0 differ diff --git a/libpkg/testfiles/c++utilities/libc++utilities.so.4.5.0 b/libpkg/testfiles/c++utilities/libc++utilities.so.4.5.0 new file mode 100755 index 0000000..765312c Binary files /dev/null and b/libpkg/testfiles/c++utilities/libc++utilities.so.4.5.0 differ diff --git a/libpkg/testfiles/cmake/ccmake b/libpkg/testfiles/cmake/ccmake new file mode 100755 index 0000000..83e9b27 Binary files /dev/null and b/libpkg/testfiles/cmake/ccmake differ diff --git a/libpkg/testfiles/cmake/cmake b/libpkg/testfiles/cmake/cmake new file mode 100755 index 0000000..361d9d7 Binary files /dev/null and b/libpkg/testfiles/cmake/cmake differ diff --git a/libpkg/testfiles/cmake/cmake-3.8.2-1-x86_64.pkg.tar.xz b/libpkg/testfiles/cmake/cmake-3.8.2-1-x86_64.pkg.tar.xz new file mode 100644 index 0000000..cd0c2fa Binary files /dev/null and b/libpkg/testfiles/cmake/cmake-3.8.2-1-x86_64.pkg.tar.xz differ diff --git a/libpkg/testfiles/cmake/cmake-gui b/libpkg/testfiles/cmake/cmake-gui new file mode 100755 index 0000000..b51da8e Binary files /dev/null and b/libpkg/testfiles/cmake/cmake-gui differ diff --git a/libpkg/testfiles/cmake/cpack b/libpkg/testfiles/cmake/cpack new file mode 100755 index 0000000..116da1f Binary files /dev/null and b/libpkg/testfiles/cmake/cpack differ diff --git a/libpkg/testfiles/cmake/ctest b/libpkg/testfiles/cmake/ctest new file mode 100755 index 0000000..b3b650e Binary files /dev/null and b/libpkg/testfiles/cmake/ctest differ diff --git a/libpkg/testfiles/core.db b/libpkg/testfiles/core.db new file mode 100644 index 0000000..e2fc843 Binary files /dev/null and b/libpkg/testfiles/core.db differ diff --git a/libpkg/testfiles/core.files b/libpkg/testfiles/core.files new file mode 100644 index 0000000..50d81f2 Binary files /dev/null and b/libpkg/testfiles/core.files differ diff --git a/libpkg/testfiles/linux-4.7.6-1-desc b/libpkg/testfiles/linux-4.7.6-1-desc new file mode 100644 index 0000000..38c190b --- /dev/null +++ b/libpkg/testfiles/linux-4.7.6-1-desc @@ -0,0 +1,65 @@ +%FILENAME% +linux-4.7.6-1-x86_64.pkg.tar.xz + +%NAME% +linux + +%BASE% +linux + +%VERSION% +4.7.6-1 + +%DESC% +The Linux kernel and modules + +%GROUPS% +base + +%CSIZE% +62364408 + +%ISIZE% +80463872 + +%MD5SUM% +9220c71ebf7235246e31ba9b7790e928 + +%SHA256SUM% +ad36c918003a4f6ad0dce802790c0b1e8d4ba74fe8b041edb537f16b2393ea3b + +%PGPSIG% +iQEcBAABCAAGBQJX7ryhAAoJEHcd9mJ+32gf2y4IALT7BXA0yKyF6oKo1AQg8w1yltSnFbGvQdXYQGfOkkSrP2zMtUflxjbkWjHG6DRBbTpsJsTeuwdFVaDyh04hMEruifuwFdbP15dOIpLGxQ9/0CJHWsuoe4LaZQB3HjaaAufr2Ugu7su88iWxAm0DMYqKvVE8xXoxLBtigN3FWdTJo9hwb2sOZgjYImxJCoL5qnhy8UDWSVPvHtT7JtK76mVUwhCEgIBm/yW8XttDdo5UhAtiRh+e33AB1G5m+2T/7ZqqXALVfM4sq3w/WXD/MaqUjO0ysmQxAMxn+SQkyuT3oLehiHgN6TlZ44+1DIF88axW1dm4g1ZkSbvMCXS88Hs= + +%URL% +http://www.kernel.org/ + +%LICENSE% +GPL2 + +%ARCH% +x86_64 + +%BUILDDATE% +1475256605 + +%PACKAGER% +Tobias Powalowski + +%DEPENDS% +coreutils +linux-firmware +kmod +mkinitcpio>=0.7 + +%OPTDEPENDS% +crda: to set the correct wireless channels of your country + +%MAKEDEPENDS% +xmlto +docbook-xsl +kmod +inetutils +bc +libelf + diff --git a/libpkg/testfiles/linux-4.7.6-1-files b/libpkg/testfiles/linux-4.7.6-1-files new file mode 100644 index 0000000..c3c60d3 --- /dev/null +++ b/libpkg/testfiles/linux-4.7.6-1-files @@ -0,0 +1,4596 @@ +%FILES% +boot/ +boot/vmlinuz-linux +etc/ +etc/mkinitcpio.d/ +etc/mkinitcpio.d/linux.preset +usr/ +usr/lib/ +usr/lib/modules/ +usr/lib/modules/extramodules-4.7-ARCH/ +usr/lib/modules/4.7.6-1-ARCH/ +usr/lib/modules/4.7.6-1-ARCH/build/ +usr/lib/modules/4.7.6-1-ARCH/modules.devname +usr/lib/modules/4.7.6-1-ARCH/modules.builtin.bin +usr/lib/modules/4.7.6-1-ARCH/modules.symbols.bin +usr/lib/modules/4.7.6-1-ARCH/modules.symbols +usr/lib/modules/4.7.6-1-ARCH/modules.softdep +usr/lib/modules/4.7.6-1-ARCH/modules.alias.bin +usr/lib/modules/4.7.6-1-ARCH/modules.alias +usr/lib/modules/4.7.6-1-ARCH/modules.dep.bin +usr/lib/modules/4.7.6-1-ARCH/modules.dep +usr/lib/modules/4.7.6-1-ARCH/extramodules +usr/lib/modules/4.7.6-1-ARCH/modules.builtin +usr/lib/modules/4.7.6-1-ARCH/modules.order +usr/lib/modules/4.7.6-1-ARCH/kernel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/virt/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/ +usr/lib/modules/4.7.6-1-ARCH/kernel/security/ +usr/lib/modules/4.7.6-1-ARCH/kernel/mm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/ +usr/lib/modules/4.7.6-1-ARCH/kernel/kernel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/pci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/oprofile/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kvm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kernel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/events/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/twofish-avx-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/twofish-x86_64-3way.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/sha256-ssse3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/twofish-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/sha512-ssse3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/sha1-ssse3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/serpent-sse2-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/sha-mb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/serpent-avx2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/glue_helper.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/serpent-avx-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/crct10dif-pclmul.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/des3_ede-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/salsa20-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/poly1305-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/crc32c-intel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/ghash-clmulni-intel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/chacha20-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/crc32-pclmul.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/cast6-avx-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/cast5-avx-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/camellia-aesni-avx2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/aesni-intel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/blowfish-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/camellia-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/aes-x86_64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/crypto/sha-mb/sha1-mb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/events/intel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/events/amd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/events/amd/power.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/events/intel/intel-rapl-perf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/events/intel/intel-cstate.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kernel/msr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kernel/cpuid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kvm/kvm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kvm/kvm-intel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/kvm/kvm-amd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/oprofile/oprofile.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/arch/x86/pci/vmd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/xts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/twofish_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/xor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/wp512.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/xcbc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/vmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/twofish_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/tgr192.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/tea.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/tcrypt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/sha512_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/sha1_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/sha256_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/seqiv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/serpent_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/seed.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/salsa20_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/rmd160.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/rmd256.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/rmd320.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/rsa_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/rmd128.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/poly1305_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/pcrypt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/md4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/lz4hc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/pcbc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/mcryptd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/michael_mic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/lz4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/lrw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/khazad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/keywrap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/fcrypt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/ghash-generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/jitterentropy_rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/hmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/gf128mul.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/gcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/drbg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/echainiv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/deflate.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/des_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cryptd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/ctr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/crypto_user.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/crc32c_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/crc32_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/chacha20poly1305.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/chacha20_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cbc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/ccm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cast_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cast6_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/cast5_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/camellia_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/blowfish_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/blowfish_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/authencesn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/authenc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/arc4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/asymmetric_keys/ +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/algif_skcipher.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/algif_rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/anubis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/ansi_cprng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/algif_aead.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/af_alg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/algif_hash.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/ablk_helper.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/842.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/asymmetric_keys/x509_key_parser.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/asymmetric_keys/public_key.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/asymmetric_keys/pkcs7_message.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/raid6test.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/async_pq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/async_xor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/async_raid6_recov.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/async_tx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/crypto/async_tx/async_memcpy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vhost/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vfio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thunderbolt/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ssb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spmi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/remoteproc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ptp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/powercap/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/power/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/phy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvmem/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/parport/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvme/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvdimm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/macintosh/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iommu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ipack/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/idle/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hv/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/fmc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firewire/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/extcon/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/devfreq/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dca/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dax/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cdrom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bcma/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/auxdisplay/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/sbs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/video.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/sbshc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/custom_method.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/ec_sys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/nfit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/fan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/button.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/battery.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/apei/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/acpi_ipmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/acpi_extlog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/acpi_pad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/ac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/apei/einj.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/acpi/apei/erst-dbg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_vsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_uli.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_via.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_sx4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_sis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_sil.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_sil24.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_svw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_nv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_qstor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_promise.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_mv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_via.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pdc_adma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_inic162x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/sata_dwc_460ex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_triflex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_sch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_serverworks.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_sl82c105.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_sis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_sil680.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_rz1000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_rdc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_radisys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_pdc202xx_old.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_pdc2027x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_piccolo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_pcmcia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_optidma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_opti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_ns87415.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_oldpiix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_netcell.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_ns87410.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_ninja32.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_mpiix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_marvell.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_legacy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_jmicron.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_it821x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_it8213.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_cmd64x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_hpt3x3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_hpt3x2n.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_atiixp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_hpt37x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_hpt366.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_cypress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_efar.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_cmd640.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_atp867x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_amd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_artop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_ali.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/pata_acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/libahci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/libata.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/libahci_platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/ata_piix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/ata_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/ahci_platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/acard-ahci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ata/ahci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/suni.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/zatm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/uPD98402.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/nicstar.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/solos-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/lanai.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/iphase.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/horizon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/idt77252.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/he.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/eni.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/ambassador.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/firestream.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/atmtcp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/atm/fore_200e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/auxdisplay/cfag12864bfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/auxdisplay/ks0108.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/auxdisplay/cfag12864b.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bcma/bcma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/osdblk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/xen-blkfront.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/zram/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/virtio_blk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/xen-blkback/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/sx8.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/skd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/pktcdvd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/rbd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/rsxx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/nbd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/loop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/floppy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/mtip32xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/cryptoloop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/cciss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/brd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/drbd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/DAC960.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/aoe/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/aoe/aoe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/drbd/drbd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/mtip32xx/mtip32xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/rsxx/rsxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/xen-blkback/xen-blkback.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/block/zram/zram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/dtl1_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/hci_uart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/hci_vhci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btwilink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btrtl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btsdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btuart_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btqca.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btmrvl_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btmrvl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/bt3c_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btintel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/bluecard_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/bpa10x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/btbcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/bfusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/bcm203x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/bluetooth/ath3k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cdrom/cdrom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/virtio_console.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tlclk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/raw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/nvram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ppdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/pcmcia/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/mwave/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/lp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hangcheck-timer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/applicom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/agp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/agp/via-agp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/agp/intel-gtt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/agp/sis-agp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/agp/intel-agp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/agp/amd64-agp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/virtio-rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/intel-rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/via-rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/timeriomem-rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/tpm-rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/rng-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/hw_random/amd-rng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ipmi_ssif.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ipmi_watchdog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ipmi_si.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ipmi_poweroff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ipmi_msghandler.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/ipmi/ipmi_devintf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/mwave/mwave.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/pcmcia/cm4000_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/pcmcia/synclink_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/pcmcia/cm4040_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/xen-tpmfront.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_tis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_i2c_nuvoton.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_nsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_infineon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_crb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_i2c_infineon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_i2c_atmel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm_atmel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/char/tpm/tpm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/powernow-k8.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/speedstep-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/pcc-cpufreq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/cpufreq_userspace.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/p4-clockmod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/cpufreq_powersave.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/cpufreq_stats.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/cpufreq_conservative.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/amd_freq_sensitivity.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/cpufreq/acpi-cpufreq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/padlock-sha.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/padlock-aes.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/ccp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/ccp/ccp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/ccp/ccp-crypto.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_common/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_dh895xcc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_dh895xccvf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c62xvf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c3xxx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c62x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c3xxxvf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c3xxxvf/qat_c3xxxvf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c62x/qat_c62x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c3xxx/qat_c3xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_c62xvf/qat_c62xvf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_dh895xccvf/qat_dh895xccvf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_dh895xcc/qat_dh895xcc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/crypto/qat/qat_common/intel_qat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dax/dax.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dca/dca.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/devfreq/governor_userspace.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/devfreq/governor_simpleondemand.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/devfreq/governor_powersave.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/devfreq/governor_performance.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/devfreq/governor_passive.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/idma64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/mic_x100_dma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/ioat/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/dw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/dw/dw_dmac_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/dma/ioat/ioatdma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/x38_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/sb_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i82975x_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i7core_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/ie31200_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i7300_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i5100_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i5400_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i3000_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i3200_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/i5000_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/edac_mce_amd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/edac_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/e752x_edac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/edac/amd64_edac_mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/extcon/extcon-sm5502.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/extcon/extcon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/extcon/extcon-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firewire/firewire-sbp2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firewire/nosy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firewire/firewire-ohci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firewire/firewire-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firewire/firewire-net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/qemu_fw_cfg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/iscsi_ibft.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/edd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/efi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/dmi-sysfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/dell_rbu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/dcdbas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/firmware/efi/capsule-loader.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/fmc/fmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/fmc/fmc-write-eeprom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/fmc/fmc-trivial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/fmc/fmc-fakedev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/fmc/fmc-chardev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpio/gpio-viperboard.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpio/gpio-sch311x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpio/gpio-ich.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpio/gpio-amd8111.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpio/gpio-sch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/vmwgfx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/virtio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/via/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/vgem/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/udl/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/ttm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/sis/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/tdfx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/savage/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/radeon/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/r128/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/qxl/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/nouveau/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/mgag200/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/mga/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/i915/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/i2c/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/gma500/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/drm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/drm_kms_helper.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/cirrus/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/bridge/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/bochs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/ast/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/amd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/amd/amdkfd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/amd/amdgpu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/amd/amdgpu/amdgpu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/amd/amdkfd/amdkfd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/ast/ast.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/bochs/bochs-drm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/bridge/analogix-anx78xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/cirrus/cirrus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/gma500/gma500_gfx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/i2c/tda998x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/i2c/ch7006.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/i2c/sil164.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/i915/i915.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/mga/mga.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/mgag200/mgag200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/nouveau/nouveau.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/qxl/qxl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/r128/r128.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/radeon/radeon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/savage/savage.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/tdfx/tdfx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/sis/sis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/ttm/ttm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/udl/udl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/vgem/vgem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/via/via.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/virtio/virtio-gpu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/gpu/drm/vmwgfx/vmwgfx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/uhid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/wacom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/usbhid/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-zydacron.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/i2c-hid/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-zpff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-waltop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-xinmo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-wiimote.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-uclogic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-twinhan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-topseed.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-tmff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-sunplus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-tivo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-thingm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-steelseries.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-sony.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-speedlink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-sjoy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-sensor-hub.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-samsung.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-saitek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-ryos.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-savu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-pyra.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-kovaplus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-lua.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-koneplus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-konepure.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-kone.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-isku.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-arvo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-rmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-roccat-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-primax.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-pl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-prodikeys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-plantronics.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-picolcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-petalynx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-ortek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-penmount.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-ntrig.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-multitouch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-monterey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-microsoft.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-magicmouse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-logitech.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-logitech-hidpp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-logitech-dj.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-lenovo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-lcpower.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-kye.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-keytouch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-kensington.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-icade.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-hyperv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-holtek-kbd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-holtekff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-holtek-mouse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-gyration.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-gt683r.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-gfrm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-gembird.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-ezkey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-gaff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-emsff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-elo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-cypress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-dr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-elecom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-cp2112.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-cmedia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-corsair.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-chicony.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-betopff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-cherry.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-belkin.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-axff.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-aureal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-asus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-a4tech.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-appleir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/hid-apple.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/i2c-hid/i2c-hid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hid/usbhid/usbhid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hv/hv_vmbus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hv/hv_utils.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hv/hv_balloon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83l785ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83795.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83l786ng.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83793.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83792d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/via-cputemp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83781d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83627hf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83791d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/vt1211.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/vt8231.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/w83627ehf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/via686a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/tmp421.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/tmp401.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/tmp103.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/tmp102.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/thmc50.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/smsc47m192.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/tc74.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/smsc47m1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/smsc47b397.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/smm665.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/sht21.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/shtc1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/sis5595.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/sht15.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/sch5636.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/sch56xx-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/sch5627.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/powr1220.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pc87360.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pc87427.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pcf8591.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ntc_thermistor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/nct7802.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/nct6775.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/nct7904.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/nct6683.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/mcp3021.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/mc13783-adc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max6650.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max6697.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max6642.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max6639.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max31790.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max197.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max31722.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max1619.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max16065.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max1668.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc4261.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/max1111.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc4260.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc4245.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc4222.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc4215.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc4151.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc2945.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ltc2990.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm95245.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm95241.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm95234.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm93.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm92.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm85.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm87.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm90.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm80.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm83.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm78.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm75.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm77.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm70.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm73.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lm63.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/lineage-pem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/k10temp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/k8temp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/jc42.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/it87.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ina209.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ina2xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ibmpex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ibmaem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/i5k_amb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/hwmon-vid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/gpio-fan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/hih6130.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/i5500_temp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/gl520sm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/gl518sm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/g762.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/fam15h_power.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/fschmd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/g760a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/f75375s.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/f71882fg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/f71805f.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/emc6w201.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/emc2103.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/emc1403.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ds620.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ds1621.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/dme1737.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/dell-smm-hwmon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/coretemp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/asus_atk0110.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/atxp1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/amc6821.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7475.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/asc7621.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/asb100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/applesmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7470.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7x10.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7411.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7462.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7310.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adt7410.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ads7871.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ads1015.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ads7828.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adm1026.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adm9240.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adm1031.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adm1025.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adm1029.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adm1021.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adc128d818.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/adcxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ad7418.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ad7414.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/acpi_power_meter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/ad7314.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/abituguru3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/abituguru.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/zl6100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/ucd9200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/ucd9000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/tps40422.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/pmbus_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/pmbus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/max8688.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/max34440.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/max20751.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/ltc3815.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/max16064.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/ltc2978.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/adm1275.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/hwmon/pmbus/lm25066.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/i2c-smbus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/i2c-dev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/i2c-mux.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/i2c-slave-eeprom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/muxes/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/algos/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/algos/i2c-algo-pca.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/algos/i2c-algo-bit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-viapro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-xiic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-viperboard.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-tiny-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-via.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-taos-evm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-sis96x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-sis630.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-sis5595.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-simtec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-parport-light.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-scmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-robotfuzz-osif.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-piix4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-pca-platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-parport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-nforce2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-ocores.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-nforce2-s4985.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-designware-platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-ismt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-diolan-u2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-isch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-i801.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-designware-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-designware-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-amd8111.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-amd756.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-ali1563.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-amd756-s4882.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-ali15x3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/busses/i2c-ali1535.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/muxes/i2c-mux-pca954x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/i2c/muxes/i2c-mux-pca9541.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/idle/i7300_idle.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/trigger/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/orientation/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/industrialio-sw-trigger.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/industrialio-configfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/industrialio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/health/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/gyro/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/buffer/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/accel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/accel/st_accel_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/accel/st_accel_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/accel/kxcjk-1013.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/accel/st_accel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/accel/hid-sensor-accel-3d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/buffer/kfifo_buf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/buffer/industrialio-triggered-buffer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/buffer/industrialio-buffer-cb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/st_sensors/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/hid-sensors/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/hid-sensors/hid-sensor-trigger.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/hid-sensors/hid-sensor-iio-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/st_sensors/st_sensors_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/st_sensors/st_sensors.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/common/st_sensors/st_sensors_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/gyro/st_gyro_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/gyro/st_gyro_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/gyro/hid-sensor-gyro-3d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/gyro/st_gyro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/health/max30100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/opt3001.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/stk3310.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/rpr0521.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/pa12203001.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/hid-sensor-als.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/bh1780.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/bh1750.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/light/acpi-als.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/st_magn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/st_magn_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/st_magn_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/hmc5843_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/hmc5843_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/hmc5843_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/magnetometer/hid-sensor-magn-3d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/orientation/hid-sensor-rotation.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/orientation/hid-sensor-incl-3d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iio/trigger/iio-trig-interrupt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/sw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/rdma_cm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/rdma_ucm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/iw_cm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/ib_uverbs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/ib_umad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/ib_ucm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/ib_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/core/ib_cm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/qib/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/usnic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/ocrdma/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/nes/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/mthca/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/mlx5/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/mlx4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/i40iw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/hfi1/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/cxgb4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/cxgb3/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/cxgb3/iw_cxgb3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/cxgb4/iw_cxgb4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/hfi1/hfi1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/i40iw/i40iw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/mthca/ib_mthca.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/nes/iw_nes.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/ocrdma/ocrdma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/usnic/usnic_verbs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/hw/qib/ib_qib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/srpt/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/srp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/isert/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/iser/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/ipoib/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/ipoib/ib_ipoib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/iser/ib_iser.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/isert/ib_isert.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/srp/ib_srp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/ulp/srpt/ib_srpt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/sw/rdmavt/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/infiniband/sw/rdmavt/rdmavt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/sparse-keymap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mousedev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/rmi4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/matrix-keymap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/input-polldev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joydev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/input-leds.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/ff-memless.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/gameport/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/evdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/gameport/ns558.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/gameport/lightning.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/gameport/gameport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/gameport/fm801-gp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/gameport/emu10k1-gp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/zhenhua.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/xpad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/warrior.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/walkera0701.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/twidjoy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/turbografx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/tmdc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/stinger.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/spaceorb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/sidewinder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/spaceball.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/magellan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/interact.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/joydump.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/guillemot.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/gf2k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/grip_mp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/grip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/iforce/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/adi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/gamecon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/db9.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/cobra.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/analog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/as5011.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/a3d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/joystick/iforce/iforce.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/tca8418_keypad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/tca6416-keypad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/qt1070.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/qt2160.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/mpr121_touchkey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/mcs_touchkey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/gpio_keys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/lm8333.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/matrix_keypad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/adp5588-keys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/atkbd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/keyboard/adp5589-keys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/xen-kbdfront.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/yealink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/uinput.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/soc_button_array.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/tps65218-pwrbutton.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/rotary_encoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/retu-pwrbutton.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/powermate.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/pcspkr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/mma8450.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/pcf8574_keypad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/pcf50633-input.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/mpu3050.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/mc13783-pwrbutton.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/ideapad_slidebar.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/kxtj9.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/e3x0-button.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/keyspan_remote.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/gp2ap002a00f.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/cma3000_d0x_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/cma3000_d0x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/atlas_btns.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/cm109.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/bma150.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/adxl34x-spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/ati_remote2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/apanel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/adxl34x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/adxl34x-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/ad714x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/ad714x-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/misc/ad714x-spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/sermouse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/synaptics_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/vsxxxaa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/synaptics_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/elan_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/psmouse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/cyapatp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/bcm5974.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/mouse/appletouch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/rmi4/rmi_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/rmi4/rmi_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/rmi4/rmi_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/serio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/serport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/serio_raw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/pcips2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/ps2mult.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/parkbd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/i8042.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/libps2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/hyperv-keyboard.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/arc_ps2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/ct82c710.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/serio/altera_ps2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/kbtab.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/wacom_serial4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/hanwang.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/gtco.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/aiptek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/tablet/acecad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/zforce_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/wdt87xx_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/wm97xx-ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/wacom_w8001.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/wacom_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/tsc40.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/usbtouchscreen.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ucb1400_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/tsc200x-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/touchwin.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/tps6507x-ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/tsc2004.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/tsc2007.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ti_am335x_tsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/touchright.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/sur40.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/touchit213.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/mtouch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/st1232.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/pixcir_i2c_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/rohm_bu21023.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/penmount.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/mms114.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/melfas_mip4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/mk712.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/mc13783_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/mcs5000_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/max11801_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ili210x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/inexio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/hampshire.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/gunze.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/goodix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/elo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/fujitsu_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ft6236.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cyttsp_i2c_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/elants_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/egalax_ts_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/eeti_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/edt-ft5x06.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/dynapro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cyttsp_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cyttsp4_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cyttsp_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cyttsp4_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cyttsp4_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/auo-pixcir-ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/cy8ctmg110_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/bu21013_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/atmel_mxt_ts.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ad7879-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/input/touchscreen/ad7879.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ipack/ipack.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ipack/devices/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ipack/carriers/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ipack/carriers/tpci200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ipack/devices/ipoctal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/mISDN/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/i4l/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hysdn/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/divert/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/gigaset/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/capi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/capi/kernelcapi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/capi/capidrv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/capi/capi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/gigaset/ser_gigaset.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/gigaset/usb_gigaset.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/gigaset/bas_gigaset.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/gigaset/gigaset.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/divert/dss1_divert.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/eicon/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/t1pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/c4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/b1pcmcia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/b1pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/b1dma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/b1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/avm/avm_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/eicon/divas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/eicon/diva_mnt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/eicon/divadidd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/eicon/divacapi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/eicon/diva_idi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/netjet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/w6692.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/speedfax.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/mISDNisar.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/hfcsusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/mISDNinfineon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/hfcpci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/mISDNipac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/hfcmulti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hardware/mISDN/avmfritz.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/sedlbauer_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/teles_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/hisax_st5481.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/hisax_isac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/hisax_fcpcipnp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/hisax.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/hfc_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/avma1_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/hfc4s8s_l1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hisax/elsa_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/hysdn/hysdn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/i4l/isdnhdlc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/i4l/isdn_bsdcomp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/i4l/isdn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/mISDN/mISDN_dsp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/mISDN/mISDN_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/isdn/mISDN/l1oip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/iommu/amd_iommu_v2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-tlc591xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-tca6507.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-pca9532.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-pca955x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-ss4200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-pca963x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-mc13783.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lt3593.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lp8860.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lp3944.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lm3642.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lm355x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lm3533.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-lm3530.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-dac124s085.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-clevo-mail.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-blinkm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/leds-bd2802.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/led-class.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/led-class-flash.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/dell-led.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-transient.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-timer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-default-on.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-heartbeat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-oneshot.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-camera.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/leds/trigger/ledtrig-backlight.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/macintosh/mac_hid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/raid456.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/raid10.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/raid1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/raid0.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/multipath.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/linear.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/md-mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/persistent-data/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/faulty.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-verity.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-zero.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-thin-pool.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-switch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-snapshot.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-round-robin.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-raid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-region-hash.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-service-time.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-queue-length.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-multipath.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-mirror.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-log-writes.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-log.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-flakey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-log-userspace.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-cache.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-cache-cleaner.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-delay.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-era.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-crypt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-cache-smq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-bio-prison.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/dm-bufio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/bcache/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/bcache/bcache.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/md/persistent-data/dm-persistent-data.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/platform/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/media.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/mmc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/firewire/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/tveeprom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/siano/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/cypress_firmware.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/saa7146/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/cx2341x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/b2c2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/b2c2/b2c2-flexcop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/saa7146/saa7146.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/saa7146/saa7146_vv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/siano/smsmdtv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/common/siano/smsdvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-core/dvb-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/zl10353.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/zl10039.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ves1820.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/zl10036.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tua6100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ves1x93.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda8261.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ts2020.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda8083.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda826x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda665x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda18271c2dd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda10086.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda1004x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda10071.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda10048.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda10023.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tda10021.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv6110x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/tc90522.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv6110.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv0367.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv090x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv0299.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv0900.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv0297.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stb6100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stv0288.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stb0899.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/stb6000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/sp2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/sp8870.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/sp887x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/si21xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/si2165.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/s5h1420.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/si2168.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/s921.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/s5h1411.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/s5h1409.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/rtl2832.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/rtl2832_sdr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/rtl2830.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/or51211.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/or51132.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/nxt200x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/nxt6000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/mt352.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/m88ds3103.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/mt312.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/m88rs2000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lnbp22.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/mb86a20s.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/mb86a16.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lgs8gxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lnbp21.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lnbh25.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lgdt3305.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lgdt330x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/l64781.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lgdt3306a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/lg2160.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ix2505v.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/isl6423.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/itd1000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ds3000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/isl6421.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/isl6405.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/horus3a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ec100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dvb-pll.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/drxk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/drxd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib8000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dibx000_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/drx39xyj/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib3000mc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib7000p.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib7000m.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib0090.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib3000mb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/dib0070.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cxd2820r.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cxd2841er.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx24123.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx24120.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx24117.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx24116.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx22702.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx24110.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx24113.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/au8522_dig.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/cx22700.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/bcm3510.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/au8522_decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/atbm8830.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/af9013.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/au8522_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/ascot2e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/as102_fe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/af9033.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/a8293.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/dvb-frontends/drx39xyj/drx39xyj.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/firewire/firedtv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/wm8775.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/vpx3220.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/vp27smpx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/wm8739.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/upd64031a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/upd64083.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/uda1342.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tw9906.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tw9903.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tw2804.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tvp5150.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tvaudio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tea6415c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tea6420.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tda7432.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/tda9840.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/sony-btf-mpx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa7185.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa717x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa7127.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa7115.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa7110.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa6752hs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/ov7640.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/saa6588.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/mt9v011.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/msp3400.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/m52790.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/ks0127.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/ir-kbd-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/cs5345.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/cs53l32a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/cs3308.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/cx25840/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/bt866.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/bt819.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/bt856.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/adv7842.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/adv7604.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/adv7511.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/adv7175.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/adv7170.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/i2c/cx25840/cx25840.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/mmc/siano/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/mmc/siano/smssdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/tw686x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/zoran/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/tw68/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/solo6x10/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/smipcie/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7164/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7146/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/pt3/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7134/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/pluto2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/pt1/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/netup_unidvb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ngene/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/meye/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/mantis/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ivtv/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/dt3155/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ddbridge/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/dm1105/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx25821/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx23885/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx18/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cobalt/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/bt8xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/b2c2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/b2c2/b2c2-flexcop-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/bt8xx/dvb-bt8xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/bt8xx/dst_ca.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/bt8xx/bttv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/bt8xx/dst.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/bt8xx/bt878.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cobalt/cobalt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx18/cx18.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx18/cx18-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx23885/cx23885.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx23885/altera-ci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx25821/cx25821.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx25821/cx25821-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx88xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx8802.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx8800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx88-vp3054-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx88-blackbird.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx88-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/cx88/cx88-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/dm1105/dm1105.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ddbridge/ddbridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/dt3155/dt3155.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ivtv/ivtv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ivtv/ivtv-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ivtv/ivtvfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/mantis/mantis_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/mantis/mantis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/mantis/hopper.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/meye/meye.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ngene/ngene.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/netup_unidvb/netup-unidvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/pt1/earth-pt1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/pluto2/pluto2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7134/saa7134-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7134/saa7134.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7134/saa7134-go7007.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7134/saa7134-empress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7134/saa7134-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/pt3/earth-pt3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7146/mxb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7146/hexium_orion.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7146/hexium_gemini.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/saa7164/saa7164.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/smipcie/smipcie.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/budget.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/ttpci-eeprom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/dvb-ttpci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/budget-ci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/budget-patch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/budget-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/ttpci/budget-av.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/solo6x10/solo6x10.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/tw68/tw68.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/zoran/zr36067.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/zoran/zr36050.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/zoran/zr36060.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/zoran/zr36016.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/zoran/videocodec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/pci/tw686x/tw686x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/platform/sh_veu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/platform/m2m-deinterlace.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/wl128x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/tef6862.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/tea575x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/shark2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si4713/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si470x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-tea5764.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/saa7706h.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-wl1273.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-si476x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-shark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-mr800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-raremono.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-maxiradio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-ma901.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/radio-keene.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/dsbr100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si470x/radio-i2c-si470x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si470x/radio-usb-si470x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si4713/radio-usb-si4713.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si4713/si4713.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/si4713/radio-platform-si4713.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/radio/wl128x/fm_drv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/winbond-cir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/streamzap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/rc-loopback.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/redrat3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ttusbir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/rc-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/nuvoton-cir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/mceusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/lirc_dev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ite-cir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-sanyo-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-sharp-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-xmp-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-sony-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-rc5-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-rc6-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-nec-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-mce_kbd-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-jvc-decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-lirc-codec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ir-hix5hd2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/imon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/igorplugusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/iguanair.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/gpio-ir-recv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ene_ir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/fintek-cir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/ati_remote.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-winfast.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-videomate-tv-pvr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-videomate-s350.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-videomate-m1f.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-twinhan1027.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-tt-1500.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-trekstor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-total-media-in-hand.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-terratec-slim.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-tivo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-total-media-in-hand-02.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-tevii-nec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-terratec-slim-2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-technisat-ts35.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-technisat-usb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-su3000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-tbs-nec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-snapstream-firefly.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-streamzap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-reddo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-rc6-mce.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pv951.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-proteus-2309.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-purpletv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-powercolor-real-angel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pixelview.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pixelview-mk12.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pixelview-002t.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pixelview-new.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pinnacle-grey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pinnacle-color.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-pctv-sedna.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-npgtech.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-norwood.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-nebula.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-msi-tvanywhere.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-msi-digivox-iii.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-medion-x10.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-medion-x10-digitainer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-msi-digivox-ii.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-medion-x10-or2x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-manli.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-lme2510.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-lirc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-leadtek-y04g0051.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-kworld-pc150u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-it913x-v2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-kaiomy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-kworld-315u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-iodata-bctv7e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-it913x-v1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-imon-pad.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-imon-mce.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-hauppauge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-gotview7135.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-fusionhdtv-mce.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-flyvideo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-gadmei-rm008z.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-flydvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-eztv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-evga-indtube.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-encore-enltv2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-encore-enltv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-encore-enltv-fm53.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-em-terratec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-dvbsky.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-dib0700-rc5.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-dm1105-nec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-digittrade.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-cinergy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-dib0700-nec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-delock-61959.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-cinergy-1400.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-budget-ci-old.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-behold.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-behold-columbus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avertv-303.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia-rm-ks.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia-a16d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia-dvbt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia-m135a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-avermedia-cardbus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-ati-x10.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-asus-pc39.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-asus-ps3-100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-alink-dtu-m.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-apac-viewcomp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-anysee.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/xc4000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/xc5000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tuner-xc2028.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tuner-types.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tua9001.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tea5767.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tda9887.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tuner-simple.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tea5761.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tda8290.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tda827x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tda18212.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tda18218.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/tda18271.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/si2157.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/qt1010.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/r820t.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mxl5007t.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mt2266.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/qm1d1c0042.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mxl5005s.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mt2063.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mxl301rf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mt20xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mt2131.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mc44s803.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/mt2060.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/msi001.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/max2165.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/m88rs6000t.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/fc2580.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/it913x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/fc0013.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/fc0011.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/fc0012.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/tuners/e4000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/zr364xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/usbtv/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/uvc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/usbvision/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/ttusb-budget/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/ttusb-dec/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/tm6000/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/stk1160/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/stkwebcam/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/siano/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/s2255/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/pwc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/pvrusb2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/msi2500/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/hdpvr/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/hackrf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/go7007/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/em28xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/cx231xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/cpia2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/b2c2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/airspy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/au0828/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/as102/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/as102/dvb-as102.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/au0828/au0828.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/airspy/airspy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/b2c2/b2c2-flexcop-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/cpia2/cpia2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/cx231xx/cx231xx-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/cx231xx/cx231xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/cx231xx/cx231xx-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb_usb_v2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-rtl28xxu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-gl861.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-mxl111sf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-lmedm04.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-ec168.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-dvbsky.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-ce6230.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-az6007.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-au6610.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-af9015.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-anysee.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb-v2/dvb-usb-af9035.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-vp702x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-vp7045.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-umt-010.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-ttusb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-nova-t-usb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-technisat-usb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-pctv452e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-opera.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-m920x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-gp8psk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-friio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dw2102.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dtv5100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dibusb-mc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dtt200u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-digitv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dibusb-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dibusb-mb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-cxusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-dib0700.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-cinergyT2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-af9005.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-az6027.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-a800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/dvb-usb/dvb-usb-af9005-remote.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/em28xx/em28xx-v4l.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/em28xx/em28xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/em28xx/em28xx-rc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/em28xx/em28xx-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/em28xx/em28xx-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/go7007/go7007.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/go7007/go7007-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/go7007/go7007-loader.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/stv06xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_zc3xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/m5602/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_xirlink_cit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_vicam.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_vc032x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_touptek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_tv8532.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_topro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_t613.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sunplus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_stv0680.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sq930x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_stk1135.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sq905c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_stk014.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sq905.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca561.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca508.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca501.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca506.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca1528.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca505.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_spca500.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sonixj.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sn9c20x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sonixb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_pac7311.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_se401.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_sn9c2028.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_pac207.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_pac7302.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_ov534_9.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_ov519.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_ov534.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_nw80x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_mr97310a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_jl2005bcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_main.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_konica.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_mars.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_kinect.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_jeilinj.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_finepix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_etoms.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_dtcs033.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_cpia1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_conex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gspca_benq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gl860/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/gl860/gspca_gl860.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/m5602/gspca_m5602.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/gspca/stv06xx/gspca_stv06xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/hackrf/hackrf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/hdpvr/hdpvr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/msi2500/msi2500.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/pvrusb2/pvrusb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/pwc/pwc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/s2255/s2255drv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/siano/smsusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/stkwebcam/stkwebcam.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/stk1160/stk1160.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/tm6000/tm6000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/tm6000/tm6000-alsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/tm6000/tm6000-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/ttusb-dec/ttusbdecfe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/ttusb-dec/ttusb_dec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/usbvision/usbvision.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/uvc/uvcvideo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/usbtv/usbtv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/usb/zr364xx/zr364xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videodev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-vmalloc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-memops.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-v4l2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf-dvb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-dma-sg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-dma-contig.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf2-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf-vmalloc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf-dma-sg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/videobuf-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/v4l2-mem2mem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/v4l2-dv-timings.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/tuner.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/media/v4l2-core/v4l2-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/host/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/core/mspro_block.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/core/ms_block.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/core/memstick.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/host/tifm_ms.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/host/jmb38x_ms.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/host/rtsx_usb_ms.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/host/rtsx_pci_ms.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/memstick/host/r592.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/mptspi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/mptscsih.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/mptsas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/mptfc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/mptctl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/message/fusion/mptbase.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/wl1273-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/vx855.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/tps65217.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/ucb1400_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/viperboard.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/tps65218.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/tps6507x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/tps65010.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/ti_am335x_tscadc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/rtsx_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/rtsx_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/si476x-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/sm501.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/rt5033.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/rn5t618.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/retu-mfd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/rdc321x-southbridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/pcf50633.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/pcf50633-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/pcf50633-adc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/mc13xxx-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/mc13xxx-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/lpc_sch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/lpc_ich.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/lp3943.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/lm3533-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/intel-lpss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/intel-lpss-acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/lm3533-ctrlbank.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/intel-lpss-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/htc-pasic3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/cros_ec_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/cros_ec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/bcm590xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mfd/arizona-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/tifm_7xx1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/vmw_balloon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/tsl2550.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/vmw_vmci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/tifm_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/phantom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ti-st/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/lkdtm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mei/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/isl29020.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/lis3lv02d/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/isl29003.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/hpilo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ics932s401.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ioc4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/hmc6352.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/fsa9480.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/genwqe/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/enclosure.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ds1682.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/echo/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/eeprom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/bmp085.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/cb710/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/c2port/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/bh1780gli.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/bmp085-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/bh1770glc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/apds990x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ad525x_dpot.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ad525x_dpot-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/apds9802als.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/altera-stapl/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/altera-stapl/altera-stapl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/c2port/core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/c2port/c2port-duramar2150.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/cb710/cb710.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/eeprom/max6875.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/eeprom/eeprom_93cx6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/eeprom/eeprom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/eeprom/at24.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/echo/echo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/genwqe/genwqe_card.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/lis3lv02d/lis3lv02d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/lis3lv02d/lis3lv02d_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mei/mei-me.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mei/mei.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mei/mei-txe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/vop/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/scif/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/host/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/cosm_client/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/cosm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/card/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/bus/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/bus/scif_bus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/bus/vop_bus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/bus/mic_bus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/bus/cosm_bus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/card/mic_card.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/cosm/mic_cosm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/cosm_client/cosm_client.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/host/mic_host.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/scif/scif.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/mic/vop/vop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/ti-st/st_drv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/misc/vmw_vmci/vmw_vmci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/card/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/card/sdio_uart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/card/mmc_block.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/core/mmc_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/via-sdmmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/wbsd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/vub300.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/usdhi6rol0.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/toshsd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/ushc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/tifm_sd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/sdhci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/sdricoh_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/sdhci-pltfm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/sdhci-acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/rtsx_usb_sdmmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/sdhci-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/mmc_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/rtsx_pci_sdmmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/mtk-sd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mmc/host/cb710-mmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/ubi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/redboot.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/ssfdc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/rfd_ftl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nftl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/mtdswap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/mtdoops.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/mtdblock_ro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/mtd_blkdevs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/mtdblock.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/mtd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/maps/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/inftl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/ftl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/lpddr/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/cmdlinepart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/ar7part.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/map_rom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/map_ram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/map_absent.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/jedec_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/chipreg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/gen_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/cfi_util.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/cfi_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/cfi_cmdset_0020.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/cfi_cmdset_0002.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/chips/cfi_cmdset_0001.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/slram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/mtdram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/pmc551.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/phram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/docg3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/devices/block2mtd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/lpddr/qinfo_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/lpddr/lpddr_cmds.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/maps/scb2_flash.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/maps/plat-ram.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/maps/pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/maps/pcmciamtd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/maps/map_funcs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/r852.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/sm_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/nandsim.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/nand_ids.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/nand.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/nand_ecc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/docg4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/denali_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/diskonchip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/denali.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/nand/cafe_nand.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_oobtest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_torturetest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_subpagetest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_stresstest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_speedtest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_readtest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_pagetest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_nandecctest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/tests/mtd_nandbiterrs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/mtd/ubi/ubi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/xen-netfront.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/xen-netback/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wimax/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/vrf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/vxlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/virtio_net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/veth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/vmxnet3/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/tun.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/sungem_phy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/sb1000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/slip/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/rionet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/plip/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/nlmon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/mdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/netconsole.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/mii.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/macvtap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/macvlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/macsec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ifb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ipvlan/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hyperv/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ieee802154/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/gtp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/geneve.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/fjes/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/dummy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/eql.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/dsa/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/bonding/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/caif/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/caif/cfspi_slave.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/caif/caif_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/caif/caif_virtio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/caif/caif_hsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/bonding/bonding.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/vcan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/slcan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/softing/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/can-dev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/ifi_canfd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/m_can/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/cc770/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/c_can/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/c_can/c_can_platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/c_can/c_can_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/c_can/c_can.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/cc770/cc770_platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/cc770/cc770.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/m_can/m_can.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/ifi_canfd/ifi_canfd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/kvaser_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/peak_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/sja1000_platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/sja1000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/plx_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/sja1000/ems_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/softing/softing.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/kvaser_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/usb_8dev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/gs_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/ems_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/esd_usb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/peak_usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/can/usb/peak_usb/peak_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/dsa/mv88e6xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/dsa/mv88e6060.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/dsa/bcm_sf2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/xircom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/wiznet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/via/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/ti/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/tehuti/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/silan/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sun/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/stmicro/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/smsc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sis/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sfc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/samsung/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/rocker/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/realtek/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/rdc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/netronome/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/packetengines/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/nvidia/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/neterion/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/natsemi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/myricom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/microchip/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/micrel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/jme.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/marvell/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/fealnx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/fujitsu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/ethoc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/hp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/ec_bhf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/emulex/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dlink/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/cisco/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/cadence/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/brocade/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/aurora/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/amd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/altera/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/alteon/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/agere/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/adaptec/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/8390/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/3com/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/3com/typhoon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/3com/3c59x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/3com/3c589_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/3com/3c574_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/8390/axnet_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/8390/pcnet_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/8390/ne2k-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/8390/8390.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/adaptec/starfire.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/agere/et131x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/alteon/acenic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/altera/altera_tse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/amd/pcnet32.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/amd/nmclan_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/amd/amd8111e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atlx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atl1c/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atl1e/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/alx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/alx/alx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atl1e/atl1e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atl1c/atl1c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atlx/atl2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/atheros/atlx/atl1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/tg3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/cnic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/genet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/bnx2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/b44.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/bnx2x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/bnxt/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/bnxt/bnxt_en.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/broadcom/genet/genet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/aurora/nb8800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/brocade/bna/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/brocade/bna/bna.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/cadence/macb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb4vf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb3/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb/cxgb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb3/cxgb3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/chelsio/cxgb4/cxgb4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/cisco/enic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/cisco/enic/enic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/winbond-840.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/xircom_cb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/uli526x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/de4x5.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/tulip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/dmfe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dec/tulip/de2104x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dlink/sundance.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/dlink/dl2k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/emulex/benet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/emulex/benet/be2net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ixgbevf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ixgbe/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ixgb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/igbvf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/igb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/i40evf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/i40e/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/e1000e/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/fm10k/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/e100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/e1000/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/e1000/e1000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/fm10k/fm10k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/i40e/i40e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/i40evf/i40evf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/igb/igb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/igbvf/igbvf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ixgb/ixgb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ixgbe/ixgbe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/hp/hp100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/fujitsu/fmvj18x_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/marvell/sky2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/marvell/skge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/marvell/mvmdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlxsw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlx5/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlx4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlx5/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_switchx2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/micrel/ksz884x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/micrel/ks8851_mll.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/micrel/ks8851.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/micrel/ks8842.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/microchip/encx24j600.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/microchip/encx24j600-regmap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/microchip/enc28j60.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/myricom/myri10ge/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/myricom/myri10ge/myri10ge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/natsemi/ns83820.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/natsemi/natsemi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/neterion/s2io.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/neterion/vxge/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/neterion/vxge/vxge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/nvidia/forcedeth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/packetengines/yellowfin.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/packetengines/hamachi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/netronome/nfp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/netronome/nfp/nfp_netvf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qede/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qla3xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qlcnic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qlge/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qed/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/netxen/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/netxen/netxen_nic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qed/qed.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qlge/qlge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qlcnic/qlcnic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/qlogic/qede/qede.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/rdc/r6040.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/realtek/r8169.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/realtek/atp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/realtek/8139too.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/realtek/8139cp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/rocker/rocker.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/samsung/sxgbe/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/samsung/sxgbe/samsung-sxgbe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sfc/sfc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sis/sis900.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sis/sis190.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/smsc/smsc9420.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/smsc/smsc911x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/smsc/smc91c92_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/smsc/epic100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/stmicro/stmmac/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sun/sungem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sun/niu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sun/sunhme.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/sun/cassini.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/silan/sc92031.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/tehuti/tehuti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/ti/tlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/ti/cpsw_ale.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/via/via-velocity.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/via/via-rhine.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/wiznet/w5300.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/wiznet/w5100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/wiznet/w5100-spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ethernet/xircom/xirc2ps_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/fjes/fjes.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/yam.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/bpqether.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/mkiss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/hdlcdrv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/baycom_ser_fdx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/baycom_ser_hdx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/baycom_par.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hamradio/6pack.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ieee802154/fakelb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ieee802154/atusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ieee802154/at86rf230.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/hyperv/hv_netvsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ipvlan/ipvlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/w83977af_ir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/toim3232-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/vlsi_ir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/via-ircc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/tekram-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/stir4200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/smsc-ircc2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/sir-dev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/old_belkin-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/nsc-ircc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/mcs7780.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/mcp2120-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/ma600-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/ksdazzle-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/litelink-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/ks959-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/kingsun-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/irtty-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/irda-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/girbil-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/ali-ircc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/esi-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/actisys-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/irda/act200l-sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/vitesse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/teranetics.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/realtek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/ste10Xp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/smsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/national.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/qsemi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/microchip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/mdio-octeon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/micrel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/mdio-cavium.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/mdio-bcm-unimac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/mdio-bitbang.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/marvell.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/icplus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/lxt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/libphy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/fixed_phy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/et1011c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/dp83867.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/dp83848.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/dp83640.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/davicom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/bcm7xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/bcm87xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/cicada.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/broadcom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/bcm-phy-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/aquantia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/at803x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/phy/amd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/plip/plip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/pptp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/pppoe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/pppox.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/ppp_synctty.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/ppp_mppe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/ppp_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/ppp_async.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/ppp_deflate.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/ppp/bsd_comp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/slip/slhc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/slip/slip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/team_mode_random.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/team_mode_roundrobin.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/team_mode_loadbalance.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/team_mode_broadcast.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/team_mode_activebackup.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/team/team.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/zaurus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/usbnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/sr9800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/sr9700.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/smsc95xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/qmi_wwan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/sierra_net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/smsc75xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/rndis_host.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/rtl8150.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/r8152.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/plusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/net1080.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/pegasus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/lg-vl600.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/mcs7830.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/lan78xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/kaweth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/kalmia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/ipheth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/hso.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/int51x1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/gl620a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/huawei_cdc_ncm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/dm9601.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cx82310_eth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/ch9200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cdc_subset.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cdc_ncm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cdc_mbim.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cdc_ether.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cdc_eem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/cdc-phonet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/catc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/ax88179_178a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/usb/asix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/vmxnet3/vmxnet3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wimax/i2400m/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wimax/i2400m/i2400m.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wimax/i2400m/i2400m-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/zydas/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/wl3501_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/st/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/rndis_wlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/rsi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ray_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/mediatek/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/cisco/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/atmel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/admtek/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/admtek/adm8211.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/wil6210/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/wcn36xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/carl9170/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath9k/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath6kl/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath5k/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath10k/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ar5523/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ar5523/ar5523.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath10k/ath10k_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath5k/ath5k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath6kl/ath6kl_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath6kl/ath6kl_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath6kl/ath6kl_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath9k/ath9k_hw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath9k/ath9k_htc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath9k/ath9k_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/ath9k/ath9k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/carl9170/carl9170.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/wcn36xx/wcn36xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ath/wil6210/wil6210.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/atmel/atmel_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/atmel/atmel_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/atmel/atmel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/atmel/at76c50x-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/b43legacy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/b43/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/b43/b43.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/b43legacy/b43legacy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/brcmutil/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcmsmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/cisco/airo_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/cisco/airo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlwifi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlegacy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/ipw2x00/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/ipw2x00/ipw2200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/ipw2x00/libipw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/ipw2x00/ipw2100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlegacy/iwlegacy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlegacy/iwl3945.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlegacy/iwl4965.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlwifi/iwlwifi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlwifi/mvm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlwifi/dvm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlwifi/dvm/iwldvm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intel/iwlwifi/mvm/iwlmvm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/prism54/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/p54/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/hostap/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/hostap/hostap_plx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/hostap/hostap_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/hostap/hostap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/hostap/hostap_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/spectrum_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco_tmd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco_plx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco_nortel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/orinoco/orinoco.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/p54/p54usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/p54/p54common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/p54/p54spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/p54/p54pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/intersil/prism54/prism54.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/mwl8k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/mwifiex/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas_tf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas/usb8xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas/libertas_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas/libertas_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas/libertas_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas/libertas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas_tf/libertas_tf_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/libertas_tf/libertas_tf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/mwifiex/mwifiex_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/mwifiex/mwifiex_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/mwifiex/mwifiex_pcie.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/marvell/mwifiex/mwifiex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt73usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2800pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt61pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2x00usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2x00lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2800usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2x00pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2800lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2800mmio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2400pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2500pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ralink/rt2x00/rt2500usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/mediatek/mt7601u/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/mediatek/mt7601u/mt7601u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl8xxxu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl818x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl818x/rtl8187/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl818x/rtl8180/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl818x/rtl8180/rtl818x_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl818x/rtl8187/rtl8187.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtlwifi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8723com/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8723be/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192se/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192de/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192c/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/btcoexist/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/btcoexist/btcoexist.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/rtl8188ee.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rtl8192ce.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192c/rtl8192c-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rtl8192cu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rtl8192de.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/rtl8192ee.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rtl8192se.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/rtl8723ae.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8723be/rtl8723be.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/rtl8821ae.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/realtek/rtlwifi/rtl8723com/rtl8723-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/rsi/rsi_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/rsi/rsi_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/rsi/rsi_91x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/st/cw1200/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/st/cw1200/cw1200_wlan_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/st/cw1200/cw1200_wlan_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/st/cw1200/cw1200_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wlcore/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl18xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl12xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl1251/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl1251/wl1251_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl1251/wl1251.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl12xx/wl12xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wl18xx/wl18xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wlcore/wlcore_sdio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/ti/wlcore/wlcore.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/zydas/zd1201.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/zydas/zd1211rw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/wireless/zydas/zd1211rw/zd1211rw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/net/xen-netback/xen-netback.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/trf7970a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/port100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/st-nci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/nfcwilink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/nfcsim.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn544/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn533/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/mei_phy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/nfcmrvl/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/microread/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/fdp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/fdp/fdp_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/fdp/fdp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/microread/microread_mei.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/microread/microread.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/nfcmrvl/nfcmrvl_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/nfcmrvl/nfcmrvl_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/nfcmrvl/nfcmrvl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn533/pn533.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn533/pn533_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn533/pn533_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn544/pn544_mei.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/pn544/pn544.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/st-nci/st-nci_i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nfc/st-nci/st-nci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvdimm/nd_blk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvdimm/nd_btt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvdimm/nd_pmem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvme/host/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvme/host/nvme-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvme/host/nvme.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/parport/parport_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/parport/parport_pc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/parport/parport_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/parport/parport_ax88796.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/parport/parport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/nvmem/nvmem_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/xen-pcifront.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/pci-stub.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/hotplug/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/host/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/host/pci-hyperv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/hotplug/cpcihp_zt5550.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/hotplug/acpiphp_ibm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/hotplug/shpchp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pci/hotplug/cpcihp_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/yenta_socket.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/pd6729.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/i82092.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/pcmcia_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/pcmcia_rsrc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pcmcia/pcmcia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/phy/phy-bcm-kona-usb2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/chrome/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/chrome/cros_kbd_led_backlight.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/chrome/cros_ec_lpc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/chrome/cros_ec_devs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/chrome/chromeos_pstore.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/chrome/chromeos_laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/toshiba_haps.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/toshiba_bluetooth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/toshiba_acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/toshiba-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/topstar-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/thinkpad_acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/panasonic-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/surfacepro3_button.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/sony-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/samsung-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/samsung-q10.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/pvpanic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/msi-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/msi-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/mxm-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_telemetry_pltdrv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_telemetry_debugfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_punit_ipc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_telemetry_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_pmc_ipc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_oaktrail.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_menlow.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel_ips.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel-smartconnect.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel-hid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/intel-rst.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/ideapad-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/ibm_rtl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/hp_accel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/hp-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/fujitsu-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/fujitsu-tablet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/hp-wireless.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/hdaps.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/eeepc-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/eeepc-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/dell-smo8800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/dell-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/dell-wmi-aio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/dell-smbios.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/dell-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/dell-rbtn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/compal-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/classmate-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/asus-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/asus-wireless.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/asus-nb-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/asus-laptop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/apple-gmux.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/amilo-rfkill.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/alienware-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/acerhdf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/platform/x86/acer-wmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/power/test_power.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/power/bq24735-charger.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/power/pda_power.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/powercap/intel_rapl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/pps_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/clients/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/clients/pps-ldisc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/clients/pps-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/clients/pps_parport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/pps/clients/pps-ktimer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ptp/ptp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/rapidio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/rio-scan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/switches/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/devices/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/devices/tsi721_mport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/devices/rio_mport_cdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/switches/tsi57x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/switches/tsi568.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/switches/idt_gen2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rapidio/switches/idtcps.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/remoteproc/ste_modem_rproc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/remoteproc/remoteproc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-x1205.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-rx8581.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-v3020.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-s35390a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-test.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-stk17ta8.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-rx8025.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-rx8010.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-rv3029c2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-rs5c372.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-rp5c01.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-pcf8583.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-pcf8563.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-pcf8523.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-pcf85063.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-msm6242.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-pcf50633.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-pcf2127.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-max6900.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-m48t86.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-mc13xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-m48t59.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-m41t80.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-m48t35.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-isl1208.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-isl12057.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds3232.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-fm3130.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-isl12022.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-em3027.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1685.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1742.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds2404.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1672.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1307.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1374.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1553.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1511.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-bq4802.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-abx80x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-bq32k.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/rtc/rtc-ds1286.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/vmw_pvscsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/wd719x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/virtio_scsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/stex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/st.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ufs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/sym53c8xx_2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/sr_mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/snic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/sg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/sd_mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/scsi_transport_srp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ses.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/scsi_transport_spi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qla4xxx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/scsi_transport_sas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/scsi_transport_iscsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/scsi_transport_fc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/scsi_mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qlogicfas408.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/raid_class.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qla1280.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qla2xxx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ppa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pmcraid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pm8001/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pcmcia/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/osst.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/mvumi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/osd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/mpt3sas/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/mvsas/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/megaraid/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/megaraid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/lpfc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/libiscsi_tcp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/libiscsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/libsas/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/iscsi_boot_sysfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/iscsi_tcp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ips.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/libfc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/isci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ipr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/initio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/imm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/hv_storvsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/hptiop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/hpsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/gdth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/fnic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/fdomain.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/esp_scsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/dpt_i2o.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/fcoe/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/eata.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/esas2r/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/dmx3191d.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/device_handler/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/dc395x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/cxgbi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/csiostor/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/bnx2i/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/bnx2fc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/bfa/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/atp870u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/be2iscsi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/advansys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/arcmsr/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/am53c974.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aic94xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aic7xxx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/BusLogic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/3w-xxxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/a100u2w.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/3w-sas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aacraid/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/3w-9xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aacraid/aacraid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aic7xxx/aic7xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aic7xxx/aic79xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/aic94xx/aic94xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/arcmsr/arcmsr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/be2iscsi/be2iscsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/bfa/bfa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/bnx2fc/bnx2fc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/bnx2i/bnx2i.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/csiostor/csiostor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/cxgbi/libcxgbi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/cxgbi/cxgb4i/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/cxgbi/cxgb3i/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/device_handler/scsi_dh_emc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/device_handler/scsi_dh_rdac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/device_handler/scsi_dh_alua.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/esas2r/esas2r.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/fcoe/fcoe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/fcoe/libfcoe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/fnic/fnic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/isci/isci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/libfc/libfc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/libsas/libsas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/lpfc/lpfc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/megaraid/megaraid_sas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/megaraid/megaraid_mm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/megaraid/megaraid_mbox.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/mvsas/mvsas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/mpt3sas/mpt3sas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/osd/osd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/osd/libosd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pcmcia/qlogic_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pcmcia/sym53c500_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pcmcia/aha152x_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pcmcia/fdomain_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/pm8001/pm80xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qla2xxx/qla2xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/qla4xxx/qla4xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/snic/snic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/sym53c8xx_2/sym53c8xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ufs/ufshcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ufs/ufshcd-pltfrm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/scsi/ufs/ufshcd-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spidev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-xilinx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-zynqmp-gqspi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-xcomm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-tle62x0.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-sc18is602.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-rockchip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-pxa2xx-platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-oc-tiny.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-pxa2xx-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-lm70llp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-dw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-dw-mmio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-dw-midpci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-butterfly.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-bitbang.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spi/spi-altera.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/spmi/spmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/ssb/ssb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/wlan-ng/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/vt6656/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/xgifb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/vt6655/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/slicoss/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rts5208/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8723au/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8712/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192u/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8188eu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/gs_fpgaboot/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/gdm724x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/fwserial/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/dgnc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/kcomedilib/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/comedi_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/comedi_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/comedi_pcmcia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/comedi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/usbduxsigma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/serial2002.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/s626.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/vmk80xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/usbduxfast.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/usbdux.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/quatech_daqp_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/rtd520.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_usb6501.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_tiocmd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_tio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_pcimio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_pcidio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_mio_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_labpc_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_labpc_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_daq_700.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_labpc_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_670x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_daq_dio24.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_660x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_65xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ni_6527.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/mite.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/mf6x4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/me_daq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/me4000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/ke_counter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/jr3_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/icp_multi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/gsc_hpdi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/dt9812.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/dyna_pci10xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/dt3000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/das08_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/das08.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/das08_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/contec_pci_dio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/daqboard2000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/comedi_test.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/comedi_bond.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/comedi_parport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/comedi_8255.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/comedi_8254.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/cb_pcimdda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/cb_pcimdas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/cb_pcidas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/cb_pcidda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/cb_pcidas64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/cb_das16_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_pci230.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_pci263.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_pci224.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_pci236.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_dio200_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_pc236_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/amplc_dio200_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adv_pci1760.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adv_pci_dio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adv_pci1720.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adv_pci1724.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adv_pci1723.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adv_pci1710.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adl_pci9118.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adl_pci9111.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adl_pci7x3x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adl_pci8164.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_3xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/adl_pci6208.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_watchdog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_3501.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_3120.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_2032.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_2200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_16xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_1564.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_1516.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_1032.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/addi_apci_1500.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/8255_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/drivers/8255.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/comedi/kcomedilib/kcomedilib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/dgnc/dgnc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/fwserial/firewire-serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/gdm724x/gdmulte.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/gdm724x/gdmtty.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/gs_fpgaboot/gs_fpga.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/selftest/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/lnet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/libcfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/klnds/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/klnds/socklnd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/klnds/o2iblnd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/klnds/o2iblnd/ko2iblnd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/klnds/socklnd/ksocklnd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/libcfs/libcfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/lnet/lnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lnet/selftest/lnet_selftest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/ptlrpc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/osc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/obdecho/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/obdclass/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/mgc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/mdc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/lov/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/lmv/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/llite/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/fld/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/fid/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/fid/fid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/fld/fld.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/llite/lustre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/llite/llite_lloop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/lmv/lmv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/lov/lov.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/mdc/mdc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/mgc/mgc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/obdclass/obdclass.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/obdecho/obdecho.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/osc/osc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/lustre/lustre/ptlrpc/ptlrpc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/mn88472/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/cxd2099/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/bcm2048/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/bcm2048/radio-bcm2048.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/cxd2099/cxd2099.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_zilog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_sir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_sasem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_parallel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_imon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/lirc/lirc_bt829.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/media/mn88472/mn88472.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8188eu/r8188eu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/rtllib_crypt_wep.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/rtllib_crypt_tkip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/rtllib_crypt_ccmp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/rtllib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/rtl8192e/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192e/rtl8192e/r8192e_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8192u/r8192u_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8712/r8712u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rtl8723au/r8723au.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/rts5208/rts5208.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/slicoss/slicoss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_txprt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_spkout.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_soft.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_dummy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_ltlk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_dectlk.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_decext.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_bns.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_audptr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_apollo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup_acntsa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/speakup/speakup.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/vt6655/vt6655_stage.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/xgifb/xgifb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/vt6656/vt6656_stage.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/staging/wlan-ng/prism2_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/target_core_user.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/target_core_pscsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/tcm_fc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/target_core_mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/target_core_iblock.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/target_core_file.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/sbp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/loopback/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/iscsi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/iscsi/iscsi_target_mod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/iscsi/cxgbit/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/iscsi/cxgbit/cxgbit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/loopback/tcm_loop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/sbp/sbp_target.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/target/tcm_fc/tcm_fc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/x86_pkg_temp_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/thermal-generic-adc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/intel_soc_dts_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/intel_soc_dts_iosf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/intel_pch_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/intel_powerclamp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/processor_thermal_device.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/int340x_thermal_zone.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/int3406_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/int3403_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/int3402_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/int3400_thermal.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thermal/int340x_thermal/acpi_thermal_rel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/thunderbolt/thunderbolt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/synclink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/synclinkmp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/rocket.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/n_tracesink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/nozomi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/n_tracerouter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/n_hdlc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/n_r3964.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/cyclades.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/n_gsm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/ipwireless/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/ipwireless/ipwireless.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/sc16is7xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/sccnxp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/rp2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/fsl_lpuart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/jsm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/altera_uart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/arc_uart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/altera_jtaguart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/8250/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/8250/serial_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/8250/8250_moxa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/8250/8250_dw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/tty/serial/jsm/jsm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_sercos3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_pdrv_genirq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_pruss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_pci_generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_cif.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_netx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio_aec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uio/uio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/wusbcore/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/usbip/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/phy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/musb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/mon/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/image/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/common/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/class/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/c67x00/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/atm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/atm/xusbatm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/atm/usbatm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/atm/ueagle-atm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/atm/cxacru.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/atm/speedtch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/c67x00/c67x00.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/class/usbtmc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/class/usblp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/class/cdc-wdm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/class/cdc-acm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/common/usb-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/core/usbcore.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/libcomposite.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_uvc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_uac2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_tcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_rndis.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_printer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_obex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_ncm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_mass_storage.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_midi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_hid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_fs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_ecm_subset.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_acm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/usb_f_ecm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/u_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/function/u_ether.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/tcm_usb_gadget.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_webcam.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/gadgetfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_printer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_midi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_ncm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_mass_storage.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_hid.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_ffs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_ether.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_dbgp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/legacy/g_audio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/r8a66597-udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/udc-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/pxa27x_udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/pch_udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/net2280.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/net2272.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/mv_udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/gr_udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/mv_u3d_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/m66592-udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/goku_udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/dummy_hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/amd5536udc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/bdc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/bdc/bdc_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/gadget/udc/bdc/bdc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/xhci-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/xhci-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/sl811_cs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/u132-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/uhci-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/sl811-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/r8a66597-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/whci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/hwa-hc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/isp1362-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/oxu210hp-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/max3421-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/ohci-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/ohci-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/isp116x-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/ehci-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/ehci-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/host/whci/whci-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/image/microtek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/image/mdc800.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/yurex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/uss720.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/usbtest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/usbsevseg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/usblcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/usbled.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/usb3503.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/ucsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/trancevibrator.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/rio500.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/lvstest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/sisusbvga/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/legousbtower.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/ldusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/ftdi-elan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/isight_firmware.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/emi62.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/iowarrior.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/idmouse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/ezusb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/emi26.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/cytherm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/adutux.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/cypress_cy7c63.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/chaoskey.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/appledisplay.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/misc/sisusbvga/sisusbvga.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/mon/usbmon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/musb/musb_hdrc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/phy/phy-isp1301.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/phy/phy-tahvo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/phy/phy-generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/xsens_mt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/wishbone-serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/whiteheat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/visor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/usbserial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/usb-serial-simple.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/usb_wwan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/symbolserial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ti_usb_3410_5052.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ssu100.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/spcp8x5.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/sierra.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/safe_serial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/quatech2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/qcserial.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/qcaux.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/pl2303.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/opticon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/omninet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/option.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/oti6858.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/navman.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/mxuport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/kobil_sct.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/mos7720.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/mos7840.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/mct_u232.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/metro-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/kl5kusb105.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/keyspan_pda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/keyspan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ipw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/iuu_phoenix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ir-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ipaq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/io_ti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/io_edgeport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/f81232.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/garmin_gps.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ftdi_sio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/empeg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/digi_acceleport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/aircable.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/cypress_m8.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/cyberjack.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/cp210x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ch341.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/belkin_sa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/serial/ark3116.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/usb-storage.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-usbat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-jumpshot.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-sddr09.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-sddr55.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-karma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-realtek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-onetouch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-isd200.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-datafab.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-freecom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-eneub6250.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-cypress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/ums-alauda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/storage/uas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/usbip/usbip-host.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/usbip/vhci-hcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/usbip/usbip-vudc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/usbip/usbip-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/wusbcore/wusbcore.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/wusbcore/wusb-wa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/usb/wusbcore/wusb-cbaf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/whci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/whc-rc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/uwb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/umc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/hwa-rc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/i1480/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/i1480/i1480-est.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/i1480/dfu/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/uwb/i1480/dfu/i1480-dfu-usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vfio/vfio_virqfd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vfio/vfio_iommu_type1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vfio/vfio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vfio/pci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vfio/pci/vfio-pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vhost/vringh.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vhost/vhost_scsi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vhost/vhost_net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/vhost/vhost.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/vgastate.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/backlight/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/backlight/lm3533_bl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/backlight/platform_lcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/backlight/lcd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/backlight/pcf50633-backlight.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/backlight/apple_bl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/xen-fbfront.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/vga16fb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/sstfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/via/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/hyperv_fb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/vfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/uvesafb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/udlfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/ocfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/core/sysimgblt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/core/sysfillrect.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/core/syscopyarea.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/core/fb_sys_fops.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/video/fbdev/via/viafb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/virtio_ring.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/virtio_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/virtio_mmio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/virtio_balloon.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/virtio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/virtio/virtio_input.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/ziirave_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/wdt_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/xen_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/wafer5823wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/w83977f_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/w83877f_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/w83627hf_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/via_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/sp5100_tco.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/softdog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/smsc37b787_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/sc1200wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/sch311x_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/sbc_epx_c3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/sbc_fitpc2_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/sbc60xxwdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/retu_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/pcwd_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/of_xilinx_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/pcwd_pci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/pc87413_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/nv_tco.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/ni903x_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/mena21_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/it87_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/mei_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/ie6xx_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/it8712f_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/ibmasr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/ib700wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/machzwd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/iTCO_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/i6300esb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/iTCO_vendor_support.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/hpwdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/dw_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/eurotechwdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/cpu5wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/f71808e_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/alim7101_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/advantechwdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/alim1535_wdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/watchdog/acquirewdt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-scsiback.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-privcmd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xenfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-gntdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-gntalloc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-pciback/ +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-evtchn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-acpi-processor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/tmem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xen-pciback/xen-pciback.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/drivers/xen/xenfs/xenfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/udf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/xfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ufs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/squashfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ubifs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/romfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/reiserfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/quota/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/overlayfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/orangefs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/omfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ntfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nilfs2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfsd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs_common/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ncpfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/mbcache.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/minix/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/logfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/lockd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/jfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/jffs2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/jbd2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/isofs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/hfsplus/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/gfs2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/hfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fuse/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fscache/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fat/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/f2fs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ext4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/crypto/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/exofs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ecryptfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/dlm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/cramfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/coda/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/cifs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ceph/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/cachefiles/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/befs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/btrfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/afs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/affs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/9p/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/9p/9p.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/affs/affs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/afs/kafs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/btrfs/btrfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/befs/befs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/cachefiles/cachefiles.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ceph/ceph.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/cifs/cifs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/coda/coda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/cramfs/cramfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/dlm/dlm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ecryptfs/ecryptfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/exofs/libore.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/exofs/exofs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/crypto/fscrypto.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ext4/ext4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/f2fs/f2fs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fat/fat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fat/vfat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fat/msdos.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fscache/fscache.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fuse/cuse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/fuse/fuse.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/hfs/hfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/gfs2/gfs2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/hfsplus/hfsplus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/isofs/isofs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/jbd2/jbd2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/jffs2/jffs2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/jfs/jfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/lockd/lockd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/logfs/logfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/minix/minix.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ncpfs/ncpfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/nfsv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/nfsv2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/nfsv3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/nfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/objlayout/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/flexfilelayout/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/filelayout/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/blocklayout/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/blocklayout/blocklayoutdriver.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/filelayout/nfs_layout_nfsv41_files.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/flexfilelayout/nfs_layout_flexfiles.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs/objlayout/objlayoutdriver.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs_common/nfs_acl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfs_common/grace.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nfsd/nfsd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nilfs2/nilfs2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_koi8-u.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_utf8.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_koi8-r.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_koi8-ru.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-7.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-9.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-5.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-15.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-14.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_euc-jp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_iso8859-13.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp950.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp936.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp949.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp932.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp874.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp869.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp866.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp865.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp864.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp863.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp862.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp860.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp861.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp857.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp852.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp855.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp850.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp775.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp437.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp737.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp1255.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp1250.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_cp1251.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/nls_ascii.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-turkish.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-romanian.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-roman.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-inuit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-iceland.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-greek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-gaelic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-cyrillic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-croatian.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-celtic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/nls/mac-centeuro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ntfs/ntfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/ocfs2_stack_o2cb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/ocfs2_stack_user.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/ocfs2_stackglue.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/ocfs2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/dlmfs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/dlm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/cluster/ +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/cluster/ocfs2_nodemanager.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/dlm/ocfs2_dlm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ocfs2/dlmfs/ocfs2_dlmfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/omfs/omfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/orangefs/orangefs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/overlayfs/overlay.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/quota/quota_v2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/quota/quota_v1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/quota/quota_tree.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/reiserfs/reiserfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/romfs/romfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ubifs/ubifs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/squashfs/squashfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/ufs/ufs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/xfs/xfs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/fs/udf/udf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/kernel/trace/ +usr/lib/modules/4.7.6-1-ARCH/kernel/kernel/sched/ +usr/lib/modules/4.7.6-1-ARCH/kernel/kernel/sched/cpufreq_schedutil.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/kernel/trace/ring_buffer_benchmark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/ts_fsm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/ts_kmp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/ts_bm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/test-kstrtox.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/percpu_test.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/oid_registry.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/raid6/ +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/crc8.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/lru_cache.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/libcrc32c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/lz4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/crc7.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/crc16.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/crc-itu-t.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/crc-ccitt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/cordic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/bch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/asn1_decoder.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/842/ +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/842/842_decompress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/842/842_compress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/lz4/lz4hc_compress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/lz4/lz4_compress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/lib/raid6/raid6_pq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/xfrm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wireless/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wimax/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/vmw_vsock/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/unix/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sctp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rxrpc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rose/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rfkill/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/phonet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/packet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/openvswitch/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netlink/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netrom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mpls/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mac802154/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mac80211/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/llc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/key/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/kcm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ieee802154/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dsa/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dns_resolver/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/hsr/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dccp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ceph/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/can/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/caif/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/batman-adv/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ax25/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/9p/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/8021q/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/802/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_routing.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_udp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_mobility.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_hop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ghc_udp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ghc_ext_hop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ghc_icmpv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ghc_ext_route.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ghc_ext_frag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_ghc_ext_dest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_fragment.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/6lowpan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/6lowpan/nhc_dest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/802/stp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/802/p8022.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/802/psnap.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/802/mrp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/8021q/8021q.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/9p/9pnet_virtio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/9p/9pnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/mpoa.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/pppoatm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/lec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/clip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/br2684.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/atm/atm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ax25/ax25.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/batman-adv/batman-adv.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/rfcomm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/hidp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/cmtp/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/bluetooth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/bluetooth_6lowpan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/bnep/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/bnep/bnep.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/cmtp/cmtp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/hidp/hidp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bluetooth/rfcomm/rfcomm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/bridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/br_netfilter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/nft_reject_bridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/nf_tables_bridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/nft_meta_bridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/nf_log_bridge.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebtable_filter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebtable_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebtables.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebtable_broute.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_stp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_vlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_snat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_nflog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_redirect.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_mark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_pkttype.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_mark_m.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_log.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_limit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_ip6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_ip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_dnat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_802_3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_arpreply.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_arp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/bridge/netfilter/ebt_among.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/caif/caif_usb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/caif/chnl_net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/caif/caif.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/caif/caif_socket.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/can/can.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/can/can-raw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/can/can-gw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/can/can-bcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ceph/libceph.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/core/pktgen.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/core/devlink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/core/drop_monitor.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dccp/dccp_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dccp/dccp_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dccp/dccp_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dccp/dccp_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dccp/dccp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/hsr/hsr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dns_resolver/dns_resolver.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/dsa/dsa_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ieee802154/ieee802154_socket.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ieee802154/ieee802154.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ieee802154/6lowpan/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ieee802154/6lowpan/ieee802154_6lowpan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/xfrm4_mode_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/xfrm4_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/xfrm4_mode_transport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/udp_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/xfrm4_mode_beet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tunnel4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/udp_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_westwood.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_yeah.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_veno.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_vegas.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_illinois.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_lp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_scalable.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_hybla.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_htcp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_highspeed.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_dctcp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_cdg.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/tcp_bic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ipip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ip_vti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ipcomp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/inet_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ip_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ip_gre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/gre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/fou.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/esp4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/ah4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nft_reject_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nft_redir_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nft_masq_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nft_dup_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nft_chain_nat_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_nat_proto_gre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nft_chain_route_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_tables_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_reject_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_tables_arp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_nat_masquerade_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_nat_snmp_basic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_nat_pptp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_nat_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_nat_h323.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_log_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_log_arp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_defrag_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_dup_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/iptable_raw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/iptable_security.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/iptable_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/iptable_mangle.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/iptable_filter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_rpfilter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_ECN.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ip_tables.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_ah.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_SYNPROXY.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_MASQUERADE.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_REJECT.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/ipt_CLUSTERIP.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/arptable_filter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/arpt_mangle.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv4/netfilter/arp_tables.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/xfrm6_mode_transport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/xfrm6_mode_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/xfrm6_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/xfrm6_mode_ro.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/tunnel6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/xfrm6_mode_beet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/sit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/mip6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ip6_vti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ipcomp6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ip6_udp_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ip6_tunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ip6_gre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/fou6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/esp6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ah6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ila/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/ila/ila.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nft_masq_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nft_reject_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nft_redir_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nft_dup_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nft_chain_route_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nft_chain_nat_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_reject_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_tables_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_log_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_nat_masquerade_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_nat_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_dup_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_defrag_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6table_raw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6table_security.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/nf_conntrack_ipv6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6table_mangle.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6table_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6table_filter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_rt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_rpfilter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_ipv6header.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_hbh.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_mh.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_frag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_ah.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_SYNPROXY.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_eui64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_REJECT.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_NPT.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6_tables.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/ipv6/netfilter/ip6t_MASQUERADE.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/irda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/irnet/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/irlan/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/ircomm/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/ircomm/ircomm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/ircomm/ircomm-tty.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/irlan/irlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/irda/irnet/irnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/kcm/kcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/key/af_key.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/l2tp_netlink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/l2tp_ppp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/l2tp_ip6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/l2tp_ip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/l2tp_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/l2tp/l2tp_eth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/llc/llc2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/llc/llc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mac80211/mac80211.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mac802154/mac802154.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mpls/mpls_router.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mpls/mpls_iptunnel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/mpls/mpls_gso.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_u32.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_time.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_tcpudp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_state.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_tcpmss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_statistic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_string.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_socket.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_sctp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_set.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_recent.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_realm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_rateest.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_pkttype.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_physdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_quota.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_osf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_policy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_owner.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_nfacct.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_multiport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_mark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_mac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_length.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_limit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_ipvs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_l2tp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_iprange.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_ipcomp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_hl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_helper.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_hashlimit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_esp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_ecn.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_dscp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_devgroup.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_dccp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_conntrack.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_connlimit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_cpu.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_connbytes.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_connlabel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_connmark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_comment.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_cluster.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_TRACE.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_cgroup.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_bpf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_addrtype.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_TCPMSS.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_TCPOPTSTRIP.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_TPROXY.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_TEE.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_RATEEST.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_REDIRECT.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_SECMARK.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_NFLOG.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_NFQUEUE.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_NETMAP.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_LOG.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_LED.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_IDLETIMER.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_HMARK.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_HL.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_CLASSIFY.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_CT.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_DSCP.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_CONNSECMARK.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/x_tables.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/xt_CHECKSUM.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_reject_inet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_reject.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_redir.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_rbtree.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_queue.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_meta.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_masq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_log.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_hash.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_limit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_fwd_netdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_dup_netdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_exthdr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_ct.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nfnetlink_acct.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_compat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nfnetlink_queue.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nfnetlink_cttimeout.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nft_counter.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nfnetlink_log.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nfnetlink_cthelper.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nfnetlink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_tables_netdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_synproxy_core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_tables_inet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_tables.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_tftp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_sip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_redirect.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_proto_sctp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_proto_udplite.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_proto_dccp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_irc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_ftp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_dup_netdev.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_nat_amanda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_log_common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_tftp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_snmp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_sip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_sane.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_proto_gre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_proto_sctp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_proto_udplite.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_proto_dccp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_irc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_netlink.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_pptp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_netbios_ns.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_h323.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_broadcast.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_ftp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack_amanda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/nf_conntrack.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_netport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_netportnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_list_set.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_netnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_netiface.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_net.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_mac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_ipportip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_ipport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_ipmark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_hash_ip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_bitmap_ipmac.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_bitmap_port.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set_bitmap_ip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipset/ip_set.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_wrr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_sh.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_wlc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_rr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_sed.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_pe_sip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_fo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_nq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_ovf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_lc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_lblcr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_lblc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_ftp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs_dh.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netfilter/ipvs/ip_vs.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netrom/netrom.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/netlink/netlink_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/nfc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/nfc_digital.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/nci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/hci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/hci/hci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/nfc/nci/nci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/openvswitch/vport-gre.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/openvswitch/vport-geneve.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/openvswitch/vport-vxlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/openvswitch/openvswitch.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/packet/af_packet_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/phonet/pn_pep.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/phonet/phonet.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rfkill/rfkill.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rfkill/rfkill-gpio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rose/rose.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/rxrpc/af-rxrpc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_tbf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_teql.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_sfq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_sfb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_red.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_qfq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_prio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_multiq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_plug.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_netem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_ingress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_pie.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_mqprio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_htb.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_drr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_gred.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_hhf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_hfsc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_fq_codel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_dsmark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_codel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_fq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_choke.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_u32.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_atm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/sch_cbq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_rsvp6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_tcindex.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_rsvp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_route.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_flower.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_fw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_flow.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_cgroup.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_bpf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/cls_basic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_vlan.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_skbedit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_simple.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_police.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_pedit.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_mirred.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_nat.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_meta_skbprio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_meta_mark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_ipt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_ife.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_gact.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_csum.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_connmark.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sched/act_bpf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sctp/sctp_probe.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sctp/sctp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sctp/sctp_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/sunrpc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/xprtrdma/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/auth_gss/ +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/auth_gss/rpcsec_gss_krb5.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/auth_gss/auth_rpcgss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/sunrpc/xprtrdma/rpcrdma.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/unix/unix_diag.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/vmw_vsock/vsock.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/vmw_vsock/vmw_vsock_vmci_transport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wimax/wimax.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wireless/lib80211_crypt_wep.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wireless/lib80211_crypt_tkip.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wireless/lib80211_crypt_ccmp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wireless/lib80211.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/wireless/cfg80211.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/xfrm/xfrm_ipcomp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/xfrm/xfrm_algo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/net/xfrm/xfrm_user.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/mm/hwpoison-inject.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/security/keys/ +usr/lib/modules/4.7.6-1-ARCH/kernel/security/keys/trusted.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/security/keys/encrypted-keys/ +usr/lib/modules/4.7.6-1-ARCH/kernel/security/keys/encrypted-keys/encrypted-keys.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soundcore.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/synth/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pcmcia/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/isa/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/hda/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/ac97_bus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-timer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-pcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-rawmidi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-hwdep.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-pcm-dmaengine.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-hrtimer.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/snd-compress.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/oss/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq-virmidi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq-midi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq-midi-event.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq-midi-emul.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq-device.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/snd-seq-dummy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/oss/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/seq/oss/snd-seq-oss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/oss/snd-mixer-oss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/core/oss/snd-pcm-oss.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-virmidi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-serial-u16550.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-mtpav.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-mts64.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-portman2x4.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/vx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-dummy.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/snd-aloop.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/opl3/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/mpu401/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/mpu401/snd-mpu401.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/mpu401/snd-mpu401-uart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/opl3/snd-opl3-synth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/opl3/snd-opl3-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/drivers/vx/snd-vx-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/tascam/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/snd-isight.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/snd-firewire-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/fireworks/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/oxfw/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/digi00x/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/dice/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/bebob/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/bebob/snd-bebob.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/dice/snd-dice.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/digi00x/snd-firewire-digi00x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/oxfw/snd-oxfw.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/fireworks/snd-fireworks.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/firewire/tascam/snd-firewire-tascam.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/hda/snd-hda-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/hda/ext/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/hda/ext/snd-hda-ext-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/snd-i2c.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/snd-cs8427.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/other/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/other/snd-ak4xxx-adda.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/other/snd-pt2258.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/other/snd-ak4114.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/other/snd-ak4117.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/i2c/other/snd-ak4113.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/isa/sb/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/isa/sb/snd-sb-common.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-via82xx-modem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ymfpci/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/vx222/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-via82xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/trident/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-maestro3.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-sonicvibes.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-rme96.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-rme32.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-intel8x0m.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-fm801.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-intel8x0.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-es1968.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-ens1371.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-es1938.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-ens1370.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-cs4281.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-cmipci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-azt3328.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-bt87x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-ad1889.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-als300.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-atiixp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-atiixp-modem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/snd-als4000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/rme9652/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/riptide/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/pcxhr/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/oxygen/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/lx6464es/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/nm256/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/mixart/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/lola/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/korg1212/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ice1712/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/emu10k1/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ctxfi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ca0106/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/cs46xx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/asihpi/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/aw2/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/au88x0/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ali5451/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ac97/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ac97/snd-ac97-codec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ali5451/snd-ali5451.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/au88x0/snd-au8830.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/au88x0/snd-au8810.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/au88x0/snd-au8820.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/aw2/snd-aw2.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/asihpi/snd-asihpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/cs46xx/snd-cs46xx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ca0106/snd-ca0106.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ctxfi/snd-ctxfi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-mia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-mona.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-layla24.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-layla20.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-indigodjx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-indigoiox.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-indigoio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-indigodj.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-indigo.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-gina24.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-gina20.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-echo3g.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-darla24.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/echoaudio/snd-darla20.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/emu10k1/snd-emu10k1x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/emu10k1/snd-emu10k1.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/emu10k1/snd-emu10k1-synth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-intel.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-si3054.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-via.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-realtek.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-idt.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-hdmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-cirrus.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-generic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-conexant.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-cmedia.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-ca0132.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-ca0110.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/hda/snd-hda-codec-analog.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ice1712/snd-ice1712.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ice1712/snd-ice1724.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ice1712/snd-ice17xx-ak4xxx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/korg1212/snd-korg1212.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/lola/snd-lola.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/mixart/snd-mixart.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/nm256/snd-nm256.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/lx6464es/snd-lx6464es.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/oxygen/snd-virtuoso.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/oxygen/snd-oxygen-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/oxygen/snd-oxygen.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/pcxhr/snd-pcxhr.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/riptide/snd-riptide.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/rme9652/snd-hdspm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/rme9652/snd-rme9652.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/rme9652/snd-hdsp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/trident/snd-trident.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/vx222/snd-vx222.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pci/ymfpci/snd-ymfpci.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pcmcia/vx/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pcmcia/pdaudiocf/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pcmcia/pdaudiocf/snd-pdaudiocf.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/pcmcia/vx/snd-vxpocket.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/snd-soc-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/dwc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/generic/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/amd/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/amd/snd-soc-acp-pcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-ts3a227e.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-ssm4567.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-spdif-tx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-spdif-rx.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-si476x.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rt5670.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rt286.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rt5645.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rt5651.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rt298.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rt5640.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rl6347a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-rl6231.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-nau8825.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-max98357a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-max98090.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-dmic.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-ac97.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/codecs/snd-soc-hdac-hdmi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/generic/snd-soc-simple-card.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/haswell/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/skylake/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/common/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/atom/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/atom/snd-soc-sst-mfld-platform.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/atom/sst/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/atom/sst/snd-intel-sst-acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/atom/sst/snd-intel-sst-core.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-cht-bsw-rt5672.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-haswell.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-cht-bsw-rt5645.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-cht-bsw-max98090_ti.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-skl_nau88l25_ssm4567.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-bytcr-rt5651.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-bytcr-rt5640.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-bxt-rt298.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-sst-broadwell.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-soc-skl_rt286.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/boards/snd-skl_nau88l25_max98357a.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/common/snd-soc-sst-match.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/common/snd-soc-sst-dsp.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/common/snd-soc-sst-ipc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/common/snd-soc-sst-acpi.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/skylake/snd-soc-skl.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/skylake/snd-soc-skl-ipc.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/intel/haswell/snd-soc-sst-haswell-pcm.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/soc/dwc/designware_i2s.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/synth/snd-util-mem.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/synth/emux/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/synth/emux/snd-emux-synth.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/snd-usbmidi-lib.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/usx2y/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/snd-usb-audio.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/misc/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/line6/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/hiface/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/caiaq/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/bcd2000/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/6fire/ +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/6fire/snd-usb-6fire.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/bcd2000/snd-bcd2000.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/caiaq/snd-usb-caiaq.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/hiface/snd-usb-hiface.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/line6/snd-usb-variax.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/line6/snd-usb-podhd.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/line6/snd-usb-pod.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/line6/snd-usb-toneport.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/line6/snd-usb-line6.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/misc/snd-ua101.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/usx2y/snd-usb-us122l.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/sound/usb/usx2y/snd-usb-usx2y.ko.gz +usr/lib/modules/4.7.6-1-ARCH/kernel/virt/lib/ +usr/lib/modules/4.7.6-1-ARCH/kernel/virt/lib/irqbypass.ko.gz +usr/lib/modules/4.7.6-1-ARCH/build/vmlinux +usr/lib/modules/extramodules-4.7-ARCH/version diff --git a/libpkg/testfiles/makepkg.conf b/libpkg/testfiles/makepkg.conf new file mode 100644 index 0000000..058da9b --- /dev/null +++ b/libpkg/testfiles/makepkg.conf @@ -0,0 +1,147 @@ +# +# /etc/makepkg.conf +# + +######################################################################### +# SOURCE ACQUISITION +######################################################################### +# +#-- The download utilities that makepkg should use to acquire sources +# Format: 'protocol::agent' +DLAGENTS=('ftp::/usr/bin/curl -fC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u' + 'http::/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' + 'https::/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' + 'rsync::/usr/bin/rsync --no-motd -z %u %o' + 'scp::/usr/bin/scp -C %u %o') + +# Other common tools: +# /usr/bin/snarf +# /usr/bin/lftpget -c +# /usr/bin/wget + +#-- The package required by makepkg to download VCS sources +# Format: 'protocol::package' +VCSCLIENTS=('bzr::bzr' + 'git::git' + 'hg::mercurial' + 'svn::subversion') + +######################################################################### +# ARCHITECTURE, COMPILE FLAGS +######################################################################### +# +CARCH="x86_64" +CHOST="x86_64-pc-linux-gnu" + +#-- Compiler and Linker Flags +# -march (or -mcpu) builds exclusively for an architecture +# -mtune optimizes for an architecture, but builds for whole processor family +CPPFLAGS="-D_FORTIFY_SOURCE=2" +CFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong" +CXXFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong" +LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro" +#-- Make Flags: change this for DistCC/SMP systems +#MAKEFLAGS="-j2" +#-- Debugging flags +DEBUG_CFLAGS="-g -fvar-tracking-assignments" +DEBUG_CXXFLAGS="-g -fvar-tracking-assignments" + +######################################################################### +# BUILD ENVIRONMENT +######################################################################### +# +# Defaults: BUILDENV=(!distcc color !ccache check !sign) +# A negated environment option will do the opposite of the comments below. +# +#-- distcc: Use the Distributed C/C++/ObjC compiler +#-- color: Colorize output messages +#-- ccache: Use ccache to cache compilation +#-- check: Run the check() function if present in the PKGBUILD +#-- sign: Generate PGP signature file +# +BUILDENV=(!distcc color !ccache check !sign) +# +#-- If using DistCC, your MAKEFLAGS will also need modification. In addition, +#-- specify a space-delimited list of hosts running in the DistCC cluster. +#DISTCC_HOSTS="" +# +#-- Specify a directory for package building. +#BUILDDIR=/tmp/makepkg + +######################################################################### +# GLOBAL PACKAGE OPTIONS +# These are default values for the options=() settings +######################################################################### +# +# Default: OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge !optipng !upx !debug) +# A negated option will do the opposite of the comments below. +# +#-- strip: Strip symbols from binaries/libraries +#-- docs: Save doc directories specified by DOC_DIRS +#-- libtool: Leave libtool (.la) files in packages +#-- staticlibs: Leave static library (.a) files in packages +#-- emptydirs: Leave empty directories in packages +#-- zipman: Compress manual (man and info) pages in MAN_DIRS with gzip +#-- purge: Remove files specified by PURGE_TARGETS +#-- upx: Compress binary executable files using UPX +#-- optipng: Optimize PNG images with optipng +#-- debug: Add debugging flags as specified in DEBUG_* variables +# +OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge !optipng !upx !debug) + +#-- File integrity checks to use. Valid: md5, sha1, sha256, sha384, sha512 +INTEGRITY_CHECK=(md5) +#-- Options to be used when stripping binaries. See `man strip' for details. +STRIP_BINARIES="--strip-all" +#-- Options to be used when stripping shared libraries. See `man strip' for details. +STRIP_SHARED="--strip-unneeded" +#-- Options to be used when stripping static libraries. See `man strip' for details. +STRIP_STATIC="--strip-debug" +#-- Manual (man and info) directories to compress (if zipman is specified) +MAN_DIRS=({usr{,/local}{,/share},opt/*}/{man,info}) +#-- Doc directories to remove (if !docs is specified) +DOC_DIRS=(usr/{,local/}{,share/}{doc,gtk-doc} opt/*/{doc,gtk-doc}) +#-- Files to be removed from all packages (if purge is specified) +PURGE_TARGETS=(usr/{,share}/info/dir .packlist *.pod) + +######################################################################### +# PACKAGE OUTPUT +######################################################################### +# +# Default: put built package and cached source in build directory +# +#-- Destination: specify a fixed directory where all packages will be placed +#PKGDEST=/home/packages +#-- Source cache: specify a fixed directory where source files will be cached +#SRCDEST=/home/sources +#-- Source packages: specify a fixed directory where all src packages will be placed +#SRCPKGDEST=/home/srcpackages +#-- Log files: specify a fixed directory where all log files will be placed +#LOGDEST=/home/makepkglogs +#-- Packager: name/email of the person or organization building packages +#PACKAGER="John Doe " +#-- Specify a key to use for package signing +#GPGKEY="" + +######################################################################### +# COMPRESSION DEFAULTS +######################################################################### +# +COMPRESSGZ=(gzip -c -f -n) +COMPRESSBZ2=(bzip2 -c -f) +COMPRESSXZ=(xz -c -z -) +COMPRESSLRZ=(lrzip -q) +COMPRESSLZO=(lzop -q) +COMPRESSZ=(compress -c -f) + +######################################################################### +# EXTENSION DEFAULTS +######################################################################### +# +# WARNING: Do NOT modify these variables unless you know what you are +# doing. +# +PKGEXT='.pkg.tar.xz' +SRCEXT='.src.tar.gz' + +# vim: set ft=sh ts=2 sw=2 et: diff --git a/libpkg/testfiles/mingw-w64-crt/libkernel32.a b/libpkg/testfiles/mingw-w64-crt/libkernel32.a new file mode 100644 index 0000000..0f16c47 Binary files /dev/null and b/libpkg/testfiles/mingw-w64-crt/libkernel32.a differ diff --git a/libpkg/testfiles/mingw-w64-crt/mingw-w64-crt-6.0.0-1-any.pkg.tar.xz b/libpkg/testfiles/mingw-w64-crt/mingw-w64-crt-6.0.0-1-any.pkg.tar.xz new file mode 100644 index 0000000..da3a26a Binary files /dev/null and b/libpkg/testfiles/mingw-w64-crt/mingw-w64-crt-6.0.0-1-any.pkg.tar.xz differ diff --git a/libpkg/testfiles/mingw-w64-harfbuzz/BUILDINFO b/libpkg/testfiles/mingw-w64-harfbuzz/BUILDINFO new file mode 100644 index 0000000..7d96e2a --- /dev/null +++ b/libpkg/testfiles/mingw-w64-harfbuzz/BUILDINFO @@ -0,0 +1,148 @@ +builddir = /build +pkgbuild_sha256sum = e7fb5b2beb940fb088549b98ce47da825044c1ebcd9a5cee9042553329e529a5 +buildenv = !distcc +buildenv = color +buildenv = ccache +buildenv = check +buildenv = !sign +options = strip +options = docs +options = !libtool +options = !staticlibs +options = emptydirs +options = zipman +options = purge +options = !upx +options = !debug +installed = acl-2.2.52-3 +installed = archlinux-keyring-20170104-1 +installed = attr-2.4.47-2 +installed = autoconf-2.69-4 +installed = automake-1.15-2 +installed = bash-4.4.011-2 +installed = binutils-2.27-1 +installed = bison-3.0.4-2 +installed = bzip2-1.0.6-5 +installed = ca-certificates-20160507-1 +installed = ca-certificates-cacert-20140824-4 +installed = ca-certificates-mozilla-3.28.1-1 +installed = ca-certificates-utils-20160507-1 +installed = ccache-3.3.3-1 +installed = clang-ccache-1-1 +installed = coreutils-8.26-1 +installed = cracklib-2.9.6-1 +installed = curl-7.52.1-2 +installed = db-5.3.28-3 +installed = diffutils-3.5-1 +installed = e2fsprogs-1.43.3-1 +installed = expat-2.2.0-2 +installed = fakeroot-1.21-2 +installed = file-5.29-1 +installed = filesystem-2016.12-2 +installed = findutils-4.6.0-2 +installed = flex-2.6.3-1 +installed = gawk-4.1.4-2 +installed = gc-7.6.0-1 +installed = gcc-libs-multilib-6.3.1-1 +installed = gcc-multilib-6.3.1-1 +installed = gdbm-1.12-2 +installed = gettext-0.19.8.1-2 +installed = glib2-2.50.2-1 +installed = glibc-2.24-2 +installed = gmp-6.1.2-1 +installed = gnupg-2.1.18-1 +installed = gnutls-3.5.8-2 +installed = gpgme-1.7.1-2 +installed = grep-2.27-1 +installed = groff-1.22.3-7 +installed = guile-2.0.13-2 +installed = gzip-1.8-2 +installed = iana-etc-20161101-1 +installed = icu-58.2-1 +installed = keyutils-1.5.9-1 +installed = krb5-1.13.7-1 +installed = less-487-1 +installed = lib32-gcc-libs-6.3.1-1 +installed = lib32-glibc-2.24-2 +installed = libarchive-3.2.2-2 +installed = libassuan-2.4.3-1 +installed = libatomic_ops-7.4.4-1 +installed = libcap-2.25-1 +installed = libffi-3.2.1-2 +installed = libgcrypt-1.7.6-1 +installed = libgpg-error-1.26-1 +installed = libidn-1.33-1 +installed = libksba-1.3.4-2 +installed = libldap-2.4.44-3 +installed = libmpc-1.0.3-2 +installed = libpsl-0.16.1-1 +installed = libsasl-2.1.26-8 +installed = libsecret-0.18.5-1 +installed = libssh2-1.8.0-1 +installed = libsystemd-232-8 +installed = libtasn1-4.10-1 +installed = libtirpc-1.0.1-2 +installed = libtool-2.4.6-7 +installed = libunistring-0.9.7-1 +installed = libutil-linux-2.29.1-1 +installed = linux-api-headers-4.7-1 +installed = lz4-1:1.7.5-1 +installed = lzo-2.09-1 +installed = m4-1.4.18-1 +installed = make-4.2.1-1 +installed = mingw-w64-binutils-2.27-1 +installed = mingw-w64-bzip2-1.0.6-8 +installed = mingw-w64-cairo-1.14.8-1 +installed = mingw-w64-ccache-1-1 +installed = mingw-w64-configure-0.1-2 +installed = mingw-w64-crt-5.0.1-1 +installed = mingw-w64-expat-2.1.0-5 +installed = mingw-w64-fontconfig-2.12.1-1 +installed = mingw-w64-freetype2-2.7.1-1 +installed = mingw-w64-gcc-6.3.1-1 +installed = mingw-w64-gettext-0.19.8.1-1 +installed = mingw-w64-glib2-2.50.2-1 +installed = mingw-w64-graphite-1.3.9-1 +installed = mingw-w64-harfbuzz-1.4.1-1 +installed = mingw-w64-headers-5.0.1-1 +installed = mingw-w64-icu-57.1-2 +installed = mingw-w64-libffi-3.2.1-1 +installed = mingw-w64-libiconv-1.14-9 +installed = mingw-w64-libpng-1.6.28-1 +installed = mingw-w64-libunistring-0.9.7-1 +installed = mingw-w64-lzo-2.09-1 +installed = mingw-w64-pcre-8.39-2.1 +installed = mingw-w64-pixman-0.34.0-1 +installed = mingw-w64-pkg-config-2-3 +installed = mingw-w64-termcap-1.3.1-8 +installed = mingw-w64-winpthreads-5.0.1-1 +installed = mingw-w64-zlib-1.2.11-1 +installed = mpfr-3.1.5.p2-1 +installed = ncurses-6.0+20161224-1 +installed = nettle-3.3-1 +installed = npth-1.3-1 +installed = openssl-1.0.2.k-1 +installed = p11-kit-0.23.2-1 +installed = pacman-5.0.1-4 +installed = pacman-mirrorlist-20170106-1 +installed = pam-1.3.0-1 +installed = pambase-20130928-1 +installed = patch-2.7.5-1 +installed = pcre-8.39-2 +installed = perl-5.24.1-1 +installed = pinentry-1.0.0-1 +installed = pkg-config-0.29.1-2 +installed = pth-2.0.7-5 +installed = readline-7.0.001-1 +installed = sed-4.3-1 +installed = shadow-4.4-3 +installed = sqlite-3.16.2-1 +installed = sudo-1.8.19.p2-1 +installed = tar-1.29-2 +installed = texinfo-6.3-1 +installed = tzdata-2016j-1 +installed = util-linux-2.29.1-1 +installed = vi-1:070224-2 +installed = which-2.21-2 +installed = xz-5.2.3-1 +installed = zlib-1:1.2.11-1 diff --git a/libpkg/testfiles/mingw-w64-harfbuzz/PKGBUILD b/libpkg/testfiles/mingw-w64-harfbuzz/PKGBUILD new file mode 100644 index 0000000..b504323 --- /dev/null +++ b/libpkg/testfiles/mingw-w64-harfbuzz/PKGBUILD @@ -0,0 +1,87 @@ +pkgbase=mingw-w64-harfbuzz +pkgname=(mingw-w64-harfbuzz mingw-w64-harfbuzz-icu) +pkgver=1.4.2 +pkgrel=1 +pkgdesc="OpenType text shaping engine (mingw-w64)" +arch=(any) +url="http://www.freedesktop.org/wiki/Software/HarfBuzz" +license=("MIT") +makedepends=(mingw-w64-configure mingw-w64-cairo mingw-w64-icu mingw-w64-graphite mingw-w64-freetype2) +options=(!strip !buildflags staticlibs) +source=("http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-${pkgver}.tar.bz2") +sha256sums=('8f234dcfab000fdec24d43674fffa2fdbdbd654eb176afbde30e8826339cb7b3') + +_architectures="i686-w64-mingw32 x86_64-w64-mingw32" + +prepare() { + cd harfbuzz-${pkgver} + # disable tests (thanks to chenxiaolong) + sed -i '/SUBDIRS/s/test//' Makefile.am + autoreconf -fi +} + + +build() { + cd harfbuzz-$pkgver + unset LDFLAGS + for _arch in ${_architectures}; do + # Build static and shared libs separately due to necessity of defining DGRAPHITE2_STATIC + # manually when building static version + + # static build + mkdir -p build-${_arch}-static && pushd build-${_arch}-static + CFLAGS=-DGRAPHITE2_STATIC CXXFLAGS=-DGRAPHITE2_STATIC ${_arch}-configure \ + --with-glib \ + --with-freetype \ + --with-cairo \ + --with-icu \ + --with-gobject \ + --with-graphite2 \ + --enable-static=yes \ + --enable-shared=no + make + popd + # shared build + mkdir -p build-${_arch}-shared && pushd build-${_arch}-shared + ${_arch}-configure \ + --with-glib \ + --with-freetype \ + --with-cairo \ + --with-icu \ + --with-gobject \ + --with-graphite2 \ + --enable-static=no \ + --enable-shared=yes + make + popd + done +} + +package_mingw-w64-harfbuzz() { + depends=(mingw-w64-freetype2 mingw-w64-glib2 mingw-w64-graphite) + for _arch in ${_architectures}; do + cd "${srcdir}/harfbuzz-${pkgver}/build-${_arch}-static" + make DESTDIR="${pkgdir}" install + cd "${srcdir}/harfbuzz-${pkgver}/build-${_arch}-shared" + make DESTDIR="${pkgdir}" install + find "$pkgdir/usr/${_arch}" -name '*.exe' -exec ${_arch}-strip {} \; + find "$pkgdir/usr/${_arch}" -name '*.dll' -exec ${_arch}-strip --strip-unneeded {} \; + find "$pkgdir/usr/${_arch}" -name '*.a' -o -name '*.dll' | xargs ${_arch}-strip -g + + mkdir -p hb-icu/usr/${_arch}/{bin,include/harfbuzz,lib/pkgconfig}; cd hb-icu + mv "$pkgdir"/usr/${_arch}/bin/libharfbuzz-icu* ./usr/${_arch}/bin + mv "$pkgdir"/usr/${_arch}/lib/libharfbuzz-icu* ./usr/${_arch}/lib + mv "$pkgdir"/usr/${_arch}/lib/pkgconfig/harfbuzz-icu.pc ./usr/${_arch}/lib/pkgconfig + mv "$pkgdir"/usr/${_arch}/include/harfbuzz/hb-icu.h ./usr/${_arch}/include/harfbuzz + done +} + +package_mingw-w64-harfbuzz-icu() { + pkgdesc="OpenType text shaping engine (ICU integration) (mingw-w64)" + depends=(mingw-w64-harfbuzz mingw-w64-icu) + for _arch in ${_architectures}; do + cd "${srcdir}/harfbuzz-${pkgver}/build-${_arch}-shared" + mkdir -p "$pkgdir/usr/${_arch}" + mv hb-icu/usr/${_arch}/* "$pkgdir/usr/${_arch}" + done +} diff --git a/libpkg/testfiles/mingw-w64-harfbuzz/PKGINFO b/libpkg/testfiles/mingw-w64-harfbuzz/PKGINFO new file mode 100644 index 0000000..50784b3 --- /dev/null +++ b/libpkg/testfiles/mingw-w64-harfbuzz/PKGINFO @@ -0,0 +1,21 @@ +# Generated by makepkg 5.0.1 +# using fakeroot version 1.21 +# Tue Jan 31 22:54:12 UTC 2017 +pkgname = mingw-w64-harfbuzz +pkgbase = mingw-w64-harfbuzz +pkgver = 1.4.2-1 +pkgdesc = OpenType text shaping engine (mingw-w64) +url = http://www.freedesktop.org/wiki/Software/HarfBuzz +builddate = 1485903252 +packager = Martchus <@.net> +size = 4268032 +arch = any +license = MIT +depend = mingw-w64-freetype2 +depend = mingw-w64-glib2 +depend = mingw-w64-graphite +makedepend = mingw-w64-configure +makedepend = mingw-w64-cairo +makedepend = mingw-w64-icu +makedepend = mingw-w64-graphite +makedepend = mingw-w64-freetype2 diff --git a/libpkg/testfiles/mingw-w64-harfbuzz/SRCINFO b/libpkg/testfiles/mingw-w64-harfbuzz/SRCINFO new file mode 100644 index 0000000..0db0e46 --- /dev/null +++ b/libpkg/testfiles/mingw-w64-harfbuzz/SRCINFO @@ -0,0 +1,30 @@ +# Generated by mksrcinfo v8 +# Wed Jan 25 20:40:21 UTC 2017 +pkgbase = mingw-w64-harfbuzz + pkgdesc = OpenType text shaping engine (mingw-w64) + pkgver = 1.4.2 + pkgrel = 1 + url = http://www.freedesktop.org/wiki/Software/HarfBuzz + arch = any + license = MIT + makedepends = mingw-w64-configure + makedepends = mingw-w64-cairo + makedepends = mingw-w64-icu + makedepends = mingw-w64-graphite + makedepends = mingw-w64-freetype2 + options = !strip + options = !buildflags + options = staticlibs + source = http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-1.4.2.tar.bz2 + sha256sums = 8f234dcfab000fdec24d43674fffa2fdbdbd654eb176afbde30e8826339cb7b3 + +pkgname = mingw-w64-harfbuzz + depends = mingw-w64-freetype2 + depends = mingw-w64-glib2 + depends = mingw-w64-graphite + +pkgname = mingw-w64-harfbuzz-icu + pkgdesc = OpenType text shaping engine (ICU integration) (mingw-w64) + depends = mingw-w64-harfbuzz + depends = mingw-w64-icu + diff --git a/libpkg/testfiles/mingw-w64-harfbuzz/desc b/libpkg/testfiles/mingw-w64-harfbuzz/desc new file mode 100644 index 0000000..57c350a --- /dev/null +++ b/libpkg/testfiles/mingw-w64-harfbuzz/desc @@ -0,0 +1,54 @@ +%FILENAME% +mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz + +%NAME% +mingw-w64-harfbuzz + +%BASE% +mingw-w64-harfbuzz + +%VERSION% +1.4.2-1 + +%DESC% +OpenType text shaping engine (mingw-w64) + +%CSIZE% +589408 + +%ISIZE% +4268032 + +%MD5SUM% +a05d4618090b0294bc075e85791485f8 + +%SHA256SUM% +ff62339041c19d2a986eed8231fb8e1be723b3afd354cca833946305456e8ec7 + +%URL% +http://www.freedesktop.org/wiki/Software/HarfBuzz + +%LICENSE% +MIT + +%ARCH% +any + +%BUILDDATE% +1485903252 + +%PACKAGER% +Martchus <@.net> + +%DEPENDS% +mingw-w64-freetype2 +mingw-w64-glib2 +mingw-w64-graphite + +%MAKEDEPENDS% +mingw-w64-configure +mingw-w64-cairo +mingw-w64-icu +mingw-w64-graphite +mingw-w64-freetype2 + diff --git a/libpkg/testfiles/mingw-w64-harfbuzz/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz b/libpkg/testfiles/mingw-w64-harfbuzz/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz new file mode 100644 index 0000000..57a7844 Binary files /dev/null and b/libpkg/testfiles/mingw-w64-harfbuzz/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz differ diff --git a/libpkg/testfiles/mirrorlist b/libpkg/testfiles/mirrorlist new file mode 100644 index 0000000..8129643 --- /dev/null +++ b/libpkg/testfiles/mirrorlist @@ -0,0 +1,461 @@ +## +## Arch Linux repository mirrorlist +## Generated on 2017-02-01 +## + +## Worldwide +#Server = https://archlinux.surlyjake.com/archlinux/$repo/os/$arch +#Server = http://mirrors.evowise.com/archlinux/$repo/os/$arch +#Server = http://mirror.rackspace.com/archlinux/$repo/os/$arch + +## Australia +#Server = https://mirror.aarnet.edu.au/pub/archlinux/$repo/os/$arch +#Server = http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch +#Server = http://ftp.iinet.net.au/pub/archlinux/$repo/os/$arch +#Server = http://mirror.internode.on.net/pub/archlinux/$repo/os/$arch +#Server = http://ftp.swin.edu.au/archlinux/$repo/os/$arch +#Server = http://archlinux.uberglobalmirror.com/$repo/os/$arch + +## Austria +#Server = http://mirror.digitalnova.at/archlinux/$repo/os/$arch +#Server = http://mirror.easyname.at/archlinux/$repo/os/$arch +#Server = http://mirror1.htu.tugraz.at/archlinux/$repo/os/$arch + +## Belarus +#Server = http://ftp.byfly.by/pub/archlinux/$repo/os/$arch +#Server = http://mirror.datacenter.by/pub/archlinux/$repo/os/$arch + +## Belgium +#Server = http://archlinux.cu.be/$repo/os/$arch +#Server = http://archlinux.mirror.kangaroot.net/$repo/os/$arch + +## Bosnia and Herzegovina +#Server = http://burek.archlinux.ba/$repo/os/$arch +#Server = http://archlinux.mirror.ba/$repo/os/$arch + +## Brazil +#Server = http://archlinux-br.mirror.host1plus.com/$repo/os/$arch +#Server = http://archlinux.c3sl.ufpr.br/$repo/os/$arch +#Server = http://linorg.usp.br/archlinux/$repo/os/$arch +#Server = http://pet.inf.ufsc.br/mirrors/archlinux/$repo/os/$arch +#Server = http://archlinux.pop-es.rnp.br/$repo/os/$arch + +## Bulgaria +#Server = http://mirrors.netix.net/archlinux/$repo/os/$arch +#Server = http://mirror.telepoint.bg/archlinux/$repo/os/$arch +#Server = http://mirrors.uni-plovdiv.net/archlinux/$repo/os/$arch +#Server = https://mirrors.uni-plovdiv.net/archlinux/$repo/os/$arch + +## Canada +#Server = http://mirror.cedille.club/archlinux/$repo/os/$arch +#Server = http://mirror.csclub.uwaterloo.ca/archlinux/$repo/os/$arch +#Server = http://mirror.frgl.pw/archlinux/$repo/os/$arch +#Server = https://mirror.frgl.pw/archlinux/$repo/os/$arch +#Server = http://mirror.its.dal.ca/archlinux/$repo/os/$arch +#Server = http://muug.ca/mirror/archlinux/$repo/os/$arch +#Server = https://muug.ca/mirror/archlinux/$repo/os/$arch +#Server = http://archlinux.mirror.rafal.ca/$repo/os/$arch + +## Chile +#Server = http://mirror.archlinux.cl/$repo/os/$arch + +## China +#Server = http://mirrors.163.com/archlinux/$repo/os/$arch +#Server = http://mirrors.cqu.edu.cn/archlinux/$repo/os/$arch +#Server = http://mirror.lzu.edu.cn/archlinux/$repo/os/$arch +#Server = http://mirrors.neusoft.edu.cn/archlinux/$repo/os/$arch +#Server = https://mirrors.skyshe.cn/archlinux/$repo/os/$arch +#Server = http://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch +#Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch +#Server = http://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch +#Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch +#Server = http://mirrors.xjtu.edu.cn/archlinux/$repo/os/$arch +#Server = https://mirrors.xjtu.edu.cn/archlinux/$repo/os/$arch +#Server = http://mirrors.zju.edu.cn/archlinux/$repo/os/$arch + +## Colombia +#Server = http://mirror.edatel.net.co/archlinux/$repo/os/$arch +#Server = http://www.laqee.unal.edu.co/archlinux/$repo/os/$arch +#Server = http://mirror.upb.edu.co/archlinux/$repo/os/$arch + +## Croatia +#Server = http://archlinux.iskon.hr/$repo/os/$arch + +## Czech Republic +#Server = http://mirror.dkm.cz/archlinux/$repo/os/$arch +#Server = https://mirror.dkm.cz/archlinux/$repo/os/$arch +#Server = http://ftp.fi.muni.cz/pub/linux/arch/$repo/os/$arch +#Server = http://ftp.linux.cz/pub/linux/arch/$repo/os/$arch +#Server = http://gluttony.sin.cvut.cz/arch/$repo/os/$arch +#Server = https://gluttony.sin.cvut.cz/arch/$repo/os/$arch +#Server = http://mirrors.nic.cz/archlinux/$repo/os/$arch +#Server = http://ftp.sh.cvut.cz/arch/$repo/os/$arch +#Server = https://ftp.sh.cvut.cz/arch/$repo/os/$arch +#Server = http://mirror.vpsfree.cz/archlinux/$repo/os/$arch + +## Denmark +#Server = http://mirrors.dotsrc.org/archlinux/$repo/os/$arch +#Server = https://mirrors.dotsrc.org/archlinux/$repo/os/$arch +#Server = http://ftp.klid.dk/ftp/archlinux/$repo/os/$arch +#Server = http://mirror.one.com/archlinux/$repo/os/$arch +#Server = https://mirror.one.com/archlinux/$repo/os/$arch + +## Ecuador +#Server = http://mirror.cedia.org.ec/archlinux/$repo/os/$arch +#Server = http://mirror.espoch.edu.ec/archlinux/$repo/os/$arch +#Server = http://mirror.uta.edu.ec/archlinux/$repo/os/$arch + +## Finland +#Server = http://arch.mirror.far.fi/$repo/os/$arch + +## France +#Server = http://archlinux.de-labrusse.fr/$repo/os/$arch +#Server = http://mirror.archlinux.ikoula.com/archlinux/$repo/os/$arch +#Server = http://archlinux.vi-di.fr/$repo/os/$arch +#Server = https://archlinux.vi-di.fr/$repo/os/$arch +#Server = http://mirror.armbrust.me/archlinux/$repo/os/$arch +#Server = https://mirror.armbrust.me/archlinux/$repo/os/$arch +#Server = https://archlinux.ec-tech.fr/$repo/os/$arch +#Server = http://fooo.biz/archlinux/$repo/os/$arch +#Server = https://fooo.biz/archlinux/$repo/os/$arch +#Server = http://mirror.gerhard.re/archlinux/$repo/os/$arch +#Server = http://mirror.ibcp.fr/pub/archlinux/$repo/os/$arch +#Server = http://mirror.lastmikoi.net/archlinux/$repo/os/$arch +#Server = http://archlinux.mailtunnel.eu/$repo/os/$arch +#Server = https://www.mailtunnel.eu/archlinux/$repo/os/$arch +#Server = http://mir.archlinux.fr/$repo/os/$arch +#Server = http://arch.nimukaito.net/$repo/os/$arch +#Server = https://arch.nimukaito.net/$repo/os/$arch +#Server = http://archlinux.mirrors.ovh.net/archlinux/$repo/os/$arch +#Server = http://archlinux.mirror.pkern.at/$repo/os/$arch +#Server = https://archlinux.mirror.pkern.at/$repo/os/$arch +#Server = http://archlinux.polymorf.fr/$repo/os/$arch +#Server = http://arch.static.lu/$repo/os/$arch +#Server = http://arch.tamcore.eu/$repo/os/$arch +#Server = http://mirrors.theblazehen.com/archlinux/$repo/os/$arch +#Server = http://mirror.tyborek.pl/arch/$repo/os/$arch +#Server = https://mirror.tyborek.pl/arch/$repo/os/$arch +#Server = http://ftp.u-strasbg.fr/linux/distributions/archlinux/$repo/os/$arch +#Server = https://mirror.wormhole.eu/archlinux/$repo/os/$arch +#Server = http://arch.yourlabs.org/$repo/os/$arch + +## Germany +#Server = http://mirror.23media.de/archlinux/$repo/os/$arch +#Server = https://arch.32g.eu/$repo/os/$arch +#Server = http://artfiles.org/archlinux.org/$repo/os/$arch +#Server = http://mirror5.bastelfreak.org/archlinux/$repo/os/$arch +#Server = https://mirror.bethselamin.de/$repo/os/$arch +#Server = http://mirrors.cicku.me/archlinux/$repo/os/$arch +#Server = https://mirrors.cicku.me/archlinux/$repo/os/$arch +#Server = http://mirror.euserv.net/linux/archlinux/$repo/os/$arch +#Server = http://mirror.f4st.host/archlinux/$repo/os/$arch +#Server = https://mirror.f4st.host/archlinux/$repo/os/$arch +Server = http://ftp.fau.de/archlinux/$repo/os/$arch +Server = https://ftp.fau.de/archlinux/$repo/os/$arch +#Server = http://mirror.fluxent.de/archlinux/$repo/os/$arch +#Server = https://mirror.fluxent.de/archlinux/$repo/os/$arch +#Server = http://mirror.gnomus.de/$repo/os/$arch +#Server = http://www.gutscheindrache.com/mirror/archlinux/$repo/os/$arch +#Server = http://ftp.gwdg.de/pub/linux/archlinux/$repo/os/$arch +#Server = http://mirror.hactar.xyz/$repo/os/$arch +#Server = https://mirror.hactar.xyz/$repo/os/$arch +#Server = http://archlinux.honkgong.info/$repo/os/$arch +#Server = https://archlinux.honkgong.info/$repo/os/$arch +#Server = http://ftp.hosteurope.de/mirror/ftp.archlinux.org/$repo/os/$arch +#Server = http://ftp-stud.hs-esslingen.de/pub/Mirrors/archlinux/$repo/os/$arch +#Server = http://archlinux.mirror.iphh.net/$repo/os/$arch +#Server = https://mirror.jankoppe.de/$repo/os/$arch +#Server = http://arch.jensgutermuth.de/$repo/os/$arch +#Server = https://arch.jensgutermuth.de/$repo/os/$arch +#Server = http://mirror.js-webcoding.de/pub/archlinux/$repo/os/$arch +#Server = https://mirror.js-webcoding.de/pub/archlinux/$repo/os/$arch +#Server = http://k42.ch/mirror/archlinux/$repo/os/$arch +#Server = https://k42.ch/mirror/archlinux/$repo/os/$arch +#Server = http://mirror.de.leaseweb.net/archlinux/$repo/os/$arch +#Server = http://mirror.loli.forsale/arch/$repo/os/$arch +#Server = https://mirror.loli.forsale/arch/$repo/os/$arch +#Server = http://mirror.metalgamer.eu/archlinux/$repo/os/$arch +#Server = https://mirror.metalgamer.eu/archlinux/$repo/os/$arch +#Server = http://mirror.michael-eckert.net/archlinux/$repo/os/$arch +#Server = https://mirror.michael-eckert.net/archlinux/$repo/os/$arch +#Server = http://mirrors.n-ix.net/archlinux/$repo/os/$arch +#Server = http://mirror.netcologne.de/archlinux/$repo/os/$arch +#Server = https://mirror.netcologne.de/archlinux/$repo/os/$arch +#Server = http://mirrors.niyawe.de/archlinux/$repo/os/$arch +#Server = https://mirrors.niyawe.de/archlinux/$repo/os/$arch +#Server = http://mirror.pseudoform.org/$repo/os/$arch +#Server = https://mirror.pseudoform.org/$repo/os/$arch +#Server = https://www.ratenzahlung.de/mirror/archlinux/$repo/os/$arch +#Server = http://ftp.halifax.rwth-aachen.de/archlinux/$repo/os/$arch +#Server = http://linux.rz.rub.de/archlinux/$repo/os/$arch +#Server = http://mirror.selfnet.de/archlinux/$repo/os/$arch +#Server = http://ftp.spline.inf.fu-berlin.de/mirrors/archlinux/$repo/os/$arch +#Server = https://ftp.spline.inf.fu-berlin.de/mirrors/archlinux/$repo/os/$arch +#Server = http://archlinux.thaller.ws/$repo/os/$arch +#Server = https://archlinux.thaller.ws/$repo/os/$arch +#Server = http://archlinux.thelinuxnetworx.rocks/$repo/os/$arch +#Server = https://archlinux.thelinuxnetworx.rocks/$repo/os/$arch +#Server = http://ftp.tu-chemnitz.de/pub/linux/archlinux/$repo/os/$arch +#Server = http://mirror.ubrco.de/archlinux/$repo/os/$arch +#Server = https://mirror.ubrco.de/archlinux/$repo/os/$arch +#Server = http://ftp.uni-bayreuth.de/linux/archlinux/$repo/os/$arch +#Server = http://ftp.uni-hannover.de/archlinux/$repo/os/$arch +#Server = http://ftp.uni-kl.de/pub/linux/archlinux/$repo/os/$arch +#Server = http://mirror.united-gameserver.de/archlinux/$repo/os/$arch +#Server = http://mirror.vfn-nrw.de/archlinux/$repo/os/$arch +#Server = https://mirror.vfn-nrw.de/archlinux/$repo/os/$arch + +## Greece +#Server = http://ftp.cc.uoc.gr/mirrors/linux/archlinux/$repo/os/$arch +#Server = http://foss.aueb.gr/mirrors/linux/archlinux/$repo/os/$arch +#Server = https://foss.aueb.gr/mirrors/linux/archlinux/$repo/os/$arch +#Server = http://mirrors.myaegean.gr/linux/archlinux/$repo/os/$arch +#Server = http://ftp.ntua.gr/pub/linux/archlinux/$repo/os/$arch +#Server = http://ftp.otenet.gr/linux/archlinux/$repo/os/$arch + +## Hong Kong +#Server = http://arch-mirror.wtako.net/$repo/os/$arch +#Server = https://arch-mirror.wtako.net/$repo/os/$arch + +## Hungary +#Server = http://ftp.energia.mta.hu/pub/mirrors/ftp.archlinux.org/$repo/os/$arch +#Server = http://archmirror.hbit.sztaki.hu/archlinux/$repo/os/$arch + +## Iceland +#Server = http://mirror.system.is/arch/$repo/os/$arch +#Server = https://mirror.system.is/arch/$repo/os/$arch + +## India +#Server = http://mirror.cse.iitk.ac.in/archlinux/$repo/os/$arch +#Server = http://ftp.iitm.ac.in/archlinux/$repo/os/$arch + +## Indonesia +#Server = http://mirror.devilzc0de.org/archlinux/$repo/os/$arch +#Server = http://mirror.kavalinux.com/archlinux/$repo/os/$arch +#Server = http://mirror.poliwangi.ac.id/archlinux/$repo/os/$arch +#Server = http://suro.ubaya.ac.id/archlinux/$repo/os/$arch + +## Iran +#Server = http://repo.sadjad.ac.ir/arch/$repo/os/$arch +#Server = https://repo.sadjad.ac.ir/arch/$repo/os/$arch + +## Ireland +#Server = http://ftp.heanet.ie/mirrors/ftp.archlinux.org/$repo/os/$arch +#Server = https://ftp.heanet.ie/mirrors/ftp.archlinux.org/$repo/os/$arch + +## Israel +#Server = http://mirror.isoc.org.il/pub/archlinux/$repo/os/$arch + +## Italy +#Server = http://archlinux.openlabto.org/archlinux/$repo/os/$arch +#Server = https://archlinux.beccacervello.it/archlinux/$repo/os/$arch +#Server = http://mi.mirror.garr.it/mirrors/archlinux/$repo/os/$arch +#Server = http://mirrors.prometeus.net/archlinux/$repo/os/$arch +#Server = http://archlinux.students.cs.unibo.it/$repo/os/$arch + +## Japan +#Server = http://ftp.tsukuba.wide.ad.jp/Linux/archlinux/$repo/os/$arch +#Server = http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/$repo/os/$arch + +## Kazakhstan +#Server = http://mirror.neolabs.kz/archlinux/$repo/os/$arch + +## Latvia +#Server = http://archlinux.koyanet.lv/archlinux/$repo/os/$arch + +## Lithuania +#Server = http://mirrors.atviras.lt/archlinux/$repo/os/$arch +#Server = https://mirrors.atviras.lt/archlinux/$repo/os/$arch +#Server = http://arch.relevant.lt/$repo/os/$arch +#Server = https://arch.relevant.lt/$repo/os/$arch + +## Luxembourg +#Server = http://archlinux.mirror.root.lu/$repo/os/$arch + +## Macedonia +#Server = http://arch.softver.org.mk/archlinux/$repo/os/$arch +#Server = http://mirror.t-home.mk/archlinux/$repo/os/$arch +#Server = https://mirror.t-home.mk/archlinux/$repo/os/$arch + +## Netherlands +#Server = http://arch.apt-get.eu/$repo/os/$arch +#Server = http://mirror.i3d.net/pub/archlinux/$repo/os/$arch +#Server = https://mirror.i3d.net/pub/archlinux/$repo/os/$arch +#Server = http://mirror.nl.leaseweb.net/archlinux/$repo/os/$arch +#Server = http://ftp.nluug.nl/os/Linux/distr/archlinux/$repo/os/$arch +#Server = http://ftp.snt.utwente.nl/pub/os/linux/archlinux/$repo/os/$arch +#Server = http://archlinux.mirror.wearetriple.com/$repo/os/$arch +#Server = https://archlinux.mirror.wearetriple.com/$repo/os/$arch + +## New Caledonia +#Server = http://mirror.lagoon.nc/pub/archlinux/$repo/os/$arch +#Server = http://archlinux.nautile.nc/archlinux/$repo/os/$arch + +## New Zealand +#Server = https://mirror.smith.geek.nz/archlinux/$repo/os/$arch + +## Norway +#Server = http://mirror.archlinux.no/$repo/os/$arch +#Server = http://archlinux.uib.no/$repo/os/$arch +#Server = http://mirror.neuf.no/archlinux/$repo/os/$arch +#Server = https://mirror.neuf.no/archlinux/$repo/os/$arch + +## Philippines +#Server = http://mirror.rise.ph/archlinux/$repo/os/$arch + +## Poland +#Server = http://mirror.chmuri.net/archmirror/$repo/os/$arch +#Server = http://arch.midov.pl/arch/$repo/os/$arch +#Server = http://mirror.onet.pl/pub/mirrors/archlinux/$repo/os/$arch +#Server = http://piotrkosoft.net/pub/mirrors/ftp.archlinux.org/$repo/os/$arch +#Server = http://ftp.vectranet.pl/archlinux/$repo/os/$arch + +## Portugal +#Server = http://ftp.rnl.tecnico.ulisboa.pt/pub/archlinux/$repo/os/$arch + +## Qatar +#Server = http://mirror.qnren.qa/archlinux/$repo/os/$arch + +## Romania +#Server = http://mirror.archlinux.ro/archlinux/$repo/os/$arch +#Server = http://archlinux.mirrors.linux.ro/$repo/os/$arch +#Server = http://mirrors.m247.ro/archlinux/$repo/os/$arch + +## Russia +#Server = http://mirror.aur.rocks/$repo/os/$arch +#Server = https://mirror.aur.rocks/$repo/os/$arch +#Server = http://mirror.rol.ru/archlinux/$repo/os/$arch +#Server = https://mirror.rol.ru/archlinux/$repo/os/$arch +#Server = http://mirror.yandex.ru/archlinux/$repo/os/$arch +#Server = https://mirror.yandex.ru/archlinux/$repo/os/$arch + +## Serbia +#Server = http://mirror.pmf.kg.ac.rs/archlinux/$repo/os/$arch + +## Singapore +#Server = http://mirror.0x.sg/archlinux/$repo/os/$arch +#Server = http://download.nus.edu.sg/mirror/arch/$repo/os/$arch + +## Slovakia +#Server = http://mirror.lnx.sk/pub/linux/archlinux/$repo/os/$arch +#Server = https://mirror.lnx.sk/pub/linux/archlinux/$repo/os/$arch +#Server = http://tux.rainside.sk/archlinux/$repo/os/$arch + +## Slovenia +#Server = http://archimonde.ts.si/archlinux/$repo/os/$arch +#Server = https://archimonde.ts.si/archlinux/$repo/os/$arch + +## South Africa +#Server = http://archlinux-za.mirror.host1plus.com/$repo/os/$arch +#Server = http://ftp.wa.co.za/pub/archlinux/$repo/os/$arch +#Server = http://mirror.is.co.za/mirror/archlinux.org/$repo/os/$arch +#Server = http://mirror.wbs.co.za/archlinux/$repo/os/$arch + +## South Korea +#Server = http://ftp.kaist.ac.kr/ArchLinux/$repo/os/$arch +#Server = http://mirror.premi.st/archlinux/$repo/os/$arch + +## Spain +#Server = http://osl.ugr.es/archlinux/$repo/os/$arch +#Server = http://sunsite.rediris.es/mirror/archlinux/$repo/os/$arch + +## Sweden +#Server = http://ftp.acc.umu.se/mirror/archlinux/$repo/os/$arch +#Server = https://ftp.acc.umu.se/mirror/archlinux/$repo/os/$arch +#Server = http://archlinux.dynamict.se/$repo/os/$arch +#Server = http://ftp.lysator.liu.se/pub/archlinux/$repo/os/$arch +#Server = https://ftp.lysator.liu.se/pub/archlinux/$repo/os/$arch +#Server = http://ftp.myrveln.se/pub/linux/archlinux/$repo/os/$arch +#Server = https://ftp.myrveln.se/pub/linux/archlinux/$repo/os/$arch +#Server = https://mirror.osbeck.com/archlinux/$repo/os/$arch +#Server = http://ftp.portlane.com/pub/os/linux/archlinux/$repo/os/$arch + +## Switzerland +#Server = http://pkg.adfinis-sygroup.ch/archlinux/$repo/os/$arch +#Server = https://pkg.adfinis-sygroup.ch/archlinux/$repo/os/$arch +#Server = http://archlinux.puzzle.ch/$repo/os/$arch + +## Taiwan +#Server = http://archlinux.cs.nctu.edu.tw/$repo/os/$arch +#Server = http://shadow.ind.ntou.edu.tw/archlinux/$repo/os/$arch +#Server = http://ftp.tku.edu.tw/Linux/ArchLinux/$repo/os/$arch +#Server = http://ftp.yzu.edu.tw/Linux/archlinux/$repo/os/$arch + +## Thailand +#Server = http://mirror.adminbannok.com/archlinux/$repo/os/$arch +#Server = http://mirror.kku.ac.th/archlinux/$repo/os/$arch +#Server = https://mirror.kku.ac.th/archlinux/$repo/os/$arch + +## Turkey +#Server = http://ftp.linux.org.tr/archlinux/$repo/os/$arch + +## Ukraine +#Server = http://archlinux.ip-connect.vn.ua/$repo/os/$arch +#Server = https://archlinux.ip-connect.vn.ua/$repo/os/$arch +#Server = http://mirrors.nix.org.ua/linux/archlinux/$repo/os/$arch +#Server = https://mirrors.nix.org.ua/linux/archlinux/$repo/os/$arch + +## United Kingdom +#Server = http://mirror.bytemark.co.uk/archlinux/$repo/os/$arch +#Server = http://mirror.cinosure.com/archlinux/$repo/os/$arch +#Server = http://mirrors.manchester.m247.com/arch-linux/$repo/os/$arch +#Server = http://www.mirrorservice.org/sites/ftp.archlinux.org/$repo/os/$arch +#Server = http://arch.serverspace.co.uk/arch/$repo/os/$arch +#Server = http://archlinux.mirrors.uk2.net/$repo/os/$arch + +## United States +#Server = http://mirrors.acm.wpi.edu/archlinux/$repo/os/$arch +#Server = http://mirrors.advancedhosters.com/archlinux/$repo/os/$arch +#Server = http://mirrors.aggregate.org/archlinux/$repo/os/$arch +#Server = http://archlinux.surlyjake.com/archlinux/$repo/os/$arch +#Server = http://arlm.tyzoid.com/$repo/os/$arch +#Server = http://ca.us.mirror.archlinux-br.org/$repo/os/$arch +#Server = http://mirrors.cat.pdx.edu/archlinux/$repo/os/$arch +#Server = http://mirror.cc.columbia.edu/pub/linux/archlinux/$repo/os/$arch +#Server = http://cosmos.cites.illinois.edu/pub/archlinux/$repo/os/$arch +#Server = http://mirror.cs.pitt.edu/archlinux/$repo/os/$arch +#Server = http://mirror.es.its.nyu.edu/archlinux/$repo/os/$arch +#Server = http://mirrors.gigenet.com/archlinux/$repo/os/$arch +#Server = http://mirror.grig.io/archlinux/$repo/os/$arch +#Server = https://mirror.grig.io/archlinux/$repo/os/$arch +#Server = http://www.gtlib.gatech.edu/pub/archlinux/$repo/os/$arch +#Server = http://mirror1.hackingand.coffee/arch/$repo/os/$arch +#Server = http://mirror2.hackingand.coffee/arch/$repo/os/$arch +#Server = http://mirror3.hackingand.coffee/arch/$repo/os/$arch +#Server = http://mirror.htnshost.com/archlinux/$repo/os/$arch +#Server = http://mirror.jmu.edu/pub/archlinux/$repo/os/$arch +#Server = http://mirrors.kernel.org/archlinux/$repo/os/$arch +#Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch +#Server = http://mirror.us.leaseweb.net/archlinux/$repo/os/$arch +#Server = http://il.mirrors.linaxe.net/archlinux/$repo/os/$arch +#Server = http://mirrors.liquidweb.com/archlinux/$repo/os/$arch +#Server = http://arch.localmsp.org/arch/$repo/os/$arch +#Server = https://arch.localmsp.org/arch/$repo/os/$arch +#Server = http://mirror.lty.me/archlinux/$repo/os/$arch +#Server = https://mirror.lty.me/archlinux/$repo/os/$arch +#Server = http://mirrors.lug.mtu.edu/archlinux/$repo/os/$arch +#Server = https://mirrors.lug.mtu.edu/archlinux/$repo/os/$arch +#Server = http://mirror.math.princeton.edu/pub/archlinux/$repo/os/$arch +#Server = http://mirror.metrocast.net/archlinux/$repo/os/$arch +#Server = http://mirror.kaminski.io/archlinux/$repo/os/$arch +#Server = http://mirror.nexcess.net/archlinux/$repo/os/$arch +#Server = http://mirrors.ocf.berkeley.edu/archlinux/$repo/os/$arch +#Server = https://mirrors.ocf.berkeley.edu/archlinux/$repo/os/$arch +#Server = http://ftp.osuosl.org/pub/archlinux/$repo/os/$arch +#Server = http://arch.mirrors.pair.com/$repo/os/$arch +#Server = http://mirror.redsox.cc/pub/archlinux/$repo/os/$arch +#Server = https://mirror.redsox.cc/archlinux/$repo/os/$arch +#Server = http://mirrors.rit.edu/archlinux/$repo/os/$arch +#Server = https://mirrors.rit.edu/archlinux/$repo/os/$arch +#Server = http://mirrors.rutgers.edu/archlinux/$repo/os/$arch +#Server = https://mirrors.rutgers.edu/archlinux/$repo/os/$arch +#Server = https://mirrors.tuxns.net/archlinux/$repo/os/$arch +#Server = http://mirror.umd.edu/archlinux/$repo/os/$arch +#Server = http://mirror.vtti.vt.edu/archlinux/$repo/os/$arch +#Server = http://mirrors.xmission.com/archlinux/$repo/os/$arch +#Server = http://mirror.yellowfiber.net/archlinux/$repo/os/$arch + +## Vietnam +#Server = http://f.archlinuxvn.org/archlinux/$repo/os/$arch +#Server = http://mirror-fpt-telecom.fpt.net/archlinux/$repo/os/$arch + diff --git a/libpkg/testfiles/pacman.conf b/libpkg/testfiles/pacman.conf new file mode 100644 index 0000000..45f0280 --- /dev/null +++ b/libpkg/testfiles/pacman.conf @@ -0,0 +1,100 @@ +# +# /etc/pacman.conf +# +# See the pacman.conf(5) manpage for option and repository directives + +# +# GENERAL OPTIONS +# +[options] +# The following paths are commented out with their default values listed. +# If you wish to use different paths, uncomment and update the paths. +#RootDir = / +DBPath = /db/path/ +CacheDir = /cache/path/ +#LogFile = /var/log/pacman.log +#GPGDir = /etc/pacman.d/gnupg/ +#HookDir = /etc/pacman.d/hooks/ +HoldPkg = pacman glibc +#XferCommand = /usr/bin/curl -C - -f %u > %o +#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u +#CleanMethod = KeepInstalled +#UseDelta = 0.7 +Architecture = i686 + +# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup +#IgnorePkg = +#IgnoreGroup = + +#NoUpgrade = +#NoExtract = + +# Misc options +#UseSyslog +#Color +#TotalDownload +CheckSpace +#VerbosePkgLists + +# By default, pacman accepts packages signed by keys that its local keyring +# trusts (see pacman-key and its man page), as well as unsigned packages. +SigLevel = Required DatabaseOptional +LocalFileSigLevel = Optional +#RemoteFileSigLevel = Required + +# NOTE: You must run `pacman-key --init` before first using pacman; the local +# keyring can then be populated with the keys of all official Arch Linux +# packagers with `pacman-key --populate archlinux`. + +# +# REPOSITORIES +# - can be defined here or included from another file +# - pacman will search repositories in the order defined here +# - local/custom mirrors can be added here or in separate files +# - repositories listed first will take precedence when packages +# have identical names, regardless of version number +# - URLs will have $repo replaced by the name of the current repo +# - URLs will have $arch replaced by the name of the architecture +# +# Repository entries are of the format: +# [repo-name] +# Server = ServerName +# Include = IncludePath +# +# The header [repo-name] is crucial - it must be present and +# uncommented to enable the repo. +# + +# The testing repositories are disabled by default. To enable, uncomment the +# repo name header and Include lines. You can add preferred servers immediately +# after the header, and they will be used before the default mirrors. + +#[testing] +#Include = /etc/pacman.d/mirrorlist + +[core] +Include = /etc/pacman.d/mirrorlist + +[extra] +Include = /etc/pacman.d/mirrorlist + +#[community-testing] +#Include = /etc/pacman.d/mirrorlist + +[community] +Include = /etc/pacman.d/mirrorlist + +# If you want to run 32 bit applications on your x86_64 system, +# enable the multilib repositories as required here. + +#[multilib-testing] +#Include = /etc/pacman.d/mirrorlist + +#[multilib] +#Include = /etc/pacman.d/mirrorlist + +# An example of a custom package repository. See the pacman manpage for +# tips on creating your own repositories. +#[custom] +#SigLevel = Optional TrustAll +#Server = file:///home/custompkgs diff --git a/libpkg/testfiles/perl-data-dumper-concise/PKGBUILD b/libpkg/testfiles/perl-data-dumper-concise/PKGBUILD new file mode 100644 index 0000000..b7b1c7c --- /dev/null +++ b/libpkg/testfiles/perl-data-dumper-concise/PKGBUILD @@ -0,0 +1,14 @@ +pkgname='perl-data-dumper-concise' +pkgver='2.023' +_distdir="Data-Dumper-Concise-${pkgver}" +pkgrel='1' +pkgdesc="Perl/CPAN Module Data::Dumper::Concise: Less indentation and newlines plus sub deparsing" +arch=('any') +license=('PerlArtistic' 'GPL') +options=('!emptydirs') +depends=('perl>=5.006') +makedepends=() +url='https://metacpan.org/release/Data-Dumper-Concise' +source=("http://search.cpan.org/CPAN/authors/id/E/ET/ETHER/${_distdir}.tar.gz") +md5sums=('12aee80af6ffb8ad9db6f008620300ba') +sha512sums=('cc193bc60228dd3bb1c50d4545ee6a507d80ada27548303bb04fd720504746a2d5a4f32d7c33f52dfaf135a2456e019d794a7d693e2c56df9ea9d0ff385e8cd0') diff --git a/libpkg/testfiles/perl-data-dumper-concise/PKGBUILD.newpkgrel b/libpkg/testfiles/perl-data-dumper-concise/PKGBUILD.newpkgrel new file mode 100644 index 0000000..2089364 --- /dev/null +++ b/libpkg/testfiles/perl-data-dumper-concise/PKGBUILD.newpkgrel @@ -0,0 +1,14 @@ +pkgname='perl-data-dumper-concise' +pkgver='2.023' +_distdir="Data-Dumper-Concise-${pkgver}" +pkgrel=1.1 +pkgdesc="Perl/CPAN Module Data::Dumper::Concise: Less indentation and newlines plus sub deparsing" +arch=('any') +license=('PerlArtistic' 'GPL') +options=('!emptydirs') +depends=('perl>=5.006') +makedepends=() +url='https://metacpan.org/release/Data-Dumper-Concise' +source=("http://search.cpan.org/CPAN/authors/id/E/ET/ETHER/${_distdir}.tar.gz") +md5sums=('12aee80af6ffb8ad9db6f008620300ba') +sha512sums=('cc193bc60228dd3bb1c50d4545ee6a507d80ada27548303bb04fd720504746a2d5a4f32d7c33f52dfaf135a2456e019d794a7d693e2c56df9ea9d0ff385e8cd0') diff --git a/libpkg/testfiles/perl/perl-linux-desktopfiles-0.22-2-any.pkg.tar.xz b/libpkg/testfiles/perl/perl-linux-desktopfiles-0.22-2-any.pkg.tar.xz new file mode 100644 index 0000000..135a86b Binary files /dev/null and b/libpkg/testfiles/perl/perl-linux-desktopfiles-0.22-2-any.pkg.tar.xz differ diff --git a/libpkg/testfiles/python/sphinxbase-5prealpha-7-i686.pkg.tar.xz b/libpkg/testfiles/python/sphinxbase-5prealpha-7-i686.pkg.tar.xz new file mode 100644 index 0000000..490acde Binary files /dev/null and b/libpkg/testfiles/python/sphinxbase-5prealpha-7-i686.pkg.tar.xz differ diff --git a/libpkg/testfiles/repo/bar/cmake-3.8.2-1-x86_64.pkg.tar.xz b/libpkg/testfiles/repo/bar/cmake-3.8.2-1-x86_64.pkg.tar.xz new file mode 120000 index 0000000..e6f4924 --- /dev/null +++ b/libpkg/testfiles/repo/bar/cmake-3.8.2-1-x86_64.pkg.tar.xz @@ -0,0 +1 @@ +../../cmake/cmake-3.8.2-1-x86_64.pkg.tar.xz \ No newline at end of file diff --git a/libpkg/testfiles/repo/foo/fake-0-any.pkg.tar.zst b/libpkg/testfiles/repo/foo/fake-0-any.pkg.tar.zst new file mode 100644 index 0000000..2fcaace --- /dev/null +++ b/libpkg/testfiles/repo/foo/fake-0-any.pkg.tar.zst @@ -0,0 +1 @@ +not a real package diff --git a/libpkg/testfiles/repo/foo/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz b/libpkg/testfiles/repo/foo/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz new file mode 120000 index 0000000..1327910 --- /dev/null +++ b/libpkg/testfiles/repo/foo/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz @@ -0,0 +1 @@ +../../mingw-w64-harfbuzz/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz \ No newline at end of file diff --git a/libpkg/testfiles/repo/foo/missing-0-any.pkg.tar.zst b/libpkg/testfiles/repo/foo/missing-0-any.pkg.tar.zst new file mode 120000 index 0000000..ee1f6cb --- /dev/null +++ b/libpkg/testfiles/repo/foo/missing-0-any.pkg.tar.zst @@ -0,0 +1 @@ +does_not_exist \ No newline at end of file diff --git a/libpkg/testfiles/repo/foo/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz b/libpkg/testfiles/repo/foo/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz new file mode 120000 index 0000000..cf5883e --- /dev/null +++ b/libpkg/testfiles/repo/foo/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz @@ -0,0 +1 @@ +../../syncthingtray/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz \ No newline at end of file diff --git a/libpkg/testfiles/syncthingtray/libsyncthingconnector.so.0.6.2 b/libpkg/testfiles/syncthingtray/libsyncthingconnector.so.0.6.2 new file mode 100755 index 0000000..b36170b Binary files /dev/null and b/libpkg/testfiles/syncthingtray/libsyncthingconnector.so.0.6.2 differ diff --git a/libpkg/testfiles/syncthingtray/libsyncthingmodel.so.0.6.2 b/libpkg/testfiles/syncthingtray/libsyncthingmodel.so.0.6.2 new file mode 100755 index 0000000..39843bf Binary files /dev/null and b/libpkg/testfiles/syncthingtray/libsyncthingmodel.so.0.6.2 differ diff --git a/libpkg/testfiles/syncthingtray/libsyncthingwidgets.so.0.6.2 b/libpkg/testfiles/syncthingtray/libsyncthingwidgets.so.0.6.2 new file mode 100755 index 0000000..1bbe9f7 Binary files /dev/null and b/libpkg/testfiles/syncthingtray/libsyncthingwidgets.so.0.6.2 differ diff --git a/libpkg/testfiles/syncthingtray/syncthingctl b/libpkg/testfiles/syncthingtray/syncthingctl new file mode 100755 index 0000000..3a50f12 Binary files /dev/null and b/libpkg/testfiles/syncthingtray/syncthingctl differ diff --git a/libpkg/testfiles/syncthingtray/syncthingtray b/libpkg/testfiles/syncthingtray/syncthingtray new file mode 100755 index 0000000..d010124 Binary files /dev/null and b/libpkg/testfiles/syncthingtray/syncthingtray differ diff --git a/libpkg/testfiles/syncthingtray/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz b/libpkg/testfiles/syncthingtray/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz new file mode 100644 index 0000000..ccdda1c Binary files /dev/null and b/libpkg/testfiles/syncthingtray/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz differ diff --git a/libpkg/tests/cppunit.cpp b/libpkg/tests/cppunit.cpp new file mode 100644 index 0000000..67aaee6 --- /dev/null +++ b/libpkg/tests/cppunit.cpp @@ -0,0 +1 @@ +#include diff --git a/libpkg/tests/data.cpp b/libpkg/tests/data.cpp new file mode 100644 index 0000000..250a450 --- /dev/null +++ b/libpkg/tests/data.cpp @@ -0,0 +1,484 @@ +#include "../data/config.h" + +#include +#include +#include +#include + +using CppUtilities::operator<<; // must be visible prior to the call site +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; +using namespace CppUtilities::Literals; +using namespace LibPkg; + +class DataTests : public TestFixture { + CPPUNIT_TEST_SUITE(DataTests); + CPPUNIT_TEST(testPackageVersionComparsion); + CPPUNIT_TEST(testDependencyStringConversion); + CPPUNIT_TEST(testDependencyMatching); + CPPUNIT_TEST(testPackageSearch); + CPPUNIT_TEST(testComputingFileName); + CPPUNIT_TEST(testDetectingUnresolved); + CPPUNIT_TEST(testComputingBuildOrder); + CPPUNIT_TEST(testComputingDatabaseOrder); + CPPUNIT_TEST(testComputingDatabasesRequiringDatabase); + CPPUNIT_TEST(testUpdateCheck); + CPPUNIT_TEST(testLocatePackage); + CPPUNIT_TEST(testAddingDepsAndProvidesFromOtherPackage); + CPPUNIT_TEST(testDependencyExport); + CPPUNIT_TEST(testMisc); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + + void testPackageVersionComparsion(); + void testDependencyStringConversion(); + void testDependencyMatching(); + void testPackageSearch(); + void testComputingFileName(); + void testDetectingUnresolved(); + void testComputingBuildOrder(); + void setupTestDbs(std::size_t dbCount = 5); + Database &db(std::size_t dbNum); + std::vector dbs(std::initializer_list dbNums); + void testComputingDatabaseOrder(); + void testComputingDatabasesRequiringDatabase(); + void testUpdateCheck(); + void testLocatePackage(); + void testAddingDepsAndProvidesFromOtherPackage(); + void testDependencyExport(); + void testMisc(); + +private: + Config m_config; + shared_ptr m_pkg1, m_pkg2, m_pkg3; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DataTests); + +void DataTests::setUp() +{ + m_pkg1 = make_shared(); + m_pkg1->name = "foo"; + m_pkg1->version = "5.6-6"; + m_pkg1->dependencies.emplace_back("bar>=5.5"); + m_pkg1->dependencies.emplace_back("bar<5.6"); + m_pkg2 = make_shared(); + m_pkg2->name = "bar"; + m_pkg2->version = "5.5-1"; + m_pkg2->provides.emplace_back("foo", "5.8-1"); + m_pkg3 = make_shared(); + m_pkg3->name = "foo"; + m_pkg3->version = "5.7-1"; + Database db1; + db1.name = "db1"; + db1.updatePackage(m_pkg1); + db1.updatePackage(m_pkg2); + m_config.databases.emplace_back(move(db1)); + Database db2; + db2.name = "db2"; + db2.updatePackage(m_pkg3); + m_config.databases.emplace_back(move(db2)); +} + +void DataTests::tearDown() +{ +} + +void DataTests::testPackageVersionComparsion() +{ + Package pkg1, pkg2; + pkg1.version = pkg2.version = "1234.5678-9"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::Equal, pkg1.compareVersion(pkg2)); + pkg2.version = "01234.5678-9"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::Equal, pkg1.compareVersion(pkg2)); + pkg2.version = "1235.5678-9"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::SoftwareUpgrade, pkg1.compareVersion(pkg2)); + pkg2.version = "1:12"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::SoftwareUpgrade, pkg1.compareVersion(pkg2)); + pkg2.version = "1235.5679-1"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::SoftwareUpgrade, pkg1.compareVersion(pkg2)); + pkg2.version = "12356.5678-1"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::SoftwareUpgrade, pkg1.compareVersion(pkg2)); + pkg2.version = "1234.5678-10"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::PackageUpgradeOnly, pkg1.compareVersion(pkg2)); + pkg2.version = "1234.5678-9.1"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::PackageUpgradeOnly, pkg1.compareVersion(pkg2)); + pkg2.version = "1234.5678-8"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::NewerThanSyncVersion, pkg1.compareVersion(pkg2)); + pkg2.version = "1224.5678-9"; + CPPUNIT_ASSERT_EQUAL(PackageVersionComparison::NewerThanSyncVersion, pkg1.compareVersion(pkg2)); +} + +void DataTests::testDependencyStringConversion() +{ + CPPUNIT_ASSERT_EQUAL("foo>=4.5: bar"s, Dependency("foo", "4.5", DependencyMode::GreatherEqual, "bar").toString()); + CPPUNIT_ASSERT_EQUAL("foo=4.5"s, Dependency("foo", "4.5", DependencyMode::Equal).toString()); + CPPUNIT_ASSERT_EQUAL("foo: bar"s, Dependency("foo", "4.5", DependencyMode::Any, "bar").toString()); + CPPUNIT_ASSERT_EQUAL("foo"s, Dependency("foo", "4.5", DependencyMode::Any).toString()); +} + +void DataTests::testDependencyMatching() +{ + Package pkg; + pkg.name = "my-foo"; + pkg.version = "123.2.4-1"; + pkg.provides.emplace_back("foo", "123.2.4-1"); + + Dependency dep; + dep.name = "my-foo"; + CPPUNIT_ASSERT_MESSAGE("without constraint, direct match", pkg.providesDependency(dep)); + dep.name = "bar"; + CPPUNIT_ASSERT_MESSAGE("without constraint, no match", !pkg.providesDependency(dep)); + dep.name = "foo"; + CPPUNIT_ASSERT_MESSAGE("without constraint, match via provides", pkg.providesDependency(dep)); + dep.name = "my-foo"; + dep.version = pkg.version; + dep.mode = DependencyMode::Equal; + CPPUNIT_ASSERT_MESSAGE("equal constraint, direct match", pkg.providesDependency(dep)); + dep.mode = DependencyMode::GreatherEqual; + CPPUNIT_ASSERT_MESSAGE("greather equal constraint, no match", pkg.providesDependency(dep)); + dep.mode = DependencyMode::GreatherThan; + CPPUNIT_ASSERT_MESSAGE("greather constraint, no match", !pkg.providesDependency(dep)); + dep.mode = DependencyMode::LessEqual; + CPPUNIT_ASSERT_MESSAGE("less equal constraint, match", pkg.providesDependency(dep)); + dep.mode = DependencyMode::LessThan; + CPPUNIT_ASSERT_MESSAGE("less constraint, no match", !pkg.providesDependency(dep)); + dep.mode = DependencyMode::Equal; + pkg.version += '0'; + CPPUNIT_ASSERT_MESSAGE("equal constraint, no match", !pkg.providesDependency(dep)); + dep.mode = DependencyMode::GreatherEqual; + CPPUNIT_ASSERT_MESSAGE("greather equal constraint, direct match", pkg.providesDependency(dep)); + pkg.version = "123.4"; + CPPUNIT_ASSERT_MESSAGE("greather equal constraint, direct match", pkg.providesDependency(dep)); + dep.name = "foo"; + CPPUNIT_ASSERT_MESSAGE("greather equal constraint, indirect match", pkg.providesDependency(dep)); + + Package pkg2; + pkg2.name = "crypto++"; + pkg2.version = "5.6.5-1"; + const char *depStr = "crypto++=5.6.5-1"; + CPPUNIT_ASSERT_MESSAGE("name with plus signs", pkg2.providesDependency(Dependency::fromString(depStr, 8))); + CPPUNIT_ASSERT_MESSAGE("equal constraint with explicitely specified pkgrel", pkg2.providesDependency(Dependency::fromString(depStr, 16))); + CPPUNIT_ASSERT_MESSAGE("equal constraint, default pkgrel should match", pkg2.providesDependency(Dependency::fromString(depStr, 14))); + Package pkg3; + pkg3.name = "crypto++"; + pkg3.version = "5.6.5-2"; + CPPUNIT_ASSERT_MESSAGE("equal constraint, any pkgrel should match", pkg3.providesDependency(Dependency::fromString(depStr, 14))); + CPPUNIT_ASSERT_MESSAGE( + "equal constraint with explicitely specified pkgrel must not match", !pkg3.providesDependency(Dependency::fromString(depStr, 16))); + + depStr = "crypto++>=5.6.5-1"; + CPPUNIT_ASSERT_MESSAGE( + "greather equal constraint with explicitely specified pkgrel", pkg2.providesDependency(Dependency::fromString(depStr, 17))); + CPPUNIT_ASSERT_MESSAGE("greather equal constraint, default pkgrel should match", pkg2.providesDependency(Dependency::fromString(depStr, 15))); + CPPUNIT_ASSERT_MESSAGE("greather equal constrainer, any pkgrel should match", pkg3.providesDependency(Dependency::fromString(depStr, 15))); + CPPUNIT_ASSERT_MESSAGE( + "greather equal constraint with explicitely specified pkgrel must match", pkg3.providesDependency(Dependency::fromString(depStr, 17))); + + Package pkg4; + pkg3.name = "ffmpeg"; + pkg3.version = "1:4.1-3"; + CPPUNIT_ASSERT(pkg3.providesDependency(Dependency::fromString("ffmpeg<1:4.3"))); + pkg3.version = "1:4.1-2"; + CPPUNIT_ASSERT(!pkg3.providesDependency(Dependency::fromString("ffmpeg>=1:4.1-3"))); +} + +void DataTests::testPackageSearch() +{ + auto pkg = m_config.findPackage(Dependency("foo")); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "find package returns the package from the first database", &m_config.databases.front(), std::get(pkg.db)); + CPPUNIT_ASSERT_EQUAL(m_pkg1, pkg.pkg); + + auto pkgs = m_config.findPackages("foo"sv); + CPPUNIT_ASSERT_EQUAL(2_st, pkgs.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg1, pkgs.front().pkg); + CPPUNIT_ASSERT_EQUAL(m_pkg3, pkgs.back().pkg); + + pkgs = m_config.findPackages("bar"sv); + CPPUNIT_ASSERT_EQUAL(1_st, pkgs.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg2, pkgs.front().pkg); + + pkgs = m_config.findPackages("db2/foo"sv); + CPPUNIT_ASSERT_EQUAL(1_st, pkgs.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg3, pkgs.front().pkg); + + pkgs = m_config.findPackages("db2/bar"sv); + CPPUNIT_ASSERT_EQUAL(0_st, pkgs.size()); + + pkgs = m_config.findPackages(Dependency("foo", "5.7-1", DependencyMode::Any)); + CPPUNIT_ASSERT_EQUAL(3_st, pkgs.size()); + + pkgs = m_config.findPackages(Dependency("foo", "5.8-1", DependencyMode::GreatherEqual)); + CPPUNIT_ASSERT_EQUAL(1_st, pkgs.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg2, pkgs.front().pkg); + + pkgs = m_config.findPackages(Dependency("bar", "5.5-1", DependencyMode::Equal)); + CPPUNIT_ASSERT_EQUAL(1_st, pkgs.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg2, pkgs.front().pkg); + + pkgs = m_config.findPackages(Dependency("bar", "5.8-1", DependencyMode::Equal)); + CPPUNIT_ASSERT_EQUAL(0_st, pkgs.size()); +} + +void DataTests::testComputingFileName() +{ + Package pkg; + pkg.name = "test"; + pkg.version = "1.2-3"; + CPPUNIT_ASSERT_EQUAL_MESSAGE("packageInfo required for computing filename", string(), pkg.computeFileName()); + pkg.packageInfo = make_unique(); + pkg.packageInfo->arch = "x86_64"; + CPPUNIT_ASSERT_EQUAL_MESSAGE("file name computed from name, version and arch", "test-1.2-3-x86_64.pkg.tar.zst"s, pkg.computeFileName()); + pkg.packageInfo->fileName = "explicitely-specified-filename"; + CPPUNIT_ASSERT_EQUAL_MESSAGE("explicitely specified filename returned", "explicitely-specified-filename"s, pkg.computeFileName()); +} + +void DataTests::testDetectingUnresolved() +{ + auto &db1(m_config.databases[0]); + + CPPUNIT_ASSERT_EQUAL(0_st, db1.detectUnresolvedPackages(m_config, {}, {}).size()); + + // upgrade bar to 5.6, foo should be unresolvable + m_pkg2->version = "5.6"; + DependencySet removedPackages; + removedPackages.add(Dependency("bar", "5.5"), m_pkg2); + const auto failures(db1.detectUnresolvedPackages(m_config, { m_pkg2 }, removedPackages)); + CPPUNIT_ASSERT_EQUAL(1_st, failures.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg1, failures.begin()->first); +} + +void DataTests::testComputingBuildOrder() +{ + // order correctly changed according to dependencies + auto res = m_config.computeBuildOrder({ "foo", "bar" }, BuildOrderOptions::None); + CPPUNIT_ASSERT_EQUAL(true, res.success); + CPPUNIT_ASSERT_EQUAL(2_st, res.order.size()); + CPPUNIT_ASSERT_EQUAL("bar"s, res.order[0].pkg->name); + CPPUNIT_ASSERT_EQUAL("foo"s, res.order[1].pkg->name); + CPPUNIT_ASSERT_EQUAL(0_st, res.ignored.size()); + + // unknown package ignored + const vector ignored = { "baz" }; + res = m_config.computeBuildOrder({ "foo", "bar", ignored[0] }, BuildOrderOptions::None); + CPPUNIT_ASSERT_EQUAL(false, res.success); + CPPUNIT_ASSERT_EQUAL(2_st, res.order.size()); + CPPUNIT_ASSERT_EQUAL("bar"s, res.order[0].pkg->name); + CPPUNIT_ASSERT_EQUAL("foo"s, res.order[1].pkg->name); + CPPUNIT_ASSERT_EQUAL(ignored, res.ignored); + + // add cycle + auto &db(m_config.databases[0]); + auto tar = make_shared(); + tar->name = "tar"; + tar->version = "5.6-6"; + tar->dependencies.emplace_back("foo"); + m_pkg2->dependencies.emplace_back("tar"); // let bar depend on tar + db.updatePackage(tar); + + // fail due to cycle + res = m_config.computeBuildOrder({ "foo", "bar", "tar" }, BuildOrderOptions::None); + CPPUNIT_ASSERT_EQUAL(false, res.success); + CPPUNIT_ASSERT_EQUAL(3_st, res.cycle.size()); + CPPUNIT_ASSERT_EQUAL("foo"s, res.cycle[0].pkg->name); + CPPUNIT_ASSERT_EQUAL("bar"s, res.cycle[1].pkg->name); + CPPUNIT_ASSERT_EQUAL("tar"s, res.cycle[2].pkg->name); + CPPUNIT_ASSERT_EQUAL(0_st, res.order.size()); + CPPUNIT_ASSERT_EQUAL(0_st, res.ignored.size()); + + // fail due to cycle even if package not in the specified list + res = m_config.computeBuildOrder({ "foo", "bar" }, BuildOrderOptions::None); + CPPUNIT_ASSERT_EQUAL(false, res.success); + CPPUNIT_ASSERT_EQUAL(3_st, res.cycle.size()); + CPPUNIT_ASSERT_EQUAL("foo"s, res.cycle[0].pkg->name); + CPPUNIT_ASSERT_EQUAL("bar"s, res.cycle[1].pkg->name); + CPPUNIT_ASSERT_EQUAL("tar"s, res.cycle[2].pkg->name); + CPPUNIT_ASSERT_EQUAL(0_st, res.order.size()); + CPPUNIT_ASSERT_EQUAL(0_st, res.ignored.size()); + + // ignore cycle if not interested in that particular package + m_pkg2->packageInfo = make_unique(); + tar->packageInfo = make_unique(); + tar->dependencies.clear(); + tar->dependencies.emplace_back("bar"); + res = m_config.computeBuildOrder({ "foo" }, BuildOrderOptions::None); + CPPUNIT_ASSERT_EQUAL(true, res.success); + CPPUNIT_ASSERT_EQUAL(0_st, res.cycle.size()); + CPPUNIT_ASSERT_EQUAL(1_st, res.order.size()); + CPPUNIT_ASSERT_EQUAL("foo"s, res.order[0].pkg->name); + CPPUNIT_ASSERT_EQUAL(0_st, res.ignored.size()); +} + +void DataTests::setupTestDbs(std::size_t dbCount) +{ + m_config.databases.reserve(m_config.databases.size() + dbCount); + for (std::size_t i = 1; i <= dbCount; ++i) { + m_config.findOrCreateDatabase(argsToString("db", i), "x86_64"); + } +} + +inline Database &DataTests::db(std::size_t dbNum) +{ + return m_config.databases[dbNum - 1]; +} + +inline std::vector DataTests::dbs(std::initializer_list dbNums) +{ + std::vector res; + res.reserve(dbNums.size()); + for (const auto dbNum : dbNums) { + res.emplace_back(&db(dbNum)); + } + return res; +} + +void DataTests::testComputingDatabaseOrder() +{ + setupTestDbs(); + db(1).dependencies = { "db2", "db3" }; + db(2).dependencies = { "db4" }; + db(3).dependencies = { "db5", "db2" }; + + CPPUNIT_ASSERT_EQUAL(dbs({ 5 }), get>(m_config.computeDatabaseDependencyOrder(db(5)))); + CPPUNIT_ASSERT_EQUAL(dbs({ 4 }), get>(m_config.computeDatabaseDependencyOrder(db(4)))); + CPPUNIT_ASSERT_EQUAL(dbs({ 5, 4, 2, 3 }), get>(m_config.computeDatabaseDependencyOrder(db(3)))); + CPPUNIT_ASSERT_EQUAL(dbs({ 4, 2 }), get>(m_config.computeDatabaseDependencyOrder(db(2)))); + CPPUNIT_ASSERT_EQUAL(dbs({ 4, 2, 5, 3, 1 }), get>(m_config.computeDatabaseDependencyOrder(db(1)))); + + db(4).dependencies = { "db1" }; + db(5).dependencies = { "db6" }; + + CPPUNIT_ASSERT_EQUAL("cycle at db1"s, get(m_config.computeDatabaseDependencyOrder(db(1)))); + CPPUNIT_ASSERT_EQUAL( + "database \"db6\" required by \"db5\" does not exist (architecture x86_64)"s, get(m_config.computeDatabaseDependencyOrder(db(3)))); +} + +void DataTests::testComputingDatabasesRequiringDatabase() +{ + setupTestDbs(); + db(1).dependencies = { "db2", "db3" }; + db(2).dependencies = { "db4" }; + db(3).dependencies = { "db5" }; + + CPPUNIT_ASSERT_EQUAL(dbs({ 1 }), m_config.computeDatabasesRequiringDatabase(db(1))); + CPPUNIT_ASSERT_EQUAL(dbs({ 2, 1 }), m_config.computeDatabasesRequiringDatabase(db(2))); + CPPUNIT_ASSERT_EQUAL(dbs({ 3, 1 }), m_config.computeDatabasesRequiringDatabase(db(3))); + CPPUNIT_ASSERT_EQUAL(dbs({ 4, 2, 1 }), m_config.computeDatabasesRequiringDatabase(db(4))); + CPPUNIT_ASSERT_EQUAL(dbs({ 5, 3, 1 }), m_config.computeDatabasesRequiringDatabase(db(5))); +} + +void DataTests::testUpdateCheck() +{ + auto &db1 = m_config.databases.front(); + auto &db2 = m_config.databases.back(); + const auto result = db1.checkForUpdates({ &db2 }); + CPPUNIT_ASSERT_EQUAL(1_st, result.versionUpdates.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg1, result.versionUpdates.front().oldVersion.pkg); + CPPUNIT_ASSERT_EQUAL(m_pkg3, result.versionUpdates.front().newVersion.pkg); + CPPUNIT_ASSERT_EQUAL(0_st, result.packageUpdates.size()); + CPPUNIT_ASSERT_EQUAL(0_st, result.downgrades.size()); + CPPUNIT_ASSERT_EQUAL(1_st, result.orphans.size()); + CPPUNIT_ASSERT_EQUAL(m_pkg2, result.orphans.front().pkg); +} + +void DataTests::testLocatePackage() +{ + const auto fakePkgPath = testFilePath("repo/foo/fake-0-any.pkg.tar.zst"); + const auto syncthingTrayPkgPath = testFilePath("repo/foo/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz"); + const auto syncthingTrayStorageLocation = std::filesystem::canonical(testFilePath("syncthingtray/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz")); + + auto &db = m_config.databases.front(); + db.localPkgDir = std::filesystem::path(fakePkgPath).parent_path(); + + const auto emptyPkg = db.locatePackage(std::string()); + CPPUNIT_ASSERT_EQUAL(std::string(), emptyPkg.pathWithinRepo.string()); + CPPUNIT_ASSERT_EQUAL(std::string(), emptyPkg.storageLocation.string()); + CPPUNIT_ASSERT(!emptyPkg.exists); + CPPUNIT_ASSERT(!emptyPkg.error.has_value()); + + const auto missingPkg = db.locatePackage("missing-0-any.pkg.tar.zst"); + CPPUNIT_ASSERT_EQUAL(db.localPkgDir + "/missing-0-any.pkg.tar.zst", missingPkg.pathWithinRepo.string()); + CPPUNIT_ASSERT_EQUAL(db.localPkgDir + "/does_not_exist"s, missingPkg.storageLocation.string()); + CPPUNIT_ASSERT(!missingPkg.exists); + CPPUNIT_ASSERT(!missingPkg.error.has_value()); + + const auto fakePkg = db.locatePackage("fake-0-any.pkg.tar.zst"); + CPPUNIT_ASSERT_EQUAL(fakePkgPath, fakePkg.pathWithinRepo.string()); + CPPUNIT_ASSERT_EQUAL(std::string(), fakePkg.storageLocation.string()); + CPPUNIT_ASSERT(fakePkg.exists); + CPPUNIT_ASSERT(!fakePkg.error.has_value()); + + const auto syncthingPkg = db.locatePackage("syncthingtray-0.6.2-1-x86_64.pkg.tar.xz"); + CPPUNIT_ASSERT_EQUAL(syncthingTrayPkgPath, syncthingPkg.pathWithinRepo.string()); + CPPUNIT_ASSERT_EQUAL(syncthingTrayStorageLocation, syncthingPkg.storageLocation); + CPPUNIT_ASSERT(fakePkg.exists); + CPPUNIT_ASSERT(!fakePkg.error.has_value()); +} + +void DataTests::testAddingDepsAndProvidesFromOtherPackage() +{ + const std::unordered_set dependenciesToTakeOver = { + Dependency{ "python2", "2.18", DependencyMode::LessThan }, + Dependency{ "python", "3.5", DependencyMode::LessThan }, + Dependency{ "perl", "5.32", DependencyMode::GreatherEqual }, + }; + m_pkg1->origin = PackageOrigin::PackageContents; + m_pkg1->dependencies.insert(m_pkg1->dependencies.end(), dependenciesToTakeOver.begin(), dependenciesToTakeOver.end()); + m_pkg1->libdepends.emplace("foo"); + m_pkg1->libprovides.emplace("bar"); + + Package pkg1; + CPPUNIT_ASSERT_MESSAGE("adding from itself always prevented", !pkg1.addDepsAndProvidesFromOtherPackage(pkg1, true)); + CPPUNIT_ASSERT_MESSAGE("not adding on version mismatch", !pkg1.addDepsAndProvidesFromOtherPackage(*m_pkg1)); + CPPUNIT_ASSERT_MESSAGE("adding from other package forced", pkg1.addDepsAndProvidesFromOtherPackage(*m_pkg1, true)); + CPPUNIT_ASSERT_EQUAL(PackageOrigin::PackageContents, pkg1.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("libdeps taken over", m_pkg1->libdepends, pkg1.libdepends); + CPPUNIT_ASSERT_EQUAL_MESSAGE("libprovides taken over", m_pkg1->libprovides, pkg1.libprovides); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "relevant deps taken over", dependenciesToTakeOver, std::unordered_set(pkg1.dependencies.begin(), pkg1.dependencies.end())); + + pkg1.version = m_pkg1->version; + CPPUNIT_ASSERT_MESSAGE("adding from other package when version matches", pkg1.addDepsAndProvidesFromOtherPackage(*m_pkg1)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no duplicate libdeps after 2nd run", m_pkg1->libdepends, pkg1.libdepends); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no duplicate libprovides after 2nd run", m_pkg1->libprovides, pkg1.libprovides); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no duplicate deps after 2nd run", dependenciesToTakeOver, + std::unordered_set(pkg1.dependencies.begin(), pkg1.dependencies.end())); +} + +void DataTests::testDependencyExport() +{ + m_pkg2->provides.emplace_back("yet-another-dependency"); + m_pkg2->libprovides.emplace("libfoo"); + m_pkg2->libprovides.emplace("libbar"); + DependencySet provides; + std::unordered_set libProvides; + Package::exportProvides(m_pkg2, provides, libProvides); + CPPUNIT_ASSERT_EQUAL(3_st, provides.size()); + CPPUNIT_ASSERT(provides.provides("foo", DependencyDetail())); + CPPUNIT_ASSERT(provides.provides("bar", DependencyDetail())); + CPPUNIT_ASSERT(provides.provides("yet-another-dependency", DependencyDetail())); + CPPUNIT_ASSERT_EQUAL(2_st, libProvides.size()); + CPPUNIT_ASSERT(libProvides.contains("libfoo")); + CPPUNIT_ASSERT(libProvides.contains("libbar")); +} + +void DataTests::testMisc() +{ + CPPUNIT_ASSERT_EQUAL("123.4"s, PackageVersion::trimPackageVersion("123.4"s)); + CPPUNIT_ASSERT_EQUAL("123"s, PackageVersion::trimPackageVersion("123-4"s)); + CPPUNIT_ASSERT_EQUAL("123"s, PackageVersion::trimPackageVersion("123"s)); +} diff --git a/libpkg/tests/parser.cpp b/libpkg/tests/parser.cpp new file mode 100644 index 0000000..25ad75b --- /dev/null +++ b/libpkg/tests/parser.cpp @@ -0,0 +1,315 @@ +#include "./parser_helper.h" + +#include "../parser/binary.h" +#include "../parser/config.h" +#include "../parser/package.h" + +#include +#include +#include +#include +#include + +using CppUtilities::operator<<; // must be visible prior to the call site +#include +#include + +#include +#include +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; +using namespace LibPkg; +using namespace TestHelper; + +class ParserTests : public TestFixture { + CPPUNIT_TEST_SUITE(ParserTests); + CPPUNIT_TEST(testParsingDependencies); + CPPUNIT_TEST(testParsingPackageVersion); + CPPUNIT_TEST(testParsingPackageName); + CPPUNIT_TEST(testParsingConfig); + CPPUNIT_TEST(testParsingPlainSrcInfo); + CPPUNIT_TEST(testParsingSplitPackageSrcInfo); + CPPUNIT_TEST(testParsingPkgInfo); + CPPUNIT_TEST(testParsingPkgName); + CPPUNIT_TEST(testExtractingPkgFile); + CPPUNIT_TEST(testParsingDescriptions); + CPPUNIT_TEST(testParsingDatabase); + CPPUNIT_TEST(testParsingSignatureLevel); + CPPUNIT_TEST(testSerializingDatabaseSignatureLevel); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + + void testParsingDependencies(); + void testParsingPackageVersion(); + void testParsingPackageName(); + void testParsingConfig(); + void testParsingPlainSrcInfo(); + void testParsingSplitPackageSrcInfo(); + void testParsingPkgInfo(); + void testParsingPkgName(); + void testExtractingPkgFile(); + void testParsingDescriptions(); + void testParsingDatabase(); + void testParsingSignatureLevel(); + void testSerializingDatabaseSignatureLevel(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ParserTests); + +void ParserTests::setUp() +{ +} + +void ParserTests::tearDown() +{ +} + +void ParserTests::testParsingDependencies() +{ + const auto ffmpeg = Dependency("ffmpeg>1:4.2-3"); + CPPUNIT_ASSERT_EQUAL("ffmpeg"s, ffmpeg.name); + CPPUNIT_ASSERT_EQUAL("1:4.2-3"s, ffmpeg.version); + CPPUNIT_ASSERT_EQUAL(string(), ffmpeg.description); + + const auto ffmpeg2 = Dependency("ffmpeg: support more formats"); + CPPUNIT_ASSERT_EQUAL("ffmpeg"s, ffmpeg2.name); + CPPUNIT_ASSERT_EQUAL(string(), ffmpeg2.version); + CPPUNIT_ASSERT_EQUAL("support more formats"s, ffmpeg2.description); + + const auto ffmpeg3 = Dependency("ffmpeg:support more formats"); + CPPUNIT_ASSERT_EQUAL("ffmpeg"s, ffmpeg3.name); + CPPUNIT_ASSERT_EQUAL(string(), ffmpeg3.version); + CPPUNIT_ASSERT_EQUAL("support more formats"s, ffmpeg3.description); + + const auto ffmpeg4 = Dependency("ffmpeg=1:4.3:support more formats"); + CPPUNIT_ASSERT_EQUAL("ffmpeg"s, ffmpeg4.name); + CPPUNIT_ASSERT_EQUAL("1:4.3"s, ffmpeg4.version); + CPPUNIT_ASSERT_EQUAL("support more formats"s, ffmpeg4.description); +} + +void ParserTests::testParsingPackageVersion() +{ + PackageVersion ver = PackageVersion::fromString("2:12-3.45a-6"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("epoch", string("2"), ver.epoch); + CPPUNIT_ASSERT_EQUAL_MESSAGE("upstream", string("12-3.45a"), ver.upstream); + CPPUNIT_ASSERT_EQUAL_MESSAGE("package", string("6"), ver.package); + ver = PackageVersion::fromString("12-3.45a-"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("empty epoch", string(), ver.epoch); + CPPUNIT_ASSERT_EQUAL_MESSAGE("only upstream", string("12-3.45a"), ver.upstream); + CPPUNIT_ASSERT_EQUAL_MESSAGE("empty package", string(), ver.package); +} + +void ParserTests::testParsingPackageName() +{ + const auto gcc = PackageNameData::decompose("gcc"); + CPPUNIT_ASSERT_EQUAL("gcc"sv, gcc.actualName); + CPPUNIT_ASSERT_EQUAL(""sv, gcc.targetPrefix); + CPPUNIT_ASSERT_EQUAL(""sv, gcc.vcsSuffix); + CPPUNIT_ASSERT(!gcc.isVcsPackage()); + const auto mingwGCC = PackageNameData::decompose("mingw-w64-gcc"); + CPPUNIT_ASSERT_EQUAL("gcc"sv, mingwGCC.actualName); + CPPUNIT_ASSERT_EQUAL("mingw-w64"sv, mingwGCC.targetPrefix); + CPPUNIT_ASSERT_EQUAL(""sv, mingwGCC.vcsSuffix); + const auto armGCC = PackageNameData::decompose("aarch64-linux-gnu-gcc"); + CPPUNIT_ASSERT_EQUAL("gcc"sv, armGCC.actualName); + CPPUNIT_ASSERT_EQUAL("aarch64-linux-gnu"sv, armGCC.targetPrefix); + CPPUNIT_ASSERT_EQUAL(""sv, armGCC.vcsSuffix); + const auto qt = PackageNameData::decompose("qt5-base-git"); + CPPUNIT_ASSERT_EQUAL("qt5-base"sv, qt.actualName); + CPPUNIT_ASSERT_EQUAL(""sv, qt.targetPrefix); + CPPUNIT_ASSERT_EQUAL("git"sv, qt.vcsSuffix); + const auto mingwQt = PackageNameData::decompose("mingw-w64-qt5-base-git"); + CPPUNIT_ASSERT_EQUAL("qt5-base"sv, mingwQt.actualName); + CPPUNIT_ASSERT_EQUAL("mingw-w64"sv, mingwQt.targetPrefix); + CPPUNIT_ASSERT_EQUAL("git"sv, mingwQt.vcsSuffix); + CPPUNIT_ASSERT(mingwQt.isVcsPackage()); + const auto qwt = PackageNameData::decompose("qwt-qt6-svn"); + CPPUNIT_ASSERT_EQUAL("qwt"sv, qwt.actualName); + CPPUNIT_ASSERT_EQUAL(""sv, qwt.targetPrefix); + CPPUNIT_ASSERT_EQUAL("qt6-svn"sv, qwt.vcsSuffix); + CPPUNIT_ASSERT(qwt.isVcsPackage()); +} + +void ParserTests::testParsingConfig() +{ + // prepare pacman.conf + const string pacmanConfigWorkingCopyPath = workingCopyPath("pacman.conf"s, WorkingCopyMode::NoCopy); + { + const string mirrorListPath = testFilePath("mirrorlist"s); + string defaultPacmanConfig = readFile(testFilePath("pacman.conf"s), 5 * 1024); + findAndReplace(defaultPacmanConfig, "/etc/pacman.d/mirrorlist"s, mirrorListPath); + ofstream pacmanConfigWorkingCopy; + pacmanConfigWorkingCopy.exceptions(ios_base::failbit | ios_base::badbit); + pacmanConfigWorkingCopy.open(pacmanConfigWorkingCopyPath, ios_base::out | ios_base::trunc | ios_base::binary); + pacmanConfigWorkingCopy.write(defaultPacmanConfig.data(), static_cast(defaultPacmanConfig.size())); + } + + Config config; + config.loadPacmanConfig(pacmanConfigWorkingCopyPath.data()); + for (auto &db : config.databases) { + db.deducePathsFromLocalDirs(); + } + + CPPUNIT_ASSERT_EQUAL_MESSAGE("cache dir"s, vector{ "/cache/path/"s }, config.packageCacheDirs); + CPPUNIT_ASSERT_EQUAL_MESSAGE("pacman database path"s, "/db/path/"s, config.pacmanDatabasePath); + CPPUNIT_ASSERT_EQUAL_MESSAGE("3 databases found"s, 3ul, config.databases.size()); + CPPUNIT_ASSERT_EQUAL("core"s, config.databases[0].name); + CPPUNIT_ASSERT_EQUAL("extra"s, config.databases[1].name); + CPPUNIT_ASSERT_EQUAL("community"s, config.databases[2].name); + const vector mirrorsCore = { "http://ftp.fau.de/archlinux/core/os/i686"s, "https://ftp.fau.de/archlinux/core/os/i686"s }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("mirrors read correctly in first place"s, mirrorsCore, config.databases[0].mirrors); + const vector mirrorsExtra = { "http://ftp.fau.de/archlinux/extra/os/i686"s, "https://ftp.fau.de/archlinux/extra/os/i686"s }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("reusing already parsed mirror list"s, mirrorsExtra, config.databases[1].mirrors); + CPPUNIT_ASSERT_EQUAL_MESSAGE("regular database file"s, "/db/path/sync/extra.db"s, config.databases[1].path); + CPPUNIT_ASSERT_EQUAL_MESSAGE("database file containing files"s, "/db/path/sync/extra.files"s, config.databases[1].filesPath); + + // clean working copy + remove(pacmanConfigWorkingCopyPath.data()); +} + +void ParserTests::testParsingPlainSrcInfo() +{ + const auto srcInfo = readFile(testFilePath("c++utilities/SRCINFO"s)); + const auto packages = Package::fromInfo(srcInfo, false); + CPPUNIT_ASSERT_EQUAL_MESSAGE("1 package present"s, 1ul, packages.size()); + + const Package &pkg1 = *packages.front(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::SourceInfo, pkg1.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("name"s, "c++utilities"s, pkg1.name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("version"s, "4.5.0-1"s, pkg1.version); + CPPUNIT_ASSERT_MESSAGE("no regular dependencies"s, pkg1.dependencies.empty()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("optional dependencies"s, + vector{ Dependency("c++utilities-doc"s, string(), DependencyMode::Any, "API documentation"s) }, pkg1.optionalDependencies); + CPPUNIT_ASSERT_MESSAGE("source info present", pkg1.sourceInfo != nullptr); + CPPUNIT_ASSERT_MESSAGE("no package info present", pkg1.packageInfo == nullptr); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "make dependencies"s, vector{ Dependency("cmake"s, "3.0"s, DependencyMode::GreatherEqual) }, pkg1.sourceInfo->makeDependencies); + CPPUNIT_ASSERT_EQUAL_MESSAGE("check dependencies"s, vector{ Dependency("cppunit"s) }, pkg1.sourceInfo->checkDependencies); +} + +void ParserTests::testParsingSplitPackageSrcInfo() +{ + const auto srcInfo = readFile(testFilePath("mingw-w64-harfbuzz/SRCINFO"s)); + const auto packages = Package::fromInfo(srcInfo, false); + CPPUNIT_ASSERT_EQUAL_MESSAGE("2 (split) packages present"s, 2ul, packages.size()); + + const Package &pkg1 = *packages.front(), &pkg2 = *packages.back(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin (1)", PackageOrigin::SourceInfo, pkg1.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin (2)", PackageOrigin::SourceInfo, pkg2.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("name (1)"s, "mingw-w64-harfbuzz"s, pkg1.name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("name (2)"s, "mingw-w64-harfbuzz-icu"s, pkg2.name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("version (1)"s, "1.4.2-1"s, pkg1.version); + CPPUNIT_ASSERT_EQUAL_MESSAGE("version (2)"s, "1.4.2-1"s, pkg2.version); + const vector dependencies1 = { + Dependency("mingw-w64-freetype2"s), + Dependency("mingw-w64-glib2"s), + Dependency("mingw-w64-graphite"s), + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("dependencies (1)"s, dependencies1, pkg1.dependencies); + const vector dependencies2 = { + Dependency("mingw-w64-harfbuzz"s), + Dependency("mingw-w64-icu"s), + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("dependencies (2)"s, dependencies2, pkg2.dependencies); + CPPUNIT_ASSERT_MESSAGE("source info present (1)", pkg1.sourceInfo != nullptr); + CPPUNIT_ASSERT_MESSAGE("source info present (2)", pkg2.sourceInfo != nullptr); + CPPUNIT_ASSERT_MESSAGE("no package info present (1)", pkg1.packageInfo == nullptr); + CPPUNIT_ASSERT_MESSAGE("no package info present (2)", pkg2.packageInfo == nullptr); + CPPUNIT_ASSERT_EQUAL_MESSAGE("pkgbase (1)"s, "mingw-w64-harfbuzz"s, pkg1.sourceInfo->name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("pkgbase (2)"s, "mingw-w64-harfbuzz"s, pkg2.sourceInfo->name); + const vector archs = { "any"s }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch (1)"s, archs, pkg1.sourceInfo->archs); + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch (2)"s, archs, pkg2.sourceInfo->archs); +} + +void ParserTests::testParsingPkgInfo() +{ + const auto pkgInfo = readFile(testFilePath("mingw-w64-harfbuzz/PKGINFO")); + const auto packages = Package::fromInfo(pkgInfo, true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("1 package present"s, 1ul, packages.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageInfo, packages.front()->origin); + checkHarfbuzzPackage(*packages.front()); +} + +void ParserTests::testParsingPkgName() +{ + const auto pkg = Package::fromPkgFileName("texlive-localmanager-git-0.4.6.r0.gd71966e-1-any.pkg"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageFileName, pkg->origin); + CPPUNIT_ASSERT_EQUAL("texlive-localmanager-git"s, pkg->name); + CPPUNIT_ASSERT_EQUAL("0.4.6.r0.gd71966e-1"s, pkg->version); + CPPUNIT_ASSERT_EQUAL("any"s, pkg->packageInfo->arch); +} + +void ParserTests::testExtractingPkgFile() +{ + const auto pkgFilePath = testFilePath("mingw-w64-harfbuzz/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz"); + const auto package = Package::fromPkgFile(pkgFilePath); + checkHarfbuzzPackage(*package); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageContents, package->origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("file name"s, "mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz"s, package->packageInfo->fileName); +} + +void ParserTests::testParsingDescriptions() +{ + const auto desc = readFile(testFilePath("mingw-w64-harfbuzz/desc"s)); + const auto pkg = Package::fromDescription({ desc }); + checkHarfbuzzPackage(*pkg); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::Database, pkg->origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("file name"s, "mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz"s, pkg->packageInfo->fileName); + CPPUNIT_ASSERT_EQUAL_MESSAGE("md5sum"s, "a05d4618090b0294bc075e85791485f8"s, pkg->packageInfo->md5); + CPPUNIT_ASSERT_EQUAL_MESSAGE("sha256sum"s, "ff62339041c19d2a986eed8231fb8e1be723b3afd354cca833946305456e8ec7"s, pkg->packageInfo->sha256); + CPPUNIT_ASSERT_EQUAL_MESSAGE("sha256sum"s, "ff62339041c19d2a986eed8231fb8e1be723b3afd354cca833946305456e8ec7"s, pkg->packageInfo->sha256); +} + +void ParserTests::testParsingDatabase() +{ + // init config + Config config; + config.databases.emplace_back(); + + // init db object + Database &db = config.databases.back(); + db.path = testFilePath("core.db"); + db.filesPath = testFilePath("core.files"); + + // load packages + config.loadAllPackages(true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("all 215 packages present"s, 215ul, db.packages.size()); + const auto &autoreconf(db.packages.at("autoconf")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::Database, autoreconf->origin); + checkAutoconfPackage(*autoreconf); +} + +void ParserTests::testParsingSignatureLevel() +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("default signature level", "Optional TrustedOnly"s, signatureLevelToString(SignatureLevel::Default)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid signature level", std::string(), signatureLevelToString(SignatureLevel::Invalid)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("explicit 'when'", "Never TrustedOnly"s, signatureLevelToString(SignatureLevel::Never)); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "explicit 'when'; explicit 'what'", "Never TrustAll"s, signatureLevelToString(SignatureLevel::Never | SignatureLevel::TrustAll)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("explicit 'what'", "Optional TrustAll"s, signatureLevelToString(SignatureLevel::TrustAll)); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "same config for DBs and package", "Never TrustAll"s, SignatureLevelConfig(SignatureLevel::Never | SignatureLevel::TrustAll).toString()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("different config for DBs and package", "DatabaseOptional DatabaseTrustedOnly PackageNever PackageTrustAll"s, + SignatureLevelConfig(SignatureLevel::Default, SignatureLevel::Never | SignatureLevel::TrustAll).toString()); +} + +void ParserTests::testSerializingDatabaseSignatureLevel() +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "defaut signature level", SignatureLevelConfig(SignatureLevel::Default), SignatureLevelConfig::fromString(std::string_view())); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "invalid signature level", SignatureLevelConfig(SignatureLevel::Invalid), SignatureLevelConfig::fromString("foo bar")); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "explicit 'when'", SignatureLevelConfig(SignatureLevel::Never | SignatureLevel::TrustedOnly), SignatureLevelConfig::fromString("Never")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("explicit 'when'; explicit 'what'", SignatureLevelConfig(SignatureLevel::Never | SignatureLevel::TrustAll), + SignatureLevelConfig::fromString("Never TrustAll")); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "explicit 'what'", SignatureLevelConfig(SignatureLevel::Optional | SignatureLevel::TrustAll), SignatureLevelConfig::fromString("TrustAll")); +} diff --git a/libpkg/tests/parser_binary.cpp b/libpkg/tests/parser_binary.cpp new file mode 100644 index 0000000..01eeb7a --- /dev/null +++ b/libpkg/tests/parser_binary.cpp @@ -0,0 +1,390 @@ +#include "./parser_helper.h" + +#include "../parser/binary.h" +#include "../parser/config.h" +#include "../parser/package.h" + +#include +#include +#include +#include + +using CppUtilities::operator<<; // must be visible prior to the call site +#include +#include + +#include +#include +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; +using namespace LibPkg; +using namespace TestHelper; + +class BinaryParserTests : public TestFixture { + CPPUNIT_TEST_SUITE(BinaryParserTests); + CPPUNIT_TEST(testParsingElf); + CPPUNIT_TEST(testParsingPe); + CPPUNIT_TEST(testParsingAr); + CPPUNIT_TEST(testParsingElfFromPkgFile); + CPPUNIT_TEST(testParsingPeFromPkgFile); + CPPUNIT_TEST(testParsingArFromPkgFile); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + + void testParsingElf(); + void testParsingPe(); + void testParsingAr(); + void testParsingElfFromPkgFile(); + void testParsingPeFromPkgFile(); + void testParsingArFromPkgFile(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BinaryParserTests); + +void BinaryParserTests::setUp() +{ +} + +void BinaryParserTests::tearDown() +{ +} + +void BinaryParserTests::testParsingElf() +{ + Binary bin; + bin.load(testFilePath("c++utilities/libc++utilities.so.4.5.0").data()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("type", BinaryType::Elf, bin.type); + CPPUNIT_ASSERT_EQUAL_MESSAGE("class", BinaryClass::Class64Bit, bin.binaryClass); + CPPUNIT_ASSERT_EQUAL_MESSAGE("sub type", BinarySubType::SharedObject, bin.subType); + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch", "x86_64"s, bin.architecture); + CPPUNIT_ASSERT_EQUAL_MESSAGE("name", "libc++utilities.so.4"s, bin.name); + const set requiredLibs({ "libstdc++.so.6"s, "libm.so.6"s, "libgcc_s.so.1"s, "libc.so.6"s }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("requires", requiredLibs, bin.requiredLibs); + CPPUNIT_ASSERT_EQUAL_MESSAGE("rpath", "/test/rpath:/foo/bar"s, bin.rpath); +} + +void BinaryParserTests::testParsingPe() +{ + Binary bin; + bin.load(testFilePath("c++utilities/c++utilities.dll").data()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("type", BinaryType::Pe, bin.type); + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch", "x86_64"s, bin.architecture); + CPPUNIT_ASSERT_EQUAL_MESSAGE("name", "c++utilities.dll"s, bin.name); + const set requiredLibs({ "libgcc_s_seh-1.dll"s, "KERNEL32.dll"s, "msvcrt.dll"s, "SHELL32.dll"s, "libstdc++-6.dll"s, "libiconv-2.dll"s }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("requires", requiredLibs, bin.requiredLibs); + CPPUNIT_ASSERT_EQUAL_MESSAGE("rpath", string(), bin.rpath); +} + +void BinaryParserTests::testParsingAr() +{ + Binary bin; + bin.load(testFilePath("mingw-w64-crt/libkernel32.a").data()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("type", BinaryType::Ar, bin.type); + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch", "x86_64"s, bin.architecture); + CPPUNIT_ASSERT_EQUAL_MESSAGE("name", "KERNEL32.dll"s, bin.name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("rpath", string(), bin.rpath); +} + +void BinaryParserTests::testParsingElfFromPkgFile() +{ + const auto pkgFilePath = testFilePath("syncthingtray/syncthingtray-0.6.2-1-x86_64.pkg.tar.xz"); + const auto package = Package::fromPkgFile(pkgFilePath); + checkSyncthingTrayPackageSoDependencies(*package); + + const auto pkgFilePath2 = testFilePath("cmake/cmake-3.8.2-1-x86_64.pkg.tar.xz"); + const auto package2 = Package::fromPkgFile(pkgFilePath2); + checkCmakePackageSoDependencies(*package2); + + const auto pkgFilePath3 = testFilePath("perl/perl-linux-desktopfiles-0.22-2-any.pkg.tar.xz"); + const auto package3 = Package::fromPkgFile(pkgFilePath3); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageContents, package3->origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch", "any"s, package3->packageInfo->arch); + const unordered_set expectedPerlDependencies{ + Dependency("perl", "5.28", DependencyMode::GreatherEqual), // because contained module built against Perl 5.28 + Dependency("perl", "5.29", DependencyMode::LessThan), // because contained module built against Perl 5.28 + }; + // note: Dependency perl>=5.14.0 from PKGBUILD has been removed. I guess that's ok. + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "perl dependencies", expectedPerlDependencies, unordered_set(package3->dependencies.begin(), package3->dependencies.end())); + const set expectedPerlLibProvides; + CPPUNIT_ASSERT_EQUAL_MESSAGE("perl lib provides", expectedPerlLibProvides, package3->libprovides); + const set expectedPerlLibDepends; + CPPUNIT_ASSERT_EQUAL_MESSAGE("perl lib depends", expectedPerlLibDepends, package3->libdepends); + + const auto pkgFilePath4 = testFilePath("python/sphinxbase-5prealpha-7-i686.pkg.tar.xz"); + const auto package4 = Package::fromPkgFile(pkgFilePath4); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageContents, package4->origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("arch", "i686"s, package4->packageInfo->arch); + const unordered_set expectedPythonDependencies{ + Dependency("libpulse"), // from PKGBUILD + Dependency("lapack"), // from PKGBUILD + Dependency("python2>=2.7"), // because contains Python module built against Python 2.7 + Dependency("python>=3.6"), // because contains Python module built against Python 3.6 + Dependency("python2<2.8"), // because contains Python module built against Python 2.7 + Dependency("python<3.7"), // because contains Python module built against Python 3.6 + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "python dependencies", expectedPythonDependencies, unordered_set(package4->dependencies.begin(), package4->dependencies.end())); + const set expectedPythonLibProvides{ + "elf-i386::_sphinxbase.so.0", + "elf-i386::libsphinxad.so.3", + "elf-i386::libsphinxbase.so.3", + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("python lib provides", expectedPythonLibProvides, package4->libprovides); + const set expectedPythonLibDepends{ + "elf-i386::libblas.so.3", + "elf-i386::libc.so.6", + "elf-i386::liblapack.so.3", + "elf-i386::libm.so.6", + "elf-i386::libpthread.so.0", + "elf-i386::libpulse-simple.so.0", + "elf-i386::libpulse.so.0", + "elf-i386::libsphinxad.so.3", + "elf-i386::libsphinxbase.so.3", + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("python lib depends", expectedPythonLibDepends, package4->libdepends); +} + +void BinaryParserTests::testParsingPeFromPkgFile() +{ + const auto pkgFilePath = testFilePath("mingw-w64-harfbuzz/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz"); + const auto package = Package::fromPkgFile(pkgFilePath); + checkHarfbuzzPackagePeDependencies(*package); +} + +void BinaryParserTests::testParsingArFromPkgFile() +{ + const auto pkgFilePath = testFilePath("mingw-w64-crt/mingw-w64-crt-6.0.0-1-any.pkg.tar.xz"); + const auto package = Package::fromPkgFile(pkgFilePath); + CPPUNIT_ASSERT_EQUAL("mingw-w64-crt"s, package->name); + CPPUNIT_ASSERT_EQUAL("6.0.0-1"s, package->version); + const set expectedDLLs{ "pe-i386::aclui.dll", "pe-i386::activeds.dll", "pe-i386::advapi32.dll", "pe-i386::authz.dll", + "pe-i386::avicap32.dll", "pe-i386::avifil32.dll", "pe-i386::avrt.dll", "pe-i386::bootvid.dll", "pe-i386::cap.dll", "pe-i386::cfgmgr32.dll", + "pe-i386::classpnp.sys", "pe-i386::clusapi.dll", "pe-i386::comctl32.dll", "pe-i386::comdlg32.dll", "pe-i386::crypt32.dll", + "pe-i386::cryptnet.dll", "pe-i386::cryptsp.dll", "pe-i386::cryptxml.dll", "pe-i386::cscapi.dll", "pe-i386::ctl3d32.dll", + "pe-i386::cabinet.dll", "pe-i386::d3dcompiler_43.dll", "pe-i386::d3dcompiler_46.dll", "pe-i386::d3dcompiler_47.dll", + "pe-i386::d3dcompiler.dll", "pe-i386::d3dcompiler_37.dll", "pe-i386::d3dcompiler_38.dll", "pe-i386::d3dcompiler_39.dll", + "pe-i386::d3dcompiler_40.dll", "pe-i386::d3dcompiler_41.dll", "pe-i386::d3dcompiler_42.dll", "pe-i386::davhlpr.dll", "pe-i386::devmgr.dll", + "pe-i386::devobj.dll", "pe-i386::devrtl.dll", "pe-i386::dhcpcsvc.dll", "pe-i386::dhcpsapi.dll", "pe-i386::dlcapi.dll", "pe-i386::dnsapi.dll", + "pe-i386::dpapi.dll", "pe-i386::dssec.dll", "pe-i386::dwrite.dll", "pe-i386::esent.dll", "pe-i386::evr.dll", "pe-i386::gdi32.dll", + "pe-i386::glaux.dll", "pe-i386::glu32.dll", "pe-i386::gpapi.dll", "pe-i386::gpedit.dll", "pe-i386::gpscript.dll", "pe-i386::gptext.dll", + "pe-i386::genericui.dll", "pe-i386::hal.dll", "pe-i386::hidclass.sys", "pe-i386::hidparse.sys", "pe-i386::httpapi.dll", "pe-i386::icmui.dll", + "pe-i386::imagehlp.dll", "pe-i386::imm32.dll", "pe-i386::iphlpapi.dll", "pe-i386::iscsidsc.dll", "pe-i386::kernel32.dll", "pe-i386::lz32.dll", + "pe-i386::mapi32.dll", "pe-i386::mf.dll", "pe-i386::mfcuia32.dll", "pe-i386::mfplat.dll", "pe-i386::mgmtapi.dll", "pe-i386::mpr.dll", + "pe-i386::mprapi.dll", "pe-i386::mqrt.dll", "pe-i386::msacm32.dll", "pe-i386::msctf.dll", "pe-i386::mshtml.dll", "pe-i386::msimg32.dll", + "pe-i386::msvcp60.dll", "pe-i386::msvcr110.dll", "pe-i386::msvcr120.dll", "pe-i386::msvcr120d.dll", "pe-i386::msvcr90d.dll", + "pe-i386::msvfw32.dll", "pe-i386::mswsock.dll", "pe-i386::nddeapi.dll", "pe-i386::ndfapi.dll", "pe-i386::ndis.sys", "pe-i386::netapi32.dll", + "pe-i386::ntdsapi.dll", "pe-i386::ntmsapi.dll", "pe-i386::normaliz.dll", "pe-i386::odbc32.dll", "pe-i386::odbccp32.dll", + "pe-i386::oleacc.dll", "pe-i386::oleaut32.dll", "pe-i386::olecli32.dll", "pe-i386::olepro32.dll", "pe-i386::olesvr32.dll", + "pe-i386::olethk32.dll", "pe-i386::opengl32.dll", "pe-i386::p2p.dll", "pe-i386::p2pcollab.dll", "pe-i386::p2pgraph.dll", + "pe-i386::penwin32.dll", "pe-i386::pkpd32.dll", "pe-i386::powrprof.dll", "pe-i386::psapi.dll", "pe-i386::qutil.dll", "pe-i386::rapi.dll", + "pe-i386::rasapi32.dll", "pe-i386::rasdlg.dll", "pe-i386::resutils.dll", "pe-i386::rpcdce4.dll", "pe-i386::rpcns4.dll", "pe-i386::rpcrt4.dll", + "pe-i386::rtm.dll", "pe-i386::rtutils.dll", "pe-i386::rpcdiag.dll", "pe-i386::rstrtmgr.dll", "pe-i386::schannel.dll", "pe-i386::setupapi.dll", + "pe-i386::shell32.dll", "pe-i386::shfolder.dll", "pe-i386::shlwapi.dll", "pe-i386::slwga.dll", "pe-i386::spoolss.dll", "pe-i386::svrapi.dll", + "pe-i386::secur32.dll", "pe-i386::security.dll", "pe-i386::sspicli.dll", "pe-i386::tapi32.dll", "pe-i386::url.dll", "pe-i386::usbcamd.sys", + "pe-i386::usbcamd2.sys", "pe-i386::usbd.sys", "pe-i386::usbport.sys", "pe-i386::user32.dll", "pe-i386::userenv.dll", "pe-i386::usp10.dll", + "pe-i386::uxtheme.dll", "pe-i386::vdmdbg.dll", "pe-i386::version.dll", "pe-i386::vssapi.dll", "pe-i386::virtdisk.dll", + "pe-i386::wdsclientapi.dll", "pe-i386::wdscore.dll", "pe-i386::wdscsl.dll", "pe-i386::wdstptc.dll", "pe-i386::wdsutil.dll", + "pe-i386::wevtfwd.dll", "pe-i386::wiadss.dll", "pe-i386::win32spl.dll", "pe-i386::winhttp.dll", "pe-i386::wininet.dll", "pe-i386::winmm.dll", + "pe-i386::winspool.drv", "pe-i386::winstrm.dll", "pe-i386::wintrust.dll", "pe-i386::winusb.dll", "pe-i386::wmilib.sys", "pe-i386::wow32.dll", + "pe-i386::ws2_32.dll", "pe-i386::wsock32.dll", "pe-i386::wst.dll", "pe-i386::wtsapi32.dll", "pe-i386::wdsclient.dll", "pe-i386::wdsimage.dll", + "pe-i386::wdsupgcompl.dll", "pe-i386::wecapi.dll", "pe-i386::winscard.dll", "pe-i386::wlanapi.dll", "pe-i386::x3daudio1_2.dll", + "pe-i386::x3daudio1_3.dll", "pe-i386::x3daudio1_4.dll", "pe-i386::x3daudio1_5.dll", "pe-i386::x3daudio1_6.dll", "pe-i386::x3daudio1_7.dll", + "pe-i386::x3daudiod1_7.dll", "pe-i386::xapofx1_0.dll", "pe-i386::xapofx1_1.dll", "pe-i386::xapofx1_2.dll", "pe-i386::xapofx1_3.dll", + "pe-i386::xapofx1_4.dll", "pe-i386::xapofx1_5.dll", "pe-i386::xapofxd1_5.dll", "pe-i386::xaudio2_8.dll", "pe-i386::xinput1_1.dll", + "pe-i386::xinput1_2.dll", "pe-i386::xinput1_3.dll", "pe-i386::xinput1_4.dll", "pe-i386::xinput9_1_0.dll", "pe-i386::adsldpc.dll", + "pe-i386::apcups.dll", "pe-i386::bcrypt.dll", "pe-i386::browcli.dll", "pe-i386::bthprops.cpl", "pe-i386::clfsw32.dll", "pe-i386::cmutil.dll", + "pe-i386::connect.dll", "pe-i386::credui.dll", "pe-i386::crtdll.dll", "pe-i386::d2d1.dll", "pe-i386::d3d11.dll", "pe-i386::d3d8.dll", + "pe-i386::d3d9.dll", "pe-i386::d3dcsx_46.dll", "pe-i386::d3dcsxd_43.dll", "pe-i386::d3dim.dll", "pe-i386::d3drm.dll", + "pe-i386::d3dx10_33.dll", "pe-i386::d3dx10_34.dll", "pe-i386::d3dx10_35.dll", "pe-i386::d3dx10_36.dll", "pe-i386::d3dx10_37.dll", + "pe-i386::d3dx10_38.dll", "pe-i386::d3dx10_39.dll", "pe-i386::d3dx10_40.dll", "pe-i386::d3dx10_41.dll", "pe-i386::d3dx10_42.dll", + "pe-i386::d3dx10_43.dll", "pe-i386::d3dx11_42.dll", "pe-i386::d3dx11_43.dll", "pe-i386::d3dx8d.dll", "pe-i386::d3dx9_24.dll", + "pe-i386::d3dx9_25.dll", "pe-i386::d3dx9_26.dll", "pe-i386::d3dx9_27.dll", "pe-i386::d3dx9_28.dll", "pe-i386::d3dx9_29.dll", + "pe-i386::d3dx9_30.dll", "pe-i386::d3dx9_31.dll", "pe-i386::d3dx9_32.dll", "pe-i386::d3dx9_33.dll", "pe-i386::d3dx9_34.dll", + "pe-i386::d3dx9_35.dll", "pe-i386::d3dx9_36.dll", "pe-i386::d3dx9_37.dll", "pe-i386::d3dx9_38.dll", "pe-i386::d3dx9_39.dll", + "pe-i386::d3dx9_40.dll", "pe-i386::d3dx9_41.dll", "pe-i386::d3dx9_42.dll", "pe-i386::d3dx9_43.dll", "pe-i386::d3dx9d.dll", + "pe-i386::d3dxof.dll", "pe-i386::davclnt.dll", "pe-i386::dbgeng.dll", "pe-i386::dbghelp.dll", "pe-i386::ddraw.dll", "pe-i386::dfscli.dll", + "pe-i386::dhcpcsvc6.dll", "pe-i386::dinput.dll", "pe-i386::dinput8.dll", "pe-i386::dplayx.dll", "pe-i386::dpnaddr.dll", "pe-i386::dpnet.dll", + "pe-i386::dpnlobby.def", "pe-i386::dpvoice.dll", "pe-i386::dsetup.dll", "pe-i386::dsound.dll", "pe-i386::dsrole.dll", "pe-i386::dwmapi.dll", + "pe-i386::dxapi.sys", "pe-i386::dxgi.dll", "pe-i386::dxva2.dll", "pe-i386::eappcfg.dll", "pe-i386::eapphost.dll", "pe-i386::eappprxy.dll", + "pe-i386::elscore.dll", "pe-i386::faultrep.dll", "pe-i386::fwpuclnt.dll", "pe-i386::gdiplus.dll", "pe-i386::glut.dll", "pe-i386::glut32.dll", + "pe-i386::hid.dll", "pe-i386::igmpagnt.dll", "pe-i386::ks.sys", "pe-i386::ksproxy.ax", "pe-i386::ksuser.dll", "pe-i386::ktmw32.dll", + "pe-i386::logoncli.dll", "pe-i386::mcd.sys", "pe-i386::mscms.dll", "pe-i386::msdmo.dll", "pe-i386::msdrm.dll", "pe-i386::mshtmled.dll", + "pe-i386::msi.dll", "pe-i386::mstask.dll", "pe-i386::msvcp120_app.dll", "pe-i386::msvcr100.dll", "pe-i386::msvcr120_app.dll", + "pe-i386::msvcr80.dll", "pe-i386::msvcr90.dll", "pe-i386::msvcrt.dll", "pe-i386::ncrypt.dll", "pe-i386::netjoin.dll", "pe-i386::netutils.dll", + "pe-i386::newdev.dll", "pe-i386::ntdll.dll", "pe-i386::ntoskrnl.exe", "pe-i386::ole32.dll", "pe-i386::oledlg.dll", "pe-i386::pcwum.dll", + "pe-i386::pdh.dll", "pe-i386::pdhui.dll", "pe-i386::polprocl.dll", "pe-i386::quartz.dll", "pe-i386::qwave.dll", "pe-i386::rpchttp.dll", + "pe-i386::samcli.dll", "pe-i386::schedcli.dll", "pe-i386::scsiport.sys", "pe-i386::slc.dll", "pe-i386::slcext.dll", "pe-i386::snmpapi.dll", + "pe-i386::srvcli.dll", "pe-i386::sxs.dll", "pe-i386::t2embed.dll", "pe-i386::tbs.dll", "pe-i386::tdh.dll", "pe-i386::tdi.sys", + "pe-i386::txfw32.dll", "pe-i386::ucrtbase.dll", "pe-i386::urlmon.dll", "pe-i386::vcruntime140_app.dll", "pe-i386::videoprt.sys", + "pe-i386::vss_ps.dll", "pe-i386::wer.dll", "pe-i386::wevtapi.dll", "pe-i386::win32k.sys", "pe-i386::windowscodecs.dll", "pe-i386::wkscli.dll", + "pe-i386::wlanui.dll", "pe-i386::wlanutil.dll", "pe-i386::wldap32.dll", "pe-i386::wsdapi.dll", "pe-i386::wsnmp32.dll", + "pe-x86_64::acledit.dll", "pe-x86_64::aclui.dll", "pe-x86_64::activeds.dll", "pe-x86_64::admwprox.dll", "pe-x86_64::adsiisex.dll", + "pe-x86_64::advapi32.dll", "pe-x86_64::advpack.dll", "pe-x86_64::aksclass.dll", "pe-x86_64::appmgmts.dll", "pe-x86_64::asycfilt.dll", + "pe-x86_64::atl.dll", "pe-x86_64::atmlib.dll", "pe-x86_64::audiosrv.dll", "pe-x86_64::authz.dll", "pe-x86_64::autodial.dll", + "pe-x86_64::avicap32.dll", "pe-x86_64::avifil32.dll", "pe-x86_64::avrt.dll", "pe-x86_64::agentanm.dll", "pe-x86_64::autodisc.dll", + "pe-x86_64::basesrv.dll", "pe-x86_64::bootvid.dll", "pe-x86_64::batmeter.dll", "pe-x86_64::cabview.dll", "pe-x86_64::cards.dll", + "pe-x86_64::cdm.dll", "pe-x86_64::cfgmgr32.dll", "pe-x86_64::cintime.dll", "pe-x86_64::classpnp.sys", "pe-x86_64::clbcatq.dll", + "pe-x86_64::cliconfg.dll", "pe-x86_64::clusapi.dll", "pe-x86_64::cnetcfg.dll", "pe-x86_64::coadmin.dll", "pe-x86_64::comctl32.dll", + "pe-x86_64::compstui.dll", "pe-x86_64::comres.dll", "pe-x86_64::crypt32.dll", "pe-x86_64::cryptdlg.dll", "pe-x86_64::cryptext.dll", + "pe-x86_64::cryptnet.dll", "pe-x86_64::cryptsp.dll", "pe-x86_64::cryptsvc.dll", "pe-x86_64::cryptui.dll", "pe-x86_64::cryptxml.dll", + "pe-x86_64::cscapi.dll", "pe-x86_64::cscdll.dll", "pe-x86_64::cscui.dll", "pe-x86_64::csrsrv.dll", "pe-x86_64::cufat.dll", + "pe-x86_64::cabinet.dll", "pe-x86_64::cdfview.dll", "pe-x86_64::cfgbkend.dll", "pe-x86_64::comsnap.dll", "pe-x86_64::comuid.dll", + "pe-x86_64::console.dll", "pe-x86_64::d3dcompiler_43.dll", "pe-x86_64::d3dcompiler_46.dll", "pe-x86_64::d3dcompiler_47.dll", + "pe-x86_64::d3dcompiler.dll", "pe-x86_64::d3dcompiler_37.dll", "pe-x86_64::d3dcompiler_38.dll", "pe-x86_64::d3dcompiler_39.dll", + "pe-x86_64::d3dcompiler_40.dll", "pe-x86_64::d3dcompiler_41.dll", "pe-x86_64::d3dcompiler_42.dll", "pe-x86_64::dbnetlib.dll", + "pe-x86_64::dbnmpntw.dll", "pe-x86_64::dciman32.dll", "pe-x86_64::ddraw.dll", "pe-x86_64::devmgr.dll", "pe-x86_64::devobj.dll", + "pe-x86_64::devrtl.dll", "pe-x86_64::dhcpsapi.dll", "pe-x86_64::digest.dll", "pe-x86_64::dinput.dll", "pe-x86_64::dinput8.dll", + "pe-x86_64::diskcopy.dll", "pe-x86_64::dmdskmgr.dll", "pe-x86_64::dnsapi.dll", "pe-x86_64::dpapi.dll", "pe-x86_64::dpnhupnp.dll", + "pe-x86_64::dpnlobby.dll", "pe-x86_64::dpnet.dll", "pe-x86_64::dpvoice.dll", "pe-x86_64::ds32gt.dll", "pe-x86_64::dsauth.dll", + "pe-x86_64::dskquota.dll", "pe-x86_64::dsound.dll", "pe-x86_64::dsound3d.dll", "pe-x86_64::dssec.dll", "pe-x86_64::dssenh.dll", + "pe-x86_64::duser.dll", "pe-x86_64::dwrite.dll", "pe-x86_64::efsadu.dll", "pe-x86_64::es.dll", "pe-x86_64::esent.dll", + "pe-x86_64::esentprf.dll", "pe-x86_64::evr.dll", "pe-x86_64::exprfdll.dll", "pe-x86_64::fcachdll.dll", "pe-x86_64::filemgmt.dll", + "pe-x86_64::fltlib.dll", "pe-x86_64::fmifs.dll", "pe-x86_64::fontsub.dll", "pe-x86_64::ftpctrs2.dll", "pe-x86_64::ftpmib.dll", + "pe-x86_64::fxsapi.dll", "pe-x86_64::fxscfgwz.dll", "pe-x86_64::fxsocm.dll", "pe-x86_64::fxsperf.dll", "pe-x86_64::fxsst.dll", + "pe-x86_64::fxst30.dll", "pe-x86_64::fxstiff.dll", "pe-x86_64::fxswzrd.dll", "pe-x86_64::fastprox.dll", "pe-x86_64::feclient.dll", + "pe-x86_64::fldrclnr.dll", "pe-x86_64::fxsdrv.dll", "pe-x86_64::fxsroute.dll", "pe-x86_64::gdi32.dll", "pe-x86_64::glmf32.dll", + "pe-x86_64::glu32.dll", "pe-x86_64::gpedit.dll", "pe-x86_64::gpkcsp.dll", "pe-x86_64::gptext.dll", "pe-x86_64::guitrn.dll", + "pe-x86_64::genericui.dll", "pe-x86_64::getuname.dll", "pe-x86_64::hal.dll", "pe-x86_64::hbaapi.dll", "pe-x86_64::hid.dll", + "pe-x86_64::hidclass.sys", "pe-x86_64::hidparse.sys", "pe-x86_64::hmmapi.dll", "pe-x86_64::hnetwiz.dll", "pe-x86_64::hnetcfg.dll", + "pe-x86_64::htrn_jis.dll", "pe-x86_64::httpapi.dll", "pe-x86_64::httpext.dll", "pe-x86_64::httpmib.dll", "pe-x86_64::httpodbc.dll", + "pe-x86_64::hypertrm.dll", "pe-x86_64::icaapi.dll", "pe-x86_64::icfgnt.dll", "pe-x86_64::icm32.dll", "pe-x86_64::icmui.dll", + "pe-x86_64::icwconn.dll", "pe-x86_64::icwutil.dll", "pe-x86_64::idq.dll", "pe-x86_64::ieakeng.dll", "pe-x86_64::iernonce.dll", + "pe-x86_64::igmpagnt.dll", "pe-x86_64::iis.dll", "pe-x86_64::iisadmin.dll", "pe-x86_64::iiscfg.dll", "pe-x86_64::iisprov.dll", + "pe-x86_64::iisutil.dll", "pe-x86_64::imeskdic.dll", "pe-x86_64::imm32.dll", "pe-x86_64::imsinsnt.dll", "pe-x86_64::inetcfg.dll", + "pe-x86_64::inetcomm.dll", "pe-x86_64::infoadmn.dll", "pe-x86_64::infocomm.dll", "pe-x86_64::infoctrs.dll", "pe-x86_64::initpki.dll", + "pe-x86_64::iphlpapi.dll", "pe-x86_64::ipmontr.dll", "pe-x86_64::ipnathlp.dll", "pe-x86_64::iprop.dll", "pe-x86_64::ipsecspd.dll", + "pe-x86_64::irclass.dll", "pe-x86_64::isatq.dll", "pe-x86_64::iscomlog.dll", "pe-x86_64::iscsidsc.dll", "pe-x86_64::iyuv_32.dll", + "pe-x86_64::iisrtl.dll", "pe-x86_64::imgutil.dll", "pe-x86_64::input.dll", "pe-x86_64::jet500.dll", "pe-x86_64::jsproxy.dll", + "pe-x86_64::kd1394.dll", "pe-x86_64::kernel32.dll", "pe-x86_64::keymgr.dll", "pe-x86_64::kerberos.dll", "pe-x86_64::linkinfo.dll", + "pe-x86_64::log.dll", "pe-x86_64::lonsint.dll", "pe-x86_64::lpk.dll", "pe-x86_64::lprhelp.dll", "pe-x86_64::lsasrv.dll", + "pe-x86_64::lz32.dll", "pe-x86_64::localspl.dll", "pe-x86_64::loghours.dll", "pe-x86_64::mapi32.dll", "pe-x86_64::mcastmib.dll", + "pe-x86_64::mcd32.dll", "pe-x86_64::mcdsrv32.dll", "pe-x86_64::mciavi32.dll", "pe-x86_64::mcicda.dll", "pe-x86_64::mciole32.dll", + "pe-x86_64::mciqtz32.dll", "pe-x86_64::mciseq.dll", "pe-x86_64::mciwave.dll", "pe-x86_64::mdminst.dll", "pe-x86_64::mf.dll", + "pe-x86_64::mfc42.dll", "pe-x86_64::mfc42u.dll", "pe-x86_64::mfplat.dll", "pe-x86_64::midimap.dll", "pe-x86_64::migism.dll", + "pe-x86_64::miglibnt.dll", "pe-x86_64::mlang.dll", "pe-x86_64::mll_hp.dll", "pe-x86_64::mll_mtf.dll", "pe-x86_64::mll_qic.dll", + "pe-x86_64::mmfutil.dll", "pe-x86_64::mpr.dll", "pe-x86_64::mprapi.dll", "pe-x86_64::mprddm.dll", "pe-x86_64::mprui.dll", + "pe-x86_64::mqcertui.dll", "pe-x86_64::mqperf.dll", "pe-x86_64::mqrtdep.dll", "pe-x86_64::mqupgrd.dll", "pe-x86_64::msacm32.dll", + "pe-x86_64::msadcs.dll", "pe-x86_64::msado15.dll", "pe-x86_64::msafd.dll", "pe-x86_64::msasn1.dll", "pe-x86_64::mscat32.dll", + "pe-x86_64::msctf.dll", "pe-x86_64::msdart.dll", "pe-x86_64::msdtclog.dll", "pe-x86_64::msdtcprx.dll", "pe-x86_64::msdtctm.dll", + "pe-x86_64::msdtcuiu.dll", "pe-x86_64::msftedit.dll", "pe-x86_64::msgina.dll", "pe-x86_64::mshtml.dll", "pe-x86_64::msimg32.dll", + "pe-x86_64::mslbui.dll", "pe-x86_64::msmqocm.dll", "pe-x86_64::msobdl.dll", "pe-x86_64::msoe.dll", "pe-x86_64::msoeacct.dll", + "pe-x86_64::msoert2.dll", "pe-x86_64::msports.dll", "pe-x86_64::msrating.dll", "pe-x86_64::msrle32.dll", "pe-x86_64::mssign32.dll", + "pe-x86_64::mssip32.dll", "pe-x86_64::msutb.dll", "pe-x86_64::msvcr110.dll", "pe-x86_64::msvcr120.dll", "pe-x86_64::msvcr120d.dll", + "pe-x86_64::msvcr90d.dll", "pe-x86_64::msvfw32.dll", "pe-x86_64::msvidc32.dll", "pe-x86_64::msw3prt.dll", "pe-x86_64::mswsock.dll", + "pe-x86_64::msyuv.dll", "pe-x86_64::mtxclu.dll", "pe-x86_64::mtxdm.dll", "pe-x86_64::mtxoci.dll", "pe-x86_64::mag_hook.dll", + "pe-x86_64::ncobjapi.dll", "pe-x86_64::ncxp.dll", "pe-x86_64::nddenb32.dll", "pe-x86_64::ndfapi.dll", "pe-x86_64::ndis.sys", + "pe-x86_64::ndisnpp.dll", "pe-x86_64::nddeapi.dll", "pe-x86_64::netapi32.dll", "pe-x86_64::netid.dll", "pe-x86_64::netlogon.dll", + "pe-x86_64::netplwiz.dll", "pe-x86_64::netrap.dll", "pe-x86_64::netui0.dll", "pe-x86_64::netui1.dll", "pe-x86_64::netui2.dll", + "pe-x86_64::nntpapi.dll", "pe-x86_64::npptools.dll", "pe-x86_64::nshipsec.dll", "pe-x86_64::ntdsapi.dll", "pe-x86_64::ntlanman.dll", + "pe-x86_64::ntlanui.dll", "pe-x86_64::ntmarta.dll", "pe-x86_64::ntmsapi.dll", "pe-x86_64::ntoc.dll", "pe-x86_64::ntprint.dll", + "pe-x86_64::nwprovau.dll", "pe-x86_64::normaliz.dll", "pe-x86_64::occache.dll", "pe-x86_64::ocmanage.dll", "pe-x86_64::ocmsn.dll", + "pe-x86_64::ocsbs.dll", "pe-x86_64::odbc32.dll", "pe-x86_64::odbc32gt.dll", "pe-x86_64::odbccp32.dll", "pe-x86_64::odbccr32.dll", + "pe-x86_64::odbctrac.dll", "pe-x86_64::oeimport.dll", "pe-x86_64::oemiglib.dll", "pe-x86_64::oleacc.dll", "pe-x86_64::oleaut32.dll", + "pe-x86_64::olecli32.dll", "pe-x86_64::olecnv32.dll", "pe-x86_64::oledb32.dll", "pe-x86_64::olesvr32.dll", "pe-x86_64::opengl32.dll", + "pe-x86_64::osuninst.dll", "pe-x86_64::p2p.dll", "pe-x86_64::p2pcollab.dll", "pe-x86_64::p2pgraph.dll", "pe-x86_64::pautoenr.dll", + "pe-x86_64::photowiz.dll", "pe-x86_64::pidgen.dll", "pe-x86_64::policman.dll", "pe-x86_64::polstore.dll", "pe-x86_64::powrprof.dll", + "pe-x86_64::printui.dll", "pe-x86_64::profmap.dll", "pe-x86_64::proppage.dll", "pe-x86_64::psapi.dll", "pe-x86_64::psbase.dll", + "pe-x86_64::pstorec.dll", "pe-x86_64::pstorsvc.dll", "pe-x86_64::perfdisk.dll", "pe-x86_64::perfnet.dll", "pe-x86_64::perfos.dll", + "pe-x86_64::perfproc.dll", "pe-x86_64::perfts.dll", "pe-x86_64::pschdprf.dll", "pe-x86_64::quartz.dll", "pe-x86_64::qutil.dll", + "pe-x86_64::rasapi32.dll", "pe-x86_64::raschap.dll", "pe-x86_64::rasdlg.dll", "pe-x86_64::rasmontr.dll", "pe-x86_64::rasrad.dll", + "pe-x86_64::rassapi.dll", "pe-x86_64::rdpcfgex.dll", "pe-x86_64::rdpsnd.dll", "pe-x86_64::rdpwsx.dll", "pe-x86_64::regapi.dll", + "pe-x86_64::resutils.dll", "pe-x86_64::riched20.dll", "pe-x86_64::rnr20.dll", "pe-x86_64::routemsg.dll", "pe-x86_64::routetab.dll", + "pe-x86_64::rpcns4.dll", "pe-x86_64::rpcref.dll", "pe-x86_64::rpcrt4.dll", "pe-x86_64::rpcss.dll", "pe-x86_64::rsaenh.dll", + "pe-x86_64::rpcdiag.dll", "pe-x86_64::rstrtmgr.dll", "pe-x86_64::samlib.dll", "pe-x86_64::samsrv.dll", "pe-x86_64::scarddlg.dll", + "pe-x86_64::sccbase.dll", "pe-x86_64::scecli.dll", "pe-x86_64::scesrv.dll", "pe-x86_64::schannel.dll", "pe-x86_64::sclgntfy.dll", + "pe-x86_64::script.dll", "pe-x86_64::scrobj.dll", "pe-x86_64::seo.dll", "pe-x86_64::serialui.dll", "pe-x86_64::serwvdrv.dll", + "pe-x86_64::setupapi.dll", "pe-x86_64::sfmapi.dll", "pe-x86_64::shdocvw.dll", "pe-x86_64::shell32.dll", "pe-x86_64::shfolder.dll", + "pe-x86_64::shlwapi.dll", "pe-x86_64::shscrap.dll", "pe-x86_64::shsvcs.dll", "pe-x86_64::sigtab.dll", "pe-x86_64::skdll.dll", + "pe-x86_64::slbcsp.dll", "pe-x86_64::slwga.dll", "pe-x86_64::smtpapi.dll", "pe-x86_64::smtpctrs.dll", "pe-x86_64::snapin.dll", + "pe-x86_64::snmpmib.exe", "pe-x86_64::softpub.dll", "pe-x86_64::spoolss.dll", "pe-x86_64::sqlsrv32.dll", "pe-x86_64::sqlxmlx.dll", + "pe-x86_64::srclient.dll", "pe-x86_64::srrstr.dll", "pe-x86_64::ssdpapi.dll", "pe-x86_64::ssinc.dll", "pe-x86_64::staxmem.dll", + "pe-x86_64::sti.dll", "pe-x86_64::subauth.dll", "pe-x86_64::svcpack.dll", "pe-x86_64::synceng.dll", "pe-x86_64::syncui.dll", + "pe-x86_64::sysmod.dll", "pe-x86_64::syssetup.dll", "pe-x86_64::scrrun.dll", "pe-x86_64::secur32.dll", "pe-x86_64::security.dll", + "pe-x86_64::sens.dll", "pe-x86_64::sensapi.dll", "pe-x86_64::senscfg.dll", "pe-x86_64::shimeng.dll", "pe-x86_64::sspicli.dll", + "pe-x86_64::sysinv.dll", "pe-x86_64::tapi32.dll", "pe-x86_64::tapiperf.dll", "pe-x86_64::tcpmib.dll", "pe-x86_64::traffic.dll", + "pe-x86_64::tsappcmp.dll", "pe-x86_64::tsbyuv.dll", "pe-x86_64::ufat.dll", "pe-x86_64::umandlg.dll", "pe-x86_64::uniime.dll", + "pe-x86_64::unimdmat.dll", "pe-x86_64::untfs.dll", "pe-x86_64::upnp.dll", "pe-x86_64::url.dll", "pe-x86_64::urlauth.dll", + "pe-x86_64::usbcamd2.sys", "pe-x86_64::usbd.sys", "pe-x86_64::usbport.sys", "pe-x86_64::user32.dll", "pe-x86_64::userenv.dll", + "pe-x86_64::usp10.dll", "pe-x86_64::utildll.dll", "pe-x86_64::uxtheme.dll", "pe-x86_64::verifier.dll", "pe-x86_64::version.dll", + "pe-x86_64::vgx.dll", "pe-x86_64::vmx_mode.dll", "pe-x86_64::vssapi.dll", "pe-x86_64::virtdisk.dll", "pe-x86_64::w32topl.dll", + "pe-x86_64::w3ctrs.dll", "pe-x86_64::w3tp.dll", "pe-x86_64::wab32.dll", "pe-x86_64::wabimp.dll", "pe-x86_64::wbemcore.dll", + "pe-x86_64::wdmaud.drv", "pe-x86_64::wdsclientapi.dll", "pe-x86_64::wdscore.dll", "pe-x86_64::wdscsl.dll", "pe-x86_64::wdstptc.dll", + "pe-x86_64::wdsutil.dll", "pe-x86_64::webhits.dll", "pe-x86_64::wevtfwd.dll", "pe-x86_64::wiadss.dll", "pe-x86_64::winfax.dll", + "pe-x86_64::winhttp.dll", "pe-x86_64::wininet.dll", "pe-x86_64::winipsec.dll", "pe-x86_64::winmm.dll", "pe-x86_64::winrnr.dll", + "pe-x86_64::winspool.drv", "pe-x86_64::winsta.dll", "pe-x86_64::wintrust.dll", "pe-x86_64::winusb.dll", "pe-x86_64::wldap32.dll", + "pe-x86_64::wlstore.dll", "pe-x86_64::wmi.dll", "pe-x86_64::wmilib.sys", "pe-x86_64::wmisvc.dll", "pe-x86_64::ws2help.dll", + "pe-x86_64::ws2_32.dll", "pe-x86_64::wscapi.dll", "pe-x86_64::wscsvc.dll", "pe-x86_64::wsock32.dll", "pe-x86_64::wtsapi32.dll", + "pe-x86_64::wbemupgd.dll", "pe-x86_64::wdsclient.dll", "pe-x86_64::wdsimage.dll", "pe-x86_64::wdsupgcompl.dll", "pe-x86_64::webcheck.dll", + "pe-x86_64::wecapi.dll", "pe-x86_64::winscard.dll", "pe-x86_64::windowscodecs.dll", "pe-x86_64::wlnotify.dll", "pe-x86_64::wmiaprpl.dll", + "pe-x86_64::wmiprop.dll", "pe-x86_64::x3daudio1_2.dll", "pe-x86_64::x3daudio1_3.dll", "pe-x86_64::x3daudio1_4.dll", + "pe-x86_64::x3daudio1_5.dll", "pe-x86_64::x3daudio1_6.dll", "pe-x86_64::x3daudio1_7.dll", "pe-x86_64::x3daudiod1_7.dll", + "pe-x86_64::xapofx1_0.dll", "pe-x86_64::xapofx1_1.dll", "pe-x86_64::xapofx1_2.dll", "pe-x86_64::xapofx1_3.dll", "pe-x86_64::xapofx1_4.dll", + "pe-x86_64::xapofx1_5.dll", "pe-x86_64::xapofxd1_5.dll", "pe-x86_64::xaudio2_8.dll", "pe-x86_64::xinput1_1.dll", "pe-x86_64::xinput1_2.dll", + "pe-x86_64::xinput1_3.dll", "pe-x86_64::xinput1_4.dll", "pe-x86_64::xinput9_1_0.dll", "pe-x86_64::zoneoc.dll", "pe-x86_64::admparse.dll", + "pe-x86_64::adptif.dll", "pe-x86_64::adsldpc.dll", "pe-x86_64::alrsvc.dll", "pe-x86_64::apcups.dll", + "pe-x86_64::api-ms-win-core-winrt-l1-1-0.dll", "pe-x86_64::apphelp.dll", "pe-x86_64::aqueue.dll", "pe-x86_64::asp.dll", + "pe-x86_64::aspperf.dll", "pe-x86_64::atkctrs.dll", "pe-x86_64::atrace.dll", "pe-x86_64::azroles.dll", "pe-x86_64::batt.dll", + "pe-x86_64::bcrypt.dll", "pe-x86_64::browser.dll", "pe-x86_64::bthprops.cpl", "pe-x86_64::catsrv.dll", "pe-x86_64::catsrvut.dll", + "pe-x86_64::certcli.dll", "pe-x86_64::cimwin32.dll", "pe-x86_64::clb.dll", "pe-x86_64::clfsw32.dll", "pe-x86_64::cmcfg32.dll", + "pe-x86_64::cmdial32.dll", "pe-x86_64::cmpbk32.dll", "pe-x86_64::cmutil.dll", "pe-x86_64::colbact.dll", "pe-x86_64::comdlg32.dll", + "pe-x86_64::comsetup.dll", "pe-x86_64::comsvcs.dll", "pe-x86_64::connect.dll", "pe-x86_64::corpol.dll", "pe-x86_64::credui.dll", + "pe-x86_64::crtdll.dll", "pe-x86_64::cryptdll.dll", "pe-x86_64::d2d1.dll", "pe-x86_64::d3d11.dll", "pe-x86_64::d3d8thk.dll", + "pe-x86_64::d3d9.dll", "pe-x86_64::d3dcsx_46.dll", "pe-x86_64::d3dcsxd_43.dll", "pe-x86_64::d3dx10_33.dll", "pe-x86_64::d3dx10_34.dll", + "pe-x86_64::d3dx10_35.dll", "pe-x86_64::d3dx10_36.dll", "pe-x86_64::d3dx10_37.dll", "pe-x86_64::d3dx10_38.dll", "pe-x86_64::d3dx10_39.dll", + "pe-x86_64::d3dx10_40.dll", "pe-x86_64::d3dx10_41.dll", "pe-x86_64::d3dx10_42.dll", "pe-x86_64::d3dx10_43.dll", "pe-x86_64::d3dx11_42.dll", + "pe-x86_64::d3dx11_43.dll", "pe-x86_64::d3dx9_24.dll", "pe-x86_64::d3dx9_25.dll", "pe-x86_64::d3dx9_26.dll", "pe-x86_64::d3dx9_27.dll", + "pe-x86_64::d3dx9_28.dll", "pe-x86_64::d3dx9_29.dll", "pe-x86_64::d3dx9_30.dll", "pe-x86_64::d3dx9_31.dll", "pe-x86_64::d3dx9_32.dll", + "pe-x86_64::d3dx9_33.dll", "pe-x86_64::d3dx9_34.dll", "pe-x86_64::d3dx9_35.dll", "pe-x86_64::d3dx9_36.dll", "pe-x86_64::d3dx9_37.dll", + "pe-x86_64::d3dx9_38.dll", "pe-x86_64::d3dx9_39.dll", "pe-x86_64::d3dx9_40.dll", "pe-x86_64::d3dx9_41.dll", "pe-x86_64::d3dx9_42.dll", + "pe-x86_64::d3dx9_43.dll", "pe-x86_64::d3dxof.dll", "pe-x86_64::davclnt.dll", "pe-x86_64::dbgeng.dll", "pe-x86_64::dbghelp.dll", + "pe-x86_64::dhcpcsvc.dll", "pe-x86_64::dhcpcsvc6.dll", "pe-x86_64::dimsntfy.dll", "pe-x86_64::dimsroam.dll", "pe-x86_64::dmconfig.dll", + "pe-x86_64::dmivcitf.dll", "pe-x86_64::dmutil.dll", "pe-x86_64::dnsrslvr.dll", "pe-x86_64::dpnaddr.dll", "pe-x86_64::drprov.dll", + "pe-x86_64::dsprop.dll", "pe-x86_64::dsquery.dll", "pe-x86_64::dsuiext.dll", "pe-x86_64::dwmapi.dll", "pe-x86_64::dxgi.dll", + "pe-x86_64::dxva2.dll", "pe-x86_64::eappcfg.dll", "pe-x86_64::eapphost.dll", "pe-x86_64::eappprxy.dll", "pe-x86_64::eventlog.dll", + "pe-x86_64::exports.dll", "pe-x86_64::exstrace.dll", "pe-x86_64::faultrep.dll", "pe-x86_64::fdeploy.dll", "pe-x86_64::framedyn.dll", + "pe-x86_64::fwpuclnt.dll", "pe-x86_64::fxsui.dll", "pe-x86_64::gdiplus.dll", "pe-x86_64::hgfs.dll", "pe-x86_64::hlink.dll", + "pe-x86_64::hostmib.dll", "pe-x86_64::hotplug.dll", "pe-x86_64::htui.dll", "pe-x86_64::iashlpr.dll", "pe-x86_64::iaspolcy.dll", + "pe-x86_64::iassam.dll", "pe-x86_64::iassvcs.dll", "pe-x86_64::icmp.dll", "pe-x86_64::icwdl.dll", "pe-x86_64::icwphbk.dll", + "pe-x86_64::iedkcs32.dll", "pe-x86_64::iesetup.dll", "pe-x86_64::iisui.dll", "pe-x86_64::imagehlp.dll", "pe-x86_64::imedic.dll", + "pe-x86_64::imejpcus.dll", "pe-x86_64::imeshare.dll", "pe-x86_64::imeskdic.dll", "pe-x86_64::imjp81k.dll", "pe-x86_64::imjputyc.dll", + "pe-x86_64::inetmib1.dll", "pe-x86_64::infosoft.dll", "pe-x86_64::inseng.dll", "pe-x86_64::iprtprio.dll", "pe-x86_64::iprtrmgr.dll", + "pe-x86_64::ipxsap.dll", "pe-x86_64::isapitst.dll", "pe-x86_64::isignup2.dll", "pe-x86_64::kdcom.dll", "pe-x86_64::ks.sys", + "pe-x86_64::ksuser.dll", "pe-x86_64::ktmw32.dll", "pe-x86_64::lmmib2.dll", "pe-x86_64::loadperf.dll", "pe-x86_64::mchgrcoi.dll", + "pe-x86_64::mf3216.dll", "pe-x86_64::mgmtapi.dll", "pe-x86_64::mmutilse.dll", "pe-x86_64::mobsync.dll", "pe-x86_64::modemui.dll", + "pe-x86_64::mofd.dll", "pe-x86_64::mqad.dll", "pe-x86_64::mqdscli.dll", "pe-x86_64::mqise.dll", "pe-x86_64::mqrt.dll", "pe-x86_64::mqsec.dll", + "pe-x86_64::mqutil.dll", "pe-x86_64::mscms.dll", "pe-x86_64::msdadiag.dll", "pe-x86_64::msdmo.dll", "pe-x86_64::msdrm.dll", + "pe-x86_64::msgr3en.dll", "pe-x86_64::msgsvc.dll", "pe-x86_64::msi.dll", "pe-x86_64::msimtf.dll", "pe-x86_64::msir3jp.dll", + "pe-x86_64::msisip.dll", "pe-x86_64::msls31.dll", "pe-x86_64::msobmain.dll", "pe-x86_64::mspatcha.dll", "pe-x86_64::mstask.dll", + "pe-x86_64::mstlsapi.dll", "pe-x86_64::msv1_0.dll", "pe-x86_64::msvcirt.dll", "pe-x86_64::msvcp120_app.dll", "pe-x86_64::msvcp60.dll", + "pe-x86_64::msvcr100.dll", "pe-x86_64::msvcr120_app.dll", "pe-x86_64::msvcr80.dll", "pe-x86_64::msvcr90.dll", "pe-x86_64::msvcrt.dll", + "pe-x86_64::mtxex.dll", "pe-x86_64::mydocs.dll", "pe-x86_64::ncrypt.dll", "pe-x86_64::netcfgx.dll", "pe-x86_64::netman.dll", + "pe-x86_64::netoc.dll", "pe-x86_64::netshell.dll", "pe-x86_64::newdev.dll", "pe-x86_64::ntdll.dll", "pe-x86_64::ntdsbcli.dll", + "pe-x86_64::ntdtcsetup.dll", "pe-x86_64::ntlsapi.dll", "pe-x86_64::ntoskrnl.exe", "pe-x86_64::ntshrui.dll", "pe-x86_64::ntvdm64.dll", + "pe-x86_64::oakley.dll", "pe-x86_64::odbcbcp.dll", "pe-x86_64::odbcconf.dll", "pe-x86_64::ole32.dll", "pe-x86_64::oledlg.dll", + "pe-x86_64::pcwum.dll", "pe-x86_64::pdh.dll", "pe-x86_64::perfctrs.dll", "pe-x86_64::ps5ui.dll", "pe-x86_64::pscript5.dll", + "pe-x86_64::qmgr.dll", "pe-x86_64::qosname.dll", "pe-x86_64::query.dll", "pe-x86_64::qwave.dll", "pe-x86_64::rasadhlp.dll", + "pe-x86_64::rasauto.dll", "pe-x86_64::rasctrs.dll", "pe-x86_64::rasman.dll", "pe-x86_64::rasmans.dll", "pe-x86_64::rasmxs.dll", + "pe-x86_64::rasppp.dll", "pe-x86_64::rasser.dll", "pe-x86_64::rastapi.dll", "pe-x86_64::rastls.dll", "pe-x86_64::regsvc.dll", + "pe-x86_64::rpchttp.dll", "pe-x86_64::rtm.dll", "pe-x86_64::rtutils.dll", "pe-x86_64::schedsvc.dll", "pe-x86_64::scredir.dll", + "pe-x86_64::sdhcinst.dll", "pe-x86_64::seclogon.dll", "pe-x86_64::setupqry.dll", "pe-x86_64::sfc.dll", "pe-x86_64::sfc_os.dll", + "pe-x86_64::sfcfiles.dll", "pe-x86_64::shimgvw.dll", "pe-x86_64::sisbkup.dll", "pe-x86_64::slc.dll", "pe-x86_64::slcext.dll", + "pe-x86_64::snmpapi.dll", "pe-x86_64::snmpelea.dll", "pe-x86_64::srchctls.dll", "pe-x86_64::srvsvc.dll", "pe-x86_64::sti_ci.dll", + "pe-x86_64::streamci.dll", "pe-x86_64::strmfilt.dll", "pe-x86_64::sxs.dll", "pe-x86_64::t2embed.dll", "pe-x86_64::tbs.dll", + "pe-x86_64::tdh.dll", "pe-x86_64::tsd32.dll", "pe-x86_64::tsoc.dll", "pe-x86_64::txfw32.dll", "pe-x86_64::ucrtbase.dll", + "pe-x86_64::umdmxfrm.dll", "pe-x86_64::umpnpmgr.dll", "pe-x86_64::unidrv.dll", "pe-x86_64::unidrvui.dll", "pe-x86_64::uniplat.dll", + "pe-x86_64::upnpui.dll", "pe-x86_64::urlmon.dll", "pe-x86_64::vcruntime140_app.dll", "pe-x86_64::vdsutil.dll", "pe-x86_64::w32time.dll", + "pe-x86_64::w3core.dll", "pe-x86_64::w3dt.dll", "pe-x86_64::w3isapi.dll", "pe-x86_64::w3ssl.dll", "pe-x86_64::wamreg.dll", + "pe-x86_64::wdigest.dll", "pe-x86_64::webclnt.dll", "pe-x86_64::wer.dll", "pe-x86_64::wevtapi.dll", "pe-x86_64::wiarpc.dll", + "pe-x86_64::wiaservc.dll", "pe-x86_64::wiashext.dll", "pe-x86_64::winsrv.dll", "pe-x86_64::wkssvc.dll", "pe-x86_64::wlanapi.dll", + "pe-x86_64::wlanui.dll", "pe-x86_64::wlanutil.dll", "pe-x86_64::wmi2xml.dll", "pe-x86_64::wow64.dll", "pe-x86_64::wow64cpu.dll", + "pe-x86_64::wow64mib.dll", "pe-x86_64::wow64win.dll", "pe-x86_64::wpd_ci.dll", "pe-x86_64::wsdapi.dll", "pe-x86_64::wshatm.dll", + "pe-x86_64::wshbth.dll" }; + CPPUNIT_ASSERT_EQUAL(expectedDLLs, package->libprovides); +} diff --git a/libpkg/tests/parser_helper.cpp b/libpkg/tests/parser_helper.cpp new file mode 100644 index 0000000..68b5be0 --- /dev/null +++ b/libpkg/tests/parser_helper.cpp @@ -0,0 +1,120 @@ +#include "./parser_helper.h" + +#ifdef LIBREPOMGR_BUILD +#include "../../libpkg/data/package.h" +#else +#include "../data/package.h" +#endif + +#include + +using CppUtilities::operator<<; // must be visible prior to the call site +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace LibPkg; + +namespace TestHelper { + +void checkHarfbuzzPackage(const Package &pkg) +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("name"s, "mingw-w64-harfbuzz"s, pkg.name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("version"s, "1.4.2-1"s, pkg.version); + CPPUNIT_ASSERT_EQUAL_MESSAGE("license"s, vector{ "MIT"s }, pkg.licenses); + CPPUNIT_ASSERT_EQUAL_MESSAGE("upstream URL"s, "http://www.freedesktop.org/wiki/Software/HarfBuzz"s, pkg.upstreamUrl); + const vector dependencies = { + Dependency("mingw-w64-freetype2"s), + Dependency("mingw-w64-glib2"s), + Dependency("mingw-w64-graphite"s), + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("dependencies"s, dependencies, pkg.dependencies); + CPPUNIT_ASSERT_MESSAGE("source info present"s, pkg.sourceInfo); + CPPUNIT_ASSERT_MESSAGE("package info present"s, pkg.packageInfo); + CPPUNIT_ASSERT_MESSAGE("no source archs present"s, pkg.sourceInfo->archs.empty()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("package arch"s, "Martchus <@.net>"s, pkg.packageInfo->packager); + CPPUNIT_ASSERT_EQUAL_MESSAGE("packer"s, "any"s, pkg.packageInfo->arch); + const vector makeDependencies = { + Dependency("mingw-w64-configure"s), + Dependency("mingw-w64-cairo"s), + Dependency("mingw-w64-icu"s), + Dependency("mingw-w64-graphite"s), + Dependency("mingw-w64-freetype2"s), + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("make dependencies"s, makeDependencies, pkg.sourceInfo->makeDependencies); +} + +void checkHarfbuzzPackagePeDependencies(const Package &package) +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageContents, package.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("file name"s, "mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz"s, package.packageInfo->fileName); + const set dllnames({ "pe-i386::libharfbuzz-0.dll", "pe-i386::libharfbuzz-gobject-0.dll", "pe-x86_64::libharfbuzz-0.dll", + "pe-x86_64::libharfbuzz-gobject-0.dll" }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("library provides from DLL names"s, dllnames, package.libprovides); + const set required({ "pe-i386::kernel32.dll", "pe-i386::user32.dll", "pe-i386::libfreetype-6.dll", "pe-i386::libgcc_s_sjlj-1.dll", + "pe-i386::libglib-2.0-0.dll", "pe-i386::libgobject-2.0-0.dll", "pe-i386::libgraphite2.dll", "pe-i386::libharfbuzz-0.dll", + "pe-i386::msvcrt.dll", "pe-x86_64::kernel32.dll", "pe-x86_64::user32.dll", "pe-x86_64::libfreetype-6.dll", "pe-x86_64::libgcc_s_seh-1.dll", + "pe-x86_64::libglib-2.0-0.dll", "pe-x86_64::libgobject-2.0-0.dll", "pe-x86_64::libgraphite2.dll", "pe-x86_64::libharfbuzz-0.dll", + "pe-x86_64::msvcrt.dll" }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("library dependencies from requires"s, required, package.libdepends); +} + +void checkAutoconfPackage(const Package &pkg) +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("name"s, "autoconf"s, pkg.name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("version"s, "2.69-4"s, pkg.version); + CPPUNIT_ASSERT_EQUAL_MESSAGE("license"s, (vector{ "GPL2", "GPL3", "custom" }), pkg.licenses); + CPPUNIT_ASSERT_EQUAL_MESSAGE("groups"s, (vector{ "base-devel" }), pkg.groups); + CPPUNIT_ASSERT_EQUAL_MESSAGE("upstream URL"s, "http://www.gnu.org/software/autoconf"s, pkg.upstreamUrl); + CPPUNIT_ASSERT_EQUAL_MESSAGE("description"s, "A GNU tool for automatically configuring source code"s, pkg.description); + const vector dependencies = { + Dependency("awk"s), + Dependency("m4"s), + Dependency("diffutils"s), + Dependency("sh"s), + }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("dependencies"s, dependencies, pkg.dependencies); + CPPUNIT_ASSERT_MESSAGE("no optional dependencies"s, pkg.optionalDependencies.empty()); + CPPUNIT_ASSERT_MESSAGE("install info present"s, pkg.installInfo); + CPPUNIT_ASSERT_EQUAL_MESSAGE("install size correct"s, 2098176u, pkg.installInfo->installedSize); + CPPUNIT_ASSERT_MESSAGE("source info present"s, pkg.sourceInfo); + CPPUNIT_ASSERT_MESSAGE("package info present"s, pkg.packageInfo); + CPPUNIT_ASSERT_MESSAGE("no source archs present"s, pkg.sourceInfo->archs.empty()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("packer"s, "Allan McRae "s, pkg.packageInfo->packager); + CPPUNIT_ASSERT_EQUAL_MESSAGE("package arch"s, "any"s, pkg.packageInfo->arch); + CPPUNIT_ASSERT_EQUAL_MESSAGE("package file name"s, "autoconf-2.69-4-any.pkg.tar.xz"s, pkg.packageInfo->fileName); + CPPUNIT_ASSERT_EQUAL_MESSAGE("package build date"s, 636089958990000000ul, pkg.packageInfo->buildDate.totalTicks()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("package files"s, 74ul, pkg.packageInfo->files.size()); + CPPUNIT_ASSERT_EQUAL("usr/"s, pkg.packageInfo->files.front()); + const vector makeDependencies = { Dependency("help2man"s) }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("make dependencies"s, makeDependencies, pkg.sourceInfo->makeDependencies); +} + +void checkCmakePackageSoDependencies(const Package &package) +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageContents, package.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("library provides from sonames"s, set(), package.libprovides); + const set requires2({ "elf-x86_64::libQt5Core.so.5", "elf-x86_64::libQt5Gui.so.5", "elf-x86_64::libQt5Widgets.so.5", + "elf-x86_64::libarchive.so.13", "elf-x86_64::libc.so.6", "elf-x86_64::libcurl.so.4", "elf-x86_64::libgcc_s.so.1", + "elf-x86_64::libjsoncpp.so.11", "elf-x86_64::libstdc++.so.6", "elf-x86_64::libdl.so.2", "elf-x86_64::libz.so.1", "elf-x86_64::libexpat.so.1", + "elf-x86_64::libformw.so.6", "elf-x86_64::libncursesw.so.6", "elf-x86_64::libuv.so.1" }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("library dependencies from requires"s, requires2, package.libdepends); +} + +void checkSyncthingTrayPackageSoDependencies(const Package &package) +{ + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", PackageOrigin::PackageContents, package.origin); + CPPUNIT_ASSERT_EQUAL_MESSAGE("file name"s, "syncthingtray-0.6.2-1-x86_64.pkg.tar.xz"s, package.packageInfo->fileName); + const set sonames( + { "elf-x86_64::libsyncthingwidgets.so.0.6.2", "elf-x86_64::libsyncthingmodel.so.0.6.2", "elf-x86_64::libsyncthingconnector.so.0.6.2" }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("library provides from sonames"s, sonames, package.libprovides); + const set required({ "elf-x86_64::libqtutilities.so.5", "elf-x86_64::libsyncthingmodel.so.0.6.2", "elf-x86_64::libKF5KIOWidgets.so.5", + "elf-x86_64::libsyncthingconnector.so.0.6.2", "elf-x86_64::libsyncthingwidgets.so.0.6.2", "elf-x86_64::libKF5KIOCore.so.5", + "elf-x86_64::libQt5Network.so.5", "elf-x86_64::libQt5Widgets.so.5", "elf-x86_64::libQt5Gui.so.5", "elf-x86_64::libKF5CoreAddons.so.5", + "elf-x86_64::libQt5Core.so.5", "elf-x86_64::libQt5DBus.so.5", "elf-x86_64::libQt5Svg.so.5", "elf-x86_64::libQt5WebKit.so.5", + "elf-x86_64::libQt5WebKitWidgets.so.5", "elf-x86_64::libc++utilities.so.4", "elf-x86_64::libstdc++.so.6", "elf-x86_64::libgcc_s.so.1", + "elf-x86_64::libc.so.6" }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("library dependencies from requires"s, required, package.libdepends); +} + +} // namespace TestHelper diff --git a/libpkg/tests/parser_helper.h b/libpkg/tests/parser_helper.h new file mode 100644 index 0000000..fd5a018 --- /dev/null +++ b/libpkg/tests/parser_helper.h @@ -0,0 +1,16 @@ +#ifndef LIBPKG_TESTS_PARSER_HELPER +#define LIBPKG_TESTS_PARSER_HELPER + +namespace LibPkg { +struct Package; +} + +namespace TestHelper { +void checkHarfbuzzPackage(const LibPkg::Package &pkg); +void checkHarfbuzzPackagePeDependencies(const LibPkg::Package &pkg); +void checkAutoconfPackage(const LibPkg::Package &pkg); +void checkCmakePackageSoDependencies(const LibPkg::Package &pkg); +void checkSyncthingTrayPackageSoDependencies(const LibPkg::Package &pkg); +} // namespace TestHelper + +#endif // LIBPKG_TESTS_PARSER_HELPER diff --git a/libpkg/tests/utils.cpp b/libpkg/tests/utils.cpp new file mode 100644 index 0000000..7315258 --- /dev/null +++ b/libpkg/tests/utils.cpp @@ -0,0 +1,90 @@ +#include +#include +#include + +#include "../parser/database.h" +#include "../parser/package.h" +#include "../parser/utils.h" + +#include +#include +#include +#include +#include + +using CppUtilities::operator<<; // must be visible prior to the call site +#include +#include + +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; +using namespace LibPkg; + +class UtilsTests : public TestFixture { + CPPUNIT_TEST_SUITE(UtilsTests); + CPPUNIT_TEST(testFileExtraction); + CPPUNIT_TEST(testAmendingPkgbuild); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + + void testFileExtraction(); + void testAmendingPkgbuild(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(UtilsTests); + +void UtilsTests::setUp() +{ +} + +void UtilsTests::tearDown() +{ +} + +void UtilsTests::testFileExtraction() +{ + const auto dirs = extractFiles(testFilePath("core.files"), &Database::isFileRelevant); + CPPUNIT_ASSERT_EQUAL_MESSAGE("215 dirs present", 215ul, dirs.size()); + const auto &linuxDir = dirs.at("linux-4.7.6-1"); + const auto expectedDesc = readFile(testFilePath("linux-4.7.6-1-desc")); + const auto expectedFiles = readFile(testFilePath("linux-4.7.6-1-files")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("2 files present", 2ul, linuxDir.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("desc name", "desc"s, linuxDir[0].name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("files name", "files"s, linuxDir[1].name); + CPPUNIT_ASSERT_EQUAL_MESSAGE("desc content", expectedDesc, linuxDir[0].content); + CPPUNIT_ASSERT_EQUAL_MESSAGE("files content", expectedFiles, linuxDir[1].content); + const auto &zlibDir = dirs.at("zlib-1.2.8-4"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("3 files present", 3ul, zlibDir.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("also depends present", "depends"s, zlibDir[1].name); +} + +void UtilsTests::testAmendingPkgbuild() +{ + const auto pkgbuildPath = workingCopyPath("c++utilities/PKGBUILD"); + + amendPkgbuild(pkgbuildPath, PackageVersion{ .upstream = "5.0.2", .package = "3" }, PackageAmendment{}); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no changes when amendment empty", readFile(testFilePath("c++utilities/PKGBUILD")), readFile(pkgbuildPath)); + + amendPkgbuild(pkgbuildPath, PackageVersion{ .upstream = "5.0.2", .package = "3" }, + PackageAmendment{ .bumpDownstreamVersion = PackageAmendment::VersionBump::PackageVersion }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("pkgrel bumped", readFile(testFilePath("c++utilities/PKGBUILD.newpkgrel")), readFile(pkgbuildPath)); + + amendPkgbuild(pkgbuildPath, PackageVersion{ .upstream = "5.0.2", .package = "3" }, + PackageAmendment{ .bumpDownstreamVersion = PackageAmendment::VersionBump::Epoch }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("epoch bumped, pkgrel resetted", readFile(testFilePath("c++utilities/PKGBUILD.newepoch")), readFile(pkgbuildPath)); + + amendPkgbuild(pkgbuildPath, PackageVersion{ .upstream = "5.0.2", .package = "3" }, PackageAmendment{ .setUpstreamVersion = true }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("upstream version set", readFile(testFilePath("c++utilities/PKGBUILD.newpkgver")), readFile(pkgbuildPath)); + + const auto pkgbuildWithQuotingPath = workingCopyPath("perl-data-dumper-concise/PKGBUILD"); + amendPkgbuild(pkgbuildWithQuotingPath, PackageVersion{ .upstream = "2.023", .package = "1" }, + PackageAmendment{ .bumpDownstreamVersion = PackageAmendment::VersionBump::PackageVersion }); + CPPUNIT_ASSERT_EQUAL_MESSAGE("pkgrel bumped when quotes were used", readFile(testFilePath("perl-data-dumper-concise/PKGBUILD.newpkgrel")), + readFile(pkgbuildWithQuotingPath)); +} diff --git a/librepomgr/CMakeLists.txt b/librepomgr/CMakeLists.txt new file mode 100644 index 0000000..1321280 --- /dev/null +++ b/librepomgr/CMakeLists.txt @@ -0,0 +1,170 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +# add project files +set(HEADER_FILES + errorhandling.h + serversetup.h + helper.h + json.h + logging.h + multisession.h + authentication.h + webapi/server.h + webapi/session.h + webapi/render.h + webapi/routes.h + webapi/routeid.h + webapi/typedefs.h + webapi/repository.h + webapi/params.h + webclient/aur.h + webclient/database.h + webclient/session.h + buildactions/buildactionmeta.h + buildactions/buildaction.h + buildactions/buildactionfwd.h + buildactions/buildactiontemplate.h + buildactions/subprocess.h + buildactions/subprocessfwd.h) +set(SRC_FILES + json.cpp + errorhandling.cpp + serversetup.cpp + authentication.cpp + webapi/server.cpp + webapi/session.cpp + webapi/routes.cpp + webapi/render.cpp + webapi/params.cpp + webapi/repository.cpp + webclient/aur.cpp + webclient/database.cpp + webclient/session.cpp + buildactions/buildactionmeta.cpp + buildactions/buildactionlivestreaming.cpp + buildactions/buildaction.cpp + buildactions/buildactiontemplate.cpp + buildactions/buildactionprivate.h + buildactions/customcommand.cpp + buildactions/updatecheck.cpp + buildactions/makelicenseinfo.cpp + buildactions/reloaddatabase.cpp + buildactions/reloadlibrarydependencies.cpp + buildactions/reloadconfiguration.cpp + buildactions/repomanagement.cpp + buildactions/preparebuild.cpp + buildactions/conductbuild.cpp) +set(TEST_HEADER_FILES tests/parser_helper.h) +set(TEST_SRC_FILES tests/cppunit.cpp tests/buildactions.cpp tests/webapi.cpp tests/parser_helper.cpp) + +# meta data +set(META_PROJECT_NAME librepomgr) +set(META_PROJECT_TYPE library) +set(META_PROJECT_VARNAME LIBREPOMGR) +set(META_APP_AUTHOR "Martchus") +set(META_APP_NAME "Inofficial Arch Linux repository management library") +set(META_APP_DESCRIPTION "Library for managing custom Arch Linux repositories") +set(META_VERSION_MAJOR 0) +set(META_VERSION_MINOR 0) +set(META_VERSION_PATCH 1) +set(META_VERSION_CACHE 9) +set(META_VERSION_BUILD_ACTIONS_JSON 0) +set(META_VERSION_LIBRARY_DEPENDENCIES_JSON 0) +set(LINK_TESTS_AGAINST_APP_TARGET ON) + +# find c++utilities +set(CONFIGURATION_PACKAGE_SUFFIX + "" + CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities") +find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.8.0 REQUIRED) +use_cpp_utilities(VISIBILITY PUBLIC) + +# find boost libraries +option(BOOST_STATIC_LINKAGE "${STATIC_LINKAGE}" "link statically against Boost (instead of dynamically)") +set(Boost_USE_MULTITHREADED ON) +if (BOOST_STATIC_LINKAGE) + set(Boost_USE_STATIC_LIBS ON) +endif () +set(BOOST_ARGS "REQUIRED;COMPONENTS;system;filesystem") +use_package(TARGET_NAME Boost::system PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}") +use_package(TARGET_NAME Boost::filesystem PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}") + +# find reflective-rapidjson +find_package(reflective_rapidjson${CONFIGURATION_PACKAGE_SUFFIX} REQUIRED) +use_reflective_rapidjson(VISIBILITY PUBLIC) + +# find backend libraries +find_package(libpkg ${META_APP_VERSION} REQUIRED) +use_libpkg(VISIBILITY PUBLIC) + +# link against crypto and SSL library from OpenSSL +use_openssl(VISIBILITY PUBLIC) + +# link against pthread +list(APPEND PUBLIC_LIBRARIES pthread) + +# apply basic configuration +include(BasicConfig) + +# add cache version to config header +string(APPEND META_CUSTOM_CONFIG "#define ${META_PROJECT_VARNAME}_CACHE_VERSION \"${META_VERSION_CACHE}\"\n") +string(APPEND META_CUSTOM_CONFIG + "#define ${META_PROJECT_VARNAME}_BUILD_ACTIONS_JSON_VERSION \"${META_VERSION_BUILD_ACTIONS_JSON}\"\n") +string(APPEND META_CUSTOM_CONFIG + "#define ${META_PROJECT_VARNAME}_LIBRARY_DEPENDENCIES_JSON_VERSION \"${META_VERSION_LIBRARY_DEPENDENCIES_JSON}\"\n") + +# trigger code generator for tests because the tests already contain structs to be (de)serialized +include(ReflectionGenerator) +add_reflection_generator_invocation( + INPUT_FILES + errorhandling.h + serversetup.h + buildactions/buildaction.h + buildactions/buildactionmeta.h + buildactions/buildactiontemplate.h + CLANG_OPTIONS_FROM_TARGETS + "${META_TARGET_NAME}" + CLANG_OPTIONS_FROM_DEPENDENCIES + "${PUBLIC_LIBRARIES};${PRIVATE_LIBRARIES}" + GENERATORS + json + binary + OUTPUT_LISTS + SRC_FILES + JSON_VISIBILITY + ${META_PROJECT_VARNAME}_EXPORT + BINARY_VISBILITY + ${META_PROJECT_VARNAME}_EXPORT) + +# disable Boost's support for concepts to avoid compile errors +# /usr/include/boost/asio/async_result.hpp:70:20: error: concept cannot have associated constraints +# BOOST_ASIO_CONCEPT completion_handler_for = +# ^ +# /usr/include/boost/asio/async_result.hpp:492:20: error: concept cannot have associated constraints +# BOOST_ASIO_CONCEPT completion_token_for = requires(T&& t) +# ^ +list(APPEND META_PUBLIC_COMPILE_DEFINITIONS BOOST_ASIO_DISABLE_CONCEPTS) + +# include modules to apply configuration +include(WindowsResources) +include(LibraryTarget) +include(TestTarget) +include(ConfigHeader) + +# configure dummy build action +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DUMMY_BUILD_ACTION_ENABLED_BY_DEFAULT ON) +endif () +option(DUMMY_BUILD_ACTION_ENABLED "enable dummy build action" ${DUMMY_BUILD_ACTION_ENABLED_BY_DEFAULT}) +if (DUMMY_BUILD_ACTION_ENABLED) + set_property( + SOURCE buildactions/buildaction.cpp + APPEND + PROPERTY COMPILE_DEFINITIONS ${META_PROJECT_VARNAME_UPPER}_DUMMY_BUILD_ACTION_ENABLED) +endif () + +# configure test helper shared with libpkg (FIXME: create a separate test helper library) +set_property( + SOURCE tests/parser_helper.cpp + APPEND + PROPERTY COMPILE_DEFINITIONS ${META_PROJECT_VARNAME_UPPER}_BUILD) diff --git a/librepomgr/authentication.cpp b/librepomgr/authentication.cpp new file mode 100644 index 0000000..633e8f1 --- /dev/null +++ b/librepomgr/authentication.cpp @@ -0,0 +1,107 @@ +#include "./authentication.h" + +#include "./helper.h" +#include "./serversetup.h" + +#include +#include + +#include + +namespace LibRepoMgr { + +template <> inline void convertValue(const std::multimap &multimap, const std::string &key, UserPermissions &result) +{ + using namespace std; + using namespace CppUtilities::EscapeCodes; + + const auto value = getLastValueSv(multimap, key); + if (!value.has_value()) { + return; + } + + result = UserPermissions::None; + const auto parts = CppUtilities::splitStringSimple>(value.value(), " "sv); + for (const auto &part : parts) { + if (part.empty()) { + } else if (part == "read_build_actions_details") { + result = result | UserPermissions::ReadBuildActionsDetails; + } else if (part == "modify_build_actions") { + result = result | UserPermissions::ModifyBuildActions; + } else if (part == "perform_admin_actions") { + result = result | UserPermissions::PerformAdminActions; + } else { + std::cerr << Phrases::Error << "Specified permission \"" << part << "\" for key \"" << key << "\" is invalid." << Phrases::End; + std::exit(-1); + } + } +} + +void ServiceSetup::Authentication::applyConfig(const std::string &userName, const std::multimap &multimap) +{ + auto &user = users[userName]; + convertValue(multimap, "password_sha512", user.passwordSha512); + convertValue(multimap, "permissions", user.permissions); +} + +static constexpr char toLower(const char c) +{ + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +UserPermissions ServiceSetup::Authentication::authenticate(std::string_view authorizationHeader) const +{ + // extract user name and password from base64 encoded header value + if (!CppUtilities::startsWith(authorizationHeader, "Basic ") && authorizationHeader.size() < 100) { + return UserPermissions::DefaultPermissions; + } + std::pair, std::uint32_t> data; + try { + data = CppUtilities::decodeBase64(authorizationHeader.data() + 6, static_cast(authorizationHeader.size() - 6)); + } catch (const CppUtilities::ConversionException &) { + return UserPermissions::DefaultPermissions; + } + const auto parts = CppUtilities::splitStringSimple>( + std::string_view(reinterpret_cast(data.first.get()), data.second), ":", 2); + if (parts.size() != 2) { + return UserPermissions::DefaultPermissions; + } + + // find relevant user + const std::string_view userName = parts[0], password = parts[1]; + if (userName.empty() || password.empty()) { + return UserPermissions::DefaultPermissions; + } + if (userName == "try" && password == "again") { + return UserPermissions::TryAgain; + } + const auto user = users.find(std::string(userName)); + if (user == users.cend()) { + return UserPermissions::DefaultPermissions; + } + constexpr auto sha512HexSize = 128; + if (user->second.passwordSha512.size() != sha512HexSize) { + return UserPermissions::DefaultPermissions; + } + + // hash password + SHA512_CTX sha512; + SHA512_Init(&sha512); + SHA512_Update(&sha512, password.data(), password.size()); + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512_Final(hash, &sha512); + + // check whether password hash matches + auto i = user->second.passwordSha512.cbegin(); + for (unsigned char hashNumber : hash) { + const auto digits = CppUtilities::numberToString(hashNumber, 16); + if ((toLower(*(i++)) != toLower(digits.size() < 2 ? '0' : digits.front())) || (toLower(*(i++)) != toLower(digits.back()))) { + return UserPermissions::DefaultPermissions; + } + } + + // return the user's permissions + return user->second.permissions; +} + +} // namespace LibRepoMgr diff --git a/librepomgr/authentication.h b/librepomgr/authentication.h new file mode 100644 index 0000000..4a62fc9 --- /dev/null +++ b/librepomgr/authentication.h @@ -0,0 +1,30 @@ +#ifndef LIBREPOMGR_AUTHENTICATION_H +#define LIBREPOMGR_AUTHENTICATION_H + +#include +#include + +namespace LibRepoMgr { + +enum class UserPermissions : std::uint64_t { + None = 0x0, + ReadBuildActionsDetails = 0x1, + ModifyBuildActions = ReadBuildActionsDetails | 0x2, + PerformAdminActions = 0x4, + TryAgain = 0x8, + DefaultPermissions = ReadBuildActionsDetails, +}; + +constexpr UserPermissions operator|(UserPermissions lhs, UserPermissions rhs) +{ + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +struct UserInfo { + std::string passwordSha512; + UserPermissions permissions = UserPermissions::None; +}; + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_AUTHENTICATION_H diff --git a/librepomgr/buildactions/buildaction.cpp b/librepomgr/buildactions/buildaction.cpp new file mode 100644 index 0000000..9594eaf --- /dev/null +++ b/librepomgr/buildactions/buildaction.cpp @@ -0,0 +1,462 @@ +#include "./buildactionprivate.h" + +#include "../webapi/session.h" + +#include +#include + +#include "reflection/buildaction.h" + +#include + +#ifdef LIBREPOMGR_DUMMY_BUILD_ACTION_ENABLED +#include +#include +#include +#endif + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +InternalBuildAction::InternalBuildAction(ServiceSetup &setup, const std::shared_ptr &buildAction) + : m_setup(setup) + , m_buildAction(buildAction) +{ +} + +static bool isAur(const std::string &dbName) +{ + return dbName == "aur" || dbName == "AUR"; +} + +std::string InternalBuildAction::validateParameter(RequiredDatabases requiredDatabases, RequiredParameters requiredParameters) +{ + if (requiredDatabases & RequiredDatabases::OneOrMoreSources) { + if (m_buildAction->sourceDbs.empty()) { + return "no source databases specified"; + } + } else if (requiredDatabases & RequiredDatabases::OneSource) { + if (m_buildAction->sourceDbs.size() != 1) { + return "not exactly one source database specified"; + } + } else if (!(requiredDatabases & RequiredDatabases::MaybeSource)) { + if (!m_buildAction->sourceDbs.empty()) { + return "no source database must be specified"; + } + } + if (requiredDatabases & RequiredDatabases::OneOrMoreDestinations) { + if (m_buildAction->destinationDbs.empty()) { + return "no destination databases specified"; + } + } else if (requiredDatabases & RequiredDatabases::OneDestination) { + if (m_buildAction->destinationDbs.size() != 1) { + return "not exactly one destination database specified"; + } + } else if (!(requiredDatabases & RequiredDatabases::MaybeDestination)) { + if (!m_buildAction->destinationDbs.empty()) { + return "no destination database must be specified"; + } + } + for (const auto &sourceDb : m_buildAction->sourceDbs) { + if (sourceDb.empty() || sourceDb == "none") { + return "empty/invalid source database specified"; + } + if (isAur(sourceDb)) { + if (requiredDatabases & RequiredDatabases::AllowFromAur) { + m_fromAur = true; + } else { + return "source database must not be AUR"; + } + } + } + for (const auto &destinationDb : m_buildAction->destinationDbs) { + if (destinationDb.empty() || destinationDb == "none") { + return "empty/invalid destination database specified"; + } + if (isAur(destinationDb)) { + if (requiredDatabases & RequiredDatabases::AllowToAur) { + m_toAur = true; + } else { + return "destination database must not be AUR"; + } + } + } + if (requiredParameters & RequiredParameters::Packages) { + if (m_buildAction->packageNames.empty()) { + return "no packages specified"; + } + } else if (!(requiredParameters & RequiredParameters::MaybePackages)) { + if (!m_buildAction->packageNames.empty()) { + return "no packages must be specified"; + } + } + return string(); +} + +std::string InternalBuildAction::findDatabases() +{ + m_sourceDbs.clear(); + m_destinationDbs.clear(); + for (const auto &sourceDb : m_buildAction->sourceDbs) { + if (isAur(sourceDb)) { + continue; + } + if (auto *const db = m_setup.config.findDatabaseFromDenotation(sourceDb)) { + m_sourceDbs.emplace(db); + } else { + return "source database " % sourceDb + " does not exist"; + } + } + for (const auto &destinationDb : m_buildAction->destinationDbs) { + if (isAur(destinationDb)) { + continue; + } + if (auto *const db = m_setup.config.findDatabaseFromDenotation(destinationDb)) { + m_destinationDbs.emplace(db); + } else { + return "destination database " % destinationDb + " does not exist"; + } + } + return string(); +} + +typename InternalBuildAction::InitReturnType InternalBuildAction::init( + BuildActionAccess access, RequiredDatabases requiredDatabases, RequiredParameters requiredParameters) +{ + InitReturnType configLock; + if (auto error = validateParameter(requiredDatabases, requiredParameters); !error.empty()) { + reportError(move(error)); + return configLock; + } + switch (access) { + case BuildActionAccess::ReadConfig: + configLock = m_setup.config.lockToRead(); + break; + case BuildActionAccess::WriteConfig: + configLock = m_setup.config.lockToWrite(); + break; + } + if (auto error = findDatabases(); !error.empty()) { + configLock = monostate(); + reportError(move(error)); + return configLock; + } + return configLock; +} + +std::string InternalBuildAction::determineWorkingDirectory(std::string_view name) +{ + const auto workingDirectory = m_setup.building.workingDirectory % '/' % name % '/' + m_buildAction->directory; + m_buildAction->appendOutput(Phrases::InfoMessage, "Working directory: " % workingDirectory + '\n'); + return workingDirectory; +} + +const std::string &InternalBuildAction::findSetting(const std::string_view &setting) const +{ + if (const auto i = m_buildAction->settings.find(std::string(setting)); i != m_buildAction->settings.end()) { + return i->second; + } else { + static const auto empty = std::string(); + return empty; + } +} + +void InternalBuildAction::reportError(std::string &&error) +{ + const auto buildActionLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = move(error); + m_buildAction->conclude(BuildActionResult::Failure); +} + +void InternalBuildAction::reportError() +{ + m_buildAction->conclude(BuildActionResult::Failure); +} + +void InternalBuildAction::reportSuccess() +{ + m_buildAction->conclude(BuildActionResult::Success); +} + +void InternalBuildAction::reportResult(BuildActionResult result) +{ + m_buildAction->conclude(result); +} + +bool InternalBuildAction::reportAbortedIfAborted() +{ + if (!m_buildAction->isAborted()) { + return false; + } + const auto buildActionLock = m_setup.building.lockToWrite(); + m_buildAction->conclude(BuildActionResult::Aborted); + return true; +} + +BuildAction::BuildAction(IdType id, ServiceSetup *setup) noexcept + : id(id) + , m_log(this) + , m_setup(setup) + , m_stopHandler(std::bind(&BuildAction::terminateOngoingBuildProcesses, this)) +{ +} + +BuildAction::~BuildAction() +{ +} + +bool BuildAction::haveSucceeded(const std::vector> &buildActions) +{ + for (const auto &buildAction : buildActions) { + if (!buildAction->hasSucceeded()) { + return false; + } + } + return true; +} + +/*! + * \brief Starts the build action. The caller must acquire the lock to write build actions. + * \returns Returns immediately. The real work is done in a build action thread. + */ +void BuildAction::start(ServiceSetup &setup) +{ + if (!isScheduled()) { + return; + } + + started = DateTime::gmtNow(); + status = BuildActionStatus::Running; + m_setup = &setup; + + switch (type) { + case BuildActionType::Invalid: + resultData = "type is invalid"; + conclude(BuildActionResult::Failure); + break; + case BuildActionType::RemovePackages: + post(); + break; + case BuildActionType::MovePackages: + post(); + break; + case BuildActionType::CheckForUpdates: + post(); + break; + case BuildActionType::ReloadDatabase: + post(); + break; + case BuildActionType::ReloadLibraryDependencies: + post(); + break; + case BuildActionType::PrepareBuild: + post(); + break; + case BuildActionType::ConductBuild: + post(); + break; + case BuildActionType::MakeLicenseInfo: + post(); + break; + case BuildActionType::ReloadConfiguration: + post(); + break; + case BuildActionType::CheckForProblems: + post(); + break; + case BuildActionType::CleanRepository: + post(); + break; +#ifdef LIBREPOMGR_DUMMY_BUILD_ACTION_ENABLED + case BuildActionType::DummyBuildAction: + post(); + break; +#endif + case BuildActionType::CustomCommand: + post(); + break; + default: + resultData = "not implemented yet or invalid type"; + conclude(BuildActionResult::Failure); + } +} + +void BuildAction::startAfterOtherBuildActions(ServiceSetup &setup, const std::vector> &startsAfterBuildActions) +{ + auto allSucceeded = true; + for (auto &previousBuildAction : startsAfterBuildActions) { + if (!previousBuildAction->hasSucceeded()) { + previousBuildAction->m_followUpActions.emplace_back(weak_from_this()); + allSucceeded = false; + } + } + if (allSucceeded) { + start(setup); + } +} + +void BuildAction::abort() +{ + m_aborted.store(true); + if (m_setup && m_stopHandler) { + boost::asio::post(m_setup->building.ioContext.get_executor(), m_stopHandler); + } +} + +template void BuildAction::post() +{ + assert(m_setup); + m_internalBuildAction = make_unique(*m_setup, shared_from_this()); + post(bind(&InternalBuildActionType::run, static_cast(m_internalBuildAction.get()))); +} + +template void BuildAction::post(Callback &&codeToRun) +{ + assert(m_setup); + boost::asio::post(m_setup->building.ioContext.get_executor(), forward(codeToRun)); +} + +/*! + * \brief Internally called to conclude the build action. + */ +void BuildAction::conclude(BuildActionResult result) +{ + // set fields accordingly + status = BuildActionStatus::Finished; + this->result = result; + finished = DateTime::gmtNow(); + + // tell clients waiting for output that it's over + const auto outputStreamingLock = std::unique_lock(m_outputStreamingMutex); + for (auto i = m_bufferingForSession.begin(); i != m_bufferingForSession.end();) { + if (!i->second->currentlySentBuffers.empty() || !i->second->outstandingBuffersToSend.empty()) { + ++i; + continue; + } + boost::beast::net::async_write(i->first->socket(), boost::beast::http::make_chunk_last(), + std::bind(&WebAPI::Session::responded, i->first, std::placeholders::_1, std::placeholders::_2, true)); + i = m_bufferingForSession.erase(i); + } + + // start follow-up actions if succeeded + if (result == BuildActionResult::Success && m_setup) { + for (auto &maybeStillValidFollowUpAction : m_followUpActions) { + auto followUpAction = maybeStillValidFollowUpAction.lock(); + if (followUpAction && followUpAction->isScheduled()) { + followUpAction->start(*m_setup); + } + } + // note: Not cleaning up the follow-up actions here because at some point I might implement recursive restarting. + } + + if (m_concludeHandler) { + m_concludeHandler(); + } +} + +#ifdef LIBREPOMGR_DUMMY_BUILD_ACTION_ENABLED +DummyBuildAction::DummyBuildAction(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) + , m_counter(0) + , m_timer(setup.building.ioContext) +{ + m_buildAction->setStopHandler(std::bind(&DummyBuildAction::stop, this)); +} + +void DummyBuildAction::run() +{ + // validate parameter + if (auto error = validateParameter(RequiredDatabases::None, RequiredParameters::None); !error.empty()) { + reportError(move(error)); + return; + } + if (m_buildAction->directory.empty()) { + reportError("Unable to find working directory: no directory name specified"); + return; + } + + // find test files + auto testApp = TestApplication(); + auto scriptPath = std::string(); + try { + scriptPath = testFilePath("scripts/print_some_data.sh"); + } catch (const std::exception &e) { + reportError(e.what()); + return; + } + + // create working directory + m_workingDirectory = "dummy/" + m_buildAction->directory; + try { + std::filesystem::create_directories(m_workingDirectory); + } catch (const std::filesystem::filesystem_error &e) { + reportError(argsToString("Unable to make working directory: ", e.what())); + return; + } + + // add an artefact + auto buildActionsWriteLock = m_setup.building.lockToWrite(); + m_buildAction->artefacts.emplace_back(m_workingDirectory + "/some-artefact.txt"); + buildActionsWriteLock.unlock(); + try { + writeFile(m_buildAction->artefacts.back(), "artefact contents\n"); + } catch (const std::ios_base::failure &e) { + reportError(argsToString("Unable to make artefact: ", e.what())); + return; + } + + // launch subprocess producing a logfile + m_logProcess = m_buildAction->makeBuildProcess(m_workingDirectory + "/foo.log", [this](boost::process::child &&child, ProcessResult &&result) { + CPP_UTILITIES_UNUSED(child) + m_logProcess = nullptr; + m_buildAction->appendOutput("log process exited with code: ", result.exitCode, '\n'); + if (!result.error.empty()) { + m_buildAction->appendOutput("log process error: ", result.error, '\n'); + } + if (!m_buildAction->isAborted()) { + stop(); + } + }); + m_logProcess->launch(scriptPath, "1"); + + continuePrinting(); +} + +void DummyBuildAction::continuePrinting() +{ + m_timer.expires_from_now(boost::posix_time::seconds(1)); + m_timer.async_wait(std::bind(&DummyBuildAction::printLine, this)); +} + +void DummyBuildAction::printLine() +{ + if (m_buildAction->isAborted()) { + return; + } + m_buildAction->appendOutput("Output: ", ++m_counter, '\n'); + continuePrinting(); +} + +void DummyBuildAction::stop() +{ + m_buildAction->appendOutput("stopping"sv); + boost::system::error_code timerCancelError; + m_timer.cancel(timerCancelError); + if (timerCancelError.failed()) { + m_buildAction->appendOutput("failed to cancel timer: ", timerCancelError.message(), '\n'); + } + std::error_code terminateError; + if (m_logProcess) { + m_logProcess->group.terminate(terminateError); + if (terminateError) { + m_buildAction->appendOutput("failed to terminate logging process: ", terminateError.message(), '\n'); + } + } + reportSuccess(); +} +#endif // LIBREPOMGR_DUMMY_BUILD_ACTION_ENABLED + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/buildaction.h b/librepomgr/buildactions/buildaction.h new file mode 100644 index 0000000..8fbf169 --- /dev/null +++ b/librepomgr/buildactions/buildaction.h @@ -0,0 +1,363 @@ +#ifndef LIBREPOMGR_BUILD_ACTION_H +#define LIBREPOMGR_BUILD_ACTION_H + +#include "./buildactionfwd.h" +#include "./buildactionmeta.h" +#include "./subprocessfwd.h" + +#include "../webapi/routes.h" + +#include "../../libpkg/data/config.h" +#include "../../libpkg/data/lockable.h" + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class BuildActionsTests; + +namespace LibRepoMgr { + +struct LogContext { + explicit LogContext(BuildAction *buildAction = nullptr); + LogContext &operator=(const LogContext &) = delete; + template LogContext &operator()(CppUtilities::EscapeCodes::Phrases phrase, Args &&...args); + template LogContext &operator()(Args &&...args); + template LogContext &operator()(std::string &&msg); + +private: + BuildAction *const m_buildAction; +}; + +inline LogContext::LogContext(BuildAction *buildAction) + : m_buildAction(buildAction) +{ +} + +struct ServiceSetup; + +namespace WebAPI { +struct Params; +class Session; +} // namespace WebAPI + +struct InternalBuildAction; + +struct LIBREPOMGR_EXPORT PackageBuildData : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::string existingVersion; + std::vector> existingPackages; + std::string sourceDirectory; + std::string originalSourceDirectory; + std::shared_ptr sourceInfo; + std::vector> packages; + std::vector warnings; + std::string error; + std::size_t specifiedIndex = std::numeric_limits::max(); + bool hasSource = false; +}; + +struct LIBREPOMGR_EXPORT BuildPreparation : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::unordered_map buildData; + std::vector>> dbConfig, stagingDbConfig; + std::string targetDb, targetArch, stagingDb; + std::vector> batches; + std::vector cyclicLeftovers; + std::vector warnings; + std::string error; + bool manuallyOrdered = false; +}; + +enum class PackageStagingNeeded { + Undetermined, + Yes, + No, +}; + +struct LIBREPOMGR_EXPORT PackageBuildProgress : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + bool hasBeenAnyProgressMade() const; + void reset(); + + CppUtilities::DateTime started; + CppUtilities::DateTime finished; + std::string buildDirectory; + std::string chrootDirectory; + std::string chrootUser; + std::vector makechrootpkgFlags; + std::vector makepkgFlags; + std::string packageExtension; + std::vector warnings; + std::string error; + std::string updatedVersion; + PackageStagingNeeded stagingNeeded = PackageStagingNeeded::Undetermined; + bool skipChrootUpgrade = false; + bool skipChrootCleanup = false; + bool keepPreviousSourceTree = false; + bool checksumsUpdated = false; + bool hasSources = false; + bool addedToRepo = false; +}; + +struct LIBREPOMGR_EXPORT RebuildInfo : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::vector provides; + std::vector libprovides; + + void add(const LibPkg::DependencySet &deps, const std::unordered_set &libs); +}; + +using RebuildInfoByPackage = std::unordered_map; +using RebuildInfoByDatabase = std::unordered_map; + +struct LIBREPOMGR_EXPORT BuildProgress : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::unordered_map progressByPackage; + std::string targetDbFilePath; + std::string targetRepoPath; + std::string stagingDbFilePath; + std::string stagingRepoPath; + RebuildInfoByPackage producedProvides, removedProvides; + RebuildInfoByDatabase rebuildList; +}; + +struct LIBREPOMGR_EXPORT PackageMovementResult : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::vector> failedPackages; + std::vector processedPackages; + std::string errorMessage; +}; + +struct LIBREPOMGR_EXPORT RepositoryProblem : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::variant desc; + std::string pkg; + bool critical = true; +}; + +struct LIBREPOMGR_EXPORT BuildActionMessages : public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + std::vector notes; + std::vector warnings; + std::vector errors; +}; + +class BuildProcessSession; +struct OutputBufferingForSession; +struct ServiceSetup; + +struct LIBREPOMGR_EXPORT BuildAction : public std::enable_shared_from_this, + public ReflectiveRapidJSON::JsonSerializable, + public ReflectiveRapidJSON::BinarySerializable { + friend InternalBuildAction; + friend ServiceSetup; + friend BuildProcessSession; + friend BuildActionsTests; + friend void WebAPI::Routes::postBuildAction(const WebAPI::Params ¶ms, WebAPI::ResponseHandler &&handler); + friend void WebAPI::Routes::postBuildActionsFromTask(const WebAPI::Params ¶ms, WebAPI::ResponseHandler &&handler, const std::string &taskName, + const std::string &directory, const std::vector &startAfterIds, bool startImmediately); + friend void WebAPI::Routes::deleteBuildActions(const WebAPI::Params ¶ms, WebAPI::ResponseHandler &&handler); + friend void WebAPI::Routes::postCloneBuildActions(const WebAPI::Params ¶ms, WebAPI::ResponseHandler &&handler); + +public: + using IdType = BuildActionIdType; + static constexpr IdType invalidId = std::numeric_limits::max(); + + explicit BuildAction(IdType id = invalidId, ServiceSetup *setup = nullptr) noexcept; + ~BuildAction(); + bool isScheduled() const; + bool isExecuting() const; + bool isDone() const; + bool hasSucceeded() const; + static bool haveSucceeded(const std::vector> &buildActions); + bool isAborted() const; + void start(ServiceSetup &setup); + void startAfterOtherBuildActions(ServiceSetup &setup, const std::vector> &startsAfterBuildActions); + void abort(); + void appendOutput(std::string &&output); + void appendOutput(std::string_view output); + template void appendOutput(Args &&...args); + template void appendOutput(CppUtilities::EscapeCodes::Phrases phrase, Args &&...args); + LogContext &log(); + void setStopHandler(std::function &&stopHandler); + void setConcludeHandler(std::function &&concludeHandler); + std::shared_ptr findBuildProcess(const std::string &filePath); + std::shared_ptr makeBuildProcess(std::string &&logFilePath, ProcessHandler &&handler); + void terminateOngoingBuildProcesses(); + void streamFile(const WebAPI::Params ¶ms, const std::string &filePath, std::string_view fileMimeType); + void streamOutput(const WebAPI::Params ¶ms, std::size_t offset = 0); + +protected: +private: + template void post(); + template void post(Callback &&codeToRun); + void conclude(BuildActionResult result); + void continueStreamingExistingOutputToSession(std::shared_ptr session, OutputBufferingForSession &buffering, + const boost::system::error_code &error, std::size_t bytesTransferred); + void continueStreamingNewOutputToSession(std::shared_ptr session, OutputBufferingForSession &buffering, + const boost::system::error_code &error, std::size_t bytesTransferred); + template void appendOutput(OutputType &&output); + +public: + IdType id; + std::string taskName; + std::string templateName; + std::string directory; + std::vector packageNames; + std::vector sourceDbs, destinationDbs; + std::vector extraParams; // deprecated; remove at some point + std::unordered_map settings; + BuildActionFlagType flags = noBuildActionFlags; + BuildActionType type = BuildActionType::Invalid; + + // only the following member variables are supposed to change after the build action has been added + // to the overall list of build actions + BuildActionStatus status = BuildActionStatus::Created; + BuildActionResult result = BuildActionResult::None; + std::variant, LibPkg::LicenseResult, LibPkg::PackageUpdates, BuildPreparation, BuildProgress, + PackageMovementResult, std::unordered_map>, BuildActionMessages> + resultData; + std::string output; + std::string outputMimeType = "text/plain"; + std::vector logfiles; + std::vector artefacts; + CppUtilities::DateTime created = CppUtilities::DateTime::gmtNow(); + CppUtilities::DateTime started; + CppUtilities::DateTime finished; + std::vector startAfter; + +private: + LogContext m_log; + ServiceSetup *m_setup = nullptr; + std::atomic_bool m_aborted = false; + std::function m_stopHandler; + std::function m_concludeHandler; + std::mutex m_processesMutex; + std::unordered_map> m_ongoingProcesses; + std::mutex m_outputStreamingMutex; + std::unordered_map, std::unique_ptr> m_bufferingForSession; + std::unique_ptr m_internalBuildAction; + std::vector> m_followUpActions; +}; + +inline bool BuildAction::isScheduled() const +{ + return status == BuildActionStatus::Created || status == BuildActionStatus::AwaitingConfirmation; +} + +inline bool BuildAction::isExecuting() const +{ + return status == BuildActionStatus::Enqueued || status == BuildActionStatus::Running; +} + +inline bool BuildAction::isDone() const +{ + return status == BuildActionStatus::Finished; +} + +inline bool BuildAction::hasSucceeded() const +{ + return isDone() && result == BuildActionResult::Success; +} + +inline bool BuildAction::isAborted() const +{ + return m_aborted.load(); +} + +inline LogContext &BuildAction::log() +{ + return m_log; +} + +inline void BuildAction::setStopHandler(std::function &&stopHandler) +{ + m_stopHandler = std::move(stopHandler); +} + +inline void BuildAction::setConcludeHandler(std::function &&concludeHandler) +{ + m_concludeHandler = std::move(concludeHandler); +} + +inline std::shared_ptr BuildAction::findBuildProcess(const std::string &filePath) +{ + const auto i = m_ongoingProcesses.find(filePath); + return i != m_ongoingProcesses.cend() ? i->second : nullptr; +} + +/*! + * \brief Appends the specified arguments to the build action's log but *not* to the overall service log. + */ +template inline void BuildAction::appendOutput(Args &&...args) +{ + appendOutput(CppUtilities::argsToString(std::forward(args)...)); +} + +/*! + * \brief Appends the specified arguments to the build action's log and to the overall service log. + */ +template inline void BuildAction::appendOutput(CppUtilities::EscapeCodes::Phrases phrase, Args &&...args) +{ + auto msg = CppUtilities::argsToString(CppUtilities::EscapeCodes::formattedPhraseString(phrase), std::forward(args)...); + std::cerr << msg; + appendOutput(std::move(msg)); +} + +struct LIBREPOMGR_EXPORT BuildActionBasicInfo : public ReflectiveRapidJSON::JsonSerializable { + explicit BuildActionBasicInfo(const BuildAction &buildAction) + : id(buildAction.id) + , taskName(buildAction.taskName) + , directory(buildAction.directory) + , packageNames(buildAction.packageNames) + , sourceDbs(buildAction.sourceDbs) + , destinationDbs(buildAction.destinationDbs) + , startAfter(buildAction.startAfter) + , settings(buildAction.settings) + , flags(buildAction.flags) + , type(buildAction.type) + , status(buildAction.status) + , result(buildAction.result) + , created(buildAction.created) + , started(buildAction.started) + , finished(buildAction.finished) + { + } + + const BuildAction::IdType id; + const std::string &taskName; + const std::string &directory; + const std::vector &packageNames; + const std::vector &sourceDbs, &destinationDbs; + const std::vector &startAfter; + const std::unordered_map settings; + const BuildActionFlagType flags = noBuildActionFlags; + const BuildActionType type; + const BuildActionStatus status; + const BuildActionResult result; + const CppUtilities::DateTime created; + const CppUtilities::DateTime started; + const CppUtilities::DateTime finished; +}; + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_BUILD_ACTION_H diff --git a/librepomgr/buildactions/buildactionfwd.h b/librepomgr/buildactions/buildactionfwd.h new file mode 100644 index 0000000..1049acd --- /dev/null +++ b/librepomgr/buildactions/buildactionfwd.h @@ -0,0 +1,14 @@ +#ifndef LIBREPOMGR_BUILD_ACTION_FWD_H +#define LIBREPOMGR_BUILD_ACTION_FWD_H + +#include +#include + +namespace LibRepoMgr { + +struct BuildAction; +using BuildActionIdType = std::vector>::size_type; // build actions are stored in a vector and the ID is used as index + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_BUILD_ACTION_FWD_H diff --git a/librepomgr/buildactions/buildactionlivestreaming.cpp b/librepomgr/buildactions/buildactionlivestreaming.cpp new file mode 100644 index 0000000..6589717 --- /dev/null +++ b/librepomgr/buildactions/buildactionlivestreaming.cpp @@ -0,0 +1,539 @@ +#include "./buildactionprivate.h" + +#include "./logging.h" + +#include "../webapi/params.h" +#include "../webapi/render.h" +#include "../webapi/session.h" + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +static OutputBufferingForSession::BufferPoolType outputStreamingBufferPool(OutputBufferingForSession::bufferSize); + +void BuildProcessSession::BuffersToWrite::clear() +{ + currentlySentBuffers.clear(); + currentlySentBufferRefs.clear(); + outstandingBuffersToSend.clear(); +} + +void BuildProcessSession::DataForWebSession::streamFile( + const std::string &filePath, std::shared_ptr &&session, std::unique_lock &&lock) +{ + error = false; + + boost::beast::error_code error; + m_file.open(filePath.data(), boost::beast::file_mode::scan, error); + if (error) { + cerr << Phrases::WarningMessage << "Unable to open \"" << filePath << "\": " << error.message() << Phrases::EndFlush; + return; + } + const auto fileSize = m_file.size(error); + m_bytesToSendFromFile.store(fileSize); + lock.unlock(); + if (error) { + cerr << Phrases::WarningMessage << "Unable to determine size of \"" << filePath << "\": " << error.message() << Phrases::EndFlush; + return; + } + m_descriptor.assign(m_file.native_handle(), error); + if (error) { + m_bytesToSendFromFile.store(0); + cerr << Phrases::WarningMessage << "Unable to assign descriptor for \"" << filePath << "\": " << error.message() << Phrases::EndFlush; + return; + } + m_descriptor.non_blocking(true, error); + if (error) { + m_bytesToSendFromFile.store(0); + cerr << Phrases::WarningMessage << "Unable to set descriptor for \"" << filePath << "\" to non-blocking mode: " << error.message() + << Phrases::EndFlush; + return; + } + m_fileBuffer = m_session.m_bufferPool.newBuffer(); + m_descriptor.async_read_some(boost::asio::buffer(*m_fileBuffer, sizeof(std::min(fileSize, m_session.m_bufferPool.bufferSize()))), + std::bind(&DataForWebSession::writeFileData, this, std::ref(filePath), std::move(session), std::placeholders::_1, std::placeholders::_2)); +} + +void BuildProcessSession::DataForWebSession::writeFileData( + const std::string &filePath, std::shared_ptr session, const boost::system::error_code &readError, size_t bytesTransferred) +{ + // handle error + const auto eof = readError == boost::asio::error::eof; + if (!eof && readError) { + cerr << Phrases::WarningMessage << "Unable to determine size of \"" << filePath << "\": " << readError.message() << Phrases::EndFlush; + return; + } else if (eof) { + boost::system::error_code ec; + m_descriptor.close(ec); + } + // send file data to web client + const auto bytesLeftToRead = m_bytesToSendFromFile - bytesTransferred; + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk(boost::asio::buffer(*m_fileBuffer, bytesTransferred)), + [this, &filePath, session, bytesLeftToRead, moreToRead = !eof && bytesLeftToRead]( + boost::system::error_code ec, std::size_t bytesTransferred) { + // handle error + CPP_UTILITIES_UNUSED(bytesTransferred) + if (ec) { + cerr << Phrases::WarningMessage << "Error sending \"" << filePath << "\" to client: " << ec.message() << Phrases::EndFlush; + std::lock_guard lock(m_session.m_mutex); + clear(); + error = true; + m_bytesToSendFromFile.store(0); + return; + } + m_bytesToSendFromFile.store(bytesLeftToRead); + // tell the client it's over if there is nothing more to read + if (!moreToRead) { + if (m_session.m_exited.load()) { + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk_last(), + std::bind(&WebAPI::Session::responded, session, std::placeholders::_1, std::placeholders::_2, true)); + } + return; + } + // continue reading if there's more data + m_descriptor.async_read_some(boost::asio::buffer(*m_fileBuffer, sizeof(std::min(bytesLeftToRead, m_session.m_bufferPool.bufferSize()))), + std::bind( + &DataForWebSession::writeFileData, this, std::ref(filePath), std::move(session), std::placeholders::_1, std::placeholders::_2)); + }); +} + +void BuildProcessSession::registerWebSession(std::shared_ptr &&webSession) +{ + std::unique_lock lock(m_mutex); + auto &sessionInfo = m_registeredWebSessions[webSession]; + if (!sessionInfo) { + sessionInfo = std::make_unique(*this); + } + sessionInfo->streamFile(m_logFilePath, std::move(webSession), std::move(lock)); +} + +void BuildProcessSession::registerNewDataHandler(std::function &&handler) +{ + std::unique_lock lock(m_mutex); + m_newDataHandler = std::move(handler); +} + +void BuildProcessSession::prpareLogFile() +{ + // open logfile and a "file descriptor" for writing in a non-blocking way + boost::beast::error_code ec; + m_logFile.open(m_logFilePath.data(), boost::beast::file_mode::write, ec); + if (ec) { + result.errorCode = std::error_code(ec.value(), ec.category()); + result.error = CppUtilities::argsToString("unable to open \"", m_logFilePath, ": ", ec.message()); + return; + } + try { + m_logFileDescriptor.assign(m_logFile.native_handle()); + m_logFileDescriptor.non_blocking(true); + m_logFileDescriptor.native_non_blocking(true); + } catch (const boost::system::system_error &e) { + result.errorCode = e.code(); + result.error = CppUtilities::argsToString("unable to prepare descriptor for \"", m_logFilePath, ": ", e.what()); + return; + } +} + +void BuildProcessSession::readMoreFromPipe() +{ + m_buffer = m_bufferPool.newBuffer(); + m_pipe.async_read_some(boost::asio::buffer(m_buffer.get(), m_bufferPool.bufferSize()), + std::bind(&BuildProcessSession::writeDataFromPipe, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +} + +void BuildProcessSession::writeDataFromPipe(boost::system::error_code ec, std::size_t bytesTransferred) +{ + // handle error + if (ec && ec != boost::asio::stream_errc::eof) { + cerr << Phrases::ErrorMessage << "Error reading from pipe for \"" << m_logFilePath << "\": " << ec.message() << Phrases::EndFlush; + } + // write bytes to log file and web clients + if (bytesTransferred) { + std::lock_guard lock(m_mutex); + if (!m_logFileBuffers.error) { + if (m_logFileBuffers.currentlySentBuffers.empty()) { + m_logFileBuffers.currentlySentBuffers.emplace_back(std::pair(m_buffer, bytesTransferred)); + boost::asio::async_write(m_logFileDescriptor, boost::asio::buffer(m_buffer.get(), bytesTransferred), + std::bind(&BuildProcessSession::writeNextBufferToLogFile, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + } else { + m_logFileBuffers.outstandingBuffersToSend.emplace_back(std::pair(m_buffer, bytesTransferred)); + } + } + for (auto &[session, sessionInfo] : m_registeredWebSessions) { + if (sessionInfo->error) { + continue; + } + if (sessionInfo->currentlySentBuffers.empty() && !sessionInfo->bytesToSendFromFile()) { + sessionInfo->currentlySentBuffers.emplace_back(std::pair(m_buffer, bytesTransferred)); + boost::beast::net::async_write(session->socket(), + boost::beast::http::make_chunk(boost::asio::buffer(m_buffer.get(), bytesTransferred)), + std::bind(&BuildProcessSession::writeNextBufferToWebSession, shared_from_this(), std::placeholders::_1, std::placeholders::_2, + std::ref(*session), std::ref(*sessionInfo))); + } else { + sessionInfo->outstandingBuffersToSend.emplace_back(std::pair(m_buffer, bytesTransferred)); + } + } + if (m_newDataHandler) { + m_newDataHandler(m_buffer, bytesTransferred); + } + } + // continue reading from the pipe unless there was an error + if (!ec) { + readMoreFromPipe(); + return; + } + // stop reading from the pipe if there was an error; close the log file and tell web clients that it's over + if (bytesTransferred) { + return; + } + std::lock_guard lock(m_mutex); + if (m_logFileBuffers.outstandingBuffersToSend.empty()) { + boost::system::error_code error; + m_logFile.close(error); + if (error) { + cerr << Phrases::WarningMessage << "Error closing \"" << m_logFilePath << "\": " << error.message() << Phrases::EndFlush; + } + } + for (auto &[session, sessionInfo] : m_registeredWebSessions) { + if (!sessionInfo->outstandingBuffersToSend.empty()) { + continue; + } + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk_last(), + std::bind(&WebAPI::Session::responded, session, std::placeholders::_1, std::placeholders::_2, true)); + } +} + +void BuildProcessSession::writeNextBufferToLogFile(const boost::system::error_code &error, std::size_t bytesTransferred) +{ + // handle error + CPP_UTILITIES_UNUSED(bytesTransferred) + if (error) { + cerr << Phrases::ErrorMessage << "Error writing to \"" << m_logFilePath << "\": " << error.message() << Phrases::EndFlush; + std::lock_guard lock(m_mutex); + m_logFileBuffers.clear(); + m_logFileBuffers.error = true; + return; + } + // write more data to the logfile if there's more + { + std::lock_guard lock(m_mutex); + m_logFileBuffers.currentlySentBuffers.clear(); + if (m_logFileBuffers.outstandingBuffersToSend.empty()) { + // close the logfile when the process exited and we've written all the output + if (m_exited.load()) { + boost::system::error_code error; + m_logFile.close(error); + if (error) { + cerr << Phrases::WarningMessage << "Error closing \"" << m_logFilePath << "\": " << error.message() << Phrases::EndFlush; + } + } + return; + } + m_logFileBuffers.currentlySentBuffers.swap(m_logFileBuffers.outstandingBuffersToSend); + m_logFileBuffers.currentlySentBufferRefs.clear(); + for (const auto &buffer : m_logFileBuffers.currentlySentBuffers) { + m_logFileBuffers.currentlySentBufferRefs.emplace_back(boost::asio::buffer(buffer.first.get(), buffer.second)); + } + } + boost::asio::async_write(m_logFileDescriptor, m_logFileBuffers.currentlySentBufferRefs, + std::bind(&BuildProcessSession::writeNextBufferToLogFile, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +} + +void BuildProcessSession::writeNextBufferToWebSession( + const boost::system::error_code &error, std::size_t bytesTransferred, WebAPI::Session &session, BuildProcessSession::BuffersToWrite &sessionInfo) +{ + // handle error + CPP_UTILITIES_UNUSED(bytesTransferred) + if (error) { + cerr << Phrases::WarningMessage << "Error sending \"" << m_logFilePath << "\" to client: " << error.message() << Phrases::EndFlush; + std::lock_guard lock(m_mutex); + sessionInfo.clear(); + sessionInfo.error = true; + return; + } + // send more data to the client if there's more + { + std::lock_guard lock(m_mutex); + sessionInfo.currentlySentBuffers.clear(); + // tell the client it's over when the process exited and we've sent all the output + if (sessionInfo.outstandingBuffersToSend.empty()) { + if (m_exited.load()) { + boost::beast::net::async_write(session.socket(), boost::beast::http::make_chunk_last(), + std::bind(&WebAPI::Session::responded, session.shared_from_this(), std::placeholders::_1, std::placeholders::_2, true)); + } + return; + } + sessionInfo.currentlySentBuffers.swap(sessionInfo.outstandingBuffersToSend); + sessionInfo.currentlySentBufferRefs.clear(); + for (const auto &buffer : sessionInfo.currentlySentBuffers) { + sessionInfo.currentlySentBufferRefs.emplace_back(boost::asio::buffer(buffer.first.get(), buffer.second)); + } + } + boost::beast::net::async_write(session.socket(), boost::beast::http::make_chunk(sessionInfo.currentlySentBufferRefs), + std::bind(&BuildProcessSession::writeNextBufferToWebSession, shared_from_this(), std::placeholders::_1, std::placeholders::_2, + std::ref(session), std::ref(sessionInfo))); +} + +void BuildProcessSession::conclude() +{ + // set the exited flag so all async operations know there's no more data to expect + m_exited = true; + + // detach from build action + auto buildAction = m_buildAction.lock(); + if (!buildAction) { + return; + } + const auto processesLock = std::lock_guard(buildAction->m_processesMutex); + buildAction->m_ongoingProcesses.erase(m_logFilePath); +} + +void BufferSearch::operator()(const BuildProcessSession::BufferType &buffer, std::size_t bufferSize) +{ + if (m_hasResult || (!m_giveUpTerm.empty() && m_giveUpTermIterator == m_giveUpTerm.end())) { + return; + } + for (auto i = buffer->data(), end = buffer->data() + bufferSize; i != end; ++i) { + const auto currentChar = *i; + if (m_searchTermIterator == m_searchTerm.end()) { + for (const auto &terminationChar : m_terminationChars) { + if (currentChar == terminationChar) { + m_hasResult = true; + break; + } + } + if (m_hasResult) { + m_callback(std::move(m_result)); + return; + } + m_result += currentChar; + continue; + } + if (currentChar == *m_searchTermIterator) { + ++m_searchTermIterator; + } else { + m_searchTermIterator = m_searchTerm.begin(); + } + if (m_giveUpTerm.empty()) { + continue; + } + if (currentChar == *m_giveUpTermIterator) { + ++m_giveUpTermIterator; + } else { + m_giveUpTermIterator = m_giveUpTerm.begin(); + } + } +} + +std::shared_ptr BuildAction::makeBuildProcess(std::string &&logFilePath, ProcessHandler &&handler) +{ + const auto processesLock = std::lock_guard(m_processesMutex); + auto &process = m_ongoingProcesses[logFilePath]; + if (process) { + // prevent multiple ongoing processes for the same log file + // note: The build action implementations are supposed to avoid this condition but let's make this function generic. + return nullptr; + } + auto buildLock = m_setup->building.lockToWrite(); + if (find(logfiles.cbegin(), logfiles.cend(), logFilePath) == logfiles.cend()) { + logfiles.emplace_back(logFilePath); + } + buildLock.unlock(); + return process = make_shared(this, m_setup->building.ioContext, std::move(logFilePath), std::move(handler)); +} + +void BuildAction::terminateOngoingBuildProcesses() +{ + const auto processesLock = std::lock_guard(m_processesMutex); + for (auto &[logFilePath, process] : m_ongoingProcesses) { + if (process->hasExited()) { + continue; + } + std::error_code ec; + process->group.terminate(ec); + if (ec) { + log()(Phrases::ErrorMessage, "Unable to stop process group (main PID ", process->child.id(), ") for \"", logFilePath, + "\": ", ec.message(), '\n'); + } + } +} + +void BuildAction::streamFile(const WebAPI::Params ¶ms, const std::string &filePath, std::string_view fileMimeType) +{ + auto processesLock = std::unique_lock(m_processesMutex); + auto buildProcess = findBuildProcess(filePath); + processesLock.unlock(); + if (!buildProcess) { + // simply send the file if there's no ongoing process writing to it anymore + params.session.respond(filePath.data(), fileMimeType.data(), params.target.path); + return; + } + + // stream the output of the ongoing process + auto chunkResponse = WebAPI::Render::makeChunkResponse(params.request(), fileMimeType.data()); + boost::beast::http::async_write_header(params.session.socket(), chunkResponse->serializer, + [chunkResponse, filePath, buildProcess, session = params.session.shared_from_this()]( + const boost::system::error_code &error, std::size_t) mutable { + if (error) { + cerr << Phrases::WarningMessage << "Error sending header for \"" << filePath << "\" to client: " << error.message() + << Phrases::EndFlush; + return; + } + buildProcess->registerWebSession(std::move(session)); + }); +} + +void BuildAction::streamOutput(const WebAPI::Params ¶ms, std::size_t offset) +{ + if (!m_setup) { + m_setup = ¶ms.setup; + } + auto session = params.session.shared_from_this(); + auto chunkResponse = WebAPI::Render::makeChunkResponse(params.request(), "application/octet-stream"); + auto outputStreamingLock = std::unique_lock(m_outputStreamingMutex); + auto &buffersForSession = m_bufferingForSession[session]; + if (buffersForSession) { + return; // skip when already streaming to that session + } + buffersForSession = std::make_unique(); + auto buildLock = params.setup.building.lockToRead(); + buffersForSession->existingOutputSize = output.size(); + buffersForSession->bytesSent = offset; + buildLock.unlock(); + outputStreamingLock.unlock(); + boost::beast::http::async_write_header(params.session.socket(), chunkResponse->serializer, + [buildAction = shared_from_this(), session = std::move(session), &buffering = *buffersForSession, chunkResponse]( + const boost::system::error_code &error, std::size_t bytesTransferred) { + CPP_UTILITIES_UNUSED(bytesTransferred) + buildAction->continueStreamingExistingOutputToSession(std::move(session), buffering, error, 0); + }); +} + +void BuildAction::continueStreamingExistingOutputToSession(std::shared_ptr session, OutputBufferingForSession &buffering, + const boost::system::error_code &error, std::size_t bytesTransferred) +{ + auto outputStreamingLock = std::unique_lock(m_outputStreamingMutex); + if (error) { + m_bufferingForSession.erase(session); + return; + } + const auto bytesSent = buffering.bytesSent += bytesTransferred; + if (bytesSent >= buffering.existingOutputSize) { + buffering.currentlySentBuffers.clear(); + buffering.existingOutputSent = true; + if (!buffering.outstandingBuffersToSend.empty()) { + outputStreamingLock.unlock(); + continueStreamingNewOutputToSession(std::move(session), buffering, error, 0); + return; + } + if (isDone()) { + m_bufferingForSession.erase(session); + outputStreamingLock.unlock(); + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk_last(), + std::bind(&WebAPI::Session::responded, session, std::placeholders::_1, std::placeholders::_2, true)); + } + return; + } + auto buffer = buffering.currentlySentBuffers.empty() ? outputStreamingBufferPool.newBuffer() : buffering.currentlySentBuffers.front().first; + const auto bytesToCopy = std::min(output.size() - bytesSent, outputStreamingBufferPool.bufferSize()); + if (buffering.currentlySentBuffers.empty()) { + buffering.currentlySentBuffers.emplace_back(std::pair(buffer, bytesToCopy)); + } + outputStreamingLock.unlock(); + + auto buildLock = m_setup->building.lockToRead(); + output.copy(buffer->data(), bytesToCopy, bytesSent); + buildLock.unlock(); + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk(boost::asio::buffer(buffer->data(), bytesToCopy)), + std::bind(&BuildAction::continueStreamingExistingOutputToSession, shared_from_this(), session, std::ref(buffering), std::placeholders::_1, + std::placeholders::_2)); +} + +void BuildAction::continueStreamingNewOutputToSession(std::shared_ptr session, OutputBufferingForSession &buffering, + const boost::system::error_code &error, std::size_t bytesTransferred) +{ + auto outputStreamingLock = std::unique_lock(m_outputStreamingMutex); + buffering.bytesSent += bytesTransferred; + buffering.currentlySentBuffers.clear(); + buffering.currentlySentBufferRefs.clear(); + if (error) { + m_bufferingForSession.erase(session); + return; + } + if (buffering.outstandingBuffersToSend.empty()) { + if (isDone()) { + m_bufferingForSession.erase(session); + outputStreamingLock.unlock(); + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk_last(), + std::bind(&WebAPI::Session::responded, session, std::placeholders::_1, std::placeholders::_2, true)); + } + return; + } + buffering.outstandingBuffersToSend.swap(buffering.currentlySentBuffers); + buffering.currentlySentBufferRefs.reserve(buffering.currentlySentBuffers.size()); + for (const auto ¤tBuffer : buffering.currentlySentBuffers) { + buffering.currentlySentBufferRefs.emplace_back(boost::asio::buffer(*currentBuffer.first, currentBuffer.second)); + } + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk(buffering.currentlySentBufferRefs), + std::bind(&BuildAction::continueStreamingNewOutputToSession, shared_from_this(), session, std::ref(buffering), std::placeholders::_1, + std::placeholders::_2)); +} + +template void BuildAction::appendOutput(OutputType &&output) +{ + if (output.empty() || !m_setup) { + return; + } + + auto lock = m_setup->building.lockToWrite(); + this->output.append(output); + lock.unlock(); + + OutputBufferingForSession::BufferPile buffers; + for (std::size_t offset = 0; offset < output.size(); offset += buffers.back().second) { + const auto bytesToBuffer = std::min(output.size() - offset, outputStreamingBufferPool.bufferSize()); + auto buffer = buffers.emplace_back(std::pair(outputStreamingBufferPool.newBuffer(), bytesToBuffer)); + output.copy(buffer.first->data(), bytesToBuffer, offset); + } + + auto outputStreamingLock = std::unique_lock(m_outputStreamingMutex); + for (auto &bufferingForSession : m_bufferingForSession) { + auto &buffering = bufferingForSession.second; + auto ¤tlySentBuffers = buffering->currentlySentBuffers; + if (currentlySentBuffers.empty() && buffering->existingOutputSent) { + auto &session = bufferingForSession.first; + auto ¤tlySentBufferRefs = buffering->currentlySentBufferRefs; + currentlySentBuffers.insert(currentlySentBuffers.end(), buffers.begin(), buffers.end()); + for (const auto &buffer : buffers) { + currentlySentBufferRefs.emplace_back(boost::asio::buffer(buffer.first->data(), buffer.second)); + } + boost::beast::net::async_write(session->socket(), boost::beast::http::make_chunk(currentlySentBufferRefs), + std::bind(&BuildAction::continueStreamingNewOutputToSession, shared_from_this(), session, std::ref(*buffering), std::placeholders::_1, + std::placeholders::_2)); + } else { + auto &outstandingBuffersToSend = buffering->outstandingBuffersToSend; + outstandingBuffersToSend.insert(outstandingBuffersToSend.end(), buffers.begin(), buffers.end()); + } + } +} + +/*! + * \brief Internally called to append output and spread it to all waiting sessions. + */ +void BuildAction::appendOutput(std::string &&output) +{ + appendOutput(std::move(output)); +} + +/*! + * \brief Internally called to append output and spread it to all waiting sessions. + */ +void BuildAction::appendOutput(std::string_view output) +{ + appendOutput(std::forward(output)); +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/buildactionmeta.cpp b/librepomgr/buildactions/buildactionmeta.cpp new file mode 100644 index 0000000..b4f3d8a --- /dev/null +++ b/librepomgr/buildactions/buildactionmeta.cpp @@ -0,0 +1,366 @@ +#include "./buildactionmeta.h" + +#include "reflection/buildactionmeta.h" + +namespace LibRepoMgr { + +template +static MapType mapByName(const IteratableType &iteratable, MemberPtr memberPtr) +{ + MapType map; + for (const auto &element : iteratable) { + map.insert(std::pair(element.*memberPtr, std::cref(element))); + } + return map; +} + +template static OutputIteratableType mapInto(const InputIteratableType &iteratable) +{ + OutputIteratableType mappings; + mappings.reserve(iteratable.size()); + for (const auto &element : iteratable) { + mappings.emplace_back(element); + } + return mappings; +} + +BuildActionMetaInfo::BuildActionMetaInfo() + : types({ + BuildActionTypeMetaInfo{ + .id = BuildActionType::Invalid, + .name = "Invalid", + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::RemovePackages, + .category = "Repo management", + .name = "Remove packages", + .type = "remove-packages", + .flags = {}, + .settings = {}, + .directory = true, + .sourceDb = false, + .destinationDb = true, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::MovePackages, + .category = "Repo management", + .name = "Move packages", + .type = "move-packages", + .flags = {}, + .settings = {}, + .directory = true, + .sourceDb = true, + .destinationDb = true, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::CheckForUpdates, + .category = "Repo management", + .name = "Check for updates", + .type = "check-updates", + .flags = {}, + .settings = {}, + .directory = false, + .sourceDb = true, + .destinationDb = true, + .packageNames = false, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::ReloadDatabase, + .category = "Repo management", + .name = "Reload databases", + .type = "reload-database", + .flags = {}, + .settings = {}, + .directory = false, + .sourceDb = false, + .destinationDb = true, + .packageNames = false, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::ReloadLibraryDependencies, + .category = "Refresh data", + .name = "Reload library dependencies", + .type = "reload-library-dependencies", + .flags = { + BuildActionFlagMetaInfo{ + .id = static_cast(ReloadLibraryDependenciesFlags::ForceReload), + .name = "Force reload", + .desc = "Reload packages as well even though they have not changed on disk since the last reload", + .param = "force-reload", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(ReloadLibraryDependenciesFlags::SkipDependencies), + .name = "Skip dependencies", + .desc = "Do not take dependencies of the specified destination databases into account", + .param = "skip-dependencies", + }, + }, + .settings = {}, + .directory = false, + .sourceDb = false, + .destinationDb = true, + .packageNames = false, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::PrepareBuild, + .category = "Building", + .name = "Prepare build", + .type = "prepare-build", + .flags = { + BuildActionFlagMetaInfo{ + .id = static_cast(PrepareBuildFlags::ForceBumpPkgRel), + .name = "Force-bump pkgrel", + .desc = "Bump the pkgrel of the packages even if there is no existing version", + .param = "force-bump-pkgrel", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(PrepareBuildFlags::CleanSrcDir), + .name = "Clean source directory", + .desc = "Removes existing \"src\" sub-directories for the specified packages in the directory; use to update previously built packages", + .param = "clean-src-dir", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(PrepareBuildFlags::KeepOrder), + .name = "Keep dependency order", + .desc = "Build packages in the specified order", + .param = "keep-order", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(PrepareBuildFlags::KeepPkgRelAndEpoch), + .name = "Keep pkgrel/epoch", + .desc = "Never bumps pkgrel and epoch", + .param = "keep-order", + }, + }, + .settings = { + BuildActionSettingMetaInfo{ + .name = "PKGBUILDs directory", + .desc = "A colon separated list of PKGBUILDs directories to consider before checking the standard directories", + .param = "pkgbuilds-dir", + }, + }, + .directory = true, + .sourceDb = true, + .destinationDb = true, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::ConductBuild, + .category = "Building", + .name = "Conduct build", + .type = "conduct-build", + .flags = { + BuildActionFlagMetaInfo{ + .id = static_cast(ConductBuildFlags::BuildAsFarAsPossible), + .name = "Build as far as possible", + .desc = "By default the next batch is only considered when all packages in the previous batch succeeded; this option allows to build as far as possible instead", + .param = "build-as-far-as-possible", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(ConductBuildFlags::SaveChrootOfFailures), + .name = "Save chroot of failures", + .desc = "Renames the chroot working copy when a package failed to build so it will not be overridden by further builds and can be used for further investigation", + .param = "save-chroot-of-failures", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(ConductBuildFlags::UpdateChecksums), + .name = "Update checksums", + .desc = "Assumes that the checksums of the PKGBUILDs are outdated and will therefore update the checksums instead of using them for validation", + .param = "update-checksums", + }, + BuildActionFlagMetaInfo{ + .id = static_cast(ConductBuildFlags::AutoStaging), + .name = "Auto-staging", + .desc = "Adds \"breaking\" packages only to the destination DB's staging repsitory and emits a rebuild list", + .param = "auto-staging", + }, + }, + .settings = { + BuildActionSettingMetaInfo{ + .name = "Chroot directory", + .desc = "The chroot directory to use (instead of the globally configured one)", + .param = "chroot-dir", + }, + BuildActionSettingMetaInfo{ + .name = "Chroot default user", + .desc = "The default chroot user to use (instead of the globally configured one)", + .param = "chroot-dir", + }, + BuildActionSettingMetaInfo{ + .name = "CCache directory", + .desc = "The ccache directory to use (instead of the globally configured one)", + .param = "ccache-dir", + }, + BuildActionSettingMetaInfo{ + .name = "Package cache directory", + .desc = "The package cache directory to use (instead of the globally configured one)", + .param = "pkg-cache-dir", + }, + BuildActionSettingMetaInfo{ + .name = "Test files directory", + .desc = "The test files directory to use (instead of the globally configured one)", + .param = "test-files-dir", + }, + }, + .directory = true, + .sourceDb = false, + .destinationDb = false, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::MakeLicenseInfo, + .category = "Misc", + .name = "Make license info", + .type = "make-license-info", + .flags = {}, + .settings = {}, + .directory = false, + .sourceDb = false, + .destinationDb = false, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::ReloadConfiguration, + .category = "Refresh data", + .name = "Reload configuration", + .type = "reload-configuration", + .flags = {}, + .settings = {}, + .directory = false, + .sourceDb = false, + .destinationDb = false, + .packageNames = false, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::CheckForProblems, + .category = "Repo management", + .name = "Check for problems", + .type = "check-for-problems", + .flags = {}, + .settings = {}, + .directory = true, + .sourceDb = false, + .destinationDb = true, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::CleanRepository, + .category = "Repo management", + .name = "Clean repository", + .type = "clean-repository", + .flags = { + BuildActionFlagMetaInfo{ + .id = static_cast(CleanRepositoryFlags::DryRun), + .name = "Dry run", + .desc = "Only record what would be done", + .param = "dry-run", + }, + }, + .settings = {}, + .directory = true, + .sourceDb = false, + .destinationDb = true, + .packageNames = true, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::DummyBuildAction, + .category = "Misc", + .name = "Dummy action for debugging", + .type = "dummy", + .flags = {}, + .settings = {}, + .directory = true, + .sourceDb = false, + .destinationDb = false, + .packageNames = false, + }, + BuildActionTypeMetaInfo{ + .id = BuildActionType::CustomCommand, + .category = "Misc", + .name = "Execute custom Bash command", + .type = "custom-command", + .flags = {}, + .settings = { + BuildActionSettingMetaInfo{ + .name = "Command", + .desc = "The command to execute via Bash", + .param = "cmd", + }, + }, + .directory = true, + .sourceDb = false, + .destinationDb = false, + .packageNames = false, + }, + }) + , states({ + BuildActionStatusMetaInfo{ + .id = BuildActionStatus::Created, + .name = "Created", + }, + BuildActionStatusMetaInfo{ + .id = BuildActionStatus::Enqueued, + .name = "Enqueued", + }, + BuildActionStatusMetaInfo{ + .id = BuildActionStatus::AwaitingConfirmation, + .name = "Enqueued", + }, + BuildActionStatusMetaInfo{ + .id = BuildActionStatus::AwaitingConfirmation, + .name = "Awaiting confirmation", + }, + BuildActionStatusMetaInfo{ + .id = BuildActionStatus::Running, + .name = "Running", + }, + BuildActionStatusMetaInfo{ + .id = BuildActionStatus::Finished, + .name = "Finished", + }, + }) + , results({ + BuildActionResultMetaInfo{ + .id = BuildActionResult::None, + .name = "None", + }, + BuildActionResultMetaInfo{ + .id = BuildActionResult::Success, + .name = "Success", + }, + BuildActionResultMetaInfo{ + .id = BuildActionResult::Failure, + .name = "Failure", + }, + BuildActionResultMetaInfo{ + .id = BuildActionResult::ConfirmationDeclined, + .name = "ConfirmationDeclined", + }, + BuildActionResultMetaInfo{ + .id = BuildActionResult::Aborted, + .name = "Aborted", + }, + }) + , m_typeInfoByName(mapByName(types, &BuildActionTypeMetaInfo::type)) + , m_mappings(mapInto(types)) +{ +} + +const BuildActionTypeMetaInfo &BuildActionMetaInfo::typeInfoForName(std::string_view name) const +{ + if (const auto i = m_typeInfoByName.find(name); i != m_typeInfoByName.end()) { + return i->second; + } else { + return typeInfoForId(BuildActionType::Invalid); + } +} + +BuildActionTypeMetaMapping::BuildActionTypeMetaMapping(const BuildActionTypeMetaInfo &typeInfo) + : flagInfoByName(mapByName(typeInfo.flags, &BuildActionFlagMetaInfo::param)) + , settingInfoByName(mapByName(typeInfo.settings, &BuildActionSettingMetaInfo::param)) +{ +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/buildactionmeta.h b/librepomgr/buildactions/buildactionmeta.h new file mode 100644 index 0000000..171c86b --- /dev/null +++ b/librepomgr/buildactions/buildactionmeta.h @@ -0,0 +1,175 @@ +#ifndef LIBREPOMGR_BUILD_ACTION_META_H +#define LIBREPOMGR_BUILD_ACTION_META_H + +#include "../global.h" + +#include "../../libpkg/data/lockable.h" + +#include + +#include + +#include +#include +#include +#include + +namespace LibRepoMgr { + +enum class BuildActionStatus : std::uint64_t { + Created, + Enqueued, + AwaitingConfirmation, + Running, + Finished, +}; + +enum class BuildActionResult : std::uint64_t { + None, + Success, + Failure, + ConfirmationDeclined, + Aborted, +}; + +enum class BuildActionType : std::uint64_t { + Invalid, + RemovePackages, + MovePackages, + CheckForUpdates, + ReloadDatabase, + ReloadLibraryDependencies, + PrepareBuild, + ConductBuild, + MakeLicenseInfo, + ReloadConfiguration, + CheckForProblems, + CleanRepository, + DummyBuildAction, + CustomCommand, +}; + +using BuildActionFlagType = std::uint64_t; +static constexpr BuildActionFlagType noBuildActionFlags = 0; +enum class ReloadLibraryDependenciesFlags : BuildActionFlagType { + None, + ForceReload = (1 << 0), + SkipDependencies = (1 << 1), +}; +enum class PrepareBuildFlags : BuildActionFlagType { + None, + ForceBumpPkgRel = (1 << 0), + CleanSrcDir = (1 << 1), + KeepOrder = (1 << 2), + KeepPkgRelAndEpoch = (1 << 3), +}; +enum class ConductBuildFlags : BuildActionFlagType { + None, + BuildAsFarAsPossible = (1 << 0), + SaveChrootOfFailures = (1 << 1), + UpdateChecksums = (1 << 2), + AutoStaging = (1 << 3), +}; +enum class CleanRepositoryFlags : BuildActionFlagType { + None, + DryRun = (1 << 0), +}; +enum class PrepareBuildSettings : std::size_t { PKGBUILDsDirs }; +enum class ConductBuildSettings : std::size_t { ChrootDir, ChrootDefaultUser, CCacheDir, PackageCacheDir, TestFilesDir }; +enum class CustomCommandSettings : std::size_t { Command }; + +struct LIBREPOMGR_EXPORT BuildActionFlagMetaInfo : public ReflectiveRapidJSON::JsonSerializable { + const BuildActionFlagType id = 0; + const std::string_view name; + const std::string_view desc; + const std::string_view param; +}; +struct LIBREPOMGR_EXPORT BuildActionSettingMetaInfo : public ReflectiveRapidJSON::JsonSerializable { + const std::string_view name; + const std::string_view desc; + const std::string_view param; +}; +struct LIBREPOMGR_EXPORT BuildActionTypeMetaInfo : public ReflectiveRapidJSON::JsonSerializable { + const BuildActionType id = BuildActionType::Invalid; + const std::string_view category; + const std::string_view name; + const std::string_view type; + const std::vector flags; + const std::vector settings; + const bool directory = true; + const bool sourceDb = true; + const bool destinationDb = true; + const bool packageNames = true; +}; +struct LIBREPOMGR_EXPORT BuildActionStatusMetaInfo : public ReflectiveRapidJSON::JsonSerializable { + const BuildActionStatus id = BuildActionStatus::Created; + const std::string_view name; +}; +struct LIBREPOMGR_EXPORT BuildActionResultMetaInfo : public ReflectiveRapidJSON::JsonSerializable { + const BuildActionResult id = BuildActionResult::None; + const std::string_view name; +}; +struct BuildActionMetaInfo; +struct LIBREPOMGR_EXPORT BuildActionTypeMetaMapping { + using FlagMap = std::unordered_map>; + using SettingMap = std::unordered_map>; + + explicit BuildActionTypeMetaMapping(const BuildActionTypeMetaInfo &typeInfo); + + const FlagMap flagInfoByName; + const SettingMap settingInfoByName; +}; +struct LIBREPOMGR_EXPORT BuildActionMetaInfo : public ReflectiveRapidJSON::JsonSerializable, public LibPkg::Lockable { + using TypeInfoByName = std::unordered_map>; + using MetaMappingsForTypes = std::vector; + + explicit BuildActionMetaInfo(); + const TypeInfoByName &typeInfoByName() const; + const MetaMappingsForTypes &mappings() const; + bool isTypeIdValid(BuildActionType id) const; + const BuildActionTypeMetaInfo &typeInfoForId(BuildActionType id) const; + const BuildActionTypeMetaInfo &typeInfoForName(std::string_view name) const; + const BuildActionTypeMetaMapping &mappingForId(BuildActionType id) const; + + const std::vector types; + const std::vector states; + const std::vector results; + +private: + const TypeInfoByName m_typeInfoByName; + const MetaMappingsForTypes m_mappings; +}; + +inline const BuildActionMetaInfo::TypeInfoByName &BuildActionMetaInfo::typeInfoByName() const +{ + return m_typeInfoByName; +} + +inline const BuildActionMetaInfo::MetaMappingsForTypes &BuildActionMetaInfo::mappings() const +{ + return m_mappings; +} + +inline bool BuildActionMetaInfo::isTypeIdValid(BuildActionType id) const +{ + return static_cast(id) < types.size(); +} + +inline const BuildActionTypeMetaInfo &BuildActionMetaInfo::typeInfoForId(BuildActionType id) const +{ + return types[static_cast(id)]; +} + +inline const BuildActionTypeMetaMapping &BuildActionMetaInfo::mappingForId(BuildActionType id) const +{ + return m_mappings[static_cast(id)]; +} + +} // namespace LibRepoMgr + +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibRepoMgr, LibRepoMgr::ReloadLibraryDependenciesFlags) +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibRepoMgr, LibRepoMgr::PrepareBuildFlags) +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibRepoMgr, LibRepoMgr::ConductBuildFlags) +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibRepoMgr, LibRepoMgr::CleanRepositoryFlags) + +#endif // LIBREPOMGR_BUILD_ACTION_META_H diff --git a/librepomgr/buildactions/buildactionprivate.h b/librepomgr/buildactions/buildactionprivate.h new file mode 100644 index 0000000..5a8e97d --- /dev/null +++ b/librepomgr/buildactions/buildactionprivate.h @@ -0,0 +1,606 @@ +#ifndef LIBREPOMGR_BUILD_ACTION_PRIVATE_H +#define LIBREPOMGR_BUILD_ACTION_PRIVATE_H + +#include "./buildaction.h" +#include "./subprocess.h" + +#include "../webclient/aur.h" +#include "../webclient/database.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CPP_UTILITIES_DEBUG_BUILD +#include +#endif + +namespace boost::process { +class child; +} + +namespace LibRepoMgr { + +enum class BuildActionAccess { + ReadConfig, + WriteConfig, +}; + +enum class RequiredDatabases { + None = 0x0, + OneSource = 0x1, + OneDestination = 0x2, + OneOrMoreSources = 0x4, + OneOrMoreDestinations = 0x8, + MaybeSource = 0x10, + MaybeDestination = 0x20, + AllowFromAur = 0x40, + AllowToAur = 0x80, +}; + +enum class RequiredParameters { + None = 0x0, + Packages = 0x1, + MaybePackages = 0x2, +}; + +} // namespace LibRepoMgr + +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibRepoMgr, LibRepoMgr::RequiredDatabases) +CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(LibRepoMgr, LibRepoMgr::RequiredParameters) + +namespace LibRepoMgr { + +/// \brief The BufferPool struct provides fixed-sized buffers used for BuildProcessSession's live-steaming. +template struct LIBREPOMGR_EXPORT BufferPool { + explicit BufferPool(std::size_t bufferSize); + using BufferType = std::shared_ptr; + BufferType newBuffer(); + std::size_t bufferSize() const; + std::size_t storedBuffers() const; + +private: + std::vector m_buffers; + std::size_t m_bufferSize; + std::mutex m_mutex; +}; + +template +inline BufferPool::BufferPool(std::size_t bufferSize) + : m_bufferSize(bufferSize) +{ + m_buffers.reserve(16); +} + +template inline typename BufferPool::BufferType BufferPool::newBuffer() +{ + std::lock_guard lock(m_mutex); + for (auto &existingBuffer : m_buffers) { + if (!existingBuffer) { + return existingBuffer = std::make_shared(); + } else if (existingBuffer.use_count() == 1) { + return existingBuffer; + } + } + return m_buffers.emplace_back(std::make_shared()); +} + +template inline std::size_t BufferPool::bufferSize() const +{ + return m_bufferSize; +} + +template inline std::size_t BufferPool::storedBuffers() const +{ + return m_buffers.size(); +} + +/// \brief The OutputBufferingForSession struct holds buffers used by the BuildAction live-streaming. +struct LIBREPOMGR_EXPORT OutputBufferingForSession { + static constexpr std::size_t bufferSize = 4096; + using StorageType = std::array; + using BufferPoolType = BufferPool; + using BufferType = BufferPoolType::BufferType; + using BufferPile = std::vector>; + using BufferRefs = std::vector; + BufferPile currentlySentBuffers; + BufferPile outstandingBuffersToSend; + BufferRefs currentlySentBufferRefs; + std::atomic bytesSent = 0; + std::atomic existingOutputSize = 0; + std::atomic_bool existingOutputSent = false; +}; + +/// \brief The BuildProcessSession class spawns a process assoicated with a build action. +/// The process output is make available as a logfile of the build action allowing live-steaming. +class LIBREPOMGR_EXPORT BuildProcessSession : public std::enable_shared_from_this, public BaseProcessSession { +public: + static constexpr std::size_t bufferSize = 4096; + using StorageType = std::array; + using BufferPoolType = BufferPool; + using BufferType = BufferPoolType::BufferType; + + explicit BuildProcessSession(BuildAction *buildAction, boost::asio::io_context &ioContext, std::string &&logFilePath, Handler &&handler); + template void launch(ChildArgs &&...childArgs); + void registerWebSession(std::shared_ptr &&webSession); + void registerNewDataHandler(std::function &&handler); + bool hasExited() const; + +private: + using BufferPile = std::vector>; + using BufferRefs = std::vector; + struct BuffersToWrite { + BufferPile currentlySentBuffers; + BufferPile outstandingBuffersToSend; + BufferRefs currentlySentBufferRefs; + bool error = false; + void clear(); + }; + struct DataForWebSession : public BuffersToWrite { + explicit DataForWebSession(BuildProcessSession &session); + void streamFile(const std::string &filePath, std::shared_ptr &&session, std::unique_lock &&lock); + std::size_t bytesToSendFromFile() const; + + private: + void writeFileData(const std::string &filePath, std::shared_ptr session, const boost::system::error_code &error, + std::size_t bytesTransferred); + + BuildProcessSession &m_session; + std::atomic m_bytesToSendFromFile = 0; + boost::beast::file m_file; + BufferType m_fileBuffer; + boost::asio::posix::stream_descriptor m_descriptor; + }; + + void prpareLogFile(); + void readMoreFromPipe(); + void writeDataFromPipe(boost::system::error_code ec, std::size_t bytesRead); + void writeNextBufferToLogFile(const boost::system::error_code &error, std::size_t bytesTransferred); + void writeNextBufferToWebSession( + const boost::system::error_code &error, std::size_t bytesTransferred, WebAPI::Session &session, BuffersToWrite &sessionInfo); + void conclude(); + + std::weak_ptr m_buildAction; + boost::process::async_pipe m_pipe; + BufferPoolType m_bufferPool; + BufferType m_buffer; + std::string m_logFilePath; + boost::beast::file m_logFile; + boost::asio::posix::stream_descriptor m_logFileDescriptor; + std::mutex m_mutex; + BuffersToWrite m_logFileBuffers; + std::unordered_map, std::unique_ptr> m_registeredWebSessions; + std::function m_newDataHandler; + std::atomic_bool m_exited = false; +}; + +inline BuildProcessSession::DataForWebSession::DataForWebSession(BuildProcessSession &session) + : m_session(session) + , m_descriptor(session.m_ioContext) +{ +} + +inline std::size_t BuildProcessSession::DataForWebSession::bytesToSendFromFile() const +{ + return m_bytesToSendFromFile.load(); +} + +inline BuildProcessSession::BuildProcessSession( + BuildAction *buildAction, boost::asio::io_context &ioContext, std::string &&logFilePath, BaseProcessSession::Handler &&handler) + : BaseProcessSession(ioContext, std::move(handler)) + , m_buildAction(buildAction ? buildAction->weak_from_this() : std::weak_ptr()) + , m_pipe(ioContext) + , m_bufferPool(bufferSize) + , m_logFilePath(std::move(logFilePath)) + , m_logFileDescriptor(ioContext) +{ +} + +inline bool BuildProcessSession::hasExited() const +{ + return m_exited.load(); +} + +template void BuildProcessSession::launch(ChildArgs &&...childArgs) +{ + prpareLogFile(); + if (result.errorCode) { + conclude(); + return; + } + try { + child = boost::process::child( + m_ioContext, group, std::forward(childArgs)..., (boost::process::std_out & boost::process::std_err) > m_pipe, + boost::process::on_exit = + [session = shared_from_this()](int exitCode, const std::error_code &errorCode) { + session->result.exitCode = exitCode; + session->result.errorCode = errorCode; + session->conclude(); + }, + boost::process::extend::on_error = + [session = shared_from_this()](auto &, const std::error_code &errorCode) { + session->result.errorCode = errorCode; + session->conclude(); + }); + } catch (const boost::process::process_error &e) { + result.errorCode = e.code(); + result.error = CppUtilities::argsToString("unable to launch: ", e.what()); + conclude(); + return; + } + readMoreFromPipe(); +} + +/// \brief The BufferSearch struct invokes a callback if an initially given search term occurs in consecutively specified buffers. +struct LIBREPOMGR_EXPORT BufferSearch { + BufferSearch( + std::string_view searchTerm, std::string_view terminationChars, std::string_view giveUpTerm, std::function &&callback); + void operator()(const BuildProcessSession::BufferType &buffer, std::size_t bufferSize); + +private: + const std::string_view m_searchTerm; + const std::string_view m_terminationChars; + const std::string_view m_giveUpTerm; + const std::function m_callback; + std::string_view::const_iterator m_searchTermIterator; + std::string_view::const_iterator m_giveUpTermIterator; + std::string m_result; + bool m_hasResult; +}; + +inline BufferSearch::BufferSearch( + std::string_view searchTerm, std::string_view terminationChars, std::string_view giveUpTerm, std::function &&callback) + : m_searchTerm(searchTerm) + , m_terminationChars(terminationChars) + , m_giveUpTerm(giveUpTerm) + , m_callback(std::move(callback)) + , m_searchTermIterator(m_searchTerm.begin()) + , m_giveUpTermIterator(m_giveUpTerm.begin()) + , m_hasResult(false) +{ +} + +struct ProcessResult; + +/// \brief The InternalBuildAction struct contains internal details (which are not serialized / accessible via the web API) and helpers. +/// \remarks This struct is mean to be inherited from when creating a new type build action like it is down by the classes below. +struct LIBREPOMGR_EXPORT InternalBuildAction { + InternalBuildAction(ServiceSetup &setup, const std::shared_ptr &buildAction); + +protected: + std::string validateParameter(RequiredDatabases requiredDatabases, RequiredParameters requiredParameters); + std::string findDatabases(); + using InitReturnType = std::variant, std::unique_lock>; + InitReturnType init(BuildActionAccess access, RequiredDatabases requiredDatabases, RequiredParameters requiredParameters); + std::string determineWorkingDirectory(std::string_view name); + const std::string &findSetting(const std::string_view &setting) const; + void reportError(std::string &&error); + void reportError(); + void reportSuccess(); + void reportResult(BuildActionResult result); + bool reportAbortedIfAborted(); + + ServiceSetup &m_setup; + std::shared_ptr m_buildAction; + std::set m_sourceDbs; + std::set m_destinationDbs; + bool m_fromAur = false; + bool m_toAur = false; + bool m_hasError = false; + + static constexpr std::string_view buildDataWorkingDirectory = "build-data"; + static constexpr std::string_view buildPreparationFileName = "build-preparation"; + static constexpr std::string_view buildProgressFileName = "build-progress"; + static constexpr std::string_view repoManagementWorkingDirectory = "repo-management"; + static constexpr std::string_view customCommandsWorkingDirectory = "custom-commands"; +}; + +/// \brief The PackageMovementAction struct contains data and helpers for RemovePackages and MovePackages. +struct LIBREPOMGR_EXPORT PackageMovementAction : public InternalBuildAction { + PackageMovementAction(ServiceSetup &setup, const std::shared_ptr &buildAction); + +protected: + bool prepareRepoAction(RequiredDatabases requiredDatabases); + void reportResultWithData(BuildActionResult result); + +private: + void initWorkingDirectory(); + void locatePackages(); + +protected: + std::string m_sourceRepoDirectory; + std::string m_sourceDatabaseFile; + std::string m_destinationRepoDirectory; + std::string m_destinationDatabaseFile; + std::string m_workingDirectory; + std::vector m_fileNames; + PackageMovementResult m_result; + std::vector> m_packageLocations; + boost::filesystem::path m_repoRemovePath; + boost::filesystem::path m_repoAddPath; +}; + +struct LIBREPOMGR_EXPORT RemovePackages : public PackageMovementAction { + RemovePackages(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + void handleRepoRemoveResult(boost::process::child &&child, ProcessResult &&result); + void movePackagesToArchive(); +}; + +struct LIBREPOMGR_EXPORT MovePackages : public PackageMovementAction { + MovePackages(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + void handleRepoRemoveResult(MultiSession::SharedPointerType processSession, boost::process::child &&child, ProcessResult &&result); + void handleRepoAddResult(MultiSession::SharedPointerType processSession, boost::process::child &&child, ProcessResult &&result); + void conclude(); + + std::string m_addErrorMessage; +}; + +struct LIBREPOMGR_EXPORT UpdateCheck : public InternalBuildAction { + UpdateCheck(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + LibPkg::PackageUpdates checkForUpdates(); + + bool m_packageLookupDone = false; +}; + +struct LIBREPOMGR_EXPORT CustomCommand : public InternalBuildAction { + CustomCommand(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + std::string m_workingDirectory; +}; + +struct LIBREPOMGR_EXPORT ReloadDatabase : public InternalBuildAction { + ReloadDatabase(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + std::vector m_preparationFailures; +}; + +struct LIBREPOMGR_EXPORT ReloadLibraryDependencies : public InternalBuildAction { + ReloadLibraryDependencies(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + struct PackageToConsider { + std::string path; + std::string url; + CppUtilities::DateTime lastModified; + LibPkg::Package info; + }; + struct DatabaseToConsider { + std::string name; + std::string arch; + std::vector packages; + }; + + void downloadPackagesFromMirror(); + void loadPackageInfoFromContents(); + void conclude(); + + BuildActionMessages m_messages; + std::stringstream m_skippingNote; + std::vector m_relevantPackagesByDatabase; + std::atomic_size_t m_remainingPackages; + WebClient::PackageCachingDataForSession m_cachingData; +}; + +struct LIBREPOMGR_EXPORT CheckForProblems : public InternalBuildAction { + CheckForProblems(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); +}; + +struct LIBREPOMGR_EXPORT CleanRepository : public InternalBuildAction { + CleanRepository(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + void handleFatalError(InternalBuildAction::InitReturnType &init); + + BuildActionMessages m_messages; + bool m_dryRun = false; +}; + +struct LIBREPOMGR_EXPORT ReloadConfiguration : public InternalBuildAction { + ReloadConfiguration(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); +}; + +struct LIBREPOMGR_EXPORT MakeLicenseInfo : public InternalBuildAction { + MakeLicenseInfo(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); +}; + +struct BatchItem; +using Batch = std::vector; +using BatchList = std::vector; +using BatchMap = std::unordered_map; + +struct LIBREPOMGR_EXPORT PrepareBuild : public InternalBuildAction { + PrepareBuild(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + void populateDbConfig(const std::vector &dbOrder, bool forStaging = false); + bool isExistingPackageRelevant(const std::string &dependencyName, LibPkg::PackageSearchResult &package, PackageBuildData &packageBuildData, + const LibPkg::Database &destinationDb); + void makeSrcInfo( + std::shared_ptr &multiSession, const std::string &sourceDirectory, const std::string &packageName); + static void processSrcInfo(WebClient::AurSnapshotQuerySession &multiSession, const std::string &sourceDirectory, const std::string &packageName, + boost::process::child &&child, ProcessResult &&result); + static void addResultFromSrcInfo(WebClient::AurSnapshotQuerySession &multiSession, const std::string &packageName, const std::string &srcInfo); + static void addPackageToLogLine(std::string &logLine, const std::string &packageName); + void fetchMissingBuildData(); + bool pullFurtherDependencies(const std::vector &dependencies); + void bumpVersions(); + void computeDependencies(WebClient::AurSnapshotQuerySession::ContainerType &&responses); + std::unordered_map prepareBatches(); + void deduceBatchesFromSpecifiedOrder(); + void computeBatches(); + void addBatchesToResult(BatchList &&batches, Batch &&cyclicLeftovers); + BuildPreparation makeResultData(std::string &&error = std::string()); + + std::mutex m_mutex; + std::string m_workingDirectory; + boost::filesystem::path m_makePkgPath; + std::vector m_pkgbuildsDirs; + std::regex m_ignoreLocalPkgbuildsRegex; + std::unordered_map m_buildDataByPackage; + CppUtilities::IniFile::ScopeList m_dbConfig; + CppUtilities::IniFile::ScopeList m_stagingDbConfig; + std::unordered_set m_baseDbs; + std::unordered_set m_requiredDbs; + std::string m_targetDbName, m_targetArch, m_stagingDbName; + std::vector> m_batches; + std::vector m_cyclicLeftovers; + std::vector m_warnings; + bool m_forceBumpPackageVersion = false; + bool m_cleanSourceDirectory = false; + bool m_keepOrder = false; + bool m_keepPkgRelAndEpoch = false; +}; + +struct LIBREPOMGR_EXPORT BatchProcessingSession : public MultiSession { + using SharedPointerType = std::shared_ptr; + + explicit BatchProcessingSession(const std::unordered_set &relevantPackages, std::vector> &batches, + boost::asio::io_context &ioContext, HandlerType &&handler, bool skipBatchesAfterFailure = false); + bool isValid() const; + bool isStagingEnabled() const; + bool hasFailuresInPreviousBatches() const; + const std::string ¤tPackageName() const; + void selectNextPackage(); + const std::string *getCurrentPackageNameIfValidAndRelevantAndSelectNext(); + void enableStagingInNextBatch(); + +private: + const std::unordered_set &m_relevantPackages; + std::mutex m_mutex; + std::vector>::iterator m_batchIterator, m_batchEnd; + std::vector::iterator m_packageIterator, m_packageEnd; + bool m_skipBatchesAfterFailure; + bool m_hasFailuresInPreviousBatches; + std::atomic_bool m_enableStagingInNextBatch; + std::atomic_bool m_stagingEnabled; +}; + +struct BinaryPackageInfo { + const std::string *const name; + const std::string *const fileName; + std::filesystem::path path; + const bool isAny = false; + bool artefactAlreadyPresent = false; +}; + +enum class InvocationResult { + Ok, + Skipped, + Error, +}; + +struct LIBREPOMGR_EXPORT ConductBuild + : public InternalBuildAction, + private LibPkg::Lockable { // lockable is used for values of m_buildProgress.buildData in code paths where concurrency is allowed + friend BuildActionsTests; + + ConductBuild(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + +private: + void makeMakepkgConfigFile(const std::filesystem::path &makepkgConfigPath); + void makePacmanConfigFile( + const std::filesystem::path &pacmanConfigPath, const std::vector>> &dbConfig); + void makeConfigFiles(); + void downloadSourcesAndContinueBuilding(); + void enqueueDownloads(const BatchProcessingSession::SharedPointerType &downloadsSession, std::size_t maxParallelDownloads); + void enqueueMakechrootpkg(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, std::size_t maxParallelInvocations); + bool checkForFailedDependency(const std::vector *> &dependencies) const; + InvocationResult invokeUpdatePkgSums(const BatchProcessingSession::SharedPointerType &downloadsSession, const std::string &packageName, + PackageBuildProgress &packageProgress, const std::string &buildDirectory); + InvocationResult invokeMakepkgToMakeSourcePackage(const BatchProcessingSession::SharedPointerType &downloadsSession, + const std::string &packageName, PackageBuildProgress &packageProgress, const std::string &buildDirectory); + InvocationResult invokeMakechrootpkg(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName); + void addPackageToRepo( + const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName, PackageBuildProgress &packageProgress); + void checkDownloadErrorsAndMakePackages(BatchProcessingSession::ContainerType &&failedPackages); + void handleMakechrootpkgErrorsAndAddPackageToRepo(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, + const std::string &packageName, PackageBuildProgress &packageProgress, boost::process::child &&child, ProcessResult &&result); + void handleRepoAddErrorsAndMakeNextPackage(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName, + PackageBuildProgress &packageProgress, boost::process::child &&child, ProcessResult &&result); + void checkBuildErrors(BatchProcessingSession::ContainerType &&failedPackages); + void dumpBuildProgress(); + void addLogFile(std::string &&logFilePath); + void assignNewVersion(const std::string &packageName, PackageBuildProgress &packageProgress, std::string &&updatedVersionInfo); + void copyPkgbuildToOriginalSourceDirectory( + const std::string &packageName, PackageBuildProgress &packageProgress, const std::string &buildDirectory); + PackageStagingNeeded checkWhetherStagingIsNeededAndPopulateRebuildList( + const std::string &packageName, const PackageBuildData &buildData, const std::vector &builtPackages); + + std::string m_workingDirectory; + BuildPreparation m_buildPreparation; + BuildProgress m_buildProgress; + std::string m_globalPacmanConfigPath; + std::string m_globalMakepkgConfigFilePath; + std::string m_globalCcacheDir; + std::string m_globalPackageCacheDir; + std::string m_globalTestFilesDir; + std::string m_chrootRootUser; + boost::filesystem::path m_makePkgPath; + boost::filesystem::path m_makeChrootPkgPath; + boost::filesystem::path m_updatePkgSumsPath; + boost::filesystem::path m_repoAddPath; + std::filesystem::path m_makepkgConfigPath; + std::filesystem::path m_pacmanConfigPath; + std::filesystem::path m_pacmanStagingConfigPath; + std::filesystem::path m_buildPreparationFilePath; + std::string m_binaryPackageExtension; + std::string m_sourcePackageExtension; + std::unordered_set m_relevantPackages; + std::mutex m_rebuildListMutex; + bool m_buildAsFarAsPossible; + bool m_saveChrootDirsOfFailures; + bool m_updateChecksums; + bool m_autoStaging; +}; + +#ifdef LIBREPOMGR_DUMMY_BUILD_ACTION_ENABLED +struct LIBREPOMGR_EXPORT DummyBuildAction : public InternalBuildAction { + DummyBuildAction(ServiceSetup &setup, const std::shared_ptr &buildAction); + void run(); + void continuePrinting(); + +private: + void printLine(); + void stop(); + + std::uint64_t m_counter; + std::string m_workingDirectory; + boost::asio::deadline_timer m_timer; + std::shared_ptr m_logProcess; +}; +#endif // LIBREPOMGR_DUMMY_BUILD_ACTION_ENABLED + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_BUILD_ACTION_PRIVATE_H diff --git a/librepomgr/buildactions/buildactiontemplate.cpp b/librepomgr/buildactions/buildactiontemplate.cpp new file mode 100644 index 0000000..fe48c8e --- /dev/null +++ b/librepomgr/buildactions/buildactiontemplate.cpp @@ -0,0 +1,12 @@ +#include "./buildactiontemplate.h" + +#include + +#include "reflection/buildactiontemplate.h" + +using namespace std; +using namespace CppUtilities; + +namespace LibRepoMgr { + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/buildactiontemplate.h b/librepomgr/buildactions/buildactiontemplate.h new file mode 100644 index 0000000..63c008f --- /dev/null +++ b/librepomgr/buildactions/buildactiontemplate.h @@ -0,0 +1,39 @@ +#ifndef LIBREPOMGR_BUILD_ACTION_TEMPLATE_H +#define LIBREPOMGR_BUILD_ACTION_TEMPLATE_H + +#include "./buildactionmeta.h" + +#include + +#include + +#include + +namespace LibRepoMgr { + +struct LIBREPOMGR_EXPORT BuildActionTemplate : public ReflectiveRapidJSON::JsonSerializable { + std::string directory; + std::vector packageNames; + std::vector sourceDbs, destinationDbs; + std::unordered_map settings; + BuildActionFlagType flags = noBuildActionFlags; + BuildActionType type = BuildActionType::Invalid; + CppUtilities::TimeSpan maxFrequency = CppUtilities::TimeSpan::infinity(); +}; + +struct LIBREPOMGR_EXPORT BuildTask : public ReflectiveRapidJSON::JsonSerializable { + std::string name; + std::string desc; + std::string category; + std::vector actions; + CppUtilities::TimeSpan frequency = CppUtilities::TimeSpan::infinity(); +}; + +struct LIBREPOMGR_EXPORT BuildPresets : public ReflectiveRapidJSON::JsonSerializable { + std::unordered_map templates; + std::unordered_map tasks; +}; + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_BUILD_ACTION_TEMPLATE_H diff --git a/librepomgr/buildactions/conductbuild.cpp b/librepomgr/buildactions/conductbuild.cpp new file mode 100644 index 0000000..5e8cedb --- /dev/null +++ b/librepomgr/buildactions/conductbuild.cpp @@ -0,0 +1,1435 @@ +#include "./buildactionprivate.h" +#include "./subprocess.h" + +#include "../helper.h" +#include "../json.h" +#include "../logging.h" +#include "../serversetup.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +bool PackageBuildProgress::hasBeenAnyProgressMade() const +{ + return checksumsUpdated || hasSources || !finished.isNull() || addedToRepo || stagingNeeded != PackageStagingNeeded::Undetermined; +} + +void PackageBuildProgress::reset() +{ + checksumsUpdated = false; + hasSources = false; + finished = DateTime(); + addedToRepo = false; + stagingNeeded = PackageStagingNeeded::Undetermined; +} + +void RebuildInfo::add(const LibPkg::DependencySet &deps, const std::unordered_set &libs) +{ + for (const auto &dep : deps) { + const auto &[depName, depDetails] = dep; + provides.emplace_back(depName, depDetails.version, depDetails.mode); + } + libprovides.insert(libprovides.end(), libs.cbegin(), libs.cend()); +} + +/*! + * \brief Constructs a new BatchProcessingSession to start asynchronous tasks in batches. + * \param relevantPackages If non-empty, getCurrentPackageNameIfValidAndRelevantAndSelectNext() will skip packages not contained. + * \param batches The batches to build. This is a list of a list of packages. + * \param ioContext The IO context to run the specified handler. + * \param handler The handler to execute after processing all batches has finished (or is otherwise aborted). + * \param skipBatchesAfterFailure Whether processing subsequent should be aborted after an error. + */ +BatchProcessingSession::BatchProcessingSession(const std::unordered_set &relevantPackages, + std::vector> &batches, boost::asio::io_context &ioContext, BatchProcessingSession::HandlerType &&handler, + bool skipBatchesAfterFailure) + : MultiSession(ioContext, std::move(handler)) + , m_relevantPackages(relevantPackages) + , m_batchIterator(batches.begin()) + , m_batchEnd(batches.end()) + , m_packageIterator(m_batchIterator != m_batchEnd ? m_batchIterator->begin() : decltype(m_packageIterator)()) + , m_packageEnd(m_batchIterator != m_batchEnd ? m_batchIterator->end() : decltype(m_packageEnd)()) + , m_skipBatchesAfterFailure(skipBatchesAfterFailure) + , m_hasFailuresInPreviousBatches(false) + , m_enableStagingInNextBatch(false) + , m_stagingEnabled(false) +{ +} + +/*! +// * \brief Returns whether currentPackageName() and selectNextPackage() can be called. + * \remarks If not, that means there are no more packages to process or a failure within the previous batch prevents + * the session to proceed. + */ +bool BatchProcessingSession::isValid() const +{ + return m_packageIterator != m_packageEnd + && (allResponses().empty() || !m_skipBatchesAfterFailure || m_packageIterator != m_batchIterator->begin()); +} + +/*! + * \brief Returns whether staging is now active after a previous call to enableStagingInNextBatch(). + */ +bool BatchProcessingSession::isStagingEnabled() const +{ + return m_stagingEnabled; +} + +/*! + * \brief Returns whether there are failures within previously processed batches. + */ +bool BatchProcessingSession::hasFailuresInPreviousBatches() const +{ + return m_hasFailuresInPreviousBatches; +} + +/*! + * \brief Returns the current package name. Must not be called unless isValid() returns true. + */ +const std::string &BatchProcessingSession::currentPackageName() const +{ + return *m_packageIterator; +} + +/*! + * \brief Selects the next package, possibly from the next batch. Must not be called unless isValid() returns true. + * \remarks If there's no next package isValid() will return false after the call. + */ +void BatchProcessingSession::selectNextPackage() +{ + if (++m_packageIterator != m_packageEnd) { + return; // select the next package within the current batch + } + if ((m_hasFailuresInPreviousBatches = !allResponses().empty()) && m_skipBatchesAfterFailure) { + return; // invalidate the session if there were failures within the previous batch + } + if (++m_batchIterator == m_batchEnd) { + return; // invalidate the session; the current package was the last one within the last batch + } + // select the first package within the next batch + m_packageIterator = m_batchIterator->begin(); + m_packageEnd = m_batchIterator->end(); + m_stagingEnabled = m_stagingEnabled || m_enableStagingInNextBatch; +} + +/*! + * \brief Grabs the next valid package, possibly from the next batch. + * \returns Returns the package name or nullptr if there are no more packages to process. + * \remarks + * - To parallelize builds we needed an option to avoid the "possibly from the next batch" part until all packages + * from the current batch have been processed. (It needed to be an option because when downloading we can already + * grab packages from the next batch.) + * - This function might be called from multiple threads at the same time. + */ +const std::string *BatchProcessingSession::getCurrentPackageNameIfValidAndRelevantAndSelectNext() +{ + std::lock_guard lock(m_mutex); + const std::string *packageName = nullptr; + do { + if (!isValid()) { + return nullptr; + } + packageName = ¤tPackageName(); + selectNextPackage(); + } while (!m_relevantPackages.empty() && m_relevantPackages.find(*packageName) == m_relevantPackages.end()); + return packageName; +} + +/*! + * \brief + * Enables staging for the next batch. Enables staging for the current batch if the current package is the first package of + * current batch. + * \remarks + * - The behavior regarding the current batch was chosen because we call selectNextPackage() *before* enableStagingInNextBatch(). + * - This function might be called from multiple threads at the same time. + */ +void BatchProcessingSession::enableStagingInNextBatch() +{ + m_enableStagingInNextBatch = true; + if (!m_stagingEnabled) { + std::lock_guard lock(m_mutex); + m_stagingEnabled = m_batchIterator != m_batchEnd && m_packageIterator == m_batchIterator->begin(); + } +} + +ConductBuild::ConductBuild(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) + , m_buildAsFarAsPossible(false) + , m_saveChrootDirsOfFailures(false) + , m_updateChecksums(false) + , m_autoStaging(false) +{ +} + +void ConductBuild::run() +{ + // validate and read parameter/settings + if (auto error = validateParameter(RequiredDatabases::None, RequiredParameters::MaybePackages); !error.empty()) { + reportError(move(error)); + return; + } + if (m_buildAction->directory.empty()) { + reportError("Unable to find working directory: no directory name specified"); + return; + } + for (const auto &packageName : m_buildAction->packageNames) { + const auto firstSlash = packageName.find('/'); + if (firstSlash == std::string::npos) { + m_relevantPackages.emplace(packageName); + } else { + m_relevantPackages.emplace(packageName.data() + firstSlash + 1, packageName.size() - firstSlash - 1); + } + } + const auto flags = static_cast(m_buildAction->flags); + m_buildAsFarAsPossible = flags & ConductBuildFlags::BuildAsFarAsPossible; + m_saveChrootDirsOfFailures = flags & ConductBuildFlags::SaveChrootOfFailures; + m_updateChecksums = flags & ConductBuildFlags::UpdateChecksums; + m_autoStaging = flags & ConductBuildFlags::AutoStaging; + auto &metaInfo = m_setup.building.metaInfo; + auto metaInfoLock = metaInfo.lockToRead(); + const auto &typeInfo = metaInfo.typeInfoForId(BuildActionType::ConductBuild); + const auto chrootDirSetting = typeInfo.settings[static_cast(ConductBuildSettings::ChrootDir)].param; + const auto chrootDefaultUserSetting = typeInfo.settings[static_cast(ConductBuildSettings::ChrootDefaultUser)].param; + const auto ccacheDirSetting = typeInfo.settings[static_cast(ConductBuildSettings::CCacheDir)].param; + const auto pkgCacheDirSetting = typeInfo.settings[static_cast(ConductBuildSettings::PackageCacheDir)].param; + const auto testFilesDirSetting = typeInfo.settings[static_cast(ConductBuildSettings::TestFilesDir)].param; + metaInfoLock.unlock(); + const auto &chrootDir = findSetting(chrootDirSetting); + const auto &chrootDefaultUser = findSetting(chrootDefaultUserSetting); + m_globalCcacheDir = findSetting(ccacheDirSetting); + m_globalPackageCacheDir = findSetting(pkgCacheDirSetting); + m_globalTestFilesDir = findSetting(testFilesDirSetting); + auto setupReadLock = m_setup.lockToRead(); + m_workingDirectory = determineWorkingDirectory(buildDataWorkingDirectory); + m_makePkgPath = findExecutable(m_setup.building.makePkgPath); + m_makeChrootPkgPath = findExecutable(m_setup.building.makeChrootPkgPath); + m_updatePkgSumsPath = findExecutable(m_setup.building.updatePkgSumsPath); + m_repoAddPath = findExecutable(m_setup.building.repoAddPath); + setupReadLock.unlock(); + + // check executables + if (!checkExecutable(m_makePkgPath)) { + reportError("Unable to find makepkg executable \"" % m_setup.building.makePkgPath + "\" in PATH."); + return; + } + if (!checkExecutable(m_makeChrootPkgPath)) { + reportError("Unable to find makechrootpkg executable \"" % m_setup.building.makeChrootPkgPath + "\" in PATH."); + return; + } + if (!checkExecutable(m_updatePkgSumsPath)) { + reportError("Unable to find updpkgsums executable \"" % m_setup.building.updatePkgSumsPath + "\" in PATH."); + return; + } + if (!checkExecutable(m_repoAddPath)) { + reportError("Unable to find repo-add executable \"" % m_setup.building.repoAddPath + "\" in PATH."); + return; + } + + // assign paths + m_makepkgConfigPath = m_workingDirectory + "/makepkg.conf"; + m_pacmanConfigPath = m_workingDirectory + "/pacman.conf"; + m_pacmanStagingConfigPath = m_workingDirectory + "/pacman-staging.conf"; + + // parse build preparation + ReflectiveRapidJSON::JsonDeserializationErrors errors; + try { + m_buildPreparationFilePath = restoreJsonObject( + m_buildPreparation, m_workingDirectory, buildPreparationFileName, RestoreJsonExistingFileHandling::RequireExistingFile); + } catch (const std::runtime_error &e) { + reportError(e.what()); + return; + } + + // parse build progress + try { + restoreJsonObject(m_buildProgress, m_workingDirectory, buildProgressFileName, RestoreJsonExistingFileHandling::Skip); + } catch (const std::runtime_error &e) { + reportError(e.what()); + return; + } + + // check target db, arch and whether packages and source info are present + if (m_buildPreparation.targetDb.empty() || m_buildPreparation.targetArch.empty()) { + reportError("The destination database and target architecture specified in build-preparation.json must not be empty."); + return; + } + for (const auto &buildDataForPackage : m_buildPreparation.buildData) { + const auto &packageName = buildDataForPackage.first; + const auto &buildData = buildDataForPackage.second; + if (packageName.empty()) { + reportError("The build data contains an empty package name."); + return; + } + if (!buildData.sourceInfo) { + reportError(argsToString("The build data for \"" % packageName % "\" has no source info.")); + return; + } + if (buildData.packages.empty()) { + reportError(argsToString("The build data for \"" % packageName % "\" has no packages.")); + return; + } + for (const auto &package : buildData.packages) { + if (!package) { + reportError(argsToString("The package of build data for \"" % packageName % "\" is null.")); + return; + } + } + } + m_buildAction->appendOutput( + Phrases::InfoMessage, "Destination database: " % m_buildPreparation.targetDb % ", architecture: " % m_buildPreparation.targetArch + '\n'); + + // read values from global config + setupReadLock = m_setup.lockToRead(); + if (chrootDir.empty() && m_setup.building.chrootDir.empty()) { + setupReadLock.unlock(); + reportError("The chroot directory is not configured."); + return; + } + m_globalPacmanConfigPath + = (chrootDir.empty() ? m_setup.building.chrootDir : chrootDir) % "/config-" % m_buildPreparation.targetArch + "/pacman.conf"; + m_globalMakepkgConfigFilePath + = (chrootDir.empty() ? m_setup.building.chrootDir : chrootDir) % "/config-" % m_buildPreparation.targetArch + "/makepkg.conf"; + if (m_globalCcacheDir.empty()) { + m_globalCcacheDir = m_setup.building.ccacheDir; + } + if (m_globalPackageCacheDir.empty()) { + m_globalPackageCacheDir = m_setup.building.packageCacheDir; + } + if (m_globalTestFilesDir.empty()) { + m_globalTestFilesDir = m_setup.building.testFilesDir; + } + m_chrootRootUser = m_setup.building.chrootRootUser; + setupReadLock.unlock(); + + // fill ommitted build progress configuration with defaults from global config + if (m_autoStaging && m_buildPreparation.stagingDb.empty()) { + reportError("Auto-staging is enabled but no staging database has been specified in build-preparation.json."); + return; + } + if (m_buildProgress.targetDbFilePath.empty() || m_buildProgress.targetRepoPath.empty()) { + const auto configLock = m_setup.config.lockToRead(); + if (const auto *const targetDb = m_setup.config.findDatabase(m_buildPreparation.targetDb, m_buildPreparation.targetArch)) { + if (m_buildProgress.targetDbFilePath.empty()) { + m_buildProgress.targetDbFilePath = fileName(targetDb->path); + std::error_code ec; + auto symlinkTarget = std::filesystem::read_symlink(m_buildProgress.targetDbFilePath, ec); + if (!ec) { + m_buildProgress.targetDbFilePath = symlinkTarget; + } + } + if (m_buildProgress.targetRepoPath.empty()) { + m_buildProgress.targetRepoPath = targetDb->localPkgDir; + } + } + if (const auto *const stagingDb + = m_autoStaging ? m_setup.config.findDatabase(m_buildPreparation.stagingDb, m_buildPreparation.targetArch) : nullptr) { + if (m_buildProgress.stagingDbFilePath.empty()) { + m_buildProgress.stagingDbFilePath = fileName(stagingDb->path); + std::error_code ec; + auto symlinkTarget = std::filesystem::read_symlink(m_buildProgress.stagingDbFilePath, ec); + if (!ec) { + m_buildProgress.stagingDbFilePath = symlinkTarget; + } + } + if (m_buildProgress.stagingRepoPath.empty()) { + m_buildProgress.stagingRepoPath = stagingDb->localPkgDir; + } + } else if (m_autoStaging) { + reportError("Auto-staging is enabled but the staging database \"" % m_buildPreparation.stagingDb % '@' % m_buildPreparation.targetArch + + "\" specified in build-preparation.json can not be found."); + return; + } + } + if (m_buildProgress.targetDbFilePath.empty()) { + reportError("Unable to determine path for database \"" % m_buildPreparation.targetDb % '@' % m_buildPreparation.targetArch + + "\"; set \"dbdir\" for that database in the server config or amend build-progress.json manually and retry."); + return; + } + if (m_buildProgress.targetRepoPath.empty()) { + m_buildProgress.targetRepoPath = directory(m_buildProgress.targetDbFilePath); + } + if (m_autoStaging) { + if (m_buildProgress.stagingDbFilePath.empty()) { + reportError( + "Unable to determine path for staging database \"" % m_buildPreparation.stagingDb % '@' % m_buildPreparation.targetArch + "\"."); + return; + } + if (m_buildProgress.stagingRepoPath.empty()) { + m_buildProgress.stagingRepoPath = directory(m_buildProgress.stagingDbFilePath); + } + } + try { + if (!std::filesystem::exists(m_buildProgress.targetRepoPath)) { + reportError("Destination repository \"" % m_buildProgress.targetRepoPath % "\" does not exist; set \"pkgdir\" for database \"" + % m_buildPreparation.targetDb % '@' % m_buildPreparation.targetArch + + "\" to an existing directory or amend build-pgrogress.json manually and retry."); + return; + } + if (!std::filesystem::exists(m_buildProgress.targetRepoPath % '/' + m_buildProgress.targetDbFilePath)) { + reportError("Destination database file \"" % m_buildProgress.targetDbFilePath % "\" does not exist in \"" % m_buildProgress.targetRepoPath + + "\"."); + return; + } + if (m_autoStaging) { + if (!std::filesystem::exists(m_buildProgress.stagingRepoPath)) { + reportError("Staging repository \"" % m_buildProgress.stagingRepoPath % "\" does not exist; set \"pkgdir\" for database \"" + % m_buildPreparation.targetDb % '@' % m_buildPreparation.targetArch + + "-staging\" to an existing directory or amend build-pgrogress.json manually and retry."); + return; + } + if (!std::filesystem::exists(m_buildProgress.stagingRepoPath % '/' + m_buildProgress.stagingDbFilePath)) { + reportError( + "Staging database file \"" % m_buildProgress.stagingDbFilePath % "\" does not exist in \"" % m_buildProgress.stagingRepoPath + + "\"."); + return; + } + } + } catch (const std::filesystem::filesystem_error &e) { + reportError( + "Unable to check whether database/repository \"" % m_buildPreparation.targetDb % '@' % m_buildPreparation.targetArch % "\" exists: " + + e.what()); + return; + } + auto setupReadLock2 = m_setup.lockToRead(); + for (auto &progressByPackage : m_buildProgress.progressByPackage) { + const auto &packageName = progressByPackage.first; + auto &progress = progressByPackage.second; + if (progress.buildDirectory.empty()) { + progress.buildDirectory = m_workingDirectory % '/' % packageName + "/pkg"; + } + if (progress.chrootDirectory.empty()) { + progress.chrootDirectory = chrootDir.empty() ? m_setup.building.chrootDir : chrootDir; + } + if (progress.chrootUser.empty()) { + progress.chrootUser = chrootDefaultUser.empty() ? m_setup.building.chrootDefaultUser : chrootDefaultUser; + } + if (progress.makechrootpkgFlags.empty()) { + progress.makechrootpkgFlags = m_setup.building.makechrootpkgFlags; + } + if (progress.makepkgFlags.empty()) { + progress.makepkgFlags = m_setup.building.makepkgFlags; + } + } + setupReadLock2.unlock(); + dumpBuildProgress(); + + try { + makeConfigFiles(); + } catch (const std::runtime_error &e) { + reportError(argsToString("Unable to prepare pacman.conf and makepkg.conf: ", e.what())); + return; + } + + downloadSourcesAndContinueBuilding(); +} + +/// \cond +static void findPackageExtension(const IniFile::ScopeData &iniScope, const std::string &key, std::string &result) +{ + const auto *const value = getLastValue(iniScope, key); + if (!value) { + return; + } + result = value; + // trim leading and trailing quotes and white-spaces + string::size_type start = 0, end = result.size(); + const auto isQuoteOrWhitespace + = [&result](string::size_type index) { return result[index] == '\'' || result[index] == '\"' || result[index] == ' '; }; + while (start < end && isQuoteOrWhitespace(start)) { + ++start; + } + while (end > start && isQuoteOrWhitespace(end - 1)) { + --end; + } + if (start || end != result.size()) { + result = result.substr(start, end - start); + } +} +/// \endcond + +/*! + * \brief Makes the makepkg config file for the build. + * \remarks Not overriding existing files to allow manual tweaks. + * \todo Catch std::ios_base::failure to provide better context. + */ +void ConductBuild::makeMakepkgConfigFile(const std::filesystem::path &makepkgConfigPath) +{ + if (!filesystem::exists(makepkgConfigPath)) { + filesystem::copy(m_globalMakepkgConfigFilePath, makepkgConfigPath); + } + + // read PKGEXT from makepkg.conf + ifstream makepkgConfInputFile; + makepkgConfInputFile.exceptions(ios_base::badbit | ios_base::failbit); + makepkgConfInputFile.open(makepkgConfigPath, ios_base::in); + IniFile makepkgConfIni; + IniFile::ScopeList &iniData = makepkgConfIni.data(); + makepkgConfIni.parse(makepkgConfInputFile); + static const struct { + std::string pkg = "PKGEXT"; + std::string src = "SRCEXT"; + } extensionKeys; + for (auto i = iniData.rbegin(), end = iniData.rend(); i != end; ++i) { + findPackageExtension(i->second, extensionKeys.pkg, m_binaryPackageExtension); + findPackageExtension(i->second, extensionKeys.src, m_sourcePackageExtension); + if (!m_binaryPackageExtension.empty() && !m_sourcePackageExtension.empty()) { + break; + } + } + if (m_binaryPackageExtension.empty()) { + throw std::runtime_error("unable to read PKGEXT from makepkg.conf"); + } + if (m_sourcePackageExtension.empty()) { + throw std::runtime_error("unable to read SRCEXT from makepkg.conf"); + } +} + +/*! + * \brief Makes the pacman configuration for the build and the specified \a dbConfig. + * \remarks Not overriding existing files to allow manual tweaks. + * \todo Catch std::ios_base::failure to provide better context. + */ +void ConductBuild::makePacmanConfigFile( + const std::filesystem::path &pacmanConfigPath, const std::vector>> &dbConfig) +{ + // configure databases and cache directory; validate architecture + const auto pacmanConfigAlreadyExists = filesystem::exists(pacmanConfigPath); + if (pacmanConfigAlreadyExists && filesystem::last_write_time(pacmanConfigPath) > filesystem::last_write_time(m_buildPreparationFilePath)) { + // skip if already exists unless the build preparation is newer + // note: If the build preparation is newer but the pacman config already exist the *existing* config is updated. + // This allows keeping a custom config but still updating it on further build preparation runs. + return; + } + ifstream pacmanConfInputFile; + pacmanConfInputFile.exceptions(ios_base::badbit | ios_base::failbit); + pacmanConfInputFile.open(pacmanConfigAlreadyExists ? pacmanConfigPath.string() : m_globalPacmanConfigPath, ios_base::in); + AdvancedIniFile pacmanConfIni; + pacmanConfIni.parse(pacmanConfInputFile); + pacmanConfInputFile.close(); + auto §ions = pacmanConfIni.sections; + // -> remove all existing database sections + sections.erase(remove_if(sections.begin(), sections.end(), [](const AdvancedIniFile::Section §ion) { return section.name != "options"; }), + sections.end()); + // -> update options + for (auto §ion : sections) { + if (section.name != "options") { + continue; + } + section.fields.erase( + std::remove_if(section.fields.begin(), section.fields.end(), [](const AdvancedIniFile::Field &field) { return field.key == "CacheDir"; }), + section.fields.end()); + section.fields.emplace_back( + AdvancedIniFile::Field{ .key = "CacheDir", .value = m_globalPackageCacheDir % '/' + m_buildPreparation.targetArch }); + auto archField = section.findField("Architecture"); + if (archField == section.fieldEnd()) { + throw std::runtime_error("pacman.conf lacks Architecture option"); + } + for (; archField != section.fieldEnd(); archField = section.findField(archField, "Architecture")) { + if (archField->value != m_buildPreparation.targetArch) { + throw std::runtime_error( + "pacman.conf has wrong Architecture option \"" % archField->value % "\" (should have \"" % m_buildPreparation.targetArch % '\"' + + ')'); + } + } + } + // -> add database configuration + auto firstSection = true; + for (const auto &dbSection : dbConfig) { + auto §ion = sections.emplace_back(AdvancedIniFile::Section{ .name = dbSection.first }); + if (firstSection) { + section.precedingCommentBlock = argsToString( + "\n# Database configuration for build action \"", m_buildAction->id, "\" (directory \"", m_workingDirectory, "\")\n\n"); + firstSection = false; + } else { + section.precedingCommentBlock = "\n"; + } + section.fields.reserve(dbSection.second.size()); + for (const auto &dbField : dbSection.second) { + section.fields.emplace_back(AdvancedIniFile::Field{ .key = dbField.first, .value = dbField.second }); + } + } + // -> write changes to disk + ofstream pacmanConfOutputFile; + pacmanConfOutputFile.exceptions(ios_base::badbit | ios_base::failbit); + pacmanConfOutputFile.open(pacmanConfigPath, ios_base::out); + pacmanConfIni.make(pacmanConfOutputFile); + pacmanConfOutputFile.close(); +} + +/*! + * \brief Make pacman.conf, pacman-staging.conf and makepkg.conf for the build. + * \remarks Not overriding existing files to allow manual tweaks. + * \todo Catch std::ios_base::failure to provide better context. + */ +void ConductBuild::makeConfigFiles() +{ + makeMakepkgConfigFile(m_makepkgConfigPath); + makePacmanConfigFile(m_pacmanConfigPath, m_buildPreparation.dbConfig); + makePacmanConfigFile(m_pacmanStagingConfigPath, m_buildPreparation.stagingDbConfig); +} + +/*! + * \brief Enqueue the first N downloads to run in parallel; when one download has concluded it will enqueue the next download to + * have always running 4 in parallel. Invokes makePackages() when all downloads have been concluded. + */ +void ConductBuild::downloadSourcesAndContinueBuilding() +{ + if (reportAbortedIfAborted()) { + return; + } + constexpr std::size_t maxParallelDownloads = 4; + enqueueDownloads(make_shared(m_relevantPackages, m_buildPreparation.batches, m_setup.building.ioContext, + std::bind(&ConductBuild::checkDownloadErrorsAndMakePackages, this, std::placeholders::_1)), + maxParallelDownloads); +} + +/*! + * \brief Enqueues at most \a maxParallelDownloads downloads. Does nothing if there are no more outstanding downloads. + */ +void ConductBuild::enqueueDownloads(const BatchProcessingSession::SharedPointerType &downloadsSession, std::size_t maxParallelDownloads) +{ + if (reportAbortedIfAborted()) { + return; + } + decltype(lockToRead()) lock; + for (std::size_t startedDownloads = 0; startedDownloads < maxParallelDownloads;) { + if (!lock.owns_lock()) { + lock = lockToRead(); + } + const auto *const packageName = downloadsSession->getCurrentPackageNameIfValidAndRelevantAndSelectNext(); + if (!packageName) { + return; + } + auto &packageProgress = m_buildProgress.progressByPackage[*packageName]; + if (!packageProgress.finished.isNull() || packageProgress.hasSources || packageProgress.addedToRepo) { + continue; + } + + // prepare the build directory + const auto &buildData = m_buildPreparation.buildData[*packageName]; + std::filesystem::path buildDirectory; + try { + buildDirectory = std::filesystem::absolute(packageProgress.buildDirectory); + std::filesystem::create_directory(buildDirectory); + std::filesystem::copy(buildData.sourceDirectory, packageProgress.buildDirectory, + std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive); + } catch (const std::filesystem::filesystem_error &e) { + auto writeLock = lockToWrite(lock); + packageProgress.error = argsToString("unable to prepare build directory: ", e.what()); + writeLock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to prepare build directory for ", *packageName, ": ", e.what(), '\n'); + downloadsSession->addResponse(string(*packageName)); + continue; + } + + // update checksums if configured + switch (invokeUpdatePkgSums(downloadsSession, *packageName, packageProgress, buildDirectory.native())) { + case InvocationResult::Ok: + // consider the download being started; invokeMakepkgToMakeSourcePackage() will be invoked later by the handler + startedDownloads += 1; + continue; + case InvocationResult::Error: + // move on to the next package; invokeUpdatePkgSums() has already "recorded" the error + continue; + case InvocationResult::Skipped: + // updating checksums not needed; continue with invokeMakepkgToMakeSourcePackage() directly + break; + } + + // launch makepkg to make source package + switch (invokeMakepkgToMakeSourcePackage(downloadsSession, *packageName, packageProgress, buildDirectory.native())) { + case InvocationResult::Ok: + startedDownloads += 1; + continue; + case InvocationResult::Error: + // move on to the next package; invokeMakepkgToMakeSourcePackage() has already "recorded" the error + continue; + case InvocationResult::Skipped: + // the download is not needed after all; should not happen at this point + break; + } + } +} + +void ConductBuild::enqueueMakechrootpkg(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, size_t maxParallelInvocations) +{ + if (reportAbortedIfAborted()) { + return; + } + assert(maxParallelInvocations == 1); // FIXME: parallel builds not implemented yet (required unique working copies and locking repo-add) + for (std::size_t invocations = 0; invocations < maxParallelInvocations;) { + const auto *const packageName = makepkgchrootSession->getCurrentPackageNameIfValidAndRelevantAndSelectNext(); + if (!packageName) { + break; + } + switch (invokeMakechrootpkg(makepkgchrootSession, *packageName)) { + case InvocationResult::Ok: + invocations += 1; + break; + case InvocationResult::Error: + makepkgchrootSession->addResponse(string(*packageName)); + { + auto lock = lockToRead(); + auto errorMessage = formattedPhraseString(Phrases::ErrorMessage) % "Unable to start build of " % *packageName + % formattedPhraseString(Phrases::End) % "-> reason: " + + m_buildProgress.progressByPackage[*packageName].error; + lock.unlock(); + m_buildAction->log()(std::move(errorMessage)); + } + break; + default:; + } + } + const auto lock = lockToRead(); + dumpBuildProgress(); +} + +bool ConductBuild::checkForFailedDependency(const std::vector *> &dependencies) const +{ + // check whether any of the specified dependencies are provided by packages which are supposed to be built but failed + for (const auto &[packageName, buildData] : m_buildPreparation.buildData) { + // ignore packages which have been added to the repository successfully and treat any other packages as failed + const auto buildProgress = m_buildProgress.progressByPackage.find(packageName); + if (buildProgress != m_buildProgress.progressByPackage.end() && buildProgress->second.addedToRepo) { + continue; + } + for (const auto &package : buildData.packages) { + for (const auto &deps : dependencies) { + for (const auto &dependency : *deps) { + if (package->providesDependency(dependency)) { + return true; + } + } + } + } + } + return false; +} + +InvocationResult ConductBuild::invokeUpdatePkgSums(const BatchProcessingSession::SharedPointerType &downloadsSession, const std::string &packageName, + PackageBuildProgress &packageProgress, const std::string &buildDirectory) +{ + if (!m_updateChecksums || packageProgress.checksumsUpdated) { + return InvocationResult::Skipped; + } + auto processSession = m_buildAction->makeBuildProcess(packageProgress.buildDirectory + "/updpkgsums.log", + [this, downloadsSession, &packageProgress, &packageName, buildDirectory](boost::process::child &&child, ProcessResult &&result) { + const auto hasError = result.errorCode || result.exitCode != 0; + auto lock = lockToWrite(); + if (result.errorCode) { + const auto errorMessage = result.errorCode.message(); + packageProgress.error = "unable to update checksums: " + errorMessage; + lock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke updpkgsums for ", packageName, ": ", errorMessage, '\n'); + downloadsSession->addResponse(string(packageName)); + } else if (result.exitCode != 0) { + packageProgress.error = argsToString("unable to update checksums: updpkgsums returned with exit code ", result.exitCode); + lock.unlock(); + m_buildAction->log()( + Phrases::ErrorMessage, "updpkgsums invocation for ", packageName, " exited with non-zero exit code: ", child.exit_code(), '\n'); + downloadsSession->addResponse(string(packageName)); + } else { + packageProgress.checksumsUpdated = true; + lock.unlock(); + } + // move on to the next download if an error occurred; otherwise continue making the source package + if (hasError) { + enqueueDownloads(downloadsSession, 1); + return; + } + // copy the updated PKGBUILD back to original source directory + copyPkgbuildToOriginalSourceDirectory(packageName, packageProgress, buildDirectory); + // continue with the actual build + switch (invokeMakepkgToMakeSourcePackage(downloadsSession, packageName, packageProgress, buildDirectory)) { + case InvocationResult::Ok: + break; + case InvocationResult::Error: + case InvocationResult::Skipped: + // move on to the next package; invokeMakepkgToMakeSourcePackage() has already "recorded" any errors + enqueueDownloads(downloadsSession, 1); + break; + } + }); + m_buildAction->log()(Phrases::InfoMessage, "Updating checksums of ", packageName, " via ", m_updatePkgSumsPath.string(), '\n', + ps(Phrases::SubMessage), "build dir: ", buildDirectory, '\n'); + processSession->launch(boost::process::start_dir(buildDirectory), m_updatePkgSumsPath); + return InvocationResult::Ok; +} + +InvocationResult ConductBuild::invokeMakepkgToMakeSourcePackage(const BatchProcessingSession::SharedPointerType &downloadsSession, + const std::string &packageName, PackageBuildProgress &packageProgress, const std::string &buildDirectory) +{ + auto processSession = m_buildAction->makeBuildProcess(packageProgress.buildDirectory + "/download.log", + [this, downloadsSession, &packageProgress, &packageName](boost::process::child &&child, ProcessResult &&result) { + auto lock = lockToWrite(); + if (result.errorCode) { + const auto errorMessage = result.errorCode.message(); + packageProgress.error = "unable to make source package: " + errorMessage; + lock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to make source package for ", packageName, ": ", errorMessage, '\n'); + downloadsSession->addResponse(string(packageName)); + } else if (result.exitCode != 0) { + packageProgress.error = argsToString("unable to make source package: makepkg returned with exit code ", result.exitCode); + lock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "makepkg invocation to make source package for \"", packageName, + "\" exited with non-zero exit code: ", child.exit_code(), '\n'); + downloadsSession->addResponse(string(packageName)); + } else { + packageProgress.hasSources = true; + lock.unlock(); + } + enqueueDownloads(downloadsSession, 1); + }); + m_buildAction->log()(Phrases::InfoMessage, "Making source package for ", packageName, " via ", m_makePkgPath.string(), '\n', + ps(Phrases::SubMessage), "build dir: ", buildDirectory, '\n'); + processSession->launch(boost::process::start_dir(buildDirectory), m_makePkgPath, "-f", "--nodeps", "--nobuild", "--source"); + return InvocationResult::Ok; +} + +InvocationResult ConductBuild::invokeMakechrootpkg( + const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName) +{ + auto lock = lockToRead(); + auto &packageProgress = m_buildProgress.progressByPackage[packageName]; + + // skip if package has already been added to repo + if (packageProgress.addedToRepo) { + // enable staging if auto-staging is enabled and when it has already been determined to be required + if (m_autoStaging && packageProgress.stagingNeeded == PackageStagingNeeded::Yes) { + makepkgchrootSession->enableStagingInNextBatch(); + } + return InvocationResult::Skipped; + } + + // add package immediately to repository if it has already been built + if (!packageProgress.finished.isNull()) { + lock.unlock(); + addPackageToRepo(makepkgchrootSession, packageName, packageProgress); + return InvocationResult::Ok; + } + + // check whether we can build this package when building as far as possible or when the build order has been + // manuall specified (otherwise we don't need to check because we take it as given that all packages in the + // previous batch have been built and that the order batch compution is correct) + if ((m_buildAsFarAsPossible || m_buildPreparation.manuallyOrdered) && makepkgchrootSession->hasFailuresInPreviousBatches()) { + const auto &buildData = m_buildPreparation.buildData[packageName]; + std::vector *> dependencies; + dependencies.reserve(buildData.packages.size() + 2); + dependencies.emplace_back(&buildData.sourceInfo->makeDependencies); + dependencies.emplace_back(&buildData.sourceInfo->checkDependencies); + for (const auto &package : buildData.packages) { + dependencies.emplace_back(&package->dependencies); + } + if (checkForFailedDependency(dependencies)) { + const auto writeLock = lockToWrite(lock); + packageProgress.error = "unable to build because dependency failed"; + return InvocationResult::Error; + } + } + + // don't start the build if we could not even download the sources + if (!packageProgress.hasSources) { + if (packageProgress.error.empty()) { + const auto writeLock = lockToWrite(lock); + packageProgress.error = "unable to build because sources are missing"; + } + return InvocationResult::Error; + } + + // do some sanity checks with the chroot + const auto chrootDir = packageProgress.chrootDirectory % "/arch-" + m_buildPreparation.targetArch; + const auto buildRoot = chrootDir % '/' + m_chrootRootUser; + try { + if (!std::filesystem::is_directory(buildRoot)) { + auto writeLock = lockToWrite(lock); + packageProgress.error = "Chroot directory \"" % buildRoot + "\" is no directory."; + return InvocationResult::Error; + } + if (!std::filesystem::is_regular_file(buildRoot + "/.arch-chroot")) { + auto writeLock = lockToWrite(lock); + packageProgress.error = "Chroot directory \"" % buildRoot + "\" does not contain .arch-chroot file."; + return InvocationResult::Error; + } + for (const auto *const dir : { "/usr/bin", "/usr/lib", "/usr/include", "/etc" }) { + if (std::filesystem::is_directory(buildRoot + dir)) { + continue; + } + auto writeLock = lockToWrite(lock); + packageProgress.error = "Chroot directory \"" % buildRoot % "\" does not contain directory \"" % dir + "\"."; + return InvocationResult::Error; + } + } catch (const std::filesystem::filesystem_error &e) { + auto writeLock = lockToWrite(lock); + packageProgress.error = "Unable to check chroot directory \"" % buildRoot % "\": " + e.what(); + return InvocationResult::Error; + } + + // FIXME: lock the chroot directory to prevent other build tasks from using it (or does makechrootpkg already lock it?) + + // copy config files into chroot directory + try { + std::filesystem::copy_file(makepkgchrootSession->isStagingEnabled() ? m_pacmanStagingConfigPath : m_pacmanConfigPath, + buildRoot + "/etc/pacman.conf", std::filesystem::copy_options::overwrite_existing); + std::filesystem::copy_file(m_makepkgConfigPath, buildRoot + "/etc/makepkg.conf", std::filesystem::copy_options::overwrite_existing); + } catch (const std::filesystem::filesystem_error &e) { + auto writeLock = lockToWrite(lock); + packageProgress.error = "Unable to configure chroot \"" % buildRoot % "\": " + e.what(); + return InvocationResult::Error; + } + + // determine options/variables to pass + std::vector makechrootpkgFlags, makepkgFlags; + // -> cleanup/upgrade + if (!packageProgress.skipChrootCleanup) { + makechrootpkgFlags.emplace_back("-c"); + } + if (!packageProgress.skipChrootUpgrade) { + makechrootpkgFlags.emplace_back("-u"); + } + // -> ccache support (see https://wiki.archlinux.org/index.php/Ccache#makechrootpkg) + if (!m_globalCcacheDir.empty()) { + makechrootpkgFlags.emplace_back("-d"); + makechrootpkgFlags.emplace_back(m_globalCcacheDir + "/:/ccache"); + makepkgFlags.emplace_back("CCACHE_DIR=/ccache"); + } + // -> directory for testfiles (required by tagparser and tageditor) + if (!m_globalTestFilesDir.empty()) { + makechrootpkgFlags.emplace_back("-d"); + makechrootpkgFlags.emplace_back(m_globalTestFilesDir + "/:/testfiles"); + makepkgFlags.emplace_back("TEST_FILE_PATH=/testfiles"); + } + + // invoke makechrootpkg to build package + m_buildAction->log()(Phrases::InfoMessage, "Building ", packageName, '\n'); + auto processSession = m_buildAction->makeBuildProcess(packageProgress.buildDirectory + "/build.log", + std::bind(&ConductBuild::handleMakechrootpkgErrorsAndAddPackageToRepo, this, makepkgchrootSession, std::ref(packageName), + std::ref(packageProgress), std::placeholders::_1, std::placeholders::_2)); + processSession->registerNewDataHandler(BufferSearch("Updated version: ", "\e\n", "Starting build", + std::bind(&ConductBuild::assignNewVersion, this, std::ref(packageName), std::ref(packageProgress), std::placeholders::_1))); + m_buildAction->log()(Phrases::InfoMessage, "Invoking makechrootpkg for ", packageName, " via ", m_makeChrootPkgPath.string(), '\n', + ps(Phrases::SubMessage), "build dir: ", packageProgress.buildDirectory, '\n', ps(Phrases::SubMessage), "chroot dir: ", chrootDir, '\n', + ps(Phrases::SubMessage), "chroot user: ", packageProgress.chrootUser, '\n'); + processSession->launch(boost::process::start_dir(packageProgress.buildDirectory), m_makeChrootPkgPath, makechrootpkgFlags, "-C", + m_globalPackageCacheDir, "-r", chrootDir, "-l", packageProgress.chrootUser, packageProgress.makechrootpkgFlags, "--", makepkgFlags, + packageProgress.makepkgFlags); + return InvocationResult::Ok; +} + +void ConductBuild::addPackageToRepo( + const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const string &packageName, PackageBuildProgress &packageProgress) +{ + // make arrays to store binary package names + auto binaryPackages = std::vector{}; + auto binaryPackageNames = std::vector{}; + static const auto anyArch = std::vector{ "any" }; + + // determine name of source package to be copied + auto readLock = lockToRead(); + const auto &buildData = m_buildPreparation.buildData[packageName]; + const auto &firstPackage = buildData.packages.front(); + auto sourcePackageName = packageName % '-' % firstPackage->version + m_sourcePackageExtension; + + // determine names of binary packages to be copied + binaryPackages.reserve(buildData.packages.size()); + binaryPackageNames.reserve(buildData.packages.size()); + for (const auto &package : buildData.packages) { + const auto isAny = package->sourceInfo->archs + == anyArch; // FIXME: Shouldn't there still be a package->archs if e.g. base is x86_64 but a split package any? + const auto &arch = isAny ? "any" : m_buildPreparation.targetArch; + const auto &packageFileName = binaryPackageNames.emplace_back( + package->name % '-' % (packageProgress.updatedVersion.empty() ? package->version : packageProgress.updatedVersion) % '-' % arch + + m_binaryPackageExtension); + binaryPackages.emplace_back( + BinaryPackageInfo{ &package->name, &packageFileName, packageProgress.buildDirectory % '/' + packageFileName, isAny, false }); + } + + // check whether all packages exists + auto missingPackages = std::vector{}; + try { + if (!std::filesystem::is_regular_file(packageProgress.buildDirectory % '/' + sourcePackageName)) { + missingPackages.emplace_back(std::move(sourcePackageName)); + } + for (auto &binaryPackage : binaryPackages) { + if (!std::filesystem::is_regular_file(binaryPackage.path)) { + missingPackages.emplace_back(std::move(*binaryPackage.fileName)); + } + } + } catch (const std::filesystem::filesystem_error &e) { + auto writeLock = lockToWrite(readLock); + packageProgress.error = argsToString("unable to check whether resulting package exist: ", e.what()); + dumpBuildProgress(); + writeLock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to check resulting package for ", packageName, ": ", e.what()); + makepkgchrootSession->addResponse(string(packageName)); + enqueueMakechrootpkg(makepkgchrootSession, 1); + return; + } + if (!missingPackages.empty()) { + const auto missingBinaryPackagesJoined = joinStrings(missingPackages, ", "); + auto writeLock = lockToWrite(readLock); + packageProgress.error = "not all source/binary packages exist after the build as expected: " + missingBinaryPackagesJoined; + dumpBuildProgress(); + writeLock.unlock(); + m_buildAction->log()( + Phrases::ErrorMessage, "Not all source/binary packages exist after building ", packageName, ": ", missingBinaryPackagesJoined, '\n'); + makepkgchrootSession->addResponse(string(packageName)); + enqueueMakechrootpkg(makepkgchrootSession, 1); + return; + } + + // check whether staging is needed + // note: Calling checkWhetherStagingIsNeededAndPopulateRebuildList() in the condition first do prevent short-circuit evaluation. We always + // want to do the check in order to populate the rebuild list - even if staging is already enabled anyways. + auto needsStaging = makepkgchrootSession->isStagingEnabled(); + try { + if (packageProgress.stagingNeeded != PackageStagingNeeded::No) { + packageProgress.stagingNeeded = checkWhetherStagingIsNeededAndPopulateRebuildList(packageName, buildData, binaryPackages); + if (packageProgress.stagingNeeded == PackageStagingNeeded::Yes) { + needsStaging = true; + makepkgchrootSession->enableStagingInNextBatch(); + } + } + } catch (const std::runtime_error &e) { + auto writeLock = lockToWrite(readLock); + packageProgress.error = argsToString("unable to determine whether staging is needed: ", e.what()); + dumpBuildProgress(); + writeLock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to determine whether staging of ", packageName, " is needed: ", e.what(), '\n'); + makepkgchrootSession->addResponse(string(packageName)); + enqueueMakechrootpkg(makepkgchrootSession, 1); + return; + } + + // add artefacts + // -> make paths and check whether these are already present from previous runs + const auto sourcePackagePath = packageProgress.buildDirectory % '/' + sourcePackageName; + bool sourcePackageArtefactAlreadyPresent = false; + for (const auto &artefact : m_buildAction->artefacts) { + if (artefact == sourcePackagePath) { + sourcePackageArtefactAlreadyPresent = true; + break; + } + } + bool binaryPackageArtefactsAlreadyPresent = true; + for (auto &binaryPackage : binaryPackages) { + binaryPackage.path = packageProgress.buildDirectory % '/' + *binaryPackage.fileName; + for (const auto &artefact : m_buildAction->artefacts) { + if (artefact == binaryPackage.path) { + binaryPackage.artefactAlreadyPresent = true; + break; + } + if (!binaryPackage.artefactAlreadyPresent) { + binaryPackageArtefactsAlreadyPresent = false; + } + } + } + // -> add missing artefacts + if (!sourcePackageArtefactAlreadyPresent || !binaryPackageArtefactsAlreadyPresent) { + const auto buildActionsWriteLock = m_setup.building.lockToWrite(); + if (!sourcePackageArtefactAlreadyPresent) { + m_buildAction->artefacts.emplace_back(sourcePackagePath); + } + for (const auto &binaryPackage : binaryPackages) { + if (!binaryPackage.artefactAlreadyPresent) { + m_buildAction->artefacts.emplace_back(binaryPackage.path); + } + } + } + + // copy source and binary packages + const auto &repoPath = needsStaging ? m_buildProgress.stagingRepoPath : m_buildProgress.targetRepoPath; + const auto &dbFilePath = needsStaging ? m_buildProgress.stagingDbFilePath : m_buildProgress.targetDbFilePath; + try { + const auto sourceRepoPath = std::filesystem::path(repoPath + "/../src/"); + auto anyRepoPath = std::filesystem::path(); + std::filesystem::create_directories(sourceRepoPath); + std::filesystem::copy(sourcePackagePath, sourceRepoPath / sourcePackageName, std::filesystem::copy_options::update_existing); + for (const auto &binaryPackage : binaryPackages) { + if (!binaryPackage.isAny) { + std::filesystem::copy(binaryPackage.path, repoPath % '/' + *binaryPackage.fileName, std::filesystem::copy_options::update_existing); + continue; + } + if (anyRepoPath.empty()) { + std::filesystem::create_directories(anyRepoPath = repoPath + "/../any"); + } + std::filesystem::copy(binaryPackage.path, anyRepoPath / *binaryPackage.fileName, std::filesystem::copy_options::update_existing); + const std::filesystem::path symlink = repoPath % '/' + *binaryPackage.fileName; + if (std::filesystem::exists(symlink) && !std::filesystem::is_symlink(symlink)) { + std::filesystem::remove(symlink); + } + std::filesystem::create_symlink("../any/" + *binaryPackage.fileName, symlink); + } + } catch (const std::filesystem::filesystem_error &e) { + auto writeLock = lockToWrite(readLock); + packageProgress.error = argsToString("unable to copy package to destination repository: ", e.what()); + dumpBuildProgress(); + writeLock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to copy package to destination repository: ", e.what(), '\n'); + makepkgchrootSession->addResponse(string(packageName)); + enqueueMakechrootpkg(makepkgchrootSession, 1); + return; + } + + // add completed package to repository + auto processSession = m_buildAction->makeBuildProcess(packageProgress.buildDirectory + "/repo-add.log", + std::bind(&ConductBuild::handleRepoAddErrorsAndMakeNextPackage, this, makepkgchrootSession, std::ref(packageName), std::ref(packageProgress), + std::placeholders::_1, std::placeholders::_2)); + processSession->launch(boost::process::start_dir(repoPath), m_repoAddPath, dbFilePath, binaryPackageNames); + m_buildAction->log()(Phrases::InfoMessage, "Adding ", packageName, " to repo\n", ps(Phrases::SubMessage), "repo path: ", repoPath, '\n', + ps(Phrases::SubMessage), "db path: ", dbFilePath, '\n', ps(Phrases::SubMessage), "package(s): ", joinStrings(binaryPackageNames), '\n'); +} + +void ConductBuild::checkDownloadErrorsAndMakePackages(BatchProcessingSession::ContainerType &&failedPackages) +{ + // dump progress + auto readLock = lockToRead(); + dumpBuildProgress(); + readLock.unlock(); + + // validate whether we have all sources + if (!failedPackages.empty()) { + const auto failedPackagesStr = joinStrings(failedPackages, ", "); + if (!m_buildAsFarAsPossible) { + reportError("failed to download sources: " + failedPackagesStr); + return; + } + m_buildAction->log()(Phrases::WarningMessage, "Ignoring download failures: ", failedPackagesStr, '\n'); + // note: Packages without sources will be treated later as errors even before invoking makechrootpkg. At this point there's nothing to do + // for ignoring the packages. + } + + if (reportAbortedIfAborted()) { + return; + } + + // enqueue building the first package + constexpr std::size_t maxParallelInvocations = 1; + enqueueMakechrootpkg(make_shared(m_relevantPackages, m_buildPreparation.batches, m_setup.building.ioContext, + std::bind(&ConductBuild::checkBuildErrors, this, std::placeholders::_1), !m_buildAsFarAsPossible), + maxParallelInvocations); +} + +static void assignNewChrootUser(std::string &&newChrootUser, PackageBuildProgress &packageProgress) +{ + if (newChrootUser.empty()) { + return; + } + packageProgress.skipChrootCleanup = packageProgress.skipChrootUpgrade = true; + packageProgress.chrootUser = std::move(newChrootUser); +} + +void ConductBuild::handleMakechrootpkgErrorsAndAddPackageToRepo(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, + const string &packageName, PackageBuildProgress &packageProgress, boost::process::child &&child, ProcessResult &&result) +{ + // check for makechrootpkg error + const auto hasError = result.errorCode || child.exit_code() != 0; + // -> save chroot directory + auto newChrootUser = std::string(); + if (hasError && m_saveChrootDirsOfFailures) { + // rename the chroot working copy directory from e.g. buildservice to buildservice-somepkg; append number if already present + const auto chrootSuffix = '-' + packageName; + auto lock = lockToRead(); + if (!endsWith(packageProgress.chrootUser, chrootSuffix)) { + auto assignNewChrootUser = [&newChrootUser, &packageProgress, &chrootSuffix, attempt = 0u]() mutable { + newChrootUser = attempt++ ? (packageProgress.chrootUser % chrootSuffix % '-' + attempt) : (packageProgress.chrootUser + chrootSuffix); + }; + assignNewChrootUser(); + try { + const auto chrootPathStart = packageProgress.chrootDirectory % "/arch-" % m_buildPreparation.targetArch % '/'; + auto newChrootPath = std::filesystem::path(chrootPathStart + newChrootUser); + while (std::filesystem::exists(newChrootPath)) { + assignNewChrootUser(); + newChrootPath = chrootPathStart + newChrootUser; + } + std::filesystem::rename(chrootPathStart + packageProgress.chrootUser, chrootPathStart + newChrootUser); + } catch (const std::filesystem::filesystem_error &e) { + lock.unlock(); + newChrootUser.clear(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to rename chroot directory for ", packageName, ':', ' ', e.what(), '\n'); + } + } + } + // -> add error message + auto lock = lockToWrite(); + if (result.errorCode) { + const auto errorMessage = result.errorCode.message(); + packageProgress.error = "unable to build: " + errorMessage; + assignNewChrootUser(std::move(newChrootUser), packageProgress); + dumpBuildProgress(); + lock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke makechrootpkg for ", packageName, ": ", errorMessage, '\n'); + return; + } + if (child.exit_code() != 0) { + packageProgress.error = argsToString("unable to build: makechrootpkg returned with exit code ", child.exit_code()); + assignNewChrootUser(std::move(newChrootUser), packageProgress); + dumpBuildProgress(); + lock.unlock(); + m_buildAction->log()( + Phrases::ErrorMessage, "makechrootpkg invocation for ", packageName, " exited with non-zero exit code: ", child.exit_code(), '\n'); + } + if (hasError) { + makepkgchrootSession->addResponse(string(packageName)); + enqueueMakechrootpkg(makepkgchrootSession, 1); + return; + } + + // set "finished" to record the package has been built successfully + packageProgress.finished = DateTime::gmtNow(); + lock.unlock(); + + addPackageToRepo(makepkgchrootSession, packageName, packageProgress); +} + +void ConductBuild::handleRepoAddErrorsAndMakeNextPackage(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, + const string &packageName, PackageBuildProgress &packageProgress, boost::process::child &&child, ProcessResult &&result) +{ + // handle repo-add error; update build progress JSON after each package + auto lock = lockToWrite(); + if (result.errorCode) { + const auto errorMessage = result.errorCode.message(); + packageProgress.error = "unable to add package to repo: " + errorMessage; + dumpBuildProgress(); + lock.unlock(); + m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke repo-add for ", packageName, ": ", errorMessage, '\n'); + makepkgchrootSession->addResponse(string(packageName)); + } else if (child.exit_code() != 0) { + packageProgress.error = argsToString("unable to add package to repo: repo-add returned with exit code ", child.exit_code()); + dumpBuildProgress(); + lock.unlock(); + m_buildAction->log()( + Phrases::ErrorMessage, "repo-add invocation for ", packageName, " exited with non-zero exit code: ", child.exit_code(), '\n'); + makepkgchrootSession->addResponse(string(packageName)); + } else { + packageProgress.addedToRepo = true; + dumpBuildProgress(); + lock.unlock(); + } + + // make next package + enqueueMakechrootpkg(makepkgchrootSession, 1); +} + +void ConductBuild::checkBuildErrors(BatchProcessingSession::ContainerType &&failedPackages) +{ + // check whether build errors occurred + if (!failedPackages.empty()) { + reportError("failed to build packages: " + joinStrings(failedPackages, ", ")); + return; + } + + auto buildActionsWriteLock = m_setup.building.lockToWrite(); + reportSuccess(); +} + +void ConductBuild::dumpBuildProgress() +{ + try { + dumpJsonDocument( + [this] { return m_buildProgress.toJsonDocument(); }, m_workingDirectory, buildProgressFileName, DumpJsonExistingFileHandling::Override); +#ifdef CPP_UTILITIES_DEBUG_BUILD + m_buildAction->appendOutput(Phrases::InfoMessage, argsToString("Updated ", buildProgressFileName, ".json\n")); +#endif + } catch (const std::runtime_error &e) { + m_buildAction->appendOutput(Phrases::ErrorMessage, argsToString(e.what(), '\n')); + } +} + +void ConductBuild::addLogFile(std::string &&logFilePath) +{ + // skip if log file already present from previous run + auto buildActionsReadLock = m_setup.building.lockToRead(); + for (const auto &logFile : m_buildAction->logfiles) { + if (logFile == logFilePath) { + return; + } + } + + const auto buildActionsWriteLock = m_setup.building.lockToWrite(buildActionsReadLock); + m_buildAction->logfiles.emplace_back(std::move(logFilePath)); +} + +void ConductBuild::assignNewVersion(const std::string &packageName, PackageBuildProgress &packageProgress, string &&updatedVersionInfo) +{ + auto updatedVersionInfoParts = splitString(updatedVersionInfo, " ", EmptyPartsTreat::Omit); + if (updatedVersionInfoParts.empty()) { + return; + } + auto &newVersion = updatedVersionInfoParts.back(); + m_buildAction->log()(Phrases::InfoMessage, "Version of \"", packageName, "\" has been updated to: ", newVersion, '\n'); + const auto lock = lockToWrite(); + packageProgress.updatedVersion = std::move(newVersion); +} + +void ConductBuild::copyPkgbuildToOriginalSourceDirectory( + const string &packageName, PackageBuildProgress &packageProgress, const string &buildDirectory) +{ + const auto &buildData = m_buildPreparation.buildData[packageName]; + const auto &originalSourceDirectory = buildData.originalSourceDirectory; + if (originalSourceDirectory.empty()) { + const auto lock = lockToWrite(); + packageProgress.warnings.emplace_back("Unable to copy back updated PKGBUILD: original source directory empty"); + return; + } + try { + std::filesystem::copy(buildDirectory + "/PKGBUILD", originalSourceDirectory + "/PKGBUILD", std::filesystem::copy_options::overwrite_existing); + } catch (const std::filesystem::filesystem_error &e) { + const auto lock = lockToWrite(); + packageProgress.warnings.emplace_back(argsToString("Unable to copy back updated PKGBUILD: ", e.what())); + } +} + +PackageStagingNeeded ConductBuild::checkWhetherStagingIsNeededAndPopulateRebuildList( + const std::string &packageName, const PackageBuildData &buildData, const std::vector &builtPackages) +{ + CPP_UTILITIES_UNUSED(buildData) + + // skip if auto-staging is disabled + if (!m_autoStaging) { + return PackageStagingNeeded::Undetermined; + } + + m_buildAction->log()(Phrases::InfoMessage, "Checking whether staging of ", packageName, " is needed\n"); + LibPkg::DependencySet removedProvides, addedProvides; + std::unordered_set removedLibProvides, addedLibProvides; + auto needsStaging = false; + + // check for existing packages (which would be replaced/shadowed by adding the newly built packages) in the destination repository + // -> make list of provides would be removed when adding the newly built package to the repo + auto affectedDbName = std::string_view{}; + auto configLock = m_setup.config.lockToRead(); + for (const auto &[dbName, dbConfig] : m_buildPreparation.dbConfig) { + auto *const db = m_setup.config.findDatabase(dbName, m_buildPreparation.targetArch); + if (!db) { + throw std::runtime_error("Configured database \"" % dbName + "\" has been removed."); + } + const auto &packages = db->packages; + for (const auto &builtPackage : builtPackages) { + if (const auto i = packages.find(*builtPackage.name); i != packages.end()) { + LibPkg::Package::exportProvides(i->second, removedProvides, removedLibProvides); + if (affectedDbName.empty()) { + affectedDbName = dbName; + } + } + } + } + configLock.unlock(); + + // parse built packages + // -> make list of provides which would be added when adding the newly built package to the repo + for (const auto &builtPackage : builtPackages) { + LibPkg::Package::exportProvides(LibPkg::Package::fromPkgFile(builtPackage.path), addedProvides, addedLibProvides); + } + + // reduce list of removed provides if they are also provided by the newly built package + std::erase_if(removedProvides, [&addedProvides](const auto &provide) { return addedProvides.provides(provide.first, provide.second); }); + std::erase_if(removedLibProvides, [&addedLibProvides](const auto &provide) { return addedLibProvides.find(provide) != addedLibProvides.end(); }); + + // populate m_buildProgress.producedProvides and m_buildProgress.removedProvides (which only serve informal purposes) + std::lock_guard rebuildListLock(m_rebuildListMutex); + m_buildProgress.producedProvides[packageName].add(addedProvides, addedLibProvides); + m_buildProgress.removedProvides[packageName].add(removedProvides, removedLibProvides); + + // skip any further checks if nothing will be removed anyways + if (removedProvides.empty() && removedLibProvides.empty()) { + m_buildAction->log()(Phrases::SubMessage, "no: Adding the package would not replace any present provides\n"); + return PackageStagingNeeded::No; + } + + // find all relevant databases to check whether any of the removed provides are needed by other packages + configLock = m_setup.config.lockToRead(); + auto *const affectedDb = m_setup.config.findDatabase(affectedDbName, m_buildPreparation.targetArch); + if (!affectedDb) { + throw std::runtime_error("Affected database \"" % affectedDbName + "\" has been removed."); + } + const auto relevantDbs = m_setup.config.computeDatabasesRequiringDatabase(*affectedDb); + m_buildAction->log()(Phrases::SubMessage, + "checking the following databases for affected packages: ", joinStrings(names>(relevantDbs), ", "), '\n'); + auto listOfAffectedPackages = std::vector(); + for (const auto &db : relevantDbs) { + const auto isDestinationDb = db->name == m_buildPreparation.targetDb && db->arch == m_buildPreparation.targetArch; + const auto &requiredDeps = db->requiredDeps; + RebuildInfoByPackage *rebuildInfoForDb = nullptr; + for (const auto &[removedDependencyName, removedDependencyDetail] : removedProvides) { + for (auto affectedDependencies = requiredDeps.equal_range(removedDependencyName); + affectedDependencies.first != affectedDependencies.second; ++affectedDependencies.first) { + if (!LibPkg::Dependency::matches( + removedDependencyDetail.mode, removedDependencyDetail.version, affectedDependencies.first->second.version)) { + continue; + } + if (!rebuildInfoForDb) { + rebuildInfoForDb = &m_buildProgress.rebuildList[db->name]; + } + const auto &affectedPackages = affectedDependencies.first->second.relevantPackages; + for (const auto &affectedPackage : affectedPackages) { + if (isDestinationDb && affectedPackage->name == packageName) { + continue; // skip if that's just the package we want to update itself + } + needsStaging = true; + (*rebuildInfoForDb)[affectedPackage->name].provides.emplace_back( + removedDependencyName, removedDependencyDetail.version, removedDependencyDetail.mode); + listOfAffectedPackages.emplace_back(db->name % '/' + affectedPackage->name); + } + } + } + for (const auto &removedLibProvide : removedLibProvides) { + if (const auto affectedLibRequires = db->requiredLibs.find(removedLibProvide); affectedLibRequires != db->requiredLibs.end()) { + const auto &affectedPackages = affectedLibRequires->second; + if (affectedPackages.empty()) { + continue; + } + for (const auto &affectedPackage : affectedPackages) { + if (isDestinationDb && affectedPackage->name == packageName) { + continue; // skip if that's just the package we want to update itself + } + if (!rebuildInfoForDb) { + rebuildInfoForDb = &m_buildProgress.rebuildList[db->name]; + } + needsStaging = true; + (*rebuildInfoForDb)[affectedPackage->name].libprovides.emplace_back(removedLibProvide); + } + } + } + } + + if (needsStaging) { + m_buildAction->log()(Phrases::SubMessage, "yes: Other packages need to be re-built:", formattedPhraseString(Phrases::End), + joinStrings(listOfAffectedPackages, "\n", false, " - "), '\n'); + } else { + m_buildAction->log()( + Phrases::SubMessage, "no: The package replaces another one but it doesn't affect other packages", formattedPhraseString(Phrases::End)); + } + return needsStaging ? PackageStagingNeeded::Yes : PackageStagingNeeded::No; +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/customcommand.cpp b/librepomgr/buildactions/customcommand.cpp new file mode 100644 index 0000000..728315b --- /dev/null +++ b/librepomgr/buildactions/customcommand.cpp @@ -0,0 +1,76 @@ +#include "./buildactionprivate.h" +#include "./subprocess.h" + +#include "../serversetup.h" + +#include + +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +CustomCommand::CustomCommand(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void CustomCommand::run() +{ + // validate and read parameter/settings + if (auto error = validateParameter(RequiredDatabases::None, RequiredParameters::None); !error.empty()) { + reportError(move(error)); + return; + } + if (m_buildAction->directory.empty()) { + reportError("No directory specified."); + return; + } + auto &metaInfo = m_setup.building.metaInfo; + auto metaInfoLock = metaInfo.lockToRead(); + const auto &typeInfo = metaInfo.typeInfoForId(BuildActionType::CustomCommand); + const auto commandSetting = typeInfo.settings[static_cast(CustomCommandSettings::Command)].param; + metaInfoLock.unlock(); + const auto &command = findSetting(commandSetting); + if (command.empty()) { + reportError("No command specified."); + return; + } + + // prepare working dir + try { + m_workingDirectory = determineWorkingDirectory(customCommandsWorkingDirectory); + if (!std::filesystem::is_directory(m_workingDirectory)) { + std::filesystem::create_directories(m_workingDirectory); + } + } catch (const std::filesystem::filesystem_error &e) { + reportError(argsToString("Unable to create working directory: ", e.what())); + return; + } + + m_buildAction->appendOutput(Phrases::InfoMessage, "Running custom command: ", command, '\n'); + + // launch process, pass finish handler + auto process = m_buildAction->makeBuildProcess(m_workingDirectory + "/the.log", [this](boost::process::child &&, ProcessResult &&result) { + if (result.errorCode) { + m_buildAction->appendOutput(Phrases::InfoMessage, "Unable to invoke command: ", result.errorCode.message()); + reportError(result.errorCode.message()); + return; + } + m_buildAction->appendOutput( + result.exitCode == 0 ? Phrases::InfoMessage : Phrases::ErrorMessage, "Command exited with return code ", result.exitCode); + if (result.exitCode != 0) { + reportError(argsToString("non-zero exit code ", result.exitCode)); + return; + } + const auto buildLock = m_setup.building.lockToWrite(); + reportSuccess(); + }); + process->launch(boost::process::start_dir(m_workingDirectory), boost::process::search_path("bash"), "-ec", command); +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/makelicenseinfo.cpp b/librepomgr/buildactions/makelicenseinfo.cpp new file mode 100644 index 0000000..ab90c45 --- /dev/null +++ b/librepomgr/buildactions/makelicenseinfo.cpp @@ -0,0 +1,28 @@ +#include "./buildactionprivate.h" + +#include "../serversetup.h" + +namespace LibRepoMgr { + +MakeLicenseInfo::MakeLicenseInfo(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void MakeLicenseInfo::run() +{ + auto configReadLock = init(BuildActionAccess::ReadConfig, RequiredDatabases::None, RequiredParameters::Packages); + if (std::holds_alternative(configReadLock)) { + return; + } + + auto result = m_setup.config.computeLicenseInfo(m_buildAction->packageNames); + std::get>(configReadLock).unlock(); + + const auto buildActionWriteLock = m_setup.building.lockToWrite(); + m_buildAction->outputMimeType = "application/json"; + m_buildAction->resultData = std::move(result); + reportResult(result.success ? BuildActionResult::Success : BuildActionResult::Failure); +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/preparebuild.cpp b/librepomgr/buildactions/preparebuild.cpp new file mode 100644 index 0000000..1a24dba --- /dev/null +++ b/librepomgr/buildactions/preparebuild.cpp @@ -0,0 +1,1072 @@ +#include "./buildactionprivate.h" +#include "./subprocess.h" + +#include "../helper.h" +#include "../json.h" +#include "../serversetup.h" + +#include "../webclient/aur.h" + +#include "../webapi/params.h" + +#include "../../libpkg/parser/database.h" +#include "../../libpkg/parser/utils.h" + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +PrepareBuild::PrepareBuild(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void PrepareBuild::run() +{ + // check whether a directory has been specified + if (m_buildAction->directory.empty()) { + reportError("Unable to create working directory: no directory name specified"); + return; + } + + // check flags and settings + const auto flags = static_cast(m_buildAction->flags); + m_forceBumpPackageVersion = flags & PrepareBuildFlags::ForceBumpPkgRel; + m_cleanSourceDirectory = flags & PrepareBuildFlags::CleanSrcDir; + m_keepOrder = flags & PrepareBuildFlags::KeepOrder; + m_keepPkgRelAndEpoch = flags & PrepareBuildFlags::KeepPkgRelAndEpoch; + if (m_forceBumpPackageVersion && m_keepPkgRelAndEpoch) { + reportError("Can not force-bump pkgrel and keeping it at the same time."); + return; + } + auto &metaInfo = m_setup.building.metaInfo; + auto metaInfoLock = metaInfo.lockToRead(); + const auto &typeInfo = metaInfo.typeInfoForId(BuildActionType::PrepareBuild); + const auto pkgbuildsDirsSetting = std::string(typeInfo.settings[static_cast(PrepareBuildSettings::PKGBUILDsDirs)].param); + metaInfoLock.unlock(); + if (const auto i = m_buildAction->settings.find(pkgbuildsDirsSetting); i != m_buildAction->settings.end()) { + m_pkgbuildsDirs = splitString>(i->second, ":", EmptyPartsTreat::Omit); + } + + // read values from global config + auto setupReadLock = m_setup.lockToRead(); + m_makePkgPath = findExecutable(m_setup.building.makePkgPath); + copySecondVectorIntoFirstVector(m_pkgbuildsDirs, m_setup.building.pkgbuildsDirs); + m_ignoreLocalPkgbuildsRegex = m_setup.building.ignoreLocalPkgbuildsRegex; + m_workingDirectory = determineWorkingDirectory(buildDataWorkingDirectory); + setupReadLock.unlock(); + + // check executables + if (!checkExecutable(m_makePkgPath)) { + reportError("Unable to find makepkg executable \"" % m_setup.building.makePkgPath + "\" in PATH."); + return; + } + + // init build action + auto configReadLock + = init(BuildActionAccess::ReadConfig, RequiredDatabases::MaybeSource | RequiredDatabases::OneDestination, RequiredParameters::Packages); + if (std::holds_alternative(configReadLock)) { + return; + } + + // find dependencies of the destination repository + auto *const destinationDb = (*m_destinationDbs.begin()); + auto databaseDependencyOrderRes = m_setup.config.computeDatabaseDependencyOrder(*destinationDb); + if (std::holds_alternative(databaseDependencyOrderRes)) { + configReadLock = std::monostate{}; + reportError("Unable to find the destination DB's dependencies: " + get(databaseDependencyOrderRes)); + return; + } + auto &databaseDependencyOrder = get>(databaseDependencyOrderRes); + for (const auto *destinationDbOrDependency : databaseDependencyOrder) { + m_requiredDbs.emplace(destinationDbOrDependency->name); + } + + // find dependencies of the staging repository + auto *stagingDb = m_setup.config.findDatabase(destinationDb->name + "-staging", destinationDb->arch); + auto stagingDbOrder = std::variant, std::string>(); + auto hasStagingDbOrder = false; + if (!stagingDb && endsWith(destinationDb->name, "-testing")) { + stagingDb = m_setup.config.findDatabase( + argsToString(std::string_view(destinationDb->name.data(), destinationDb->name.size() - 8), "-staging"), destinationDb->arch); + } + if (!stagingDb && endsWith(destinationDb->name, "-staging")) { + // use the destination DB as staging DB if it ends with "-staging"; we can assume it was intended to use it and the auto-staging + // was just passed as usual + stagingDb = destinationDb; + stagingDbOrder = databaseDependencyOrder; + hasStagingDbOrder = true; + } + if (stagingDb) { + if (!hasStagingDbOrder) { + stagingDbOrder = m_setup.config.computeDatabaseDependencyOrder(*stagingDb); + } + if (std::holds_alternative(stagingDbOrder)) { + m_warnings.emplace_back("Unable to find the staging DB's dependencies: " + get(stagingDbOrder)); + } + } else { + m_warnings.emplace_back("Unable to find staging DB for \"" + destinationDb->name + "\". Auto-staging will not work."); + } + + // set target and staging info + m_targetDbName = destinationDb->name; + m_targetArch = destinationDb->arch; + if (stagingDb) { + m_stagingDbName = stagingDb->name; + } + + // make set of databases considered as given from the specified source databases + // notes: 1. If *no* source databases are specified the destination database and all databases it is based on are considered as "given" and + // therefore not included in the build unless their package names are explicitly specified. All direct and transitive dependencies + // of the destination repository and the destination repository itself are enabled within the build root. + // 2. If source databases are specified only packages contained by these databases will be considered as "given". Any other packages + // will be included in the build. Dependencies of the destination repository and the destination repository itself are only enabled within + // the build root when also listed as source repositories. + // 3. All specified source repositories should be equal to the destination repository or a direct or transitive dependency of the destination + // repository. If that is not the case the built packages might depend on packages which are not available at install time. Hence a warning + // is emitted in that case. + std::vector missingBaseDbs; + for (auto *const sourceDb : m_sourceDbs) { + const auto &[sourceDbName, added] = m_baseDbs.emplace(sourceDb->name); + if (!added) { + continue; + } + if (m_requiredDbs.find(*sourceDbName) == m_requiredDbs.end()) { + missingBaseDbs.emplace_back(*sourceDbName); + databaseDependencyOrder.emplace_back(sourceDb); // appending the "odd" source database (see note 3. above) should be sufficient + } + } + if (!missingBaseDbs.empty()) { + m_warnings.emplace_back( + "The following source DBs are not the destination DB or a (transitive) dependency of it: " + joinStrings(missingBaseDbs, ", ")); + } + + // compose the list of repositories to enable within the build root (for staging) + populateDbConfig(databaseDependencyOrder, false); + if (std::holds_alternative>(stagingDbOrder)) { + populateDbConfig(std::get>(stagingDbOrder), true); + } + + // try to find denoted packages to determine the actual package base and the existing version + // -> We might get something like "my-repo/foo" and "my-repo/bar" where "foo" and "bar" are only split + // packages from the base package "foobar" when blindly trying to "rebuild" packages from a binary + // repository. Luckily database files store the corresponding base packages. + // -> That trick doesn't work for new packages of course. + // FIXME: Maybe the AUR RPC provides useful information (which would at least help with AUR packages). + // -> The existing version is useful to know when we need to bump pkgrel or even epoch. + size_t currentIndex = 0; + for (const auto &packageName : m_buildAction->packageNames) { + // pick some package matching the specified packageName which has a source info; prefering packages + // from the destination db + auto existingPackages = m_setup.config.findPackages(packageName); + auto foundSourcePackage = false; + auto packageBuildData = PackageBuildData(); + for (auto &package : existingPackages) { + // skip if package not relevant + if (!isExistingPackageRelevant(packageName, package, packageBuildData, *destinationDb)) { + continue; + } + // skip if package has no source info + const auto &sourceInfo = package.pkg->sourceInfo; + if (!sourceInfo || sourceInfo->name.empty()) { + continue; + } + // + auto &buildData = m_buildDataByPackage[sourceInfo->name]; + buildData.specifiedIndex = currentIndex++; + buildData.existingPackages.emplace_back(package.pkg); + if (buildData.existingVersion.empty() + || LibPkg::PackageVersion::isNewer(LibPkg::PackageVersion::compare(package.pkg->version, buildData.existingVersion))) { + buildData.existingVersion = package.pkg->version; + } + // assume we don't have sources if going to clean up the source directory + // FIXME: It is a little bit inconsistent that in other places existing PackageBuildData is overridden and here it is re-used. + if (m_cleanSourceDirectory) { + buildData.hasSource = false; + } + foundSourcePackage = true; + if (std::get(package.db) == destinationDb) { + break; + } + } + // add an empty BuildPackage neverthless assuming packageName specifies the base package as-is + if (!foundSourcePackage) { + packageBuildData.specifiedIndex = currentIndex++; + m_buildDataByPackage[packageName] = std::move(packageBuildData); + } + } + + configReadLock = std::monostate{}; + + // make working directory + try { + filesystem::create_directories(m_workingDirectory); + } catch (const filesystem::filesystem_error &e) { + reportError("Unable to create working directory " % m_workingDirectory % ": " + e.what()); + return; + } + + if (reportAbortedIfAborted()) { + return; + } + + // locate PKGBUILDs locally or download them from AUR + fetchMissingBuildData(); +} + +/*! + * \brief Populates m_dbConfig (or m_dbStagingConfig if \a forStaging) with the specified \a dbOrder. + */ +void PrepareBuild::populateDbConfig(const std::vector &dbOrder, bool forStaging) +{ + auto &dbConfig = forStaging ? m_stagingDbConfig : m_dbConfig; + for (auto i = dbOrder.rbegin(), end = dbOrder.rend(); i != end; ++i) { + const auto *const db = *i; + auto scopeData = IniFile::ScopeData{}; + scopeData.emplace(make_pair("SigLevel", LibPkg::signatureLevelToString(db->signatureLevel))); + for (const auto &mirror : db->mirrors) { + scopeData.emplace(make_pair("Server", mirror)); + } + if (m_sourceDbs.empty() || m_baseDbs.find(db->name) != m_baseDbs.cend() || (forStaging && endsWith(db->name, "-staging"))) { + dbConfig.emplace_back(IniFile::Scope(db->name, move(scopeData))); + } + } +} + +/*! + * \brief Determines whether the existing package \a package is relevant for providing the dependency with \a dependencyName considering the + * specified \a destinationDb. + * \remarks The \a package is added to the existing packages in \a packageBuildData in case it is useful for further processing even though + * not for providing the dependency (e.g. bumping pkgrel). + */ +bool PrepareBuild::isExistingPackageRelevant( + const string &dependencyName, LibPkg::PackageSearchResult &package, PackageBuildData &packageBuildData, const LibPkg::Database &destinationDb) +{ + // skip db if arch isn't matching the destination db arch + const auto *const db = std::get(package.db); + if (db->arch != destinationDb.arch) { + return false; + } + // add an exact package match to the build data if it is present in the destination db or any dependency because it is + // still relevant for bumping versions, even if we ignore it because it is not present in the base DBs + const auto dependencyPresentInRequiredDbs = m_requiredDbs.find(db->name) != m_requiredDbs.end(); + if (dependencyPresentInRequiredDbs && dependencyName == package.pkg->name) { + if (packageBuildData.existingVersion.empty() + || LibPkg::PackageVersion::isNewer(LibPkg::PackageVersion::compare(package.pkg->version, packageBuildData.existingVersion))) { + packageBuildData.existingVersion = package.pkg->version; + } + packageBuildData.existingPackages.emplace_back(package.pkg); + } + // skip if packge is not from any database available within that build + return (m_baseDbs.empty() && dependencyPresentInRequiredDbs) || (m_baseDbs.find(db->name) != m_baseDbs.end()); +} + +void PrepareBuild::addPackageToLogLine(std::string &logLine, const std::string &packageName) +{ + logLine += ' '; + logLine += packageName; +} + +void PrepareBuild::makeSrcInfo( + std::shared_ptr &multiSession, const std::string &sourceDirectory, const std::string &packageName) +{ + auto processSession = make_shared( + m_setup.building.ioContext, [multiSession, &sourceDirectory, &packageName](boost::process::child &&child, ProcessResult &&result) { + processSrcInfo(*multiSession, sourceDirectory, packageName, std::move(child), std::move(result)); + }); + processSession->launch(boost::process::start_dir(sourceDirectory), m_makePkgPath.string(), "--printsrcinfo"); +} + +void PrepareBuild::processSrcInfo(WebClient::AurSnapshotQuerySession &multiSession, const std::string &sourceDirectory, + const std::string &packageName, boost::process::child &&child, ProcessResult &&result) +{ + if (result.errorCode) { + multiSession.addResponse(WebClient::AurSnapshotResult{ .packageName = packageName, + .errorOutput = move(result.error), + .error = argsToString("Unable to invoke makepkg --printsourceinfo: ", result.errorCode.message()) }); + return; + } + if (child.exit_code() != 0) { + multiSession.addResponse(WebClient::AurSnapshotResult{ .packageName = packageName, + .errorOutput = move(result.error), + .error = argsToString("makepkg --printsourceinfo exited with non-zero exit code: ", child.exit_code()) }); + return; + } + + const auto srcInfoPath = sourceDirectory + "/.SRCINFO"; + try { + writeFile(srcInfoPath, result.output); + } catch (const std::ios_base::failure &failure) { + multiSession.addResponse( + WebClient::AurSnapshotResult{ .packageName = packageName, .error = "Unable to write " % srcInfoPath % ": " + failure.what() }); + return; + } + + addResultFromSrcInfo(multiSession, packageName, result.output); +} + +void PrepareBuild::addResultFromSrcInfo(WebClient::AurSnapshotQuerySession &multiSession, const std::string &packageName, const std::string &srcInfo) +{ + auto snapshotResult = WebClient::AurSnapshotResult{ .packageName = packageName, .packages = LibPkg::Package::fromInfo(srcInfo, false) }; + if (snapshotResult.packages.empty() || snapshotResult.packages.front()->name.empty()) { + snapshotResult.error = "Unable to parse .SRCINFO: no package name present"; + } else if (!(snapshotResult.sourceInfo = snapshotResult.packages.front()->sourceInfo)) { + snapshotResult.error = "Unable to parse .SRCINFO: no source info present"; + } + multiSession.addResponse(std::move(snapshotResult)); +} + +void PrepareBuild::fetchMissingBuildData() +{ + auto multiSession + = WebClient::AurSnapshotQuerySession::create(m_setup.building.ioContext, bind(&PrepareBuild::computeDependencies, this, placeholders::_1)); + vector snapshotQueries; + + // prepare logging + auto logLines = vector{ + " -> Generating .SRCINFO for local PKGBUILDs:", " -> Retrieving sources from AUR:", " -> Using existing source directories:" + }; + auto needToGeneratedSrcInfo = false; + auto usingExistingSourceDirectories = false; + + // fetch build data (PKGBUILD, .SRCINFO, ...) for all the packages we want to build + for (auto &[packageName, buildData] : m_buildDataByPackage) { + + // skip packages where the source has already been fetched in a previous invocation or which have already failed on a previous invocation + if (buildData.hasSource || !buildData.error.empty()) { + continue; + } + + buildData.sourceDirectory = m_workingDirectory % '/' % packageName + "/src"; + + // skip fetching if source directory already exists (from previous run or manual provisioning) + // clean existing source directory if cleanup is enabled + try { + const auto sourceDirectoryExists = filesystem::exists(buildData.sourceDirectory); + if (m_cleanSourceDirectory && sourceDirectoryExists) { + filesystem::remove_all(buildData.sourceDirectory); + } else if (sourceDirectoryExists) { + // verify the PKGBUILD file exists + const auto pkgbuildPath = buildData.sourceDirectory + "/PKGBUILD"; + if (!filesystem::exists(pkgbuildPath)) { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = packageName, + .error = "Existing source directory \"" % buildData.sourceDirectory + "\" does not contain a PKGBUILD file." }); + continue; + } + // generate the .SRCINFO file if not already present; otherwise read the existing .SRCINFO + const auto srcInfoPath = buildData.sourceDirectory + "/.SRCINFO"; + if (!filesystem::exists(srcInfoPath) || filesystem::last_write_time(srcInfoPath) < filesystem::last_write_time(pkgbuildPath)) { + makeSrcInfo(multiSession, buildData.sourceDirectory, packageName); + needToGeneratedSrcInfo = true; + } else { + string srcInfo; + try { + srcInfo = readFile(srcInfoPath, 0x10000); + } catch (const std::ios_base::failure &e) { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = packageName, + .error = "Unable to read .SRCINFO \"" % srcInfoPath % "\" from existing source directory: " + e.what() }); + continue; + } + addResultFromSrcInfo(*multiSession, packageName, srcInfo); + } + usingExistingSourceDirectories = true; + addPackageToLogLine(logLines[0], packageName); + addPackageToLogLine(logLines[2], packageName); + buildData.hasSource = true; + continue; + } + } catch (const filesystem::filesystem_error &e) { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = packageName, + .error = "Unable to check existing source directory \"" % buildData.sourceDirectory % "\": " + e.what() }); + continue; + } + + // clear the original source directory which is possibly still assigned from a previous run + buildData.originalSourceDirectory.clear(); + + // skip next block if package name matches configured pattern + LibPkg::PackageNameData packageNameData; + if (regex_match(packageName, m_ignoreLocalPkgbuildsRegex)) { + goto addAurQuery; + } + + // copy PKGBUILD from local PKGBUILDs directory and generate .SRCINFO from it via makepkg + if (!m_pkgbuildsDirs.empty()) { + packageNameData = LibPkg::PackageNameData::decompose(packageName); + } + for (const auto &pkgbuildsDir : m_pkgbuildsDirs) { + const auto variant = packageNameData.variant(); + auto pkgbuildPath = pkgbuildsDir % '/' % packageNameData.actualName % '/' % variant; + try { + if (!filesystem::exists(pkgbuildPath + "/PKGBUILD")) { + continue; + } + buildData.originalSourceDirectory = tupleToString(pkgbuildPath); + filesystem::create_directories(buildData.sourceDirectory); + filesystem::copy(buildData.originalSourceDirectory, buildData.sourceDirectory, std::filesystem::copy_options::recursive); + + } catch (const filesystem::filesystem_error &e) { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = packageName, + .error + = "Unable to copy files from PKGBUILDs directory " % pkgbuildsDir % " to " % buildData.sourceDirectory % ": " + e.what() }); + continue; + } + makeSrcInfo(multiSession, buildData.sourceDirectory, packageName); + needToGeneratedSrcInfo = true; + addPackageToLogLine(logLines[0], packageName); + buildData.hasSource = true; + break; + } + + addAurQuery: + // download latest snapshot containing PKGBUILD and .SRCINFO from AUR + if (!buildData.hasSource) { + snapshotQueries.emplace_back(WebClient::AurSnapshotQueryParams{ + .packageName = &packageName, + .targetDirectory = &buildData.sourceDirectory, + }); + addPackageToLogLine(logLines[1], packageName); + } + } + + // schedule async AUR downloads + if (!snapshotQueries.empty()) { + WebClient::queryAurSnapshots(m_buildAction->log(), m_setup, snapshotQueries, m_setup.building.ioContext, multiSession); + } + + // log status + if (!needToGeneratedSrcInfo) { + logLines[0].clear(); + } + if (snapshotQueries.empty()) { + logLines[1].clear(); + } + if (!usingExistingSourceDirectories) { + logLines[2].clear(); + } + m_buildAction->appendOutput(Phrases::InfoMessage, "Fetching missing build data\n", joinStrings(logLines, string(), true, string(), "\n")); +} + +bool PrepareBuild::pullFurtherDependencies(const std::vector &dependencies) +{ + // pull further depencencies; similar to initial dependency lookup in run() + auto dependencyAdded = false; + const auto *const destinationDb = *m_destinationDbs.begin(); + for (const auto &dependency : dependencies) { + auto dependencyExists = false; + + // skip if the dependency is already in the list of packages to be built (check for cycles is done later when computing batches) + if (m_buildDataByPackage.find(dependency.name) != m_buildDataByPackage.end()) { + continue; + } + for (const auto &[packageName, buildData] : m_buildDataByPackage) { + for (const auto &package : buildData.packages) { + if (package->providesDependency(dependency)) { + dependencyExists = true; + break; + } + } + if (dependencyExists) { + break; + } + } + if (dependencyExists) { + continue; + } + + // skip dependency if it is already present in one of the dependencies considered as "given" (see note in run() function) + auto packageBuildData = PackageBuildData(); + auto existingPackages = m_setup.config.findPackages(dependency); + for (auto &package : existingPackages) { + // skip if package not relevant + if (!isExistingPackageRelevant(dependency.name, package, packageBuildData, *destinationDb)) { + continue; + } + dependencyExists = true; + } + if (dependencyExists) { + continue; + } + + // assume the dependency is provided by a package with the pkgbase of the dependency denotation + // FIXME: support split packages + m_buildDataByPackage[dependency.name] = std::move(packageBuildData); + dependencyAdded = true; + } + return dependencyAdded; +} + +struct BatchItem { + struct MissingDependency { + LibPkg::Dependency dependency; + + LibPkg::Dependency requiredBy; + }; + + const std::string *const name; + const PackageBuildData *const buildData; + std::unordered_map> neededItems; + LibPkg::DependencySet requiredDependencies; + LibPkg::DependencySet providedDependencies; + std::vector missingDependencies; + bool done = false; + + void determineNeededItems(LibPkg::Config &config, const std::unordered_set &requiredDbs, const LibPkg::Database *destinationDb, + unordered_map &batchItems); + template + bool addNeededItemForRequiredDependencyFromOtherItems( + const unordered_map &otherItems, DependencyParams &&...requiredDependencyParams); + static bool addItemsToBatch(Batch ¤tBatch, BatchMap &items); + +private: + void addNeededBatchItemsForPackage(LibPkg::Config &config, const std::unordered_set &requiredDbs, const LibPkg::Database *destinationDb, + unordered_map &batchItems, unordered_set &visitedPackages, LibPkg::Package &package); +}; + +template +bool BatchItem::addNeededItemForRequiredDependencyFromOtherItems( + const unordered_map &otherItems, DependencyParams &&...requiredDependencyParams) +{ + // skip if the dependency of a package is provided by the package itself (can happen when dealing with split packages) + if (providedDependencies.provides(std::forward(requiredDependencyParams)...)) { + return true; + } + + // go through all the items to find one which provides the dependency; continue to look for alternatives as well + const BatchItem *relevantItem = nullptr; + std::unordered_set alternativeItems; + for (const auto &item : otherItems) { + if (&item.second == this) { + continue; + } + if (item.second.providedDependencies.provides(std::forward(requiredDependencyParams)...)) { + if (!relevantItem) { + relevantItem = &item.second; + } else { + alternativeItems.emplace(&item.second); + } + } + } + + if (relevantItem) { + // FIXME: actually, we need to handle the case when the item is already present as the alternative items might differ + neededItems.emplace(relevantItem, move(alternativeItems)); + return true; + } + return false; +} + +void BatchItem::determineNeededItems(LibPkg::Config &config, const std::unordered_set &requiredDbs, + const LibPkg::Database *destinationDb, unordered_map &batchItems) +{ + unordered_set visitedPackages; + + for (const auto &require : requiredDependencies) { + // check whether its provided directly by some other item + if (addNeededItemForRequiredDependencyFromOtherItems(batchItems, require.first, require.second)) { + continue; + } + + // check by which other package the item is provided + // skip dependency if it is already present in one of the dbs + auto dependency = LibPkg::Dependency(require.first, require.second.version, require.second.mode); + auto foundPackage = false; + const auto existingPackages = config.findPackages(dependency); + for (const auto &package : existingPackages) { + // skip if packge is not from any database available within that build + const auto *const db = std::get(package.db); + if (requiredDbs.find(db->name) == requiredDbs.end()) { + continue; + } + if (db->arch != destinationDb->arch) { + continue; + } + addNeededBatchItemsForPackage(config, requiredDbs, destinationDb, batchItems, visitedPackages, *package.pkg); + foundPackage = true; + } + if (!foundPackage) { + missingDependencies.emplace_back(MissingDependency{ .dependency = move(dependency), .requiredBy = LibPkg::Dependency(*name) }); + } + } +} + +bool BatchItem::addItemsToBatch(Batch ¤tBatch, BatchMap &items) +{ + auto allDone = true; + + for (auto &[packageName, batchItem] : items) { + // skip items which are already done + if (batchItem.done) { + continue; + } + + allDone = false; + + // skip if not all dependencies of the items are done + auto allDepsDone = true; + for (const auto &[neededItem, alternatives] : batchItem.neededItems) { + if (neededItem->done) { + continue; + } + auto oneAlternativeDone = false; + for (const auto &alternative : alternatives) { + if (alternative->done) { + oneAlternativeDone = true; + break; + } + } + if (oneAlternativeDone) { + continue; + } + allDepsDone = false; + break; + } + if (!allDepsDone) { + continue; + } + + // add item to current batch + currentBatch.emplace_back(&batchItem); + } + + return allDone; +} + +void BatchItem::addNeededBatchItemsForPackage(LibPkg::Config &config, const std::unordered_set &requiredDbs, + const LibPkg::Database *destinationDb, unordered_map &batchItems, unordered_set &visitedPackages, + LibPkg::Package &package) +{ + // stop if there's a cycle (not a critical error here; we've just seen enough; only cycles between packages we actually want to build are interesting) + const auto [i, notVisitedBefore] = visitedPackages.emplace(&package); + if (!notVisitedBefore) { + return; + } + + // check whether the dependencies of that package lead to another build item + for (const auto &dep : package.dependencies) { + // check whether the dependency is directly one of the build items + if (addNeededItemForRequiredDependencyFromOtherItems(batchItems, dep)) { + continue; + } + + // check other packages recursively + auto foundPackage = false; + const auto existingPackages = config.findPackages(dep); + for (const auto &package : existingPackages) { + // skip if packge is not from any database available within that build + const auto *const db = std::get(package.db); + if (requiredDbs.find(db->name) == requiredDbs.end()) { + continue; + } + if (db->arch != destinationDb->arch) { + continue; + } + addNeededBatchItemsForPackage(config, requiredDbs, destinationDb, batchItems, visitedPackages, *package.pkg); + foundPackage = true; + } + if (!foundPackage) { + missingDependencies.emplace_back(MissingDependency{ .dependency = dep, .requiredBy = LibPkg::Dependency(package.name) }); + } + } +} + +static bool sortBatch(const BatchItem *lhs, const BatchItem *rhs) +{ + if (lhs->buildData->specifiedIndex != numeric_limits::max() && rhs->buildData->specifiedIndex != numeric_limits::max()) { + return lhs->buildData->specifiedIndex < rhs->buildData->specifiedIndex; + } else if (lhs->buildData->specifiedIndex != numeric_limits::max()) { + return true; + } else if (rhs->buildData->specifiedIndex != numeric_limits::max()) { + return false; + } else { + return *lhs->name < *rhs->name; + } +} + +void PrepareBuild::bumpVersions() +{ + // bump pkgrel or even epoch to ensure the new packages are actually considered newer by pacman when updating + if (m_keepPkgRelAndEpoch) { + return; + } + for (const auto &[packageName, buildData] : m_buildDataByPackage) { + auto &existingVersionStr = buildData.existingVersion; + if (existingVersionStr.empty() && !m_forceBumpPackageVersion) { + continue; + } + auto existingVersion = existingVersionStr.empty() ? LibPkg::PackageVersion{} : LibPkg::PackageVersion::fromString(existingVersionStr); + LibPkg::PackageAmendment amendment; + LibPkg::PackageVersion newVersion; + for (const auto &package : buildData.packages) { + newVersion = LibPkg::PackageVersion::fromString(package->version); + if (existingVersionStr.empty()) { + existingVersion = newVersion; + break; + } + switch (newVersion.compare(existingVersion)) { + case LibPkg::PackageVersionComparison::Equal: + case LibPkg::PackageVersionComparison::PackageUpgradeOnly: + amendment.bumpDownstreamVersion = LibPkg::PackageAmendment::VersionBump::PackageVersion; + m_warnings.emplace_back("Bumping pkgrel of " % package->name % "; version " % existingVersionStr + " already exists"); + break; + case LibPkg::PackageVersionComparison::SoftwareUpgrade: + if (package->decomposeName().isVcsPackage()) { + // skip bumping epoch of VCS packages; the pkgver is supposed to be adjusted in pkgver() + // note: Not skipping this in the pkgrel handling in the case above because when pkgver() returns a new version, pkgrel is + // resetted automatically so there's no harm in bumping it and it might actually be needed if there's no no version. + m_warnings.emplace_back("Version of package " % package->name % " is" % package->version + % " which is older than existing version " % existingVersionStr + + "; NOT bumping the epoch because it is a VCS package; be sure pkgver() actually yields a new version"); + break; + } + amendment.bumpDownstreamVersion = LibPkg::PackageAmendment::VersionBump::Epoch; + m_warnings.emplace_back("Bumping epoch of " % package->name % "; its version " % package->version % " is older than existing version " + + existingVersionStr); + goto breakLoop; + default:; + } + } + breakLoop: + if (m_forceBumpPackageVersion && amendment.isEmpty()) { + amendment.bumpDownstreamVersion = LibPkg::PackageAmendment::VersionBump::PackageVersion; + m_warnings.emplace_back("Bumping pkgrel of " % packageName + " (and its split packages); forcing pkgrel bump has been enabled"); + } + if (!amendment.isEmpty()) { + auto amendedVersions = LibPkg::amendPkgbuild(buildData.sourceDirectory + "/PKGBUILD", existingVersion, amendment); + if (!amendedVersions.newEpoch.empty()) { + newVersion.epoch = std::move(amendedVersions.newEpoch); + } + if (!amendedVersions.newPkgRel.empty()) { + newVersion.package = std::move(amendedVersions.newPkgRel); + } + const auto newVersionStr = newVersion.toString(); + for (const auto &package : buildData.packages) { + package->version = newVersionStr; + } + m_warnings.emplace_back("New version of " % packageName % " (and its split packages) is " + newVersionStr); + } + } +} + +void PrepareBuild::computeDependencies(WebClient::AurSnapshotQuerySession::ContainerType &&responses) +{ + if (reportAbortedIfAborted()) { + return; + } + + // find databases again + auto configReadLock = m_setup.config.lockToRead(); + if (auto error = findDatabases(); !error.empty()) { + reportError(move(error)); + return; + } + + // populate build data from responses and add further dependencies + auto furtherDependenciesNeeded = false; + auto sourcesMissing = false; + for (auto &response : responses) { + if (response.packageName.empty()) { + auto &buildData = m_buildDataByPackage["?"]; + auto &error = buildData.error; + if (!error.empty()) { + error += '\n'; + } + error = response.error.empty() ? "got response with no package name (internal error)" : move(response.error); + sourcesMissing = true; + continue; + } + auto &buildData = m_buildDataByPackage[response.packageName]; + buildData.sourceInfo = move(response.sourceInfo); + buildData.packages = move(response.packages); + buildData.error = move(response.error); + buildData.hasSource = buildData.sourceInfo && !buildData.packages.empty() && buildData.error.empty(); + if (buildData.hasSource) { + const auto buildActionsWriteLock = m_setup.building.lockToWrite(); + m_buildAction->artefacts.emplace_back(buildData.sourceDirectory + "/PKGBUILD"); // FIXME: add all files as artefacts + continue; + } + if (buildData.error.empty()) { + buildData.error = "no build data available"; + } + sourcesMissing = true; + } + for (auto &response : responses) { + if (response.packageName.empty()) { + continue; + } + auto &buildData = m_buildDataByPackage[response.packageName]; + if (!buildData.hasSource) { + continue; + } + furtherDependenciesNeeded = pullFurtherDependencies(buildData.sourceInfo->makeDependencies) || furtherDependenciesNeeded; + furtherDependenciesNeeded = pullFurtherDependencies(buildData.sourceInfo->checkDependencies) || furtherDependenciesNeeded; + for (const auto &package : buildData.packages) { + furtherDependenciesNeeded = pullFurtherDependencies(package->dependencies) || furtherDependenciesNeeded; + } + } + configReadLock.unlock(); + + if (reportAbortedIfAborted()) { + return; + } + + // pull missing dependencies if all sources could be retrieved so far + if (!sourcesMissing && furtherDependenciesNeeded) { + fetchMissingBuildData(); + return; + } + + // check for errors + set failedPackages; + for (const auto &buildData : m_buildDataByPackage) { + if (!buildData.second.error.empty()) { + failedPackages.emplace(buildData.first); + } + } + if (!failedPackages.empty()) { + auto errorMessage = "Unable to retrieve the following packages (see result data for details): " + joinStrings(failedPackages, " "); + m_buildAction->appendOutput(Phrases::ErrorMessage, errorMessage, '\n'); + auto resultData = makeResultData(std::move(errorMessage)); + auto buildActionWriteLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(resultData); + reportError(); + return; + } + + bumpVersions(); + + if (reportAbortedIfAborted()) { + return; + } + + if (m_keepOrder) { + deduceBatchesFromSpecifiedOrder(); + } else { + computeBatches(); + } + + auto resultData = makeResultData(); + auto buildActionWriteLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(resultData); + if (resultData.error.empty() && resultData.cyclicLeftovers.empty()) { + reportSuccess(); + } else { + reportError(); + } +} + +std::unordered_map PrepareBuild::prepareBatches() +{ + std::unordered_map batchItems; + for (const auto &[packageName, buildData] : m_buildDataByPackage) { + auto [i, newItem] = batchItems.try_emplace(packageName, BatchItem{ .name = &packageName, .buildData = &buildData }); + auto &batchItem = i->second; + const auto &sourceInfo = buildData.sourceInfo; + for (const auto &package : buildData.packages) { + for (const auto &deps : { sourceInfo->makeDependencies, sourceInfo->checkDependencies, package->dependencies }) { + for (const auto &dep : deps) { + batchItem.requiredDependencies.add(dep, package); + } + } + for (auto &dep : package->provides) { + batchItem.providedDependencies.add(dep, package); + } + } + } + return batchItems; +} + +void PrepareBuild::deduceBatchesFromSpecifiedOrder() +{ + m_buildAction->appendOutput(Phrases::InfoMessage, "Fetched sources; making batches for specified order ...\n"sv); + + auto batchItems = prepareBatches(); + BatchList batches; + for (auto &[packageName, batchItem] : batchItems) { + batches.emplace_back(std::vector{ &batchItem }); + } + std::sort(batches.begin(), batches.end(), [](const Batch &lhs, const Batch &rhs) { return sortBatch(lhs.front(), rhs.front()); }); + addBatchesToResult(std::move(batches), Batch()); +} + +void PrepareBuild::computeBatches() +{ + m_buildAction->appendOutput(Phrases::InfoMessage, "Fetched sources; computing build batches ...\n"sv); + + // prepare computing batches + auto batchItems = prepareBatches(); + auto configReadLock2 = m_setup.config.lockToRead(); + if (auto error = findDatabases(); !error.empty()) { + reportError(move(error)); + return; + } + + // add dependency relations to batch items + for (auto &batchItem : batchItems) { + batchItem.second.determineNeededItems(m_setup.config, m_requiredDbs, *m_destinationDbs.begin(), batchItems); + } + configReadLock2.unlock(); + + // FIXME: check for missing dependencies + + // add batch items into batches + BatchList batches; + bool allDone; + size_t addedItems; + do { + if (reportAbortedIfAborted()) { + return; + } + auto &newBatch = batches.emplace_back(); + allDone = BatchItem::addItemsToBatch(newBatch, batchItems); + addedItems = newBatch.size(); + if (!addedItems) { + batches.pop_back(); + } + for (auto &addedItem : newBatch) { + addedItem->done = true; + } + } while (!allDone && addedItems); + + // check for cyclic leftovers + Batch cyclicLeftovers; + if (!allDone) { + for (auto &item : batchItems) { + if (!item.second.done) { + cyclicLeftovers.emplace_back(&item.second); + } + } + if (!cyclicLeftovers.empty()) { + m_warnings.emplace_back("Dependency cycles have been detected. Add a boostrap package to the package list to resolve this."); + } + } + + // sort batches so the order in which packages have been specified is preserved within the batches + for (auto &batch : batches) { + std::sort(batch.begin(), batch.end(), sortBatch); + } + std::sort(cyclicLeftovers.begin(), cyclicLeftovers.end(), sortBatch); + + // add batches to result + addBatchesToResult(std::move(batches), std::move(cyclicLeftovers)); +} + +void PrepareBuild::addBatchesToResult(BatchList &&batches, Batch &&cyclicLeftovers) +{ + const auto returnItemName = [](const BatchItem *item) { return *item->name; }; + m_batches.reserve(batches.size()); + std::transform(batches.cbegin(), batches.cend(), std::back_inserter(m_batches), [&returnItemName](const Batch &batch) { + std::vector newBatch; + newBatch.reserve(batch.size()); + std::transform(batch.cbegin(), batch.cend(), std::back_inserter(newBatch), returnItemName); + return newBatch; + }); + m_cyclicLeftovers.reserve(cyclicLeftovers.size()); + std::transform(cyclicLeftovers.cbegin(), cyclicLeftovers.cend(), std::back_inserter(m_cyclicLeftovers), returnItemName); +} + +BuildPreparation PrepareBuild::makeResultData(std::string &&error) +{ + auto resultData = BuildPreparation{ + .buildData = std::move(m_buildDataByPackage), + .dbConfig = std::move(m_dbConfig), + .stagingDbConfig = std::move(m_stagingDbConfig), + .targetDb = std::move(m_targetDbName), + .targetArch = std::move(m_targetArch), + .stagingDb = std::move(m_stagingDbName), + .batches = std::move(m_batches), + .cyclicLeftovers = std::move(m_cyclicLeftovers), + .warnings = std::move(m_warnings), + .error = std::move(error), + .manuallyOrdered = m_keepOrder, + }; + + // write results into the working directory so the actual build can pick it up + try { + dumpJsonDocument( + [this, &resultData] { + const auto configLock = m_setup.config.lockToRead(); + return resultData.toJsonDocument(); + }, + m_workingDirectory, buildPreparationFileName, DumpJsonExistingFileHandling::Backup); + } catch (const std::runtime_error &e) { + const auto what = string_view(e.what()); + if (resultData.error.empty()) { + resultData.error = what; + } else { + resultData.warnings.emplace_back(what); + } + m_buildAction->appendOutput(Phrases::ErrorMessage, what, '\n'); + } + + // write build progress skeletion (a BuildProgress with a default-initialized PackageBuildProgress for each package) + try { + BuildProgress progress; + try { + restoreJsonObject(progress, m_workingDirectory, buildProgressFileName, RestoreJsonExistingFileHandling::Skip); + } catch (const std::runtime_error &e) { + m_buildAction->appendOutput(Phrases::ErrorMessage, "Unable to read existing build-progress.json: ", e.what(), '\n'); + } + if (dumpJsonDocument( + [this, &resultData, &progress] { + // reset database-specific fields as the database configuration might have changed + progress.targetDbFilePath.clear(); + progress.targetRepoPath.clear(); + progress.stagingDbFilePath.clear(); + progress.stagingRepoPath.clear(); + for (const auto &[packageName, buildData] : resultData.buildData) { + auto &buildProgress = progress.progressByPackage[packageName]; + // reset the build progress if the PKGBUILD has been updated + if (!buildProgress.hasBeenAnyProgressMade()) { + continue; + } + if (buildProgress.buildDirectory.empty()) { + buildProgress.reset(); + continue; + } + const std::filesystem::path srcDirPkgbuild = buildData.sourceDirectory + "/PKGBUILD"; + const std::filesystem::path buildDirPkgbuild = buildProgress.buildDirectory + "/PKGBUILD"; + try { + if (!std::filesystem::exists(buildDirPkgbuild) + || std::filesystem::last_write_time(srcDirPkgbuild) > std::filesystem::last_write_time(buildDirPkgbuild)) { + buildProgress.reset(); + } + } catch (const std::filesystem::filesystem_error &e) { + m_buildAction->appendOutput( + Phrases::ErrorMessage, "Unable to determine whether PKGBUILD has been updated: ", e.what(), '\n'); + } + } + return progress.toJsonDocument(); + }, + m_workingDirectory, buildProgressFileName, DumpJsonExistingFileHandling::Backup) + .empty()) { + m_buildAction->appendOutput(Phrases::ErrorMessage, "Unable to write build-progress.json\n"sv); + } + } catch (const std::runtime_error &e) { + const auto what = string_view(e.what()); + resultData.warnings.emplace_back(what); + m_buildAction->appendOutput(Phrases::ErrorMessage, what); + } + + return resultData; +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/reloadconfiguration.cpp b/librepomgr/buildactions/reloadconfiguration.cpp new file mode 100644 index 0000000..c823b7d --- /dev/null +++ b/librepomgr/buildactions/reloadconfiguration.cpp @@ -0,0 +1,37 @@ +#include "./buildactionprivate.h" + +#include "../serversetup.h" + +namespace LibRepoMgr { + +ReloadConfiguration::ReloadConfiguration(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void ReloadConfiguration::run() +{ + auto configLock = init(BuildActionAccess::WriteConfig, RequiredDatabases::None, RequiredParameters::None); + if (std::holds_alternative(configLock)) { + return; + } + + m_setup.config.markAllDatabasesToBeDiscarded(); + auto setupLock = m_setup.lockToWrite(); + m_setup.auth.users.clear(); + m_setup.loadConfigFiles(false); + setupLock.unlock(); + m_setup.config.discardDatabases(); + std::get>(configLock).unlock(); + + { + const auto buildActionLock = m_setup.building.lockToWrite(); + reportSuccess(); + } + { + const auto configReadLock = m_setup.config.lockToRead(); + m_setup.printDatabases(); + } +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/reloaddatabase.cpp b/librepomgr/buildactions/reloaddatabase.cpp new file mode 100644 index 0000000..aad3f84 --- /dev/null +++ b/librepomgr/buildactions/reloaddatabase.cpp @@ -0,0 +1,112 @@ +#include "./buildactionprivate.h" + +#include "../helper.h" +#include "../logging.h" +#include "../serversetup.h" + +#include "../webclient/database.h" + +#include "../webapi/params.h" + +#include +#include + +#include + +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +ReloadDatabase::ReloadDatabase(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void ReloadDatabase::run() +{ + const auto withFiles = m_setup.building.loadFilesDbs; + vector dbsToLoadFromMirror; + + auto configReadLock + = init(BuildActionAccess::ReadConfig, RequiredDatabases::MaybeDestination | RequiredDatabases::AllowToAur, RequiredParameters::None); + if (holds_alternative(configReadLock)) { + return; + } + + auto session + = WebClient::DatabaseQuerySession::create(m_setup.building.ioContext, [this](WebClient::DatabaseQuerySession::ContainerType &&failedDbs) { + if (!m_preparationFailures.empty()) { + mergeSecondVectorIntoFirstVector(failedDbs, m_preparationFailures); + } + if (!failedDbs.empty()) { + m_hasError = true; + reportError("Failed to reload the following databases, see logs for details: " + joinStrings(failedDbs, ", ")); + return; + } + if (m_hasError) { + reportError("Errors occurred, see output log for details."); + return; + } + auto buildActionWriteLock = m_setup.building.lockToWrite(); + reportSuccess(); + }); + + for (const auto &db : m_destinationDbs) { + // add database for syncing with mirror + if (db->syncFromMirror) { + dbsToLoadFromMirror.emplace_back(db); + continue; + } + // post job to reload database + auto dbPath = withFiles ? db->filesPath : db->path; + if (dbPath.empty()) { + m_hasError = true; + m_buildAction->appendOutput( + Phrases::ErrorMessage, "Unable to reload database database ", db->name, '@', db->arch, ": no path configured\n"); + continue; + } + boost::asio::post( + m_setup.building.ioContext.get_executor(), [this, session, dbName = db->name, dbArch = db->arch, dbPath = move(dbPath)]() mutable { + m_buildAction->appendOutput( + Phrases::InfoMessage, "Loading database \"", dbName, '@', dbArch, "\" from local file \"", dbPath, "\"\n"); + try { + const auto lastModified = LibPkg::lastModified(dbPath); + auto dbFile = LibPkg::extractFiles(dbPath, &LibPkg::Database::isFileRelevant); + auto packages = LibPkg::Package::fromDatabaseFile(move(dbFile)); + auto lock = m_setup.config.lockToWrite(); + auto db = m_setup.config.findDatabase(dbName, dbArch); + if (!db) { + m_buildAction->appendOutput( + Phrases::ErrorMessage, "Loaded database file for \"", dbName, '@', dbArch, "\" but it no longer exists; discarding\n"); + session->addResponse(std::move(dbName)); + return; + } + db->replacePackages(packages, lastModified); + } catch (const std::runtime_error &e) { + m_buildAction->appendOutput(Phrases::ErrorMessage, "An error occurred when reloading database \"", dbName, '@', dbArch, + "\" from local file \"", dbPath, "\": ", e.what(), '\n'); + session->addResponse(std::move(dbName)); + } + }); + } + + // query databases + auto query = WebClient::prepareDatabaseQuery(m_buildAction->log(), dbsToLoadFromMirror, withFiles); + std::get>(configReadLock).unlock(); + WebClient::queryDatabases(m_buildAction->log(), m_setup, std::move(query.queryParamsForDbs), session); + m_preparationFailures = std::move(query.failedDbs); + + // clear AUR cache + if (m_toAur) { + auto lock = m_setup.config.lockToWrite(); + m_setup.config.aur.clearPackages(); + lock.unlock(); + m_buildAction->log()(Phrases::InfoMessage, "Cleared AUR cache\n"); + } +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/reloadlibrarydependencies.cpp b/librepomgr/buildactions/reloadlibrarydependencies.cpp new file mode 100644 index 0000000..27bd6ab --- /dev/null +++ b/librepomgr/buildactions/reloadlibrarydependencies.cpp @@ -0,0 +1,355 @@ +#include "./buildactionprivate.h" + +#include "../logging.h" +#include "../serversetup.h" + +#include "../../libpkg/data/database.h" +#include "../../libpkg/data/package.h" +#include "../../libpkg/parser/utils.h" + +#include + +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +ReloadLibraryDependencies::ReloadLibraryDependencies(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void ReloadLibraryDependencies::run() +{ + // initialize + const auto flags = static_cast(m_buildAction->flags); + const auto force = flags & ReloadLibraryDependenciesFlags::ForceReload; + const auto skipDependencies = flags & ReloadLibraryDependenciesFlags::SkipDependencies; + m_remainingPackages = 0; + auto configReadLock = init(BuildActionAccess::ReadConfig, RequiredDatabases::MaybeDestination, RequiredParameters::None); + if (holds_alternative(configReadLock)) { + return; + } + + // use cache directory from global configuration + auto buildLock = m_setup.building.lockToRead(); + const auto cacheDir = m_setup.building.packageCacheDir + '/'; + buildLock.unlock(); + + // find relevant databases and packages + m_buildAction->appendOutput(Phrases::SuccessMessage, "Finding relevant databases/packages ...\n"); + m_relevantPackagesByDatabase.reserve(m_destinationDbs.empty() ? m_setup.config.databases.size() : m_destinationDbs.size()); + std::unordered_set relevantDbs; + std::unordered_set relevantPkgs; + LibPkg::DependencySet missingDeps; + if (m_destinationDbs.empty()) { + for (auto &db : m_setup.config.databases) { + relevantDbs.emplace(&db); + } + } else { + for (auto *const destinationDb : m_destinationDbs) { + if (!relevantDbs.emplace(destinationDb).second || skipDependencies) { + continue; + } + const auto databaseDependencyOrderRes = m_setup.config.computeDatabaseDependencyOrder(*destinationDb); + if (holds_alternative(databaseDependencyOrderRes)) { + m_messages.errors.emplace_back( + destinationDb->name % ": unable to consider dependencies: " + std::get(databaseDependencyOrderRes)); + } + auto &databaseDependencyOrder = std::get>(databaseDependencyOrderRes); + for (auto *const destinationDbOrDependency : databaseDependencyOrder) { + relevantDbs.emplace(destinationDbOrDependency); + } + } + for (auto *const destinationDb : m_destinationDbs) { + for (const auto &[packageName, package] : destinationDb->packages) { + m_setup.config.pullDependentPackages(package, relevantDbs, relevantPkgs, missingDeps); + } + } + } + for (const auto &[dependencyName, dependencyDetail] : missingDeps) { + std::vector packageNames; + packageNames.reserve(dependencyDetail.relevantPackages.size()); + for (const auto &package : dependencyDetail.relevantPackages) { + packageNames.emplace_back(package->name); + } + m_messages.warnings.emplace_back( + "dependency " % dependencyName % " missing, required by " + joinStrings(packageNames, ", ")); + } + for (auto *const db : relevantDbs) { + const auto isDestinationDb = m_destinationDbs.empty() || m_destinationDbs.find(db) != m_destinationDbs.end(); + auto &relevantDbInfo = m_relevantPackagesByDatabase.emplace_back(DatabaseToConsider{ .name = db->name, .arch = db->arch }); + relevantDbInfo.packages.reserve(db->packages.size()); + for (const auto &[packageName, package] : db->packages) { + // allow aborting the build action + if (reportAbortedIfAborted()) { + return; + } + // skip if the package info is missing (we need the binary package's file name here) + const auto &packageInfo = package->packageInfo; + if (!packageInfo) { + m_messages.errors.emplace_back(db->name % '/' % packageName + ": no package info"); + continue; + } + // skip the package if it is not part of the destination DB or required by a package of the destination DB + if (!isDestinationDb && relevantPkgs.find(package.get()) == relevantPkgs.end()) { + if (m_skippingNote.tellp()) { + m_skippingNote << ", "; + } + m_skippingNote << db->name << '/' << packageName; + continue; + } + // find the package on disk; otherwise add an URL to download it from the configured mirror + std::string path, url, cachePath; + std::error_code ec; + const auto &fileName = packageInfo->fileName; + const auto &arch = packageInfo->arch; + if (!db->localPkgDir.empty()) { + path = db->localPkgDir % '/' + fileName; + } else if (std::filesystem::exists(cachePath = cacheDir + fileName, ec)) { + path = std::move(cachePath); + } else if (std::filesystem::exists(cachePath = cacheDir % arch % '/' + fileName, ec)) { + path = std::move(cachePath); + } else { + for (const auto &cachePath : m_setup.config.packageCacheDirs) { + std::error_code ec; + if (std::filesystem::exists(path = cachePath % '/' + fileName, ec)) { + break; + } + path.clear(); + } + } + if (path.empty() && !db->mirrors.empty()) { + const auto &mirror = db->mirrors.front(); // just use the first mirror for now + if (startsWith(mirror, "file:")) { + std::error_code ec; + const auto canonPath + = std::filesystem::canonical(argsToString(std::string_view(mirror.data() + 5, mirror.size() - 5), '/', fileName), ec); + if (!ec) { + path = canonPath.string(); + } + } else { + path = std::move(cachePath); + url = mirror % (endsWith(mirror, "/") ? std::string() : "/") + fileName; + } + } + if (path.empty()) { + m_messages.errors.emplace_back(db->name % '/' % packageName + ": binary package not found and no mirror configured"); + continue; + } + // skip if the package info has already been loaded from package contents and the present binary package is not newer + auto lastModified = DateTime(); + if (url.empty()) { + lastModified = LibPkg::lastModified(path); + if (!force && package->origin == LibPkg::PackageOrigin::PackageContents && package->timestamp >= lastModified) { + m_messages.notes.emplace_back(db->name % '/' % packageName % ": skipping because \"" % path % "\" is newer (" + % package->timestamp.toString() % " >= " % lastModified.toString() + + ")\n"); + continue; + } + } + // add the full path to the binary package to relevant packages + auto &relevantPkg = relevantDbInfo.packages.emplace_back( + PackageToConsider{ .path = std::move(path), .url = std::move(url), .lastModified = lastModified }); + // create a temporary package object to hold the info parsed from the .PKGINFO file + relevantPkg.info.name = package->name; + // -> assing certain fields which are used by addDepsAndProvidesFromOtherPackage() to check whether the packages are matching + relevantPkg.info.version = package->version; + relevantPkg.info.packageInfo = std::make_unique(); + relevantPkg.info.packageInfo->buildDate = package->packageInfo->buildDate; + // -> gather source info such as make and check dependencies as well + relevantPkg.info.sourceInfo = std::make_shared(); + ++m_remainingPackages; + } + } + configReadLock = std::monostate{}; + + m_buildAction->appendOutput(Phrases::SubMessage, "Found ", m_remainingPackages.load(), "\n"); + + // add note about skipped packages + if (m_skippingNote.tellp()) { + m_skippingNote << ": not required by any destination DB, skipping download"; + m_messages.notes.emplace_back(m_skippingNote.str()); + m_skippingNote = std::stringstream(); + } + + // stop here if no relevant packages were found + if (m_relevantPackagesByDatabase.empty() || !m_remainingPackages) { + conclude(); + return; + } + + downloadPackagesFromMirror(); +} + +void LibRepoMgr::ReloadLibraryDependencies::downloadPackagesFromMirror() +{ + // prepare caching data + std::size_t packagesWhichNeedCaching = 0; + for (const auto &db : m_relevantPackagesByDatabase) { + for (const auto &pkg : db.packages) { + if (!pkg.url.empty()) { + auto &cachingData = m_cachingData[db.name][pkg.info.name]; + cachingData.url = pkg.url; + cachingData.destinationFilePath = pkg.path; + ++packagesWhichNeedCaching; + } + } + } + + // skip caching if not required + if (!packagesWhichNeedCaching) { + loadPackageInfoFromContents(); + return; + } + + // allow aborting the build action + if (reportAbortedIfAborted()) { + return; + } + + m_buildAction->appendOutput(Phrases::SuccessMessage, "Downloading ", packagesWhichNeedCaching, " binary packages from mirror ...\n"); + WebClient::cachePackages(m_buildAction->log(), + std::make_shared(m_cachingData, m_setup.building.ioContext, m_setup.webServer.sslContext, + std::bind(&ReloadLibraryDependencies::loadPackageInfoFromContents, this))); +} + +void ReloadLibraryDependencies::loadPackageInfoFromContents() +{ + // allow aborting the build action + if (reportAbortedIfAborted()) { + return; + } + + // load info from package contents utilizing hardware concurrency + std::mutex nextPackageMutex, submitErrorMutex; + auto dbIterator = m_relevantPackagesByDatabase.begin(), dbEnd = m_relevantPackagesByDatabase.end(); + auto pkgIterator = dbIterator->packages.begin(), pkgEnd = dbIterator->packages.end(); + m_buildAction->appendOutput(Phrases::SuccessMessage, "Parsing ", m_remainingPackages.load(), " binary packages ...\n"); + const auto processPackage = [this, &dbIterator, &dbEnd, &pkgIterator, &pkgEnd, &nextPackageMutex, &submitErrorMutex] { + for (; !m_buildAction->isAborted();) { + // get the next package + std::unique_lock nextPackagelock(nextPackageMutex); + while (pkgIterator == pkgEnd) { + if (dbIterator == dbEnd || ++dbIterator == dbEnd) { + return; + } + pkgIterator = dbIterator->packages.begin(); + pkgEnd = dbIterator->packages.end(); + } + const auto ¤tDb = *dbIterator; + auto ¤tPkg = *pkgIterator; + // increment the current package + ++pkgIterator; + // allow other threads to get a package as well + nextPackagelock.unlock(); + + // log progress + m_buildAction->appendOutput( + Phrases::InfoMessage, m_remainingPackages--, " packages remaining to parse, next package: ", currentPkg.path, '\n'); + + // check whether the package could be cached from the mirror and skip it with an error if not + if (!currentPkg.url.empty()) { + if (auto db = m_cachingData.find(currentDb.name); db != m_cachingData.end()) { + if (auto pkg = db->second.find(currentPkg.info.name); pkg != db->second.end()) { + auto &packageCachingInfo = pkg->second; + if (!packageCachingInfo.error.empty()) { + std::unique_lock submitErrorLock(submitErrorMutex); + m_messages.errors.emplace_back(currentDb.name % '/' % currentPkg.info.name % ':' % ' ' + packageCachingInfo.error); + continue; + } + } + } + } + + // extract the binary package's files + try { + std::set dllsReferencedByImportLibs; + LibPkg::walkThroughArchive( + currentPkg.path, &LibPkg::Package::isPkgInfoFileOrBinary, + [¤tPkg, &dllsReferencedByImportLibs](std::string &&directoryPath, LibPkg::ArchiveFile &&file) { + if (directoryPath.empty() && file.name == ".PKGINFO") { + currentPkg.info.addInfoFromPkgInfoFile(file.content); + return; + } + currentPkg.info.addDepsAndProvidesFromContainedFile(file, dllsReferencedByImportLibs); + }, + [¤tPkg](std::string &&directoryPath) { + if (directoryPath.empty()) { + return; + } + currentPkg.info.addDepsAndProvidesFromContainedDirectory(directoryPath); + }); + currentPkg.info.processDllsReferencedByImportLibs(std::move(dllsReferencedByImportLibs)); + currentPkg.info.origin = LibPkg::PackageOrigin::PackageContents; + } catch (const std::runtime_error &e) { + std::unique_lock submitErrorLock(submitErrorMutex); + m_messages.errors.emplace_back(currentDb.name % '/' % currentPkg.info.name % ':' % ' ' + e.what()); + } + } + }; + auto threads = std::vector(std::thread::hardware_concurrency() - 1); + for (std::thread &t : threads) { + t = std::thread(processPackage); + } + processPackage(); + for (std::thread &t : threads) { + t.join(); + } + + // store the information in the database + m_buildAction->appendOutput(Phrases::SuccessMessage, "Adding parsed information to databases ...\n"); + std::size_t counter = 0; + auto configWritelock = m_setup.config.lockToWrite(); + for (DatabaseToConsider &relevantDb : m_relevantPackagesByDatabase) { + auto *const db = m_setup.config.findDatabase(relevantDb.name, relevantDb.arch); + if (!db) { + continue; // the whole database has been removed while we were loading package contents + } + for (PackageToConsider &package : relevantDb.packages) { + // skip if package info could not be parsed from package contents + if (package.info.origin != LibPkg::PackageOrigin::PackageContents) { + continue; + } + // find the package in the database again + const auto packageIterator = db->packages.find(package.info.name); + if (packageIterator == db->packages.end()) { + continue; // the package has been removed while we were loading package contents + } + // remove the current dependencies on database level + db->removePackageDependencies(packageIterator); + // add the dependencies/provides to the existing package + const auto &existingPackage = packageIterator->second; + if (!existingPackage->addDepsAndProvidesFromOtherPackage(package.info)) { + continue; // the package does no longer match what's in the database + } + // update timestamp so we can skip this package on the next run + if (existingPackage->timestamp < package.lastModified) { + existingPackage->timestamp = package.lastModified; + } + // add the new dependencies on database-level + db->addPackageDependencies(existingPackage); + ++counter; + } + } + configWritelock.unlock(); + + m_buildAction->appendOutput(Phrases::SuccessMessage, "Added dependency information for ", counter, " packages\n"); + conclude(); +} + +void ReloadLibraryDependencies::conclude() +{ + if (reportAbortedIfAborted()) { + return; + } + const auto result = m_messages.errors.empty() ? BuildActionResult::Success : BuildActionResult::Failure; + const auto buildActionWriteLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(m_messages); + reportResult(result); +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/repomanagement.cpp b/librepomgr/buildactions/repomanagement.cpp new file mode 100644 index 0000000..03d2271 --- /dev/null +++ b/librepomgr/buildactions/repomanagement.cpp @@ -0,0 +1,723 @@ +#include "./buildactionprivate.h" + +#include "../logging.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace std::literals::string_view_literals; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { + +PackageMovementAction::PackageMovementAction(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +bool PackageMovementAction::prepareRepoAction(RequiredDatabases requiredDatabases) +{ + // initialize build action + auto configReadLock = init(BuildActionAccess::ReadConfig, requiredDatabases | RequiredDatabases::OneDestination, RequiredParameters::Packages); + if (std::holds_alternative(configReadLock)) { + return false; + } + + auto setupLock = m_setup.lockToRead(); + m_repoRemovePath = findExecutable(m_setup.building.repoRemovePath); + if (requiredDatabases & RequiredDatabases::OneSource) { + m_repoAddPath = findExecutable(m_setup.building.repoAddPath); + } + setupLock.unlock(); + + // check executables + if (!checkExecutable(m_repoRemovePath)) { + reportError("Unable to find repo-remove executable \"" % m_setup.building.repoRemovePath + "\" in PATH."); + return false; + } + if (requiredDatabases & RequiredDatabases::OneSource && !checkExecutable(m_repoAddPath)) { + reportError("Unable to find repo-add executable \"" % m_setup.building.repoAddPath + "\" in PATH."); + return false; + } + + // locate databases and packages + const auto *const destinationDb = *m_destinationDbs.begin(); + m_destinationRepoDirectory = destinationDb->localPkgDir; + m_destinationDatabaseFile = fileName(destinationDb->path); + if (requiredDatabases & RequiredDatabases::OneSource) { + const auto *const sourceDb = *m_sourceDbs.begin(); + m_sourceRepoDirectory = sourceDb->localPkgDir; + m_sourceDatabaseFile = fileName(sourceDb->path); + } + locatePackages(); + configReadLock = std::monostate(); + + // error-out early if not even a single package could be located + if (m_packageLocations.empty()) { + m_result.errorMessage = "none of the specified packages could be located"; + reportResultWithData(BuildActionResult::Failure); + return false; + } + + // init working directory + initWorkingDirectory(); + if (!m_result.errorMessage.empty()) { + reportResultWithData(BuildActionResult::Failure); + return false; + } + return true; +} + +void PackageMovementAction::initWorkingDirectory() +{ + if (m_buildAction->directory.empty()) { + auto directory = argsToString(m_buildAction->type == BuildActionType::MovePackages ? "repo-move-" : "repo-remove-", + DateTime::gmtNow().toIsoStringWithCustomDelimiters(TimeSpan(), '-', '-'), '-', + std::filesystem::path(m_destinationDatabaseFile).stem().string()); + auto buildActionLock = m_setup.building.lockToWrite(); + m_buildAction->directory = std::move(directory); + } + m_workingDirectory = determineWorkingDirectory(repoManagementWorkingDirectory); + try { + std::filesystem::create_directories(m_workingDirectory); + } catch (const std::filesystem::filesystem_error &e) { + m_buildAction->log()(Phrases::ErrorMessage, "Unable to make working directory: ", e.what(), '\n'); + m_result.errorMessage = argsToString("unable to make working directory: ", e.what()); + } +} + +void PackageMovementAction::locatePackages() +{ + // determine repo path and package paths + LibPkg::Database *db; + if (m_sourceDbs.empty()) { + db = *m_destinationDbs.begin(); + } else { + db = *m_sourceDbs.begin(); + } + const auto &packages = db->packages; + for (const auto &packageName : m_buildAction->packageNames) { + const auto &package = packages.find(packageName); + if (package == packages.end()) { + m_result.failedPackages.emplace_back(packageName, "package not listed in database file"); + continue; + } + auto packageLocation = db->locatePackage(package->second->computeFileName()); + if (packageLocation.error.has_value()) { + m_result.failedPackages.emplace_back( + packageName, argsToString("unable to locate package within repo directory: ", packageLocation.error.value().what())); + continue; + } + if (!packageLocation.exists) { + m_result.failedPackages.emplace_back(packageName, "package not present within repo directory"); + continue; + } + m_packageLocations.emplace_back(packageName, std::move(packageLocation), true); + } +} + +void PackageMovementAction::reportResultWithData(BuildActionResult result) +{ + auto buildLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(m_result); + reportResult(result); +} + +RemovePackages::RemovePackages(ServiceSetup &setup, const std::shared_ptr &buildAction) + : PackageMovementAction(setup, buildAction) +{ +} + +void RemovePackages::run() +{ + if (!prepareRepoAction(RequiredDatabases::OneDestination)) { + return; + } + + // make list of package names to pass to repo-remove + m_result.processedPackages.reserve(m_packageLocations.size()); + for (const auto &[packageName, packageLocation, ok] : m_packageLocations) { + m_result.processedPackages.emplace_back(packageName); + } + + // remove package from database file + auto repoRemoveProcess = m_buildAction->makeBuildProcess(m_workingDirectory + "/repo-remove.log", + std::bind(&RemovePackages::handleRepoRemoveResult, this, std::placeholders::_1, std::placeholders::_2)); + repoRemoveProcess->launch( + boost::process::start_dir(m_destinationRepoDirectory), m_repoRemovePath, m_destinationDatabaseFile, m_result.processedPackages); + m_buildAction->log()(Phrases::InfoMessage, "Invoking repo-remove within \"", m_destinationRepoDirectory, "\" for \"", m_destinationDatabaseFile, + "\", see logfile for details\n"); +} + +void RemovePackages::handleRepoRemoveResult(boost::process::child &&child, ProcessResult &&result) +{ + CPP_UTILITIES_UNUSED(child) + if (result.errorCode) { + const auto errorCodeMessage = result.errorCode.message(); + const auto &errorMessage = result.error.empty() ? errorCodeMessage : result.error; + m_result.errorMessage = "unable to remove packages: " + errorMessage; + m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke repo-remove: ", errorMessage, '\n'); + } else if (result.exitCode != 0) { + m_result.errorMessage = argsToString("unable to remove package: repo-remove returned with exit code ", result.exitCode); + m_buildAction->log()(Phrases::ErrorMessage, "repo-remove invocation exited with non-zero exit code: ", result.exitCode, '\n'); + } else { + movePackagesToArchive(); + return; + } + m_result.failedPackages.reserve(m_result.failedPackages.size() + m_result.processedPackages.size()); + for (auto &processedPackage : m_result.processedPackages) { + m_result.failedPackages.emplace_back(std::move(processedPackage), "repo-remove error"); + } + m_result.processedPackages.clear(); + reportResultWithData(BuildActionResult::Failure); +} + +void RemovePackages::movePackagesToArchive() +{ + m_buildAction->log()(Phrases::InfoMessage, "Moving packages to archive directory"); + std::filesystem::path archivePath; + auto processedPackageIterator = m_result.processedPackages.begin(); + for (const auto &[packageName, packageLocation, ok] : m_packageLocations) { + try { + archivePath = packageLocation.pathWithinRepo.parent_path() / "archive"; + std::filesystem::create_directory(archivePath); + std::filesystem::rename(packageLocation.pathWithinRepo, archivePath / packageLocation.pathWithinRepo.filename()); + if (packageLocation.storageLocation.empty()) { + continue; + } + // FIXME: The file at the storage location *might* still be used elsewhere. Better leave that to a repo cleanup task (to be implemented later). + archivePath = packageLocation.storageLocation.parent_path() / "archive"; + std::filesystem::create_directory(archivePath); + std::filesystem::rename(packageLocation.storageLocation, archivePath / packageLocation.storageLocation.filename()); + ++processedPackageIterator; + } catch (const std::filesystem::filesystem_error &e) { + processedPackageIterator = m_result.processedPackages.erase(processedPackageIterator); + m_result.failedPackages.emplace_back(packageName, argsToString("unable to archive: ", e.what())); + } + } + if (m_result.failedPackages.empty()) { + reportResultWithData(BuildActionResult::Success); + return; + } + m_result.errorMessage = argsToString("failed to remove ", m_result.failedPackages.size(), " packages"); + reportResultWithData(BuildActionResult::Failure); +} + +MovePackages::MovePackages(ServiceSetup &setup, const std::shared_ptr &buildAction) + : PackageMovementAction(setup, buildAction) +{ +} + +void MovePackages::run() +{ + if (!prepareRepoAction(RequiredDatabases::OneSource | RequiredDatabases::OneDestination)) { + return; + } + + // copy packages from the source repo to the destination repo + // make list of package names to pass to repo-add + m_result.processedPackages.reserve(m_packageLocations.size()); + for (auto &[packageName, packageLocation, ok] : m_packageLocations) { + try { + if (packageLocation.storageLocation.empty()) { + std::filesystem::copy_file(packageLocation.pathWithinRepo, m_destinationRepoDirectory / packageLocation.pathWithinRepo.filename()); + } else { + const auto symlinkTarget = std::filesystem::read_symlink(packageLocation.pathWithinRepo); + if (symlinkTarget.is_absolute()) { + ok = false; + m_result.failedPackages.emplace_back(packageName, + argsToString("unable to copy to destination repo: \"", packageLocation.pathWithinRepo, + "\" is a symlink with absolute target path (only relative target paths supported)")); + continue; + } + const auto newStorageLocation = m_destinationRepoDirectory / symlinkTarget; + std::filesystem::create_directory( + newStorageLocation.parent_path()); // ensure the parent, e.g. the "any" directory exists; assume further parents already exist + std::filesystem::copy(packageLocation.pathWithinRepo, m_destinationRepoDirectory / packageLocation.pathWithinRepo.filename(), + std::filesystem::copy_options::copy_symlinks); + std::filesystem::copy_file(packageLocation.storageLocation, newStorageLocation); + } + } catch (const std::filesystem::filesystem_error &e) { + ok = false; + m_result.failedPackages.emplace_back(packageName, argsToString("unable to copy to destination repo: ", e.what())); + continue; + } + m_fileNames.emplace_back(packageLocation.pathWithinRepo.filename()); + m_result.processedPackages.emplace_back(packageName); + } + + // error-out early if not even a single package could be copied + if (m_fileNames.empty()) { + m_result.errorMessage = "none of the specified packages could be copied to the destination repo"; + reportResultWithData(BuildActionResult::Failure); + return; + } + + // conclude build action when both, repo-add and repo-remove have been exited and handled + const auto processSession = MultiSession::create(m_setup.building.ioContext, std::bind(&MovePackages::conclude, this)); + + // add packages to database file of destination repo + auto repoAddProcess = m_buildAction->makeBuildProcess(m_workingDirectory + "/repo-add.log", + std::bind(&MovePackages::handleRepoAddResult, this, processSession, std::placeholders::_1, std::placeholders::_2)); + repoAddProcess->launch(boost::process::start_dir(m_destinationRepoDirectory), m_repoAddPath, m_destinationDatabaseFile, m_fileNames); + + // remove package from database file of source repo + auto repoRemoveProcess = m_buildAction->makeBuildProcess(m_workingDirectory + "/repo-remove.log", + std::bind(&MovePackages::handleRepoRemoveResult, this, processSession, std::placeholders::_1, std::placeholders::_2)); + repoRemoveProcess->launch(boost::process::start_dir(m_sourceRepoDirectory), m_repoRemovePath, m_sourceDatabaseFile, m_result.processedPackages); + + m_buildAction->log()(ps(Phrases::InfoMessage), "Invoking repo-add within \"", m_destinationRepoDirectory, "\" for \"", m_destinationDatabaseFile, + "\", see logfile for details\n", ps(Phrases::InfoMessage), "Invoking repo-remove within \"", m_sourceRepoDirectory, "\" for \"", + m_sourceDatabaseFile, "\", see logfile for details\n"); +} + +void MovePackages::handleRepoRemoveResult(MultiSession::SharedPointerType processSession, boost::process::child &&child, ProcessResult &&result) +{ + // handle error + CPP_UTILITIES_UNUSED(processSession) + CPP_UTILITIES_UNUSED(child) + if (result.errorCode) { + const auto errorCodeMessage = result.errorCode.message(); + const auto &errorMessage = result.error.empty() ? errorCodeMessage : result.error; + m_result.errorMessage = "unable to remove packages: " + errorMessage; + m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke repo-remove: ", errorMessage, '\n'); + return; + } else if (result.exitCode != 0) { + m_result.errorMessage = argsToString("unable to remove package: repo-remove returned with exit code ", result.exitCode); + m_buildAction->log()(Phrases::ErrorMessage, "repo-remove invocation exited with non-zero exit code: ", result.exitCode, '\n'); + return; + } + + // remove packages from source repo + for (auto &[packageName, packageLocation, ok] : m_packageLocations) { + // ignore packages which we've couldn't even copy to destination repo + if (!ok) { + continue; + } + // delete package within source repo; leave package at storage location because some other repo might still link to it + try { + std::filesystem::remove(packageLocation.pathWithinRepo); + } catch (const std::runtime_error &e) { + ok = false; + m_result.failedPackages.emplace_back(packageName, argsToString("unable to remove from source repo: ", e.what())); + m_result.processedPackages.erase( + std::remove(m_result.processedPackages.begin(), m_result.processedPackages.end(), packageName), m_result.processedPackages.end()); + } + } +} + +void MovePackages::handleRepoAddResult(MultiSession::SharedPointerType processSession, boost::process::child &&child, ProcessResult &&result) +{ + // handle error + CPP_UTILITIES_UNUSED(processSession) + CPP_UTILITIES_UNUSED(child) + if (result.errorCode) { + const auto errorCodeMessage = result.errorCode.message(); + const auto &errorMessage = result.error.empty() ? errorCodeMessage : result.error; + m_addErrorMessage = "unable to add packages: " + errorMessage; + m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke repo-add: ", errorMessage, '\n'); + return; + } else if (result.exitCode != 0) { + m_addErrorMessage = argsToString("unable to add packages: repo-add returned with exit code ", result.exitCode); + m_buildAction->log()(Phrases::ErrorMessage, "repo-add invocation exited with non-zero exit code: ", result.exitCode, '\n'); + return; + } + + // nothing more to do; packages have already been copied before invoking repo-add +} + +void MovePackages::conclude() +{ + // check for errors + const auto hasRepoRemoveError = !m_result.errorMessage.empty(); + const auto hasRepoAddError = !m_addErrorMessage.empty(); + std::string_view failureReason; + if (hasRepoAddError && hasRepoRemoveError) { + failureReason = "repo-add and repo-remove error"; + m_result.errorMessage = argsToString(m_result.errorMessage, ',', ' ', m_addErrorMessage); + } else if (hasRepoAddError) { + failureReason = "repo-add error"; + m_result.errorMessage = std::move(m_addErrorMessage); + } else if (hasRepoRemoveError) { + failureReason = "repo-remove error"; + } + + // report success if there are no repo-add/repo-remove errors or otherwise failed packages; otherwise report failure + if (!hasRepoAddError && !hasRepoRemoveError) { + if (m_result.errorMessage.empty() && !m_result.failedPackages.empty()) { + m_result.errorMessage = argsToString("failed to move ", m_result.failedPackages.size(), " packages"); + } + reportResultWithData(m_result.failedPackages.empty() ? BuildActionResult::Success : BuildActionResult::Failure); + return; + } + + // consider all packages failed if there are repo-add/repo-remove errors + m_result.failedPackages.reserve(m_result.failedPackages.size() + m_result.processedPackages.size()); + for (auto &processedPackage : m_result.processedPackages) { + m_result.failedPackages.emplace_back(std::move(processedPackage), failureReason); + } + m_result.processedPackages.clear(); + reportResultWithData(BuildActionResult::Failure); +} + +CheckForProblems::CheckForProblems(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void CheckForProblems::run() +{ + // initialize build action + auto configReadLock = init(BuildActionAccess::ReadConfig, RequiredDatabases::OneOrMoreDestinations, RequiredParameters::None); + if (std::holds_alternative(configReadLock)) { + return; + } + + auto result = std::unordered_map>(); + for (auto *const db : m_destinationDbs) { + // check whether files exist + auto &problems = result[db->name]; + try { + if (db->path.empty() || !std::filesystem::is_regular_file(db->path)) { + problems.emplace_back(RepositoryProblem{ .desc = "db file \"" % db->path + "\" is not a regular file" }); + } + const auto filesPath = db->filesPath.empty() ? db->filesPathFromRegularPath() : db->filesPath; + if (filesPath.empty() || !std::filesystem::is_regular_file(filesPath)) { + problems.emplace_back(RepositoryProblem{ .desc = "files db file \"" % filesPath + "\" is not a regular file" }); + } + if (db->localPkgDir.empty()) { + goto checkForUnresolvedPackages; // skip checking for presence of package if the local package directory is not configured + } + if (!std::filesystem::is_directory(db->localPkgDir)) { + problems.emplace_back( + RepositoryProblem{ .desc = "configured local package directory \"" % db->localPkgDir + "\" is not a directory" }); + } + for (const auto &[pkgName, pkg] : db->packages) { + if (!pkg->packageInfo) { + problems.emplace_back(RepositoryProblem{ .desc = "no package info present", .pkg = pkgName }); + continue; + } + const auto packageLocation = db->locatePackage(pkg->packageInfo->fileName); + if (!packageLocation.exists) { + problems.emplace_back( + RepositoryProblem{ .desc = "binary package \"" % pkg->packageInfo->fileName + "\" not present", .pkg = pkgName }); + } + } + } catch (const std::filesystem::filesystem_error &e) { + problems.emplace_back(RepositoryProblem{ .desc = argsToString("unable to check presence of files: ", e.what()) }); + } + // check for unresolved dependencies and missing libraries + checkForUnresolvedPackages: + auto unresolvedPackages + = db->detectUnresolvedPackages(m_setup.config, std::vector>(), LibPkg::DependencySet()); + for (auto &[package, unresolvedDeps] : unresolvedPackages) { + problems.emplace_back(RepositoryProblem{ .desc = std::move(unresolvedDeps), .pkg = package->name }); + } + } + + const auto buildLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(result); + reportResult(BuildActionResult::Success); +} + +CleanRepository::CleanRepository(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void CleanRepository::handleFatalError(InternalBuildAction::InitReturnType &init) +{ + init = std::monostate(); + m_buildAction->appendOutput(Phrases::ErrorMessage, "Cleanup aborted due to fatal errors\n"); + const auto buildLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(m_messages); + reportResult(BuildActionResult::Failure); +} + +void CleanRepository::run() +{ + // initialize build action + const auto flags = static_cast(m_buildAction->flags); + m_dryRun = flags & CleanRepositoryFlags::DryRun; + m_buildAction->appendOutput(Phrases::InfoMessage, m_dryRun ? "Preparing cleanup, dry run\n" : "Preparing cleanup\n"); + auto configReadLock = init(BuildActionAccess::ReadConfig, RequiredDatabases::OneOrMoreDestinations, RequiredParameters::None); + if (std::holds_alternative(configReadLock)) { + return; + } + + // find relevant repository directories + enum class RepoDirType { + New, + ArchSpecific, + Any, + Src, + }; + struct RepoDir { + std::filesystem::path canonicalPath; + std::vector> toArchive; // old packages not belonging to the DB anymore + std::vector toDelete; // non-package junk files + std::unordered_set relevantDbs; + RepoDirType type = RepoDirType::New; + }; + std::unordered_map repoDirs; + bool fatalError = false; + const auto addAnyAndSrcDir = [this, &repoDirs](LibPkg::Database &db) { + // find the "any" directory which contains arch neutral packages which are possibly shared between databases + try { + auto anyPath = std::filesystem::canonical(db.localPkgDir + "/../any"); + auto &anyDir = repoDirs[anyPath.string()]; + if (anyDir.type == RepoDirType::New) { + anyDir.type = RepoDirType::Any; + anyDir.canonicalPath = std::move(anyPath); + } + anyDir.relevantDbs.emplace(&db); + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back("Unable to consider \"any\" dir of \"" % db.name % "\": " + e.what()); + } + // find the "src" directory which contains source package + try { + auto srcPath = std::filesystem::canonical(db.localPkgDir + "/../src"); + auto &srcDir = repoDirs[srcPath.string()]; + if (srcDir.type == RepoDirType::New) { + srcDir.type = RepoDirType::Src; + srcDir.canonicalPath = std::move(srcPath); + } + srcDir.relevantDbs.emplace(&db); + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back("Unable to consider \"src\" dir of \"" % db.name % "\": " + e.what()); + } + }; + for (auto *const db : m_destinationDbs) { + if (db->localPkgDir.empty()) { + m_messages.errors.emplace_back("Unable to clean \"" % db->name + "\": no local package directory configured"); + continue; + } + // find the "arch-specific" directory which contains the packages (or links to the them in the "any" directory) and the database files + auto parentPath = std::filesystem::path(); + try { + auto archSpecificPath = std::filesystem::canonical(db->localPkgDir); + auto &archSpecificDir = repoDirs[archSpecificPath.string()]; + parentPath = archSpecificPath.parent_path(); + if (archSpecificDir.type == RepoDirType::New) { + archSpecificDir.type = RepoDirType::ArchSpecific; + archSpecificDir.canonicalPath = std::move(archSpecificPath); + } + archSpecificDir.relevantDbs.emplace(db); + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back("Unable consider \"arch-specific\" dir of \"" % db->name % "\": " + e.what()); + } + // find the "any" and "src" directory + addAnyAndSrcDir(*db); + // require the parent path to be present + if (parentPath.empty()) { + fatalError = true; + continue; + } + // find other directories next to the "arch-specific" package directory + // note: These directories belong to other databases representing the same repository but for other architectures. + try { + for (const auto &otherDir : std::filesystem::directory_iterator(parentPath)) { + if (!otherDir.is_directory() || otherDir.path() == db->localPkgDir || otherDir.path() == "any" || otherDir.path() == "src") { + continue; + } + repoDirs[otherDir.path().string()]; + } + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back("Unable consider find repositories next to \"" % db->name % "\": " + e.what()); + fatalError = true; + } + } + if (fatalError) { + handleFatalError(configReadLock); + return; + } + + // find relevant databases for repo dirs discovered in "find other directories next to …" step + std::vector> otherDbs; + for (auto &[dirName, dirInfo] : repoDirs) { + if (dirInfo.type != RepoDirType::New) { + continue; + } + std::vector dbFileNames; + try { + // find the database file + dirInfo.canonicalPath = std::filesystem::canonical(dirName); + for (const auto &repoItem : std::filesystem::directory_iterator(dirInfo.canonicalPath)) { + if (!repoItem.is_regular_file() && !repoItem.is_symlink()) { + continue; + } + if (repoItem.path().extension() == ".db") { + dbFileNames.emplace_back(repoItem.path().filename()); + } + } + if (dbFileNames.empty()) { + throw std::runtime_error("no *.db file present"); + } + if (dbFileNames.size() > 1) { + throw std::runtime_error("multiple/ambigous *.db files present: " + joinStrings(dbFileNames, ", ")); + } + // initialize temporary database object for the repository + auto &db + = otherDbs.emplace_back(std::make_unique(dirName, argsToString(dirInfo.canonicalPath, '/', dbFileNames.front()))); + db->loadPackages(); + dirInfo.relevantDbs.emplace(db.get()); + // find the "any" and "src" directory + db->localPkgDir = dirInfo.canonicalPath.string(); + addAnyAndSrcDir(*db); + // consider the repository dir arch-specific + dirInfo.type = RepoDirType::ArchSpecific; + } catch (const std::runtime_error &e) { + m_messages.errors.emplace_back("Unable read database file in repo dir \"" % dirName % "\": " + e.what()); + fatalError = true; + } + } + if (fatalError) { + handleFatalError(configReadLock); + return; + } + + // verify that each repo dir has at least one relevant database now + for (auto &[dirName, dirInfo] : repoDirs) { + if (dirInfo.relevantDbs.empty()) { + m_messages.errors.emplace_back("Unable to associate a database with repo dir \"" % dirName + "\"."); + fatalError = true; + } + } + if (fatalError) { + handleFatalError(configReadLock); + return; + } + + // flag packages no longer referenced by any database for moving it to the archive folder; flag chunk files for deletion + for (auto &[dirName, dirInfo] : repoDirs) { + try { + for (const auto &repoItem : std::filesystem::directory_iterator(dirInfo.canonicalPath)) { + // skip directories and files which are not regular files or symlinks + if (repoItem.is_directory() || repoItem.is_other()) { + continue; + } + + // skip the database file itself + const auto fileName = repoItem.path().filename().string(); + if (fileName.find(".db") != std::string::npos || fileName.find(".files") != std::string::npos) { + continue; + } + + // delete other non-package files + if (fileName.find(".pkg") == std::string::npos && fileName.find(".src") == std::string::npos) { + dirInfo.toDelete.emplace_back(repoItem); + continue; + } + + // determine package name from file name + const auto [packageName, error] = [&fileName] { + try { + const auto [name, version, arch] = LibPkg::Package::fileNameComponents(fileName); + return std::pair(std::string(name), false); + } catch (const std::runtime_error &e) { + return std::pair(std::string(e.what()), true); + } + }(); + if (error) { + m_messages.warnings.emplace_back( + "Unable to parse package name of \"" % fileName % "\" (" % packageName + "). Not touching it to be safe."); + continue; + } + + // check whether the file is still referenced by and relevant database and move it to archive if not + auto fileStillReferenced = false; + std::vector actuallyReferencedFileNames; + for (const auto *const db : dirInfo.relevantDbs) { + const auto i = db->packages.find(packageName); + if (i == db->packages.end()) { + continue; + } + const auto &pkg = i->second; + const auto &pkgInfo = pkg->packageInfo; + if (!pkgInfo || pkgInfo->fileName.empty()) { + m_messages.warnings.emplace_back( + "Database entry for package \"" % pkg->name % "\" misses the file name. Not touching \"" % fileName + "\" to be safe."); + fileStillReferenced = true; + continue; + } + if (pkgInfo->fileName == fileName) { + fileStillReferenced = true; + break; + } else { + actuallyReferencedFileNames.emplace_back(pkgInfo->fileName); + } + } + if (!fileStillReferenced) { + dirInfo.toArchive.emplace_back( + std::pair(repoItem, joinStrings(actuallyReferencedFileNames, ", "))); + } + } + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back("Unable to iterate though repo directory \"" % dirName % "\": " + e.what()); + } + } + + configReadLock = std::monostate(); + + // do the actual file system operations + for (auto &[dirName, dirInfo] : repoDirs) { + // skip source repos for now + // note: So far we would get false-positives if pkgname does not contain pkgbase. + if (dirInfo.type == RepoDirType::Src) { + continue; + } + // delete files + std::size_t processesItems = 0; + for (auto &toDelete : dirInfo.toDelete) { + try { + if (!m_dryRun) { + std::filesystem::remove(toDelete); + } + ++processesItems; + m_messages.notes.emplace_back("Deleted " + toDelete.string()); + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back(argsToString("Unable to delete: ", e.what())); + } + } + // archive files + const auto archiveDir = dirInfo.canonicalPath / "archive"; + try { + if (!std::filesystem::is_directory(archiveDir)) { + std::filesystem::create_directory(archiveDir, dirInfo.canonicalPath); + } + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back(argsToString("Unable to create archive directory: ", e.what())); + continue; + } + for (const auto &[path, referencedPath] : dirInfo.toArchive) { + try { + if (!m_dryRun) { + std::filesystem::rename(path, archiveDir / path.filename()); + } + ++processesItems; + m_messages.notes.emplace_back( + "Archived " % path.string() % " (current version: " % (referencedPath.empty() ? "removed"sv : referencedPath) + ")"); + } catch (const std::filesystem::filesystem_error &e) { + m_messages.errors.emplace_back(argsToString("Unable to archive: ", e.what())); + } + } + m_buildAction->appendOutput(Phrases::InfoMessage, "Archived/deleted ", processesItems, " files in \"", dirName, '\"', '\n'); + } + + const auto buildLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = std::move(m_messages); + reportResult(m_messages.errors.empty() ? BuildActionResult::Success : BuildActionResult::Failure); +} + +} // namespace LibRepoMgr diff --git a/librepomgr/buildactions/subprocess.h b/librepomgr/buildactions/subprocess.h new file mode 100644 index 0000000..2c6379d --- /dev/null +++ b/librepomgr/buildactions/subprocess.h @@ -0,0 +1,168 @@ +#ifndef LIBREPOMGR_SUB_PROCESS_H +#define LIBREPOMGR_SUB_PROCESS_H + +#include "./subprocessfwd.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace LibRepoMgr { + +/// \brief The ProcessResult struct holds data about a concluded BaseProcessSession/ProcessSession/BuildProcessSession. +struct ProcessResult { + ProcessResult() = default; + ProcessResult(const ProcessResult &other) = delete; + ProcessResult(ProcessResult &&other) = default; + std::string output, error; + std::error_code errorCode; + int exitCode = -1; +}; + +/// \brief The BaseProcessSession class is the base for ProcessSession and BuildProcessSession. +class BaseProcessSession { +public: + using Handler = ProcessHandler; + + explicit BaseProcessSession(boost::asio::io_context &ioContext, Handler &&handler); + ~BaseProcessSession(); + + boost::process::group group; + boost::process::child child; + ProcessResult result; + +protected: + boost::asio::io_context &m_ioContext; + Handler m_handler; +}; + +inline BaseProcessSession::BaseProcessSession(boost::asio::io_context &ioContext, Handler &&handler) + : child() + , m_ioContext(ioContext) + , m_handler(std::move(handler)) +{ +} + +inline BaseProcessSession::~BaseProcessSession() +{ + boost::asio::post(m_ioContext, [child = std::move(this->child), result = std::move(this->result), handler = std::move(m_handler)]() mutable { + handler(std::move(child), std::move(result)); + }); +} + +class BasicProcessSession : public std::enable_shared_from_this, public BaseProcessSession { +public: + explicit BasicProcessSession(boost::asio::io_context &ioContext, Handler &&handler); + template void launch(ChildArgs &&...childArgs); +}; + +inline BasicProcessSession::BasicProcessSession(boost::asio::io_context &ioContext, Handler &&handler) + : BaseProcessSession(ioContext, std::move(handler)) +{ +} + +template void BasicProcessSession::launch(ChildArgs &&...childArgs) +{ + try { + child = boost::process::child( + m_ioContext, group, std::forward(childArgs)..., + boost::process::on_exit = + [session = shared_from_this()](int exitCode, const std::error_code &errorCode) { + session->result.exitCode = exitCode; + session->result.errorCode = errorCode; + }, + boost::process::extend::on_error + = [session = shared_from_this()](auto &, const std::error_code &errorCode) { session->result.errorCode = errorCode; }); + } catch (const boost::process::process_error &e) { + result.errorCode = e.code(); + result.error = CppUtilities::argsToString("unable to launch: ", e.what()); + return; + } +} + +/// \brief The ProcessSession class is used to spawn a process, e.g. from a build action, capturing stdout/stderr in-memory. +class ProcessSession : public std::enable_shared_from_this, public BaseProcessSession { +public: + explicit ProcessSession(boost::asio::io_context &ioContext, Handler &&handler); + template void launch(ChildArgs &&...childArgs); + + boost::process::async_pipe outputPipe, errorPipe; + +private: + static std::string streambufToString(boost::asio::streambuf &buf); + + boost::asio::streambuf m_outputBuffer, m_errorBuffer; +}; + +inline ProcessSession::ProcessSession(boost::asio::io_context &ioContext, Handler &&handler) + : BaseProcessSession(ioContext, std::move(handler)) + , outputPipe(ioContext) + , errorPipe(ioContext) +{ +} + +inline std::string ProcessSession::streambufToString(boost::asio::streambuf &buf) +{ + const auto begin = boost::asio::buffers_begin(buf.data()); + return std::string(begin, begin + static_cast(buf.size())); +} + +template void ProcessSession::launch(ChildArgs &&...childArgs) +{ + try { + child = boost::process::child( + m_ioContext, group, std::forward(childArgs)..., boost::process::std_out > outputPipe, boost::process::std_err > errorPipe, + boost::process::on_exit = + [session = shared_from_this()](int exitCode, const std::error_code &errorCode) { + session->result.exitCode = exitCode; + session->result.errorCode = errorCode; + }, + boost::process::extend::on_error + = [session = shared_from_this()](auto &, const std::error_code &errorCode) { session->result.errorCode = errorCode; }); + } catch (const boost::process::process_error &e) { + result.errorCode = e.code(); + result.error = CppUtilities::argsToString("unable to launch: ", e.what()); + return; + } + boost::asio::async_read(outputPipe, m_outputBuffer, [session = shared_from_this()](const auto &ec, auto bytesTransferred) { + CPP_UTILITIES_UNUSED(ec) + CPP_UTILITIES_UNUSED(bytesTransferred) + session->result.output = streambufToString(session->m_outputBuffer); + }); + boost::asio::async_read(errorPipe, m_errorBuffer, [session = shared_from_this()](const auto &ec, auto bytesTransferred) { + CPP_UTILITIES_UNUSED(ec) + CPP_UTILITIES_UNUSED(bytesTransferred) + session->result.error = streambufToString(session->m_errorBuffer); + }); +} + +inline boost::filesystem::path findExecutable(const std::string &nameOrPath) +{ + return nameOrPath.find('/') == std::string::npos ? boost::process::search_path(nameOrPath) : boost::filesystem::path(nameOrPath); +} + +inline bool checkExecutable(const boost::filesystem::path &path) +{ + return !path.empty() && boost::filesystem::exists(path); +} + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_SUB_PROCESS_H diff --git a/librepomgr/buildactions/subprocessfwd.h b/librepomgr/buildactions/subprocessfwd.h new file mode 100644 index 0000000..002d931 --- /dev/null +++ b/librepomgr/buildactions/subprocessfwd.h @@ -0,0 +1,22 @@ +#ifndef LIBREPOMGR_SUB_PROCESS_FWD_H +#define LIBREPOMGR_SUB_PROCESS_FWD_H + +#include + +namespace boost { +namespace process { +class child; +} +} // namespace boost + +namespace LibRepoMgr { + +struct ProcessResult; +using ProcessHandler = std::function; +class BaseProcessSession; +class BasicProcessSession; +class ProcessSession; + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_SUB_PROCESS_FWD_H diff --git a/librepomgr/buildactions/updatecheck.cpp b/librepomgr/buildactions/updatecheck.cpp new file mode 100644 index 0000000..5f57096 --- /dev/null +++ b/librepomgr/buildactions/updatecheck.cpp @@ -0,0 +1,53 @@ +#include "./buildactionprivate.h" + +#include "../serversetup.h" + +#include "../webclient/aur.h" + +using namespace std; +using namespace CppUtilities; + +namespace LibRepoMgr { + +UpdateCheck::UpdateCheck(ServiceSetup &setup, const std::shared_ptr &buildAction) + : InternalBuildAction(setup, buildAction) +{ +} + +void UpdateCheck::run() +{ + auto configReadLock = init(BuildActionAccess::ReadConfig, + RequiredDatabases::OneOrMoreSources | RequiredDatabases::OneDestination | RequiredDatabases::AllowFromAur, RequiredParameters::None); + if (holds_alternative(configReadLock)) { + return; + } + + if (m_fromAur && !m_packageLookupDone + && WebClient::queryAurPackagesForDatabase(m_buildAction->log(), m_setup, m_setup.building.ioContext, + &get>(configReadLock), **m_destinationDbs.begin(), [this](std::vector> &&) { + m_packageLookupDone = true; + run(); + })) { + return; // wait for async operation to complete + } + + auto result = checkForUpdates(); + get>(configReadLock).unlock(); + + auto buildActionWriteLock = m_setup.building.lockToWrite(); + m_buildAction->resultData = move(result); + reportSuccess(); +} + +LibPkg::PackageUpdates UpdateCheck::checkForUpdates() +{ + vector sourceDbs; + sourceDbs.reserve(m_fromAur ? m_sourceDbs.size() + 1 : m_sourceDbs.size()); + sourceDbs.insert(sourceDbs.begin(), m_sourceDbs.cbegin(), m_sourceDbs.cend()); + if (m_fromAur) { + sourceDbs.emplace_back(&m_setup.config.aur); + } + return (**m_destinationDbs.begin()).checkForUpdates(sourceDbs); +} + +} // namespace LibRepoMgr diff --git a/librepomgr/errorhandling.cpp b/librepomgr/errorhandling.cpp new file mode 100644 index 0000000..e919b27 --- /dev/null +++ b/librepomgr/errorhandling.cpp @@ -0,0 +1,4 @@ +#include "./errorhandling.h" +#include "./global.h" + +#include "reflection/errorhandling.h" diff --git a/librepomgr/errorhandling.h b/librepomgr/errorhandling.h new file mode 100644 index 0000000..a298080 --- /dev/null +++ b/librepomgr/errorhandling.h @@ -0,0 +1,11 @@ +#ifndef LIBREPOMGR_ERROR_HANDLING_H +#define LIBREPOMGR_ERROR_HANDLING_H + +#include + +#include + +// allow to serialize deserialization errors so we can return errors as JSON, too +REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(ReflectiveRapidJSON::JsonDeserializationError); + +#endif // LIBREPOMGR_ERROR_HANDLING_H diff --git a/librepomgr/global.h b/librepomgr/global.h new file mode 100644 index 0000000..92db68a --- /dev/null +++ b/librepomgr/global.h @@ -0,0 +1,27 @@ +// Created via CMake from template global.h.in +// WARNING! Any changes to this file will be overwritten by the next CMake run! + +#ifndef LIBREPOMGR_GLOBAL +#define LIBREPOMGR_GLOBAL + +#include + +#ifdef LIBREPOMGR_STATIC +#define LIBREPOMGR_EXPORT +#define LIBREPOMGR_IMPORT +#else +#define LIBREPOMGR_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT +#define LIBREPOMGR_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT +#endif + +/*! + * \def LIBREPOMGR_EXPORT + * \brief Marks the symbol to be exported by the librepomgr library. + */ + +/*! + * \def LIBREPOMGR_IMPORT + * \brief Marks the symbol to be imported from the librepomgr library. + */ + +#endif // LIBREPOMGR_GLOBAL diff --git a/librepomgr/helper.h b/librepomgr/helper.h new file mode 100644 index 0000000..9138735 --- /dev/null +++ b/librepomgr/helper.h @@ -0,0 +1,156 @@ +#ifndef LIBREPOMGR_HELPER_H +#define LIBREPOMGR_HELPER_H + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace LibRepoMgr { + +namespace Traits = CppUtilities::Traits; + +inline const char *getLastValue(const std::multimap &multimap, const std::string &key) +{ + using namespace std; + const auto i = find_if(multimap.crbegin(), multimap.crend(), [&key](const pair &i) { return i.first == key; }); + if (i != multimap.rend()) { + return i->second.data(); + } + return nullptr; +} + +inline std::optional getLastValueSv(const std::multimap &multimap, const std::string &key) +{ + using namespace std; + const auto i = find_if(multimap.crbegin(), multimap.crend(), [&key](const pair &i) { return i.first == key; }); + if (i != multimap.rend()) { + return i->second.data(); + } + return std::nullopt; +} + +template void convertValue(const std::multimap &multimap, const std::string &key, TargetType &result); + +template <> +inline void convertValue(const std::multimap &multimap, const std::string &key, boost::asio::ip::address &result) +{ + using namespace std; + using namespace CppUtilities::EscapeCodes; + + if (const char *const value = getLastValue(multimap, key)) { + boost::system::error_code error; + const auto ip = boost::asio::ip::make_address(value, error); + if (ip.is_unspecified()) { + cerr << Phrases::Error << "Specified IP address \"" << value << "\" for key \"" << key << "\" is invalid" << Phrases::End << " " + << error.message() << endl; + exit(-1); + } + result = ip; + } +} + +template <> inline void convertValue(const std::multimap &multimap, const std::string &key, unsigned short &result) +{ + using namespace std; + using namespace CppUtilities; + using namespace CppUtilities::EscapeCodes; + + if (const char *const value = getLastValue(multimap, key)) { + try { + result = stringToNumber(value); + } catch (const ConversionException &) { + cerr << Phrases::Error << "Specified number \"" << value << "\" for key \"" << key << "\" is invalid." << Phrases::End; + exit(-1); + } + } +} + +template <> inline void convertValue(const std::multimap &multimap, const std::string &key, std::string &result) +{ + if (const char *const value = getLastValue(multimap, key)) { + result = value; + } +} + +template <> inline void convertValue(const std::multimap &multimap, const std::string &key, std::regex &result) +{ + using namespace std; + using namespace CppUtilities::EscapeCodes; + + if (const char *const value = getLastValue(multimap, key)) { + try { + result = value; + } catch (const regex_error &e) { + cerr << Phrases::Error << "Specified regex \"" << value << "\" for key \"" << key << "\" is invalid: " << Phrases::End; + cerr << e.what() << '\n'; + exit(-1); + } + } +} + +template <> +inline void convertValue(const std::multimap &multimap, const std::string &key, std::vector &result) +{ + for (auto range = multimap.equal_range(key); range.first != range.second; ++range.first) { + result.emplace_back(range.first->second); + } +} + +template <> inline void convertValue(const std::multimap &multimap, const std::string &key, bool &result) +{ + if (const char *const value = getLastValue(multimap, key)) { + result = !strcmp(value, "on") || !strcmp(value, "yes"); + } +} + +template void mergeSecondVectorIntoFirstVector(VectorType &firstVector, VectorType &secondVector) +{ + const auto requiredSize = firstVector.size() + secondVector.size(); + if (firstVector.capacity() < requiredSize) { + firstVector.reserve(requiredSize); + } + for (auto &i : secondVector) { + firstVector.emplace_back(std::move(i)); + } + secondVector.clear(); +} + +template void copySecondVectorIntoFirstVector(VectorType &firstVector, const VectorType &secondVector) +{ + const auto requiredSize = firstVector.size() + secondVector.size(); + if (firstVector.capacity() < requiredSize) { + firstVector.reserve(requiredSize); + } + for (auto &i : secondVector) { + firstVector.emplace_back(i); + } +} + +template auto map(const Objects &objects, Accessor accessor) +{ + ListType things; + things.reserve(objects.size()); + for (const auto &object : objects) { + things.emplace_back(accessor(object)); + } + return things; +} + +template auto names(const Objects &objects) +{ + return map(objects, [](const auto &object) { return Traits::dereferenceMaybe(object).name; }); +} + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_HELPER_H diff --git a/librepomgr/json.cpp b/librepomgr/json.cpp new file mode 100644 index 0000000..805f516 --- /dev/null +++ b/librepomgr/json.cpp @@ -0,0 +1,94 @@ +#include "./json.h" + +#include +#include + +#include + +#include +#include + +#include +#include + +using namespace CppUtilities; + +namespace LibRepoMgr { + +std::string formatJsonDeserializationError(std::string_view fileNameWithoutExtension, const ReflectiveRapidJSON::JsonDeserializationError &error) +{ + return "Unable to restore " % fileNameWithoutExtension % ".json: " + ReflectiveRapidJSON::formatJsonDeserializationError(error); +} + +std::string determineJsonPath( + std::string_view directoryPath, std::string_view fileNameWithoutExtension, DumpJsonExistingFileHandling existingFileHandling) +{ + std::filesystem::path jsonPath = directoryPath % '/' % fileNameWithoutExtension + ".json"; + if (!std::filesystem::exists(jsonPath)) { + return jsonPath; + } + switch (existingFileHandling) { + case DumpJsonExistingFileHandling::Backup: + for (size_t counter = 0;; ++counter) { + const auto backupPath = directoryPath % '/' % fileNameWithoutExtension % '-' % counter + ".json"; + if (std::filesystem::exists(backupPath)) { + continue; + } + std::filesystem::rename(jsonPath, backupPath); + break; + } + break; + case DumpJsonExistingFileHandling::Skip: + jsonPath.clear(); + break; + default:; + } + return jsonPath; +} + +std::string determineJsonPath( + std::string_view directoryPath, std::string_view fileNameWithoutExtension, RestoreJsonExistingFileHandling existingFileHandling) +{ + auto jsonPath = directoryPath % '/' % fileNameWithoutExtension + ".json"; + if (std::filesystem::exists(jsonPath)) { + return jsonPath; + } + switch (existingFileHandling) { + case RestoreJsonExistingFileHandling::RequireExistingFile: + throw std::runtime_error("file does not exist under \"" % jsonPath + '\"'); + case RestoreJsonExistingFileHandling::Skip: + jsonPath.clear(); + break; + } + return jsonPath; +} + +void writeJsonDocument(const RAPIDJSON_NAMESPACE::Document &document, std::string_view outputPath) +{ + FILE *const fileHandle = std::fopen(outputPath.data(), "wb"); + if (!fileHandle) { + throw std::runtime_error("unable to open \"" % outputPath % "\": " + strerror(errno)); + } + char writeBuffer[65536]; + RAPIDJSON_NAMESPACE::FileWriteStream fileStream(fileHandle, writeBuffer, sizeof(writeBuffer)); + RAPIDJSON_NAMESPACE::PrettyWriter writer(fileStream); + document.Accept(writer); + std::fflush(fileHandle); + if (std::ferror(fileHandle)) { + throw std::runtime_error("unable to write to \"" % outputPath % "\": " + strerror(errno)); + } + std::fclose(fileHandle); +} + +std::filesystem::path handleOldJsonFile(const std::filesystem::path &jsonFilePath) +{ + std::filesystem::path oldFilePath; + if (!std::filesystem::exists(jsonFilePath)) { + return oldFilePath; + } + oldFilePath = argsToString(jsonFilePath, ".old"); + std::filesystem::rename(jsonFilePath, oldFilePath); + return oldFilePath; +} + +} // namespace LibRepoMgr diff --git a/librepomgr/json.h b/librepomgr/json.h new file mode 100644 index 0000000..3d9a72c --- /dev/null +++ b/librepomgr/json.h @@ -0,0 +1,120 @@ +#ifndef LIBREPOMGR_JSON_H +#define LIBREPOMGR_JSON_H + +#include "./global.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +namespace LibRepoMgr { + +enum class DumpJsonExistingFileHandling { + Backup, + Skip, + Override, +}; + +enum class RestoreJsonExistingFileHandling { + RequireExistingFile, + Skip, +}; + +LIBREPOMGR_EXPORT std::string formatJsonDeserializationError( + std::string_view fileNameWithoutExtension, const ReflectiveRapidJSON::JsonDeserializationError &error); +LIBREPOMGR_EXPORT std::string determineJsonPath( + std::string_view directoryPath, std::string_view fileNameWithoutExtension, DumpJsonExistingFileHandling existingFileHandling); +LIBREPOMGR_EXPORT std::string determineJsonPath( + std::string_view directoryPath, std::string_view fileNameWithoutExtension, RestoreJsonExistingFileHandling existingFileHandling); +LIBREPOMGR_EXPORT void writeJsonDocument(const RAPIDJSON_NAMESPACE::Document &document, std::string_view outputPath); +LIBREPOMGR_EXPORT std::filesystem::path handleOldJsonFile(const std::filesystem::path &jsonFilePath); + +template > * = nullptr> +std::string dumpJsonDocument(DocumentGenerator &&makeDocument, std::string_view directoryPath, std::string_view fileNameWithoutExtension, + DumpJsonExistingFileHandling existingFileHandling) +{ + try { + const auto jsonPath = determineJsonPath(directoryPath, fileNameWithoutExtension, existingFileHandling); + if (jsonPath.empty()) { + return jsonPath; // skipping + } + const auto jsonDocument = makeDocument(); + const auto oldFilePath = handleOldJsonFile(jsonPath); + writeJsonDocument(jsonDocument, jsonPath); + if (!oldFilePath.empty()) { + std::filesystem::remove(oldFilePath); + } + return jsonPath; + } catch (const std::runtime_error &e) { + throw std::runtime_error(CppUtilities::argsToString("Unable to make ", fileNameWithoutExtension, ".json: ", e.what())); + } +} + +template +std::string dumpJsonObject(const ObjectType &obj, std::string_view directoryPath, std::string_view fileNameWithoutExtension, + DumpJsonExistingFileHandling existingFileHandling) +{ + return dumpJsonDocument([&obj] { return ReflectiveRapidJSON::JsonReflector::toJsonDocument(static_cast(obj)); }, + directoryPath, fileNameWithoutExtension, existingFileHandling); +} + +template > + * = nullptr> +std::string restoreJsonObject(RestoreFunction &&restoreFunction, std::string_view directoryPath, std::string_view fileNameWithoutExtension, + RestoreJsonExistingFileHandling existingFileHandling) +{ + try { + const auto jsonPath = determineJsonPath(directoryPath, fileNameWithoutExtension, existingFileHandling); + if (jsonPath.empty()) { + return jsonPath; // skipping + } + ReflectiveRapidJSON::JsonDeserializationErrors errors{}; + errors.throwOn = ReflectiveRapidJSON::JsonDeserializationErrors::ThrowOn::All; + restoreFunction(CppUtilities::readFile(jsonPath), &errors); + return jsonPath; + } catch (const ReflectiveRapidJSON::JsonDeserializationError &e) { + throw std::runtime_error(formatJsonDeserializationError(fileNameWithoutExtension, e)); + } catch (const RAPIDJSON_NAMESPACE::ParseResult &e) { + throw std::runtime_error(CppUtilities::argsToString("Unable to restore ", fileNameWithoutExtension, ".json: ", "parse error at ", e.Offset(), + ": ", RAPIDJSON_NAMESPACE::GetParseError_En(e.Code()))); + } catch (const std::runtime_error &e) { + throw std::runtime_error(CppUtilities::argsToString("Unable to restore ", fileNameWithoutExtension, ".json: ", e.what())); + } +} + +template > * = nullptr> +std::string restoreJsonObject( + ObjectType &obj, std::string_view directoryPath, std::string_view fileNameWithoutExtension, RestoreJsonExistingFileHandling existingFileHandling) +{ + return restoreJsonObject( + [&obj](const std::string &json, ReflectiveRapidJSON::JsonDeserializationErrors *errors) { + obj = ReflectiveRapidJSON::JsonReflector::fromJson(json, errors); + }, + directoryPath, fileNameWithoutExtension, existingFileHandling); +} + +inline auto serializeParseError(const RAPIDJSON_NAMESPACE::ParseResult &e) +{ + return std::make_tuple("parse error at ", e.Offset(), ": ", RAPIDJSON_NAMESPACE::GetParseError_En(e.Code())); +} + +inline auto serializeParseResult(const RAPIDJSON_NAMESPACE::ParseResult &e) +{ + return e.IsError() ? CppUtilities::tupleToString(serializeParseError(e)) : std::string("ok"); +} + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_JSON_H diff --git a/librepomgr/logging.h b/librepomgr/logging.h new file mode 100644 index 0000000..6738f5c --- /dev/null +++ b/librepomgr/logging.h @@ -0,0 +1,35 @@ +#ifndef LIBREPOMGR_LOGGING_H +#define LIBREPOMGR_LOGGING_H + +#include "./buildactions/buildaction.h" + +namespace LibRepoMgr { + +inline auto ps(CppUtilities::EscapeCodes::Phrases phrase) +{ + return CppUtilities::EscapeCodes::formattedPhraseString(phrase); +} + +template LogContext &LogContext::operator()(std::string &&msg) +{ + std::cerr << msg; + if (m_buildAction) { + m_buildAction->appendOutput(std::move(msg)); + } + return *this; +} + +template LogContext &LogContext::operator()(CppUtilities::EscapeCodes::Phrases phrase, Args &&...args) +{ + return (*this)(CppUtilities::argsToString(CppUtilities::EscapeCodes::formattedPhraseString(phrase), std::forward(args)...)); +} + +template LogContext &LogContext::operator()(Args &&...args) +{ + return (*this)(CppUtilities::argsToString( + CppUtilities::EscapeCodes::formattedPhraseString(CppUtilities::EscapeCodes::Phrases::InfoMessage), std::forward(args)...)); +} + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_LOGGING_H diff --git a/librepomgr/multisession.h b/librepomgr/multisession.h new file mode 100644 index 0000000..06331a8 --- /dev/null +++ b/librepomgr/multisession.h @@ -0,0 +1,129 @@ +#ifndef LIBREPOMGR_MULTI_SESSION_H +#define LIBREPOMGR_MULTI_SESSION_H + +#include +#include +#include +#include + +#include +#include + +namespace LibRepoMgr { + +template class MultiSession { +public: + using ContainerType = std::vector; + using HandlerType = std::function; + using SharedPointerType = std::shared_ptr>; + + explicit MultiSession(boost::asio::io_context &ioContext, HandlerType &&handler); + ~MultiSession(); + + static SharedPointerType create(boost::asio::io_context &ioContext, HandlerType &&handler); + void addResponses(const ContainerType &); + void addResponse(SessionResponse &&); + +protected: + boost::asio::io_context &ioContext(); + ContainerType &allResponses(); + const ContainerType &allResponses() const; + +private: + boost::asio::io_context &m_ioContext; + HandlerType m_handler; + ContainerType m_allResponses; + std::mutex m_mutex; +}; + +template +inline MultiSession::MultiSession(boost::asio::io_context &ioContext, HandlerType &&handler) + : m_ioContext(ioContext) + , m_handler(handler) +{ +} + +template inline MultiSession::~MultiSession() +{ + boost::asio::post(m_ioContext.get_executor(), + [handler = std::move(m_handler), allResponses = std::move(m_allResponses)]() mutable { handler(std::move(allResponses)); }); +} + +template inline void MultiSession::addResponses(const ContainerType &responses) +{ + const auto lock = std::unique_lock(m_mutex); + m_allResponses.insert(m_allResponses.end(), responses.begin(), responses.end()); +} + +template inline void MultiSession::addResponse(SessionResponse &&response) +{ + const auto lock = std::unique_lock(m_mutex); + m_allResponses.emplace_back(std::move(response)); +} + +template inline boost::asio::io_context &MultiSession::ioContext() +{ + return m_ioContext; +} + +template inline typename MultiSession::ContainerType &MultiSession::allResponses() +{ + return m_allResponses; +} + +template +inline const typename MultiSession::ContainerType &MultiSession::allResponses() const +{ + return m_allResponses; +} + +template +inline typename MultiSession::SharedPointerType MultiSession::create( + boost::asio::io_context &ioContext, MultiSession::HandlerType &&handler) +{ + return std::make_shared>(ioContext, std::move(handler)); +} + +template <> class MultiSession { +public: + using HandlerType = std::function; + using SharedPointerType = std::shared_ptr>; + + explicit MultiSession(boost::asio::io_context &ioContext, HandlerType &&handler); + ~MultiSession(); + + static std::shared_ptr> create(boost::asio::io_context &ioContext, HandlerType &&handler); + +protected: + boost::asio::io_context &ioContext(); + +private: + boost::asio::io_context &m_ioContext; + HandlerType m_handler; +}; + +inline MultiSession::MultiSession(boost::asio::io_context &ioContext, HandlerType &&handler) + : m_ioContext(ioContext) + , m_handler(handler) +{ +} + +inline MultiSession::~MultiSession() +{ + boost::asio::post(m_ioContext.get_executor(), std::move(m_handler)); +} + +inline typename MultiSession::SharedPointerType MultiSession::create( + boost::asio::io_context &ioContext, MultiSession::HandlerType &&handler) +{ + return std::make_shared>(ioContext, std::move(handler)); +} + +inline boost::asio::io_context &MultiSession::ioContext() +{ + return m_ioContext; +} + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_MULTI_SESSION_H diff --git a/librepomgr/serversetup.cpp b/librepomgr/serversetup.cpp new file mode 100644 index 0000000..c7a45ac --- /dev/null +++ b/librepomgr/serversetup.cpp @@ -0,0 +1,659 @@ +#define RAPIDJSON_HAS_STDSTRING 1 + +#include "./serversetup.h" + +#include "./helper.h" +#include "./json.h" + +#include "./webapi/server.h" + +#include "reflection/serversetup.h" +#include "resources/config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; +using namespace LibPkg; + +namespace LibRepoMgr { + +static void deduplicateVector(std::vector &vector) +{ + std::unordered_set visited; + vector.erase( + std::remove_if(vector.begin(), vector.end(), [&visited](const std::string &value) { return !visited.emplace(value).second; }), vector.end()); +} + +ThreadPool::ThreadPool(const char *name, boost::asio::io_context &ioContext, unsigned short threadCount) + : name(name) +{ + threads.reserve(threadCount); + for (auto i = threadCount; i > 0; --i) { + threads.emplace_back([&ioContext, name] { + ioContext.run(); + std::cout << Phrases::SubMessage << name << " terminates" << Phrases::End; + }); + } +} + +ThreadPool::~ThreadPool() +{ + for (auto &thread : threads) { + thread.join(); + } +} + +void ServiceSetup::WebServerSetup::applyConfig(const std::multimap &multimap) +{ + convertValue(multimap, "address", address); + convertValue(multimap, "port", port); + convertValue(multimap, "threads", threadCount); + convertValue(multimap, "static_files", staticFilesPath); + convertValue(multimap, "verify_ssl_certificates", verifySslCertificates); + convertValue(multimap, "log_ssl_certificate_validation", logSslCertificateValidation); + + // allow the path for static files to be comma-separated + // note: We're simpliy picking the first directory which actually exists at startup time. It makes no + // sense to go through all directories when looking up particular files because this would just + // lead to serving inconsistent files. + if (staticFilesPath.empty()) { + return; + } + const auto staticFilesPaths = splitStringSimple>(staticFilesPath, ":"); + auto foundStaticFilesPath = false; + for (const auto &path : staticFilesPaths) { + std::error_code ec; + if ((foundStaticFilesPath = std::filesystem::is_directory(path, ec))) { + staticFilesPath = path; + break; + } + } + if (foundStaticFilesPath) { + cout << Phrases::InfoMessage << "Directory for static files: " << staticFilesPath << Phrases::EndFlush; + } else { + cerr << Phrases::WarningMessage << "None of the path specified in \"static_files\" is a local directory." << Phrases::EndFlush; + } +} + +void ServiceSetup::BuildSetup::applyConfig(const std::multimap &multimap) +{ + convertValue(multimap, "threads", threadCount); + convertValue(multimap, "working_directory", workingDirectory); + convertValue(multimap, "local_pkgbuilds_dir", pkgbuildsDirs); + convertValue(multimap, "ignore_local_pkgbuilds_regex", ignoreLocalPkgbuildsRegex); + convertValue(multimap, "makepkg_path", makePkgPath); + convertValue(multimap, "makechrootpkg_path", makeChrootPkgPath); + convertValue(multimap, "updpkgsums_path", updatePkgSumsPath); + convertValue(multimap, "repo_add_path", repoAddPath); + convertValue(multimap, "repo_remove_path", repoRemovePath); + convertValue(multimap, "ccache_dir", ccacheDir); + convertValue(multimap, "chroot_dir", chrootDir); + convertValue(multimap, "chroot_root_user", chrootRootUser); + convertValue(multimap, "chroot_default_user", chrootDefaultUser); + convertValue(multimap, "pacman_config_file_path", pacmanConfigFilePath); + convertValue(multimap, "makepkg_config_file_path", makepkgConfigFilePath); + convertValue(multimap, "makechrootpkg_flags", makechrootpkgFlags); + convertValue(multimap, "makepkg_flags", makepkgFlags); + convertValue(multimap, "package_cache_dir", packageCacheDir); + convertValue(multimap, "test_files_dir", testFilesDir); + convertValue(multimap, "load_files_dbs", loadFilesDbs); +} + +void ServiceSetup::BuildSetup::readPresets(const std::string &configFilePath, const std::string &presetsFileRelativePath) +{ + if (presetsFileRelativePath.empty()) { + return; + } + auto presetsFilePath = presetsFileRelativePath; + try { + if (presetsFilePath[0] != '/' && !configFilePath.empty()) { + presetsFilePath = std::filesystem::canonical(configFilePath).parent_path() / presetsFileRelativePath; + } + ReflectiveRapidJSON::JsonDeserializationErrors errors{}; + errors.throwOn = ReflectiveRapidJSON::JsonDeserializationErrors::ThrowOn::All; + presets = BuildPresets::fromJson(readFile(presetsFilePath), &errors); + } catch (const ReflectiveRapidJSON::JsonDeserializationError &e) { + cerr << Phrases::ErrorMessage << "Unable to deserialize presets file " << presetsFilePath << Phrases::SubMessage + << ReflectiveRapidJSON::formatJsonDeserializationError(e) << Phrases::End; + } catch (const RAPIDJSON_NAMESPACE::ParseResult &e) { + cerr << Phrases::ErrorMessage << "Unable to parse presets file " << presetsFilePath << Phrases::SubMessage << "parse error at " << e.Offset() + << ": " << RAPIDJSON_NAMESPACE::GetParseError_En(e.Code()) << Phrases::End; + } catch (const std::runtime_error &e) { + cerr << Phrases::ErrorMessage << "Unable to read presets file " << presetsFilePath << Phrases::SubMessage << e.what() << Phrases::End; + } +} + +void ServiceSetup::WebServerSetup::initSsl() +{ + if (!verifySslCertificates) { + std::cerr << Phrases::SubWarning << "Certificate validation disabled!" << std::endl; + return; + } + sslContext.set_verify_mode(boost::asio::ssl::verify_peer); + if (logSslCertificateValidation) { + sslContext.set_verify_callback(&WebServerSetup::logCertificateValidation); + } + sslContext.set_default_verify_paths(); +} + +bool ServiceSetup::WebServerSetup::logCertificateValidation(bool preVerified, boost::asio::ssl::verify_context &context) +{ + constexpr auto subjectNameLength = 1024; + char subjectName[subjectNameLength]; + X509 *const cert = X509_STORE_CTX_get_current_cert(context.native_handle()); + X509_NAME_oneline(X509_get_subject_name(cert), subjectName, subjectNameLength); + std::cerr << Phrases::InfoMessage << "Verifying SSL certificate: " << subjectName << Phrases::End; + if (!preVerified) { + std::cerr << Phrases::SubError << "verification failed" << endl; + } + return preVerified; +} + +ServiceSetup::BuildSetup::Worker::Worker(ServiceSetup::BuildSetup &setup) + : boost::asio::executor_work_guard(boost::asio::make_work_guard(setup.ioContext)) + , ThreadPool("Worker thread", setup.ioContext, setup.threadCount) + , setup(setup) +{ +} + +ServiceSetup::BuildSetup::Worker::~Worker() +{ + cout << Phrases::SuccessMessage << "Stopping worker threads" << Phrases::End; + setup.ioContext.stop(); +} + +ServiceSetup::BuildSetup::Worker ServiceSetup::BuildSetup::allocateBuildWorker() +{ + return Worker(*this); +} + +BuildAction::IdType ServiceSetup::BuildSetup::allocateBuildActionID() +{ + if (!invalidActions.empty()) { + const auto i = invalidActions.begin(); + const auto id = *i; + invalidActions.erase(i); + return id; + } + const auto id = actions.size(); + actions.emplace_back(); + return id; +} + +std::vector> ServiceSetup::BuildSetup::getBuildActions(const std::vector &ids) +{ + auto buildActions = std::vector>(); + buildActions.reserve(ids.size()); + for (const auto id : ids) { + if (id < actions.size()) { + if (auto &buildAction = actions[id]) { + buildActions.emplace_back(buildAction); + } + } + } + return buildActions; +} + +void ServiceSetup::loadConfigFiles(bool restoreStateAndDiscardDatabases) +{ + // read config file + cout << Phrases::InfoMessage << "Reading config file: " << configFilePath << Phrases::EndFlush; + IniFile configIni; + try { + // parse ini + ifstream configFile; + configFile.exceptions(fstream::badbit | fstream::failbit); + configFile.open(configFilePath, fstream::in); + configIni.parse(configFile); + // read basic configuration values (not cached) + for (const auto &iniEntry : configIni.data()) { + if (iniEntry.first.empty()) { + convertValue(iniEntry.second, "pacman_config_file_path", pacmanConfigFilePath); + convertValue(iniEntry.second, "working_directory", workingDirectory); + } + } + // apply working directory + if (!workingDirectory.empty()) { + try { + workingDirectory = std::filesystem::absolute(workingDirectory); + } catch (const std::filesystem::filesystem_error &e) { + cerr << Phrases::WarningMessage << "Unable to determine absolute path of specified working directory: " << e.what() + << Phrases::EndFlush; + } + if (chdir(workingDirectory.c_str()) != 0) { + cerr << Phrases::WarningMessage << "Unable to change the working directory to \"" << workingDirectory + << "\": " << std::strerror(errno) << Phrases::EndFlush; + } + } + // restore state/cache and discard databases + if (restoreStateAndDiscardDatabases) { + restoreState(); + config.markAllDatabasesToBeDiscarded(); + restoreStateAndDiscardDatabases = false; + } + // read webserver, build and user configuration (partially cached so read it after the cache has been restored to override cached values) + for (const auto &iniEntry : configIni.data()) { + if (iniEntry.first == "webserver") { + webServer.applyConfig(iniEntry.second); + } else if (iniEntry.first == "building") { + building.applyConfig(iniEntry.second); + std::string presetsFile; + convertValue(iniEntry.second, "presets", presetsFile); + building.readPresets(configFilePath, presetsFile); + } else if (startsWith(iniEntry.first, "user/")) { + auth.applyConfig(iniEntry.first.substr(5), iniEntry.second); + } + } + } catch (const ios_base::failure &) { + cerr << Phrases::WarningMessage << "An IO error occured when parsing \"" << configFilePath << "\", using defaults" << Phrases::EndFlush; + } + + // restore state/cache and discard databases if not done yet + if (restoreStateAndDiscardDatabases) { + restoreState(); + config.markAllDatabasesToBeDiscarded(); + } + + // read pacman config + try { + config.loadPacmanConfig(pacmanConfigFilePath.data()); + } catch (const ios_base::failure &e) { + cerr << Phrases::ErrorMessage << "An IO error occured when loading pacman config: " << e.what() << Phrases::EndFlush; + } catch (const runtime_error &e) { + cerr << Phrases::ErrorMessage << "An error occured when loading pacman config: " << e.what() << Phrases::EndFlush; + } + + // add databases declared in config + std::unordered_map globalDefinitions, dbDefinitions; + for (auto &iniEntry : configIni.data()) { + const auto &iniSection = iniEntry.first; + if (iniSection == "definitions") { + globalDefinitions.reserve(globalDefinitions.size() + iniEntry.second.size()); + for (auto &definition : iniEntry.second) { + globalDefinitions['$' + definition.first] = move(definition.second); + } + continue; + } + if (!startsWith(iniSection, "database/")) { + continue; + } + // find existing database or create a new one; clear mirrors and other data from existing DBs + auto *const db = config.findOrCreateDatabaseFromDenotation(std::string_view(iniSection.data() + 9, iniSection.size() - 9)); + db->toBeDiscarded = false; + dbDefinitions.clear(); + dbDefinitions["$repo"] = db->name; + dbDefinitions["$arch"] = db->arch; + for (auto &dirEntry : iniEntry.second) { + const auto &key(dirEntry.first); + auto &value(dirEntry.second); + for (auto i = 0; i != 2; ++i) { + for (const auto &definitions : { dbDefinitions, globalDefinitions }) { + for (const auto &definition : definitions) { + findAndReplace(value, definition.first, definition.second); + } + } + } + if (key == "arch") { + db->arch = move(value); + dbDefinitions["$arch"] = db->arch; + } else if (key == "depends") { + db->dependencies = splitString>(value, " ", EmptyPartsTreat::Omit); + } else if (key == "pkgdir") { + db->localPkgDir = move(value); + } else if (key == "dbdir") { + db->localDbDir = move(value); + } else if (key == "path") { + db->path = move(value); + } else if (key == "filespath") { + db->filesPath = move(value); + } else if (key == "sync_from_mirror") { + db->syncFromMirror = value == "on"; + } else if (key == "mirror") { + db->mirrors.emplace_back(move(value)); + } else { + dbDefinitions["$" + key] = move(value); + } + } + } + + // deduce database paths from local database dirs; remove duplicated mirrors + for (auto &db : config.databases) { + db.deducePathsFromLocalDirs(); + deduplicateVector(db.mirrors); + } + + // log the most important config values + cerr << Phrases::InfoMessage << "Working directory: " << workingDirectory << Phrases::EndFlush; + cerr << Phrases::InfoMessage << "Package cache directory: " << building.packageCacheDir << Phrases::EndFlush; + cerr << Phrases::InfoMessage << "Chroot directory: " << building.chrootDir << Phrases::EndFlush; + cerr << Phrases::InfoMessage << "Chroot root user: " << building.chrootRootUser << Phrases::EndFlush; + cerr << Phrases::InfoMessage << "Chroot default user: " << building.chrootDefaultUser << Phrases::EndFlush; + cerr << Phrases::InfoMessage << "Ccache directory: " << building.ccacheDir << Phrases::EndFlush; +} + +void ServiceSetup::printDatabases() +{ + cerr << Phrases::SuccessMessage << "Found " << config.databases.size() << " databases:" << Phrases::End; + for (const auto &db : config.databases) { + cerr << Phrases::SubMessage << db.name << "@" << db.arch << ": " << db.packages.size() << " packages, last updated on " + << db.lastUpdate.toString(DateTimeOutputFormat::DateAndTime) << Phrases::End << " - path: " << db.path + << "\n - local db dir: " << db.localDbDir << "\n - local package dir: " << db.localPkgDir << '\n'; + } + cerr << Phrases::SubMessage << "AUR (" << config.aur.packages.size() << " packages cached)" << Phrases::End; +} + +std::string_view ServiceSetup::cacheFilePath() const +{ + return "cache-v" LIBREPOMGR_CACHE_VERSION ".bin"; +} + +RAPIDJSON_NAMESPACE::Document ServiceSetup::libraryDependenciesToJson() const +{ + namespace JR = ReflectiveRapidJSON::JsonReflector; + auto document = RAPIDJSON_NAMESPACE::Document(RAPIDJSON_NAMESPACE::kObjectType); + auto &alloc = document.GetAllocator(); + for (const auto &db : config.databases) { + auto dbValue = RAPIDJSON_NAMESPACE::Value(RAPIDJSON_NAMESPACE::Type::kObjectType); + for (const auto &[pkgName, pkg] : db.packages) { + if (!pkg->packageInfo) { + continue; + } + if (pkg->libdepends.empty() && pkg->libprovides.empty()) { + auto hasVersionedPythonOrPerlDep = false; + for (const auto &dependency : pkg->dependencies) { + if (dependency.mode == DependencyMode::Any || dependency.version.empty() + || (dependency.name != "python" && dependency.name != "python2" && dependency.name != "perl")) { + continue; + } + hasVersionedPythonOrPerlDep = true; + break; + } + if (!hasVersionedPythonOrPerlDep) { + continue; + } + } + auto pkgValue = RAPIDJSON_NAMESPACE::Value(RAPIDJSON_NAMESPACE::Type::kObjectType); + auto pkgObj = pkgValue.GetObject(); + JR::push(pkg->version, "v", pkgObj, alloc); + JR::push(pkg->packageInfo->buildDate, "t", pkgObj, alloc); + JR::push(pkg->dependencies, "d", pkgObj, alloc); // for versioned Python/Perl deps + JR::push(pkg->libdepends, "ld", pkgObj, alloc); + JR::push(pkg->libprovides, "lp", pkgObj, alloc); + dbValue.AddMember(RAPIDJSON_NAMESPACE::StringRef(pkgName.data(), JR::rapidJsonSize(pkgName.size())), pkgValue, alloc); + } + document.AddMember(RAPIDJSON_NAMESPACE::Value(db.name % '@' + db.arch, alloc), dbValue, alloc); + } + return document; +} + +void ServiceSetup::restoreLibraryDependenciesFromJson(const string &json, ReflectiveRapidJSON::JsonDeserializationErrors *errors) +{ + namespace JR = ReflectiveRapidJSON::JsonReflector; + const auto document = JR::parseJsonDocFromString(json.data(), json.size()); + if (!document.IsObject()) { + errors->reportTypeMismatch>(document.GetType()); + return; + } + // FIXME: be more error resilient here, e.g. set the following line and print list of errors instead of aborting on first error + // errors->throwOn = ReflectiveRapidJSON::JsonDeserializationErrors::ThrowOn::None; + const auto dbObj = document.GetObject(); + for (const auto &dbEntry : dbObj) { + if (!dbEntry.value.IsObject()) { + errors->reportTypeMismatch(document.GetType()); + continue; + } + auto *const db = config.findOrCreateDatabaseFromDenotation(std::string_view(dbEntry.name.GetString())); + const auto pkgsObj = dbEntry.value.GetObject(); + for (const auto &pkgEntry : pkgsObj) { + if (!pkgEntry.value.IsObject()) { + errors->reportTypeMismatch(document.GetType()); + continue; + } + const auto pkgObj = pkgEntry.value.GetObject(); + auto name = std::string(pkgEntry.name.GetString()); + auto &pkg = db->packages[name]; + if (pkg) { + // do not mess with already existing packages; this restoring stuff is supposed to be done before loading packages from DBs + continue; + } + pkg = std::make_shared(); + pkg->name = std::move(name); + pkg->origin = PackageOrigin::CustomSource; + pkg->packageInfo = std::make_unique(); + JR::pull(pkg->version, "v", pkgObj, errors); + JR::pull(pkg->packageInfo->buildDate, "t", pkgObj, errors); + JR::pull(pkg->dependencies, "d", pkgObj, errors); // for versioned Python/Perl deps + JR::pull(pkg->libdepends, "ld", pkgObj, errors); + JR::pull(pkg->libprovides, "lp", pkgObj, errors); + db->addPackageDependencies(pkg); + } + } +} + +std::size_t ServiceSetup::restoreState() +{ + // clear old build actions before (There must not be any ongoing build actions when calling this function!) + building.actions.clear(); + building.invalidActions.clear(); + + // restore configuration and maybe build actions from JSON file + const auto cacheFilePath = this->cacheFilePath(); + std::size_t size = 0; + bool hasConfig = false, hasBuildActions = false; + try { + fstream cacheFile; + cacheFile.exceptions(ios_base::failbit | ios_base::badbit); + cacheFile.open(cacheFilePath.data(), ios_base::in | ios_base::binary); + ReflectiveRapidJSON::BinaryReflector::BinaryDeserializer deserializer(&cacheFile); + deserializer.read(config); + hasConfig = false; + if (!hasBuildActions) { + deserializer.read(building.actions); + hasBuildActions = true; + } + size = static_cast(cacheFile.tellg()); + cacheFile.close(); + cerr << Phrases::SuccessMessage << "Restored cache file \"" << cacheFilePath << "\", " << dataSizeToString(size) << Phrases::EndFlush; + if (hasBuildActions) { + cerr << Phrases::SubMessage << "Restored build actions from cache file 😌" << Phrases::EndFlush; + } + } catch (const ConversionException &) { + cerr << Phrases::WarningMessage << "A conversion error occured when restoring cache file \"" << cacheFilePath << "\"." << Phrases::EndFlush; + } catch (const ios_base::failure &) { + cerr << Phrases::WarningMessage << "An IO error occured when restoring cache file \"" << cacheFilePath << "\"." << Phrases::EndFlush; + } + + // restore build actions from JSON file + if (!hasBuildActions) { + try { + if (!restoreJsonObject(building.actions, workingDirectory, "build-actions-v" LIBREPOMGR_BUILD_ACTIONS_JSON_VERSION, + RestoreJsonExistingFileHandling::Skip) + .empty()) { + cerr << Phrases::SuccessMessage << "Restored build actions from JSON file 😒" << Phrases::EndFlush; + } + } catch (const std::runtime_error &e) { + cerr << Phrases::ErrorMessage << e.what() << Phrases::EndFlush; + } + } + + // restore provided/required libraries from JSON file + if (!hasConfig) { + try { + if (!restoreJsonObject(std::bind(&ServiceSetup::restoreLibraryDependenciesFromJson, this, std::placeholders::_1, std::placeholders::_2), + workingDirectory, "library-dependencies-v" LIBREPOMGR_LIBRARY_DEPENDENCIES_JSON_VERSION, RestoreJsonExistingFileHandling::Skip) + .empty()) { + cerr << Phrases::SuccessMessage << "Restored library dependencies from JSON file 😒" << Phrases::EndFlush; + } + } catch (const std::runtime_error &e) { + cerr << Phrases::ErrorMessage << e.what() << Phrases::EndFlush; + } + } + + // determine invalid build actions + if (building.actions.empty()) { + return size; + } + auto newActionsSize = building.actions.size(); + for (auto id = newActionsSize - 1;; --id) { + if (building.actions[id]) { + break; + } + newActionsSize = id; + if (!newActionsSize) { + break; + } + } + building.actions.resize(newActionsSize); + for (std::size_t buildActionId = 0, size = building.actions.size(); buildActionId != size; ++buildActionId) { + if (!building.actions[buildActionId]) { + building.invalidActions.emplace(buildActionId); + } + } + + // ensure no build actions are considered running anymore and populate follow up actions + for (auto &action : building.actions) { + if (!action) { + continue; + } + for (const auto previousBuildActionID : action->startAfter) { + if (auto previousBuildAction = building.getBuildAction(previousBuildActionID)) { + previousBuildAction->m_followUpActions.emplace_back(action->weak_from_this()); + } + } + if (action->isExecuting()) { + action->status = BuildActionStatus::Finished; + action->result = BuildActionResult::Failure; + action->resultData = "service crashed while exectuing"; + } + } + + return size; +} + +std::size_t ServiceSetup::saveState() +{ + // write cache file to be able to restore the service state when restarting the service efficiently + const auto cacheFilePath = this->cacheFilePath(); + std::size_t size = 0; + try { + fstream cacheFile; + cacheFile.exceptions(ios_base::failbit | ios_base::badbit); + cacheFile.open(cacheFilePath.data(), ios_base::out | ios_base::trunc | ios_base::binary); + ReflectiveRapidJSON::BinaryReflector::BinarySerializer serializer(&cacheFile); + serializer.write(config); + serializer.write(building.actions); + size = static_cast(cacheFile.tellp()); + cacheFile.close(); + cerr << Phrases::SuccessMessage << "Wrote cache file \"" << cacheFilePath << "\", " << dataSizeToString(size) << Phrases::EndFlush; + } catch (const ios_base::failure &) { + cerr << Phrases::WarningMessage << "An IO error occured when dumping the cache file \"" << cacheFilePath << "\"." << Phrases::EndFlush; + } + + // write build actions to a JSON file to be able to restore build actions even if the cache file can not be used due to version mismatch + // note: The JSON file's format is hopefully more stable. + try { + if (!dumpJsonObject( + building.actions, workingDirectory, "build-actions-v" LIBREPOMGR_BUILD_ACTIONS_JSON_VERSION, DumpJsonExistingFileHandling::Override) + .empty()) { + cerr << Phrases::SuccessMessage << "Wrote build actions to JSON file." << Phrases::EndFlush; + } + } catch (const std::runtime_error &e) { + cerr << Phrases::ErrorMessage << e.what() << Phrases::EndFlush; + } + + // write provided/required libraries to a JSON file to be able to restore this information even if the cache file can not be used due to version + // mismatch + try { + if (!dumpJsonDocument(std::bind(&ServiceSetup::libraryDependenciesToJson, this), workingDirectory, + "library-dependencies-v" LIBREPOMGR_LIBRARY_DEPENDENCIES_JSON_VERSION, DumpJsonExistingFileHandling::Override) + .empty()) { + cerr << Phrases::SuccessMessage << "Wrote library dependencies to JSON file." << Phrases::EndFlush; + } + } catch (const std::runtime_error &e) { + cerr << Phrases::ErrorMessage << e.what() << Phrases::EndFlush; + } + + return size; +} + +void ServiceSetup::run() +{ +#ifndef CPP_UTILITIES_DEBUG_BUILD + try { +#endif + loadConfigFiles(true); + config.discardDatabases(); + config.loadAllPackages(building.loadFilesDbs); +#ifndef CPP_UTILITIES_DEBUG_BUILD + } catch (const std::exception &e) { + cerr << Phrases::SubError << e.what() << endl; + } catch (...) { + cerr << Phrases::SubError << "An unknown error occurred." << endl; + } +#endif + + printDatabases(); + + cout << Phrases::SuccessMessage << "Initializing SSL" << Phrases::End; + webServer.initSsl(); + + { + cout << Phrases::SuccessMessage << "Allocating worker thread pool (thread count: " << building.threadCount << ")" << Phrases::End; + const auto buildWorker = building.allocateBuildWorker(); + +#ifndef CPP_UTILITIES_DEBUG_BUILD + try { +#endif + cout << Phrases::SuccessMessage << "Starting web server (thread count: " << webServer.threadCount << "):" << TextAttribute::Reset + << " http://" << webServer.address << ':' << webServer.port << endl; + WebAPI::Server::serve(*this); + cout << Phrases::SuccessMessage << "Web server stopped." << Phrases::EndFlush; +#ifndef CPP_UTILITIES_DEBUG_BUILD + } catch (const std::exception &e) { + cerr << Phrases::ErrorMessage << "Server terminated due to exception: " << Phrases::End << " " << e.what() << Phrases::EndFlush; + } catch (...) { + cerr << Phrases::ErrorMessage << "Server terminated due to an unknown error." << Phrases::EndFlush; + } +#endif + } + + for (auto &buildAction : building.actions) { + if (buildAction && buildAction->isExecuting()) { + buildAction->status = BuildActionStatus::Finished; + buildAction->result = BuildActionResult::Aborted; + } + } + + saveState(); +} + +ServiceStatus::ServiceStatus(const ServiceSetup &setup) + : version(applicationInfo.version) + , config(setup.config.computeStatus()) + , actions(setup.building.metaInfo) + , presets(setup.building.presets) +{ +} + +} // namespace LibRepoMgr diff --git a/librepomgr/serversetup.h b/librepomgr/serversetup.h new file mode 100644 index 0000000..f50e7b6 --- /dev/null +++ b/librepomgr/serversetup.h @@ -0,0 +1,155 @@ +#ifndef LIBREPOMGR_SERVER_SETUP_H +#define LIBREPOMGR_SERVER_SETUP_H + +#include "./authentication.h" +#include "./buildactions/buildaction.h" +#include "./buildactions/buildactiontemplate.h" + +#include "../libpkg/data/config.h" +#include "../libpkg/data/lockable.h" + +#include + +#include +#include +#include + +#include +#include +#include + +namespace LibRepoMgr { + +struct LIBREPOMGR_EXPORT ThreadPool { + explicit ThreadPool(const char *name, boost::asio::io_context &ioContext, unsigned short threadCount); + ~ThreadPool(); + + const char *const name; + std::vector threads; +}; + +struct ServiceStatus; + +struct LIBREPOMGR_EXPORT ServiceSetup : public LibPkg::Lockable { + // the overall configuration (databases, packages, ...) used at various places + // -> acquire the config lock for these + LibPkg::Config config; + + // service global configuration; only changed when (re)loading config + // -> acquire the setup lock for these + std::string configFilePath = "server.conf"; + std::string pacmanConfigFilePath = "/etc/pacman.conf"; + std::string workingDirectory = "workingdir"; + + // variables relevant for the web server; only changed when (re)loading config + struct LIBREPOMGR_EXPORT WebServerSetup { + // only read by build actions and routes; changed when (re)loading config + // -> acquire the setup lock for these + std::string staticFilesPath; + + // never changed after setup + boost::asio::ip::address address = boost::asio::ip::make_address("127.0.0.1"); + unsigned short port = 8090; + unsigned short threadCount = 1; + boost::asio::io_context ioContext; + boost::asio::ssl::context sslContext{ boost::asio::ssl::context::sslv23_client }; + bool verifySslCertificates = true; + bool logSslCertificateValidation = false; + + void applyConfig(const std::multimap &multimap); + void initSsl(); + static bool logCertificateValidation(bool preVerified, boost::asio::ssl::verify_context &context); + } webServer; + + // variables relevant for build actions and web server routes dealing with them + struct LIBREPOMGR_EXPORT BuildSetup : public LibPkg::Lockable { + struct LIBREPOMGR_EXPORT Worker : private boost::asio::executor_work_guard, public ThreadPool { + explicit Worker(BuildSetup &setup); + ~Worker(); + BuildSetup &setup; + }; + + // read/written by build actions and routes + // -> acquire the build lock for these + std::vector> actions; + std::unordered_set invalidActions; + + // fields which have their own locking + // -> acquire the object's lock + BuildActionMetaInfo metaInfo; // only static data so far but maybe extended to allow defining custom build actions + + // only read by build actions and routes; changed when (re)loading config + // -> acquire the setup lock for these + std::string workingDirectory = "building"; + std::vector pkgbuildsDirs; + std::regex ignoreLocalPkgbuildsRegex; + std::string makePkgPath = "makepkg"; + std::string makeChrootPkgPath = "makechrootpkg"; + std::string updatePkgSumsPath = "updpkgsums"; + std::string repoAddPath = "repo-add"; + std::string repoRemovePath = "repo-remove"; + std::string ccacheDir; + std::string chrootDir; + std::string chrootRootUser = "root"; + std::string chrootDefaultUser = "buildservice"; + std::string pacmanConfigFilePath; // FIXME: not useful after all?; using config-$arch directory within chrootDir instead + std::string makepkgConfigFilePath; // FIXME: not useful after all?; using config-$arch directory within chrootDir instead + std::vector makechrootpkgFlags; + std::vector makepkgFlags; + std::string packageCacheDir; + std::string testFilesDir; + BuildPresets presets; + bool loadFilesDbs = false; + + // never changed after startup + unsigned short threadCount = 4; + boost::asio::io_context ioContext; + + void applyConfig(const std::multimap &multimap); + void readPresets(const std::string &configFilePath, const std::string &presetsFile); + Worker allocateBuildWorker(); + BuildAction::IdType allocateBuildActionID(); + std::shared_ptr getBuildAction(BuildAction::IdType id); + std::vector> getBuildActions(const std::vector &ids); + } building; + + struct LIBREPOMGR_EXPORT Authentication : public LibPkg::Lockable { + std::unordered_map users; + + void applyConfig(const std::string &userName, const std::multimap &multimap); + UserPermissions authenticate(std::string_view authorizationHeader) const; + } auth; + + void loadConfigFiles(bool restoreStateAndDiscardDatabases); + void printDatabases(); + std::string_view cacheFilePath() const; + RAPIDJSON_NAMESPACE::Document libraryDependenciesToJson() const; + void restoreLibraryDependenciesFromJson(const std::string &json, ReflectiveRapidJSON::JsonDeserializationErrors *errors); + std::size_t restoreState(); + std::size_t saveState(); + void run(); + ServiceStatus computeStatus() const; +}; + +inline std::shared_ptr ServiceSetup::BuildSetup::getBuildAction(BuildAction::IdType id) +{ + return id < actions.size() ? actions[id] : nullptr; +} + +struct LIBREPOMGR_EXPORT ServiceStatus : public ReflectiveRapidJSON::JsonSerializable { + ServiceStatus(const ServiceSetup &setup); + + const char *const version = nullptr; + const LibPkg::Status config; + const BuildActionMetaInfo &actions; + const BuildPresets &presets; +}; + +inline ServiceStatus ServiceSetup::computeStatus() const +{ + return ServiceStatus(*this); +} + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_SERVER_SETUP_H diff --git a/librepomgr/testfiles/building/build-data/conduct-build-test/boost/src/.SRCINFO b/librepomgr/testfiles/building/build-data/conduct-build-test/boost/src/.SRCINFO new file mode 100644 index 0000000..652930e --- /dev/null +++ b/librepomgr/testfiles/building/build-data/conduct-build-test/boost/src/.SRCINFO @@ -0,0 +1,77 @@ +pkgbase = boost + pkgdesc = Free peer-reviewed portable C++ source libraries + pkgver = 1.73.0 + pkgrel = 1 + url = https://www.boost.org/ + arch = x86_64 + license = custom + makedepends = icu + makedepends = python + makedepends = python2 + makedepends = python-numpy + makedepends = python2-numpy + makedepends = bzip2 + makedepends = zlib + makedepends = openmpi + makedepends = zstd + makedepends = findutils + source = https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2 + sha256sums = 4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402 + b2sums = 5995ff6ca21f45988b187b94bf743795cca97531baa8355f488be0987b9426289dd85d6ce25b7eb03ccd690109f05ba56252a95bca50505ad9cd66f4e0e234e8 + +pkgname = boost-libs + pkgdesc = Free peer-reviewed portable C++ source libraries - runtime libraries + depends = bzip2 + depends = zlib + depends = icu + depends = zstd + optdepends = openmpi: for mpi support + provides = libboost_atomic.so + provides = libboost_chrono.so + provides = libboost_container.so + provides = libboost_context.so + provides = libboost_contract.so + provides = libboost_coroutine.so + provides = libboost_date_time.so + provides = libboost_fiber.so + provides = libboost_filesystem.so + provides = libboost_graph.so + provides = libboost_graph_parallel.so + provides = libboost_iostreams.so + provides = libboost_locale.so + provides = libboost_log.so + provides = libboost_log_setup.so + provides = libboost_math_c99.so + provides = libboost_math_c99f.so + provides = libboost_math_c99l.so + provides = libboost_math_tr1.so + provides = libboost_math_tr1f.so + provides = libboost_math_tr1l.so + provides = libboost_mpi.so + provides = libboost_numpy27.so + provides = libboost_numpy38.so + provides = libboost_prg_exec_monitor.so + provides = libboost_program_options.so + provides = libboost_python27.so + provides = libboost_python38.so + provides = libboost_random.so + provides = libboost_regex.so + provides = libboost_serialization.so + provides = libboost_stacktrace_addr2line.so + provides = libboost_stacktrace_basic.so + provides = libboost_stacktrace_noop.so + provides = libboost_system.so + provides = libboost_thread.so + provides = libboost_timer.so + provides = libboost_type_erasure.so + provides = libboost_unit_test_framework.so + provides = libboost_wave.so + provides = libboost_wserialization.so + +pkgname = boost + pkgdesc = Free peer-reviewed portable C++ source libraries - development headers + depends = boost-libs=1.73.0 + optdepends = python: for python bindings + optdepends = python2: for python2 bindings + options = staticlibs + diff --git a/librepomgr/testfiles/building/build-data/conduct-build-test/boost/src/PKGBUILD b/librepomgr/testfiles/building/build-data/conduct-build-test/boost/src/PKGBUILD new file mode 100644 index 0000000..6802227 --- /dev/null +++ b/librepomgr/testfiles/building/build-data/conduct-build-test/boost/src/PKGBUILD @@ -0,0 +1,154 @@ +# Maintainer: Levente Polyak +# Contributor: Bartłomiej Piotrowski +# Contributor: Marius Knaust +# Contributor: Ionut Biru +# Contributor: Stéphane Gaudreault +# Contributor: kevin +# Contributor: Giovanni Scafora +# Contributor: Kritoke +# Contributor: Luca Roccia + +pkgbase=boost +pkgname=('boost-libs' 'boost') +pkgver=1.73.0 +_boostver=${pkgver//./_} +pkgrel=1 +pkgdesc='Free peer-reviewed portable C++ source libraries' +url='https://www.boost.org/' +arch=('x86_64') +license=('custom') +makedepends=('icu' 'python' 'python2' 'python-numpy' 'python2-numpy' 'bzip2' 'zlib' 'openmpi' 'zstd' 'findutils') +source=(https://dl.bintray.com/boostorg/release/${pkgver}/source/boost_${_boostver}.tar.bz2) +sha256sums=('4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402') +b2sums=('5995ff6ca21f45988b187b94bf743795cca97531baa8355f488be0987b9426289dd85d6ce25b7eb03ccd690109f05ba56252a95bca50505ad9cd66f4e0e234e8') + +build() { + export _stagedir="${srcdir}/stagedir" + local JOBS="$(sed -e 's/.*\(-j *[0-9]\+\).*/\1/' <<< ${MAKEFLAGS})" + + cd ${pkgbase}_${_boostver} + + ./bootstrap.sh --with-toolset=gcc --with-icu --with-python=/usr/bin/python2 + + install -Dm755 tools/build/src/engine/b2 "${_stagedir}"/bin/b2 + + # Support for OpenMPI + echo "using mpi ;" >> project-config.jam + + # boostbook is needed by quickbook + install -dm755 "${_stagedir}"/share/boostbook + cp -a tools/boostbook/{xsl,dtd} "${_stagedir}"/share/boostbook/ + + # default "minimal" install: "release link=shared,static + # runtime-link=shared threading=single,multi" + # --layout=tagged will add the "-mt" suffix for multithreaded libraries + # and installs includes in /usr/include/boost. + # --layout=system no longer adds the -mt suffix for multi-threaded libs. + # install to ${_stagedir} in preparation for split packaging + "${_stagedir}"/bin/b2 \ + variant=release \ + debug-symbols=off \ + threading=multi \ + runtime-link=shared \ + link=shared,static \ + toolset=gcc \ + python=2.7 \ + cflags="${CPPFLAGS} ${CFLAGS} -fPIC -O3" \ + cxxflags="${CPPFLAGS} ${CXXFLAGS} -std=c++14 -fPIC -O3" \ + linkflags="${LDFLAGS}" \ + --layout=system \ + ${JOBS} \ + \ + --prefix="${_stagedir}" \ + install + + # because b2 in boost 1.62.0 doesn't seem to respect python parameter, we + # need another run for liboost_python3.so + + ./bootstrap.sh \ + --with-toolset=gcc \ + --with-icu \ + --with-python=/usr/bin/python3 \ + --with-libraries=python + + "${_stagedir}"/bin/b2 clean + "${_stagedir}"/bin/b2 \ + variant=release \ + debug-symbols=off \ + threading=multi \ + runtime-link=shared \ + link=shared,static \ + toolset=gcc \ + python=3.8 \ + cflags="${CPPFLAGS} ${CFLAGS} -fPIC -O3" \ + cxxflags="${CPPFLAGS} ${CXXFLAGS} -std=c++14 -fPIC -O3" \ + linkflags="${LDFLAGS}" \ + --layout=system \ + ${JOBS} \ + \ + --prefix="${_stagedir}/python3" \ + --with-python \ + install +} + +package_boost() { + pkgdesc+=' - development headers' + depends=("boost-libs=${pkgver}") + optdepends=('python: for python bindings' + 'python2: for python2 bindings') + options=('staticlibs') + + install -dm755 "${pkgdir}"/usr + cp -a "${_stagedir}"/{bin,include,share} "${pkgdir}"/usr + + install -d "${pkgdir}"/usr/lib + cp -a "${_stagedir}"/lib/*.a "${pkgdir}"/usr/lib/ + cp -a "${_stagedir}"/lib/cmake "${pkgdir}"/usr/lib/ + + install -Dm644 "${srcdir}/"${pkgbase}_${_boostver}/LICENSE_1_0.txt \ + "${pkgdir}"/usr/share/licenses/boost/LICENSE_1_0.txt + + install -Dm644 "${_stagedir}"/python3/lib/libboost_*.a \ + "${pkgdir}"/usr/lib/ + cp -a "${_stagedir}"/python3/lib/cmake/* "${pkgdir}"/usr/lib/cmake/ + + ln -s /usr/bin/b2 "$pkgdir"/usr/bin/bjam +} + +package_boost-libs() { + pkgdesc+=' - runtime libraries' + depends=('bzip2' 'zlib' 'icu' 'zstd') + optdepends=('openmpi: for mpi support') + provides=(libboost_atomic.so libboost_chrono.so libboost_container.so + libboost_context.so libboost_contract.so libboost_coroutine.so + libboost_date_time.so libboost_fiber.so libboost_filesystem.so + libboost_graph.so libboost_graph_parallel.so libboost_iostreams.so + libboost_locale.so libboost_log.so libboost_log_setup.so + libboost_math_c99.so libboost_math_c99f.so libboost_math_c99l.so + libboost_math_tr1.so libboost_math_tr1f.so libboost_math_tr1l.so + libboost_mpi.so libboost_numpy27.so libboost_numpy38.so + libboost_prg_exec_monitor.so libboost_program_options.so + libboost_python27.so libboost_python38.so libboost_random.so + libboost_regex.so libboost_serialization.so + libboost_stacktrace_addr2line.so libboost_stacktrace_basic.so + libboost_stacktrace_noop.so libboost_system.so libboost_thread.so + libboost_timer.so libboost_type_erasure.so libboost_unit_test_framework.so + libboost_wave.so libboost_wserialization.so) + + install -dm755 "${pkgdir}"/usr + cp -a "${_stagedir}"/lib "${pkgdir}"/usr + cp -a "${_stagedir}"/python3/lib/libboost_* "${pkgdir}"/usr/lib + rm "${pkgdir}"/usr/lib/*.a + rm -r "${pkgdir}"/usr/lib/cmake + + # https://github.com/boostorg/python/issues/203#issuecomment-391477685 + for _lib in python numpy; do + ln -srL "${pkgdir}"/usr/lib/libboost_${_lib}{27,}.so + ln -srL "${pkgdir}"/usr/lib/libboost_${_lib}3{8,}.so + done + + install -Dm644 "${srcdir}/"${pkgbase}_${_boostver}/LICENSE_1_0.txt \ + "${pkgdir}"/usr/share/licenses/boost-libs/LICENSE_1_0.txt +} + +# vim: ts=2 sw=2 et: diff --git a/librepomgr/testfiles/building/build-data/conduct-build-test/build-preparation.json b/librepomgr/testfiles/building/build-data/conduct-build-test/build-preparation.json new file mode 100644 index 0000000..6fe8f81 --- /dev/null +++ b/librepomgr/testfiles/building/build-data/conduct-build-test/build-preparation.json @@ -0,0 +1,840 @@ +{ + "buildData": { + "boost": { + "existingVersion": "1.72.0-3", + "existingPackages": [ + { + "origin": 4, + "timestamp": "0001-01-01T00:00:00", + "name": "boost", + "version": "1.72.0-3", + "description": "Free peer-reviewed portable C++ source libraries - development headers", + "upstreamUrl": "https://www.boost.org/", + "licenses": [ + "custom" + ], + "groups": [], + "dependencies": [ + { + "name": "boost-libs", + "version": "1.72.0", + "description": "", + "mode": 2 + } + ], + "optionalDependencies": [ + { + "name": "python", + "version": "", + "description": "for python bindings", + "mode": 1 + }, + { + "name": "python2", + "version": "", + "description": "for python2 bindings", + "mode": 1 + } + ], + "conflicts": [], + "provides": [ + { + "name": "boost", + "version": "1.72.0-3", + "description": "", + "mode": 1 + } + ], + "replaces": [], + "libprovides": [], + "libdepends": [], + "sourceInfo": { + "name": "boost", + "archs": [], + "makeDependencies": [ + { + "name": "icu", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "bzip2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zlib", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "openmpi", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zstd", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "findutils", + "version": "", + "description": "", + "mode": 1 + } + ], + "checkDependencies": [], + "maintainer": "", + "id": 0, + "category": 0, + "votes": 0, + "outOfDate": "0001-01-01T00:00:00", + "firstSubmitted": "0001-01-01T00:00:00", + "lastModified": "0001-01-01T00:00:00", + "url": "", + "sources": [], + "directory": "" + }, + "packageInfo": { + "fileName": "boost-1.72.0-3-x86_64.pkg.tar.zst", + "files": [], + "buildDate": "2020-05-12T10:09:15", + "packager": "Martchus ", + "md5": "e08babd70dd5d6d4e7847ebc66050c15", + "sha256": "5661fa6e62472b484bb77bf01401a066d3e301d43e93f750edc8bb82c13bd41d", + "pgpSignature": "", + "arch": "x86_64", + "size": 13872279 + }, + "installInfo": { + "installDate": "0001-01-01T00:00:00", + "installedSize": 183748402, + "backupFiles": [], + "installStatus": 20, + "validationMethods": 1 + } + } + ], + "sourceDirectory": "building/build-data/conduct-build-test/boost/src", + "originalSourceDirectory": "$ORIGINAL_SOURCE_DIRECTORY", + "sourceInfo": { + "name": "boost", + "archs": [ + "x86_64" + ], + "makeDependencies": [ + { + "name": "icu", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "bzip2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zlib", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "openmpi", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zstd", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "findutils", + "version": "", + "description": "", + "mode": 1 + } + ], + "checkDependencies": [], + "maintainer": "", + "id": 0, + "category": 0, + "votes": 0, + "outOfDate": "0001-01-01T00:00:00", + "firstSubmitted": "0001-01-01T00:00:00", + "lastModified": "0001-01-01T00:00:00", + "url": "", + "sources": [], + "directory": "" + }, + "packages": [ + { + "origin": 2, + "timestamp": "0001-01-01T00:00:00", + "name": "boost-libs", + "version": "1.73.0-1", + "description": "Free peer-reviewed portable C++ source libraries - runtime libraries", + "upstreamUrl": "https://www.boost.org/", + "licenses": [ + "custom" + ], + "groups": [], + "dependencies": [ + { + "name": "bzip2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zlib", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "icu", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zstd", + "version": "", + "description": "", + "mode": 1 + } + ], + "optionalDependencies": [ + { + "name": "openmpi", + "version": "", + "description": "for mpi support", + "mode": 1 + } + ], + "conflicts": [], + "provides": [ + { + "name": "libboost_atomic.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_chrono.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_container.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_context.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_contract.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_coroutine.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_date_time.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_fiber.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_filesystem.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_graph.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_graph_parallel.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_iostreams.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_locale.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_log.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_log_setup.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_math_c99.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_math_c99f.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_math_c99l.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_math_tr1.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_math_tr1f.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_math_tr1l.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_mpi.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_numpy27.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_numpy38.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_prg_exec_monitor.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_program_options.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_python27.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_python38.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_random.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_regex.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_serialization.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_stacktrace_addr2line.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_stacktrace_basic.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_stacktrace_noop.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_system.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_thread.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_timer.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_type_erasure.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_unit_test_framework.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_wave.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "libboost_wserialization.so", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "boost-libs", + "version": "1.73.0-1", + "description": "", + "mode": 1 + } + ], + "replaces": [], + "libprovides": [], + "libdepends": [], + "sourceInfo": { + "name": "boost", + "archs": [ + "x86_64" + ], + "makeDependencies": [ + { + "name": "icu", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "bzip2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zlib", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "openmpi", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zstd", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "findutils", + "version": "", + "description": "", + "mode": 1 + } + ], + "checkDependencies": [], + "maintainer": "", + "id": 0, + "category": 0, + "votes": 0, + "outOfDate": "0001-01-01T00:00:00", + "firstSubmitted": "0001-01-01T00:00:00", + "lastModified": "0001-01-01T00:00:00", + "url": "", + "sources": [], + "directory": "" + }, + "packageInfo": null, + "installInfo": null + }, + { + "origin": 2, + "timestamp": "0001-01-01T00:00:00", + "name": "boost", + "version": "1.73.0-1", + "description": "Free peer-reviewed portable C++ source libraries - development headers", + "upstreamUrl": "https://www.boost.org/", + "licenses": [ + "custom" + ], + "groups": [], + "dependencies": [ + { + "name": "boost-libs", + "version": "1.73.0", + "description": "", + "mode": 2 + } + ], + "optionalDependencies": [ + { + "name": "python", + "version": "", + "description": "for python bindings", + "mode": 1 + }, + { + "name": "python2", + "version": "", + "description": "for python2 bindings", + "mode": 1 + } + ], + "conflicts": [], + "provides": [ + { + "name": "boost", + "version": "1.73.0-1", + "description": "", + "mode": 1 + } + ], + "replaces": [], + "libprovides": [], + "libdepends": [], + "sourceInfo": { + "name": "boost", + "archs": [ + "x86_64" + ], + "makeDependencies": [ + { + "name": "icu", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "python2-numpy", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "bzip2", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zlib", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "openmpi", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "zstd", + "version": "", + "description": "", + "mode": 1 + }, + { + "name": "findutils", + "version": "", + "description": "", + "mode": 1 + } + ], + "checkDependencies": [], + "maintainer": "", + "id": 0, + "category": 0, + "votes": 0, + "outOfDate": "0001-01-01T00:00:00", + "firstSubmitted": "0001-01-01T00:00:00", + "lastModified": "0001-01-01T00:00:00", + "url": "", + "sources": [], + "directory": "" + }, + "packageInfo": null, + "installInfo": null + } + ], + "warnings": [], + "error": "", + "specifiedIndex": 0, + "hasSource": true + } + }, + "dbConfig": [ + [ + "misc", + { + "Server": [ + "file://$TEST_FILES_PATH/repo/misc/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ], + [ + "boost", + { + "Server": [ + "file://$TEST_FILES_PATH/repo/boost/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ], + [ + "core", + { + "Server": [ + "http://mirror.23media.de/archlinux/core/os/x86_64", + "https://ftp.fau.de/archlinux/core/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ] + ], + "stagingDbConfig": [ + [ + "boost-staging", + { + "Server": [ + "file://$TEST_FILES_PATH/repo/boost-staging/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ], + [ + "misc", + { + "Server": [ + "file://$TEST_FILES_PATH/repo/misc/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ], + [ + "boost", + { + "Server": [ + "file://$TEST_FILES_PATH/repo/boost/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ], + [ + "core", + { + "Server": [ + "http://mirror.23media.de/archlinux/core/os/x86_64", + "https://ftp.fau.de/archlinux/core/os/x86_64" + ], + "SigLevel": [ + "Optional TrustedOnly" + ] + } + ] + ], + "targetDb": "boost", + "targetArch": "x86_64", + "stagingDb": "boost-staging", + "batches": [["boost"]], + "cyclicLeftovers": [], + "warnings": [], + "error": "", + "manuallyOrdered": false +} diff --git a/librepomgr/testfiles/building/build-data/conduct-build-test/build-progress.json b/librepomgr/testfiles/building/build-data/conduct-build-test/build-progress.json new file mode 100644 index 0000000..c84e9fc --- /dev/null +++ b/librepomgr/testfiles/building/build-data/conduct-build-test/build-progress.json @@ -0,0 +1,30 @@ +{ + "progressByPackage": { + "boost": { + "started": "0001-01-01T00:00:00", + "finished": "0001-01-01T00:00:00", + "buildDirectory": "", + "chrootDirectory": "", + "chrootUser": "", + "makechrootpkgFlags": [], + "makepkgFlags": [], + "packageExtension": "", + "warnings": [], + "error": "", + "updatedVersion": "", + "skipChrootUpgrade": false, + "skipChrootCleanup": false, + "keepPreviousSourceTree": false, + "checksumsUpdated": false, + "hasSources": false, + "addedToRepo": false + } + }, + "targetDbFilePath": "", + "targetRepoPath": "", + "stagingDbFilePath": "", + "stagingRepoPath": "", + "producedProvides": {}, + "removedProvides": {}, + "rebuildList": {} +} diff --git a/librepomgr/testfiles/building/pkgbuilds/boost/default b/librepomgr/testfiles/building/pkgbuilds/boost/default new file mode 120000 index 0000000..90838f0 --- /dev/null +++ b/librepomgr/testfiles/building/pkgbuilds/boost/default @@ -0,0 +1 @@ +../../build-data/conduct-build-test/boost/src \ No newline at end of file diff --git a/librepomgr/testfiles/building/pkgbuilds/gcc/mingw-w64/.SRCINFO b/librepomgr/testfiles/building/pkgbuilds/gcc/mingw-w64/.SRCINFO new file mode 100644 index 0000000..3d69d1f --- /dev/null +++ b/librepomgr/testfiles/building/pkgbuilds/gcc/mingw-w64/.SRCINFO @@ -0,0 +1,6 @@ +pkgbase = mingw-w64-gcc + pkgdesc = Dummy .SRCINFO for mingw-w64-gcc + pkgver = 1 + pkgrel = 1 + +pkgname = mingw-w64-gcc diff --git a/librepomgr/testfiles/building/pkgbuilds/gcc/mingw-w64/PKGBUILD b/librepomgr/testfiles/building/pkgbuilds/gcc/mingw-w64/PKGBUILD new file mode 100644 index 0000000..2995a4d --- /dev/null +++ b/librepomgr/testfiles/building/pkgbuilds/gcc/mingw-w64/PKGBUILD @@ -0,0 +1 @@ +dummy \ No newline at end of file diff --git a/librepomgr/testfiles/building/pkgbuilds/zstd/default/.SRCINFO b/librepomgr/testfiles/building/pkgbuilds/zstd/default/.SRCINFO new file mode 100644 index 0000000..0e5fc17 --- /dev/null +++ b/librepomgr/testfiles/building/pkgbuilds/zstd/default/.SRCINFO @@ -0,0 +1,6 @@ +pkgbase = zstd + pkgdesc = Dummy .SRCINFO for zstd + pkgver = 1 + pkgrel = 1 + +pkgname = zstd diff --git a/librepomgr/testfiles/building/pkgbuilds/zstd/default/PKGBUILD b/librepomgr/testfiles/building/pkgbuilds/zstd/default/PKGBUILD new file mode 100644 index 0000000..2995a4d --- /dev/null +++ b/librepomgr/testfiles/building/pkgbuilds/zstd/default/PKGBUILD @@ -0,0 +1 @@ +dummy \ No newline at end of file diff --git a/librepomgr/testfiles/mirrorlist b/librepomgr/testfiles/mirrorlist new file mode 120000 index 0000000..71454d6 --- /dev/null +++ b/librepomgr/testfiles/mirrorlist @@ -0,0 +1 @@ +../../libpkg/testfiles/mirrorlist \ No newline at end of file diff --git a/librepomgr/testfiles/pacman.conf b/librepomgr/testfiles/pacman.conf new file mode 120000 index 0000000..04bb587 --- /dev/null +++ b/librepomgr/testfiles/pacman.conf @@ -0,0 +1 @@ +../../libpkg/testfiles/pacman.conf \ No newline at end of file diff --git a/librepomgr/testfiles/repo b/librepomgr/testfiles/repo new file mode 120000 index 0000000..00f15f4 --- /dev/null +++ b/librepomgr/testfiles/repo @@ -0,0 +1 @@ +../../libpkg/testfiles/repo \ No newline at end of file diff --git a/librepomgr/testfiles/scripts/fake_makechrootpkg.sh b/librepomgr/testfiles/scripts/fake_makechrootpkg.sh new file mode 100755 index 0000000..56f933e --- /dev/null +++ b/librepomgr/testfiles/scripts/fake_makechrootpkg.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "fake makechrootpkg: $@" +exit 0 diff --git a/librepomgr/testfiles/scripts/fake_makepkg.sh b/librepomgr/testfiles/scripts/fake_makepkg.sh new file mode 100755 index 0000000..5bf0d05 --- /dev/null +++ b/librepomgr/testfiles/scripts/fake_makepkg.sh @@ -0,0 +1,11 @@ +#!/bin/bash +if [[ $1 == '--printsrcinfo' ]]; then + if [[ -e '.SRCINFO' ]]; then + cat '.SRCINFO' + exit 0 + fi + echo ".SRCINFO does not exist, working dir was '$PWD'" + exit 1 +fi +echo "fake makepkg: $@" +exit 0 diff --git a/librepomgr/testfiles/scripts/fake_repo_add.sh b/librepomgr/testfiles/scripts/fake_repo_add.sh new file mode 100755 index 0000000..ceb7d52 --- /dev/null +++ b/librepomgr/testfiles/scripts/fake_repo_add.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "fake repo-add: $@" +exit 0 diff --git a/librepomgr/testfiles/scripts/fake_updatepkgsums.sh b/librepomgr/testfiles/scripts/fake_updatepkgsums.sh new file mode 100755 index 0000000..524db7d --- /dev/null +++ b/librepomgr/testfiles/scripts/fake_updatepkgsums.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "fake updatepkgsums: $@" +exit 0 diff --git a/librepomgr/testfiles/scripts/print_some_data.sh b/librepomgr/testfiles/scripts/print_some_data.sh new file mode 100755 index 0000000..c52fbc7 --- /dev/null +++ b/librepomgr/testfiles/scripts/print_some_data.sh @@ -0,0 +1,7 @@ +#!/bin/bash +echo -e "printing some numbers\r" >&2 +for i in {0001..5000}; do + echo -e "line $i\r" + [[ $1 ]] && sleep "$1" +done +exit 0 diff --git a/librepomgr/testfiles/test-config/chroot-dir/config-x86_64/makepkg.conf b/librepomgr/testfiles/test-config/chroot-dir/config-x86_64/makepkg.conf new file mode 100644 index 0000000..eb6e420 --- /dev/null +++ b/librepomgr/testfiles/test-config/chroot-dir/config-x86_64/makepkg.conf @@ -0,0 +1,157 @@ +# +# /etc/makepkg.conf +# + +######################################################################### +# SOURCE ACQUISITION +######################################################################### +# +#-- The download utilities that makepkg should use to acquire sources +# Format: 'protocol::agent' +DLAGENTS=('file::/usr/bin/curl -gqC - -o %o %u' + 'ftp::/usr/bin/curl -gqfC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u' + 'http::/usr/bin/curl -gqb "" -fLC - --retry 3 --retry-delay 3 -o %o %u' + 'https::/usr/bin/curl -gqb "" -fLC - --retry 3 --retry-delay 3 -o %o %u' + 'rsync::/usr/bin/rsync --no-motd -z %u %o' + 'scp::/usr/bin/scp -C %u %o') + +# Other common tools: +# /usr/bin/snarf +# /usr/bin/lftpget -c +# /usr/bin/wget + +#-- The package required by makepkg to download VCS sources +# Format: 'protocol::package' +VCSCLIENTS=('bzr::bzr' + 'git::git' + 'hg::mercurial' + 'svn::subversion') + +MARTCHUS_GIT_URL_PREFIX='git+https://martchus.no-ip.biz/gitea/Martchus/' +MARTCHUS_GIT_URL_PREFIX_PRIVATE='git+ssh://gitea@martchus.no-ip.biz/Martchus' + +######################################################################### +# ARCHITECTURE, COMPILE FLAGS +######################################################################### +# +CARCH="x86_64" +CHOST="x86_64-pc-linux-gnu" + +#-- Compiler and Linker Flags +# -march (or -mcpu) builds exclusively for an architecture +# -mtune optimizes for an architecture, but builds for whole processor family +CPPFLAGS="-D_FORTIFY_SOURCE=2" +CFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong -fno-plt" +CXXFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong -fno-plt" +LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now" +#-- Make Flags: change this for DistCC/SMP systems +MAKEFLAGS="-j$(nproc)" +#-- Debugging flags +DEBUG_CFLAGS="-g -fvar-tracking-assignments" +DEBUG_CXXFLAGS="-g -fvar-tracking-assignments" + +######################################################################### +# BUILD ENVIRONMENT +######################################################################### +# +# Defaults: BUILDENV=(!distcc color !ccache check !sign) +# A negated environment option will do the opposite of the comments below. +# +#-- distcc: Use the Distributed C/C++/ObjC compiler +#-- color: Colorize output messages +#-- ccache: Use ccache to cache compilation +#-- check: Run the check() function if present in the PKGBUILD +#-- sign: Generate PGP signature file +# +BUILDENV=(!distcc color ccache check !sign) +# +#-- If using DistCC, your MAKEFLAGS will also need modification. In addition, +#-- specify a space-delimited list of hosts running in the DistCC cluster. +#DISTCC_HOSTS="" +# +#-- Specify a directory for package building. +#BUILDDIR=/tmp/makepkg +BUILDDIR=/run/media/devel/build/makepkg + +######################################################################### +# GLOBAL PACKAGE OPTIONS +# These are default values for the options=() settings +######################################################################### +# +# Default: OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge !upx !debug) +# A negated option will do the opposite of the comments below. +# +#-- strip: Strip symbols from binaries/libraries +#-- docs: Save doc directories specified by DOC_DIRS +#-- libtool: Leave libtool (.la) files in packages +#-- staticlibs: Leave static library (.a) files in packages +#-- emptydirs: Leave empty directories in packages +#-- zipman: Compress manual (man and info) pages in MAN_DIRS with gzip +#-- purge: Remove files specified by PURGE_TARGETS +#-- upx: Compress binary executable files using UPX +#-- debug: Add debugging flags as specified in DEBUG_* variables +# +OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge !upx !debug) + +#-- File integrity checks to use. Valid: md5, sha1, sha256, sha384, sha512 +INTEGRITY_CHECK=(md5) +#-- Options to be used when stripping binaries. See `man strip' for details. +STRIP_BINARIES="--strip-all" +#-- Options to be used when stripping shared libraries. See `man strip' for details. +STRIP_SHARED="--strip-unneeded" +#-- Options to be used when stripping static libraries. See `man strip' for details. +STRIP_STATIC="--strip-debug" +#-- Manual (man and info) directories to compress (if zipman is specified) +MAN_DIRS=({usr{,/local}{,/share},opt/*}/{man,info}) +#-- Doc directories to remove (if !docs is specified) +DOC_DIRS=(usr/{,local/}{,share/}{doc,gtk-doc} opt/*/{doc,gtk-doc}) +#-- Files to be removed from all packages (if purge is specified) +PURGE_TARGETS=(usr/{,share}/info/dir .packlist *.pod) + +######################################################################### +# PACKAGE OUTPUT +######################################################################### +# +# Default: put built package and cached source in build directory +# +#-- Destination: specify a fixed directory where all packages will be placed +#PKGDEST=/home/packages +#-- Source cache: specify a fixed directory where source files will be cached +#SRCDEST=/home/sources +#-- Source packages: specify a fixed directory where all src packages will be placed +#SRCPKGDEST=/home/srcpackages +#-- Log files: specify a fixed directory where all log files will be placed +#LOGDEST=/home/makepkglogs +#-- Packager: name/email of the person or organization building packages +PACKAGER="Martchus " +#-- Specify a key to use for package signing +#GPGKEY="" + +######################################################################### +# COMPRESSION DEFAULTS +######################################################################### +# +COMPRESSGZ=(gzip -c -f -n) +COMPRESSBZ2=(bzip2 -c -f) +COMPRESSXZ=(xz -c -z -) +COMPRESSZST=(zstd -c -T0 --ultra -20 -) +COMPRESSLRZ=(lrzip -q) +COMPRESSLZO=(lzop -q) +COMPRESSZ=(compress -c -f) +COMPRESSLZ4=(lz4 -q) +COMPRESSLZ=(lzip -c -f) + +######################################################################### +# EXTENSION DEFAULTS +######################################################################### +# +# WARNING: Do NOT modify these variables unless you know what you are +# doing. +# +#PKGEXT='.pkg.tar.xz' +PKGEXT='.pkg.tar.zst' +SRCEXT='.src.tar.gz' + +SYNCTHING_TEST_TIMEOUT_FACTOR='3' + +# vim: set ft=sh ts=2 sw=2 et: diff --git a/librepomgr/testfiles/test-config/chroot-dir/config-x86_64/pacman.conf b/librepomgr/testfiles/test-config/chroot-dir/config-x86_64/pacman.conf new file mode 100644 index 0000000..8e11d19 --- /dev/null +++ b/librepomgr/testfiles/test-config/chroot-dir/config-x86_64/pacman.conf @@ -0,0 +1,137 @@ +# +# /etc/pacman.conf +# +# See the pacman.conf(5) manpage for option and repository directives + +# +# GENERAL OPTIONS +# +[options] +# The following paths are commented out with their default values listed. +# If you wish to use different paths, uncomment and update the paths. +#RootDir = / +#DBPath = /var/lib/pacman/ +CacheDir = /run/media/martchus/misc/cache/pacman/x86_64 +#LogFile = /var/log/pacman.log +#GPGDir = /etc/pacman.d/gnupg/ +HoldPkg = pacman glibc +#XferCommand = /usr/bin/curl -C - -f %u > %o +#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u +#CleanMethod = KeepInstalled +#UseDelta = 0.7 +Architecture = x86_64 + +# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup +#IgnorePkg = gdb +#IgnoreGroup = + +#NoUpgrade = +#NoExtract = + +# Misc options +#UseSyslog +Color +#TotalDownload +CheckSpace +#VerbosePkgLists + +# By default, pacman accepts packages signed by keys that its local keyring +# trusts (see pacman-key and its man page), as well as unsigned packages. +SigLevel = Required DatabaseOptional +LocalFileSigLevel = Optional +#RemoteFileSigLevel = Required + +# NOTE: You must run `pacman-key --init` before first using pacman; the local +# keyring can then be populated with the keys of all official Arch Linux +# packagers with `pacman-key --populate archlinux`. + +# +# REPOSITORIES +# - can be defined here or included from another file +# - pacman will search repositories in the order defined here +# - local/custom mirrors can be added here or in separate files +# - repositories listed first will take precedence when packages +# have identical names, regardless of version number +# - URLs will have $repo replaced by the name of the current repo +# - URLs will have $arch replaced by the name of the architecture +# +# Repository entries are of the format: +# [repo-name] +# Server = ServerName +# Include = IncludePath +# +# The header [repo-name] is crucial - it must be present and +# uncommented to enable the repo. +# + +# The testing repositories are disabled by default. To enable, uncomment the +# repo name header and Include lines. You can add preferred servers immediately +# after the header, and they will be used before the default mirrors. + +#[ownstuff-testing-staging] +#SigLevel = Optional TrustAll +#Server = http://localhost/repo/arch/$repo/os/$arch + +[ownstuff-experimental] +SigLevel = Optional TrustAll +Server = http://localhost/repo/arch/$repo/os/$arch + +#[ownstuff-testing] +#SigLevel = Optional TrustAll +#Server = http://localhost/repo/arch/$repo/os/$arch + +#[ownstuff-staging] +#SigLevel = Optional TrustAll +#Server = http://localhost/repo/arch/$repo/os/$arch + +#[ownstuff-protected-staging] +#SigLevel = Optional TrustAll +#Server = http://localhost/repo/arch/$repo/os/$arch + +[ownstuff-protected] +SigLevel = Optional TrustAll +Server = http://localhost/repo/arch/$repo/os/$arch + +[ownstuff] +SigLevel = Optional TrustAll +Server = http://localhost/repo/arch/$repo/os/$arch + + +#[staging] +#Include = /etc/pacman.d/mirrorlist + +#[community-staging] +#Include = /etc/pacman.d/mirrorlist + +#[testing] +#Include = /etc/pacman.d/mirrorlist + +#[community-testing] +#Include = /etc/pacman.d/mirrorlist + +[core] +Include = /etc/pacman.d/mirrorlist + +[extra] +Include = /etc/pacman.d/mirrorlist + +[community] +Include = /etc/pacman.d/mirrorlist + +# If you want to run 32 bit applications on your x86_64 system, +# enable the multilib repositories as required here. +#[multilib-staging] +#Include = /etc/pacman.d/mirrorlist + +#[multilib-testing] +#Include = /etc/pacman.d/mirrorlist + +[multilib] +SigLevel = PackageRequired TrustedOnly +Include = /etc/pacman.d/mirrorlist + +# An example of a custom package repository. See the pacman manpage for +# tips on creating your own repositories. +#[custom] +#SigLevel = Optional TrustAll +#Server = file:///home/custompkgs diff --git a/librepomgr/testfiles/test-config/chroot-skel/.arch-chroot b/librepomgr/testfiles/test-config/chroot-skel/.arch-chroot new file mode 100644 index 0000000..e69de29 diff --git a/librepomgr/testfiles/test-config/chroot-skel/etc/foo b/librepomgr/testfiles/test-config/chroot-skel/etc/foo new file mode 100644 index 0000000..e69de29 diff --git a/librepomgr/testfiles/test-config/chroot-skel/usr/bin/bar b/librepomgr/testfiles/test-config/chroot-skel/usr/bin/bar new file mode 100644 index 0000000..e69de29 diff --git a/librepomgr/testfiles/test-config/chroot-skel/usr/include/foo.h b/librepomgr/testfiles/test-config/chroot-skel/usr/include/foo.h new file mode 100644 index 0000000..e69de29 diff --git a/librepomgr/testfiles/test-config/chroot-skel/usr/lib/bar.so b/librepomgr/testfiles/test-config/chroot-skel/usr/lib/bar.so new file mode 100644 index 0000000..e69de29 diff --git a/librepomgr/testfiles/test-config/fake-build-artefacts/boost-1.73.0-1-x86_64.pkg.tar.zst b/librepomgr/testfiles/test-config/fake-build-artefacts/boost-1.73.0-1-x86_64.pkg.tar.zst new file mode 100644 index 0000000..68cbdeb Binary files /dev/null and b/librepomgr/testfiles/test-config/fake-build-artefacts/boost-1.73.0-1-x86_64.pkg.tar.zst differ diff --git a/librepomgr/testfiles/test-config/fake-build-artefacts/boost-1.73.0-1.src.tar.gz b/librepomgr/testfiles/test-config/fake-build-artefacts/boost-1.73.0-1.src.tar.gz new file mode 100644 index 0000000..e69de29 diff --git a/librepomgr/testfiles/test-config/fake-build-artefacts/boost-libs-1.73.0-1-x86_64.pkg.tar.zst b/librepomgr/testfiles/test-config/fake-build-artefacts/boost-libs-1.73.0-1-x86_64.pkg.tar.zst new file mode 100644 index 0000000..ba7459c Binary files /dev/null and b/librepomgr/testfiles/test-config/fake-build-artefacts/boost-libs-1.73.0-1-x86_64.pkg.tar.zst differ diff --git a/librepomgr/testfiles/test-config/repos/boost-staging/os/x86_64/boost-staging.db b/librepomgr/testfiles/test-config/repos/boost-staging/os/x86_64/boost-staging.db new file mode 120000 index 0000000..4642c58 --- /dev/null +++ b/librepomgr/testfiles/test-config/repos/boost-staging/os/x86_64/boost-staging.db @@ -0,0 +1 @@ +boost-staging.db.tar.zst \ No newline at end of file diff --git a/librepomgr/testfiles/test-config/repos/boost-staging/os/x86_64/boost-staging.db.tar.zst b/librepomgr/testfiles/test-config/repos/boost-staging/os/x86_64/boost-staging.db.tar.zst new file mode 100644 index 0000000..b282bc7 Binary files /dev/null and b/librepomgr/testfiles/test-config/repos/boost-staging/os/x86_64/boost-staging.db.tar.zst differ diff --git a/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost-libs-1.72.0-2-x86_64.pkg.tar.zst b/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost-libs-1.72.0-2-x86_64.pkg.tar.zst new file mode 100644 index 0000000..a4be075 Binary files /dev/null and b/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost-libs-1.72.0-2-x86_64.pkg.tar.zst differ diff --git a/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost.db b/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost.db new file mode 120000 index 0000000..8f0f4b1 --- /dev/null +++ b/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost.db @@ -0,0 +1 @@ +boost.db.tar.zst \ No newline at end of file diff --git a/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost.db.tar.zst b/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost.db.tar.zst new file mode 100644 index 0000000..0297a75 Binary files /dev/null and b/librepomgr/testfiles/test-config/repos/boost/os/x86_64/boost.db.tar.zst differ diff --git a/librepomgr/testfiles/test-config/repos/misc/os/x86_64/misc.db b/librepomgr/testfiles/test-config/repos/misc/os/x86_64/misc.db new file mode 120000 index 0000000..42080f7 --- /dev/null +++ b/librepomgr/testfiles/test-config/repos/misc/os/x86_64/misc.db @@ -0,0 +1 @@ +misc.db.tar.zst \ No newline at end of file diff --git a/librepomgr/testfiles/test-config/repos/misc/os/x86_64/misc.db.tar.zst b/librepomgr/testfiles/test-config/repos/misc/os/x86_64/misc.db.tar.zst new file mode 100644 index 0000000..eea01b7 Binary files /dev/null and b/librepomgr/testfiles/test-config/repos/misc/os/x86_64/misc.db.tar.zst differ diff --git a/librepomgr/testfiles/test-config/repos/misc/os/x86_64/source-highlight-3.1.9-2-x86_64.pkg.tar.zst b/librepomgr/testfiles/test-config/repos/misc/os/x86_64/source-highlight-3.1.9-2-x86_64.pkg.tar.zst new file mode 100644 index 0000000..5583fb9 Binary files /dev/null and b/librepomgr/testfiles/test-config/repos/misc/os/x86_64/source-highlight-3.1.9-2-x86_64.pkg.tar.zst differ diff --git a/librepomgr/testfiles/test-config/server.conf b/librepomgr/testfiles/test-config/server.conf new file mode 100644 index 0000000..bb389e0 --- /dev/null +++ b/librepomgr/testfiles/test-config/server.conf @@ -0,0 +1,57 @@ +pacman_config_file_path = + +[webserver] +threads = 4 +verify_ssl_certificates = off +log_ssl_certificate_validation = off + +[building] + +[definitions] +# path to download official DBs to +sync_db_path = sync-dbs/$arch + +# path to store own DBs +local_db_path = repos +sync_db_path = $local_db_path/$repo/os/$arch +regular_db_path = $local_db_path/$repo/os/$arch/$repo.db.tar.zst +files_db_path = $local_db_path/$repo/os/$arch/$repo.files.tar.zst + +# mirrors to sync DBs from +default_mirror = http://mirror.23media.de/archlinux +fallback_mirror = https://ftp.fau.de/archlinux +own_mirror = file://$local_db_path + +[database/core] +arch = x86_64 +sync_from_mirror = on +dbdir = $sync_db_path +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/boost] +path = $regular_db_path +files = $files_db_path +arch = x86_64 +depends = core +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $sync_db_path +mirror = $repo_mirror/$repo/os/$arch + +[database/boost-staging] +path = $regular_db_path +files = $files_db_path +arch = x86_64 +depends = boost +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $sync_db_path +mirror = $repo_mirror/$repo/os/$arch + +[database/misc] +path = $regular_db_path +files = $files_db_path +arch = x86_64 +depends = core boost +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $sync_db_path +mirror = $repo_mirror/$repo/os/$arch diff --git a/librepomgr/testfiles/test-config/sync-dbs/core.db b/librepomgr/testfiles/test-config/sync-dbs/core.db new file mode 100644 index 0000000..f75080f Binary files /dev/null and b/librepomgr/testfiles/test-config/sync-dbs/core.db differ diff --git a/librepomgr/tests/buildactions.cpp b/librepomgr/tests/buildactions.cpp new file mode 100644 index 0000000..5734259 --- /dev/null +++ b/librepomgr/tests/buildactions.cpp @@ -0,0 +1,594 @@ +#include "./parser_helper.h" + +#include "../logging.h" +#include "../serversetup.h" + +#include "../buildactions/buildaction.h" +#include "../buildactions/buildactionprivate.h" +#include "../buildactions/subprocess.h" + +#include +#include +#include +#include +#include +using CppUtilities::operator<<; // must be visible prior to the call site + +#include +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; +using namespace CppUtilities::Literals; + +using namespace LibRepoMgr; + +/*! + * \brief The BuildActionsTests class contains tests for classes/functions related to build actions. + */ +class BuildActionsTests : public TestFixture { + CPPUNIT_TEST_SUITE(BuildActionsTests); + CPPUNIT_TEST(testLogging); + CPPUNIT_TEST(testProcessSession); + CPPUNIT_TEST(testBuildActionProcess); + CPPUNIT_TEST(testBufferSearch); + CPPUNIT_TEST(testParsingInfoFromPkgFiles); + CPPUNIT_TEST(testPreparingBuild); + CPPUNIT_TEST(testConductingBuild); + CPPUNIT_TEST_SUITE_END(); + +public: + BuildActionsTests(); + void setUp() override; + void tearDown() override; + + void testLogging(); + void testProcessSession(); + void testBuildActionProcess(); + void testBufferSearch(); + void testParsingInfoFromPkgFiles(); + void testPreparingBuild(); + void testConductingBuild(); + +private: + void loadBasicTestSetup(); + void loadTestConfig(); + void logTestSetup(); + template void setupBuildAction(); + void resetBuildAction(); + void runBuildAction(const char *message, TimeSpan timeout = TimeSpan::fromSeconds(5)); + template InternalBuildActionType *internalBuildAction(); + + ServiceSetup m_setup; + std::shared_ptr m_buildAction; + std::filesystem::path m_workingDir; + double m_timeoutFactor = 0.0; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BuildActionsTests); + +BuildActionsTests::BuildActionsTests() +{ + if (const char *noBuildActionTimeout = std::getenv("BUILD_ACTION_TIMEOUT_FACTOR")) { + m_timeoutFactor = stringToNumber(noBuildActionTimeout); + } +} + +void BuildActionsTests::setUp() +{ + // save the working directory; the code under test might change it and we want to restore the initial working directory later + m_workingDir = std::filesystem::current_path(); + cerr << EscapeCodes::Phrases::Info << "test working directory: " << m_workingDir.native() << endl; +} + +void BuildActionsTests::tearDown() +{ + std::filesystem::current_path(m_workingDir); +} + +/*! + * \brief Assigns certain build variables to use fake scripts (instead of invoking e.g. the real makepkg). + * \remarks The fake scripts are esentially no-ops which merely print the script name and the passed arguments. + */ +void BuildActionsTests::loadBasicTestSetup() +{ + m_setup.workingDirectory = TestApplication::instance()->workingDirectory(); + m_setup.building.workingDirectory = m_setup.workingDirectory + "/building"; + m_setup.building.makePkgPath = std::filesystem::absolute(testFilePath("scripts/fake_makepkg.sh")); + m_setup.building.makeChrootPkgPath = std::filesystem::absolute(testFilePath("scripts/fake_makechrootpkg.sh")); + m_setup.building.updatePkgSumsPath = std::filesystem::absolute(testFilePath("scripts/fake_updatepkgsums.sh")); + m_setup.building.repoAddPath = std::filesystem::absolute(testFilePath("scripts/fake_repo_add.sh")); + m_setup.configFilePath = std::filesystem::absolute(testFilePath("test-config/server.conf")); + + std::filesystem::remove_all(m_setup.workingDirectory); + std::filesystem::create_directories(m_setup.building.workingDirectory); +} + +/*! + * \brief Runs the startup code almost like the actual service does. + * \remarks Changes the current working directory! Make paths obtained via testFilePath() absolute before calling this function + * if they are supposed to be used later. + */ +void BuildActionsTests::loadTestConfig() +{ + m_setup.loadConfigFiles(false); + m_setup.building.workingDirectory = m_setup.workingDirectory + "/building"; + m_setup.printDatabases(); + cerr << EscapeCodes::Phrases::Info << "current working directory: " << std::filesystem::current_path() << endl; + cerr << EscapeCodes::Phrases::Info << "setup working directory: " << m_setup.workingDirectory << endl; + logTestSetup(); +} + +/*! + * \brief Logs all databases and packages of the current test setup. + */ +void BuildActionsTests::logTestSetup() +{ + for (const auto &db : m_setup.config.databases) { + cout << EscapeCodes::Phrases::Info << "Packages of " << db.name << ':' << EscapeCodes::Phrases::End; + for (const auto &[pkgName, pkg] : db.packages) { + cout << " - " << pkgName << '\n'; + } + } + cout.flush(); +} + +/*! + * \brief Initializes the fixture's build action.helper + */ +template void BuildActionsTests::setupBuildAction() +{ + m_buildAction = std::make_shared(0, &m_setup); +} + +/*! + * \brief Resets the fixture's build action. + */ +void BuildActionsTests::resetBuildAction() +{ + m_buildAction->status = BuildActionStatus::Created; + m_buildAction->result = BuildActionResult::None; + m_buildAction->resultData = std::string(); +} + +/*! + * \brief Runs the fixture's build action (initialized via setupBuildAction()) until it has finished. + * \param message The message for asserting whether the build action has finished yet. + * \param timeout The max. time to wait for the build action to finish. It does not interrupt the handler which is currently + * executed (so tests can still get stuck). + * + */ +void BuildActionsTests::runBuildAction(const char *message, CppUtilities::TimeSpan timeout) +{ + resetBuildAction(); + m_buildAction->start(m_setup); + auto &ioc = m_setup.building.ioContext; + ioc.restart(); + boost::asio::executor_work_guard workGuard = boost::asio::make_work_guard(ioc); + m_buildAction->setConcludeHandler([&workGuard] { workGuard.reset(); }); + if (m_timeoutFactor == 0.0) { + ioc.run(); + } else { + ioc.run_for(std::chrono::microseconds(static_cast((timeout * m_timeoutFactor).totalMicroseconds()))); + } + CPPUNIT_ASSERT_EQUAL_MESSAGE(message, BuildActionStatus::Finished, m_buildAction->status); +} + +/*! + * \brief Returns the internal build action for the fixture's build action. + * \remarks Invokes undefined behavior if \tp InternalBuildActionType is not the actual type. + */ +template InternalBuildActionType *BuildActionsTests::internalBuildAction() +{ + auto *const internalBuildAction = m_buildAction->m_internalBuildAction.get(); + CPPUNIT_ASSERT_MESSAGE("internal build action assigned", internalBuildAction); + return static_cast(internalBuildAction); +} + +/*! + * \brief Tests basic logging. + */ +void BuildActionsTests::testLogging() +{ + using namespace EscapeCodes; + m_buildAction = make_shared(0, &m_setup); + { + const auto stderrCheck = OutputCheck( + [](const std::string &output) { + TESTUTILS_ASSERT_LIKE_FLAGS( + "messages logged on stderr", ".*ERROR.*some error: message.*\n.*info.*\n.*", std::regex::extended, output); + }, + cerr); + m_buildAction->log()(Phrases::ErrorMessage, "some error: ", "message", '\n'); + m_buildAction->log()(Phrases::InfoMessage, "info", '\n'); + } + CPPUNIT_ASSERT_EQUAL_MESSAGE("messages added to build action output", + "\e[1;31m==> ERROR: \e[0m\e[1msome error: message\n\e[1;37m==> \e[0m\e[1minfo\n"s, m_buildAction->output); +} + +/*! + * \brief Tests the ProcessSession class (which is used to spawn processes within build actions capturing the output). + */ +void BuildActionsTests::testProcessSession() +{ + auto &ioc = m_setup.building.ioContext; + auto session = std::make_shared(ioc, [&ioc](boost::process::child &&child, ProcessResult &&result) { + CPP_UTILITIES_UNUSED(child) + CPPUNIT_ASSERT_EQUAL(std::error_code(), result.errorCode); + CPPUNIT_ASSERT_EQUAL(0, result.exitCode); + CPPUNIT_ASSERT_EQUAL(std::string(), result.error); + CPPUNIT_ASSERT_EQUAL("line1\nline2"s, result.output); + ioc.stop(); + }); + session->launch(boost::process::search_path("echo"), "-n", "line1\nline2"); + session.reset(); + ioc.run(); +} + +/*! + * \brief Tests the BuildProcessSession class (which is used to spawn processes within build actions creating a log file). + */ +void BuildActionsTests::testBuildActionProcess() +{ + const auto scriptPath = testFilePath("scripts/print_some_data.sh"); + const auto logFilePath = std::filesystem::path(TestApplication::instance()->workingDirectory()) / "logfile.log"; + std::filesystem::create_directory(logFilePath.parent_path()); + if (std::filesystem::exists(logFilePath)) { + std::filesystem::remove(logFilePath); + } + + auto &ioc = m_setup.building.ioContext; + auto session = std::make_shared( + nullptr, ioc, std::string(logFilePath), [&ioc](boost::process::child &&child, ProcessResult &&result) { + CPP_UTILITIES_UNUSED(child) + CPPUNIT_ASSERT_EQUAL(std::error_code(), result.errorCode); + CPPUNIT_ASSERT_EQUAL(0, result.exitCode); + ioc.stop(); + }); + session->launch(scriptPath); + session.reset(); + ioc.run(); + + auto logLines = splitString(readFile(logFilePath), "\r\n"); + CPPUNIT_ASSERT_EQUAL(5001_st, logLines.size()); + CPPUNIT_ASSERT_EQUAL("printing some numbers"s, logLines.front()); + CPPUNIT_ASSERT_EQUAL("line 5000"s, logLines.back()); +} + +/*! + * \brief Tests the BufferSearch class. + */ +void BuildActionsTests::testBufferSearch() +{ + // make a buffer + BuildProcessSession::BufferPoolType bufferPool(30); + auto buffer = bufferPool.newBuffer(); + + // setup testing the search + std::string expectedResult; + bool hasResult = false; + BufferSearch bs("Updated version: ", "\e\n", "Starting build", [&expectedResult, &hasResult](std::string &&result) { + CPPUNIT_ASSERT_EQUAL(expectedResult, result); + CPPUNIT_ASSERT_MESSAGE("callback only invoked once", !hasResult); + hasResult = true; + }); + + // feed data into the search + bs(buffer, 0); + std::strcpy(buffer->data(), "Starting Updated"); + bs(buffer, 16); + std::strcpy(buffer->data(), " version: some "); + bs(buffer, 15); + expectedResult = "some version number"; + std::strcpy(buffer->data(), "version number\emore chars"); + bs(buffer, 25); + CPPUNIT_ASSERT(hasResult); + std::strcpy(buffer->data(), "... Starting build ..."); + bs(buffer, 22); +} + +/*! + * \brief Tests the ReloadLibraryDependencies build action. + */ +void BuildActionsTests::testParsingInfoFromPkgFiles() +{ + // init config + LibPkg::Config &config = m_setup.config; + config.databases = { { "foo.db" }, { "bar.db" }, { "baz.db" } }; + + // init db object + LibPkg::Database &fooDb = config.databases[0]; + auto harfbuzz = fooDb.packages["mingw-w64-harfbuzz"] = LibPkg::Package::fromPkgFileName("mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz"); + auto syncthingtray = fooDb.packages["syncthingtray"] = LibPkg::Package::fromPkgFileName("syncthingtray-0.6.2-1-x86_64.pkg.tar.xz"); + fooDb.localPkgDir = directory(testFilePath("repo/foo/mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz")); + LibPkg::Database &barDb = config.databases[1]; + auto cmake = barDb.packages["cmake"] = LibPkg::Package::fromPkgFileName("cmake-3.8.2-1-x86_64.pkg.tar.xz"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", LibPkg::PackageOrigin::PackageFileName, cmake->origin); + barDb.localPkgDir = directory(testFilePath("repo/bar/cmake-3.8.2-1-x86_64.pkg.tar.xz")); + + auto buildAction = std::make_shared(0, &m_setup); + auto reloadLibDependencies = ReloadLibraryDependencies(m_setup, buildAction); + reloadLibDependencies.run(); + const auto &messages = std::get(buildAction->resultData); + CPPUNIT_ASSERT_EQUAL(std::vector(), messages.errors); + CPPUNIT_ASSERT_EQUAL(std::vector(), messages.warnings); + CPPUNIT_ASSERT_EQUAL(std::vector(), messages.notes); + + using namespace TestHelper; + checkHarfbuzzPackagePeDependencies(*harfbuzz); + checkSyncthingTrayPackageSoDependencies(*syncthingtray); + checkCmakePackageSoDependencies(*cmake); + + const auto pkgsRequiringLibGCC = config.findPackagesProvidingLibrary("pe-i386::libgcc_s_sjlj-1.dll", true); + CPPUNIT_ASSERT_EQUAL(1_st, pkgsRequiringLibGCC.size()); + CPPUNIT_ASSERT_EQUAL(harfbuzz, pkgsRequiringLibGCC.front().pkg); + + const auto pkgsProvidingLibSyncthingConnector = config.findPackagesProvidingLibrary("elf-x86_64::libsyncthingconnector.so.0.6.2", false); + CPPUNIT_ASSERT_EQUAL(1_st, pkgsProvidingLibSyncthingConnector.size()); + CPPUNIT_ASSERT_EQUAL(syncthingtray, pkgsProvidingLibSyncthingConnector.front().pkg); +} + +/*! + * \brief Tests the PrepareBuild build action. + */ +void BuildActionsTests::testPreparingBuild() +{ + // get meta info + auto &metaInfo = m_setup.building.metaInfo; + const auto &typeInfo = metaInfo.typeInfoForId(BuildActionType::PrepareBuild); + const auto pkgbuildsDirsSetting = std::string(typeInfo.settings[static_cast(PrepareBuildSettings::PKGBUILDsDirs)].param); + + // load basic test setup and create build action + loadBasicTestSetup(); + m_buildAction = std::make_shared(0, &m_setup); + m_buildAction->type = BuildActionType::PrepareBuild; + m_buildAction->directory = "prepare-build-test"; + m_buildAction->flags = static_cast(PrepareBuildFlags::CleanSrcDir); + m_buildAction->settings[pkgbuildsDirsSetting] = std::filesystem::absolute(testDirPath("building/pkgbuilds")); + m_buildAction->packageNames = { "boost", "mingw-w64-gcc" }; + + // prepare test configuration + // - Pretend all dependencies of boost are there except "zstd-1.4.5-1-x86_64.pkg.tar.zst" (so zstd is supposed pulled into the build automatically). + // - There's a dummy package for zstd which incurs no further dependencies. + // - The package mingw-w64-gcc is also just a dummy here to test handling variants; it has no dependencies here. + loadTestConfig(); + auto coreDb = m_setup.config.findDatabase("core", "x86_64"); + CPPUNIT_ASSERT_MESSAGE("core db exists", coreDb); + for (const auto pkgFileName : + { "python-3.8.6-1-x86_64.pkg.tar.zst"sv, "python2-2.7.18-2-x86_64.pkg.tar.zst"sv, "bzip2-1.0.8-4-x86_64.pkg.tar.zst"sv, + "findutils-4.7.0-2-x86_64.pkg.tar.xz"sv, "icu-67.1-1-x86_64.pkg.tar.zst"sv, "openmpi-4.0.5-2-x86_64.pkg.tar.zst"sv, + "python-numpy-1.19.4-1-x86_64.pkg.tar.zst"sv, "python2-numpy-1.16.6-1-x86_64.pkg.tar.zst"sv, "zlib-1:1.2.11-4-x86_64.pkg.tar.xz"sv }) { + coreDb->updatePackage(LibPkg::Package::fromPkgFileName(pkgFileName)); + } + + // run without destination database + runBuildAction("prepare build without destination db"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("failure without destination db", BuildActionResult::Failure, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "failure without destination db", "not exactly one destination database specified"s, std::get(m_buildAction->resultData)); + + // run with destination database (yes, the database is called "boost" in this test setup as well) + m_buildAction->destinationDbs = { "boost" }; + runBuildAction("prepare build: successful preparation"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("success", BuildActionResult::Success, m_buildAction->result); + CPPUNIT_ASSERT_MESSAGE("build preparation present", std::holds_alternative(m_buildAction->resultData)); + const auto &buildPreparation = std::get(m_buildAction->resultData); + CPPUNIT_ASSERT_EQUAL_MESSAGE("target db set", "boost"s, buildPreparation.targetDb); + CPPUNIT_ASSERT_EQUAL_MESSAGE("target arch set", "x86_64"s, buildPreparation.targetArch); + CPPUNIT_ASSERT_EQUAL_MESSAGE("staging db set", "boost-staging"s, buildPreparation.stagingDb); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no cyclic leftovers", 0_st, buildPreparation.cyclicLeftovers.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no warnings", 0_st, buildPreparation.warnings.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no error", std::string(), buildPreparation.error); + CPPUNIT_ASSERT_EQUAL_MESSAGE("manually ordered not set", false, buildPreparation.manuallyOrdered); + CPPUNIT_ASSERT_EQUAL_MESSAGE("db config has 2 dbs", 2_st, buildPreparation.dbConfig.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("first db", "boost"s, buildPreparation.dbConfig[0].first); + CPPUNIT_ASSERT_EQUAL_MESSAGE("second db", "core"s, buildPreparation.dbConfig[1].first); + CPPUNIT_ASSERT_EQUAL_MESSAGE("staging db config has 3 dbs", 3_st, buildPreparation.stagingDbConfig.size()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("first staging db", "boost-staging"s, buildPreparation.stagingDbConfig[0].first); + const auto &batches = buildPreparation.batches; + CPPUNIT_ASSERT_EQUAL_MESSAGE("two batches present", 2_st, batches.size()); + const auto expectedFirstBatch = std::vector{ "mingw-w64-gcc", "zstd" }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("first batch", expectedFirstBatch, batches[0]); + const auto expectedSecondBatch = std::vector{ "boost" }; + CPPUNIT_ASSERT_EQUAL_MESSAGE("second batch", expectedSecondBatch, batches[1]); + CPPUNIT_ASSERT_MESSAGE( + "build-preparation.json created", std::filesystem::is_regular_file("building/build-data/prepare-build-test/build-preparation.json")); + CPPUNIT_ASSERT_MESSAGE( + "build-progress.json created", std::filesystem::is_regular_file("building/build-data/prepare-build-test/build-progress.json")); + for (const auto pkg : { "boost"sv, "mingw-w64-gcc"sv, "zstd"sv }) { + CPPUNIT_ASSERT_MESSAGE( + "PKGBUILD for " % pkg + " created", std::filesystem::is_regular_file("building/build-data/prepare-build-test/" % pkg + "/src/PKGBUILD")); + } +} + +/*! + * \brief Tests the ConductBuild build action. + */ +void BuildActionsTests::testConductingBuild() +{ + // load basic test setup and create build action + loadBasicTestSetup(); + m_buildAction = std::make_shared(0, &m_setup); + m_buildAction->type = BuildActionType::ConductBuild; + m_buildAction->directory = "conduct-build-test"; + m_buildAction->packageNames = { "boost" }; + m_buildAction->flags = static_cast(ConductBuildFlags::BuildAsFarAsPossible | ConductBuildFlags::SaveChrootOfFailures + | ConductBuildFlags::UpdateChecksums | ConductBuildFlags::AutoStaging); + + // run without build preparation + runBuildAction("conduct build without build preparation"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("failure without preparation JSON", BuildActionResult::Failure, m_buildAction->result); + TESTUTILS_ASSERT_LIKE( + "no preparation JSON", "Unable to restore build-preparation.json:.*not exist.*", std::get(m_buildAction->resultData)); + + // create fake build preparation + const auto origPkgbuildFile = workingCopyPathAs("building/build-data/conduct-build-test/boost/src/PKGBUILD", "orig-src-dir/boost/PKGBUILD"); + const auto origSourceDir = std::filesystem::absolute(directory(origPkgbuildFile)); + auto prepData = readFile(testFilePath("building/build-data/conduct-build-test/build-preparation.json")); + findAndReplace(prepData, "$ORIGINAL_SOURCE_DIRECTORY", origSourceDir.native()); + findAndReplace(prepData, "$TEST_FILES_PATH", "TODO"); + const auto buildDir = std::filesystem::absolute(workingCopyPath("building", WorkingCopyMode::NoCopy)); + const auto prepFile + = std::filesystem::absolute(workingCopyPath("building/build-data/conduct-build-test/build-preparation.json", WorkingCopyMode::NoCopy)); + writeFile(prepFile.native(), prepData); + auto progressData = readFile(testFilePath("building/build-data/conduct-build-test/build-progress.json")); + const auto progressFile + = std::filesystem::absolute(workingCopyPath("building/build-data/conduct-build-test/build-progress.json", WorkingCopyMode::NoCopy)); + writeFile(progressFile.native(), progressData); + std::filesystem::copy(testDirPath("building/build-data/conduct-build-test/boost"), + m_setup.workingDirectory + "/building/build-data/conduct-build-test/boost", std::filesystem::copy_options::recursive); + + // run without chroot configuration + runBuildAction("conduct build without chroot configuration"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("failure without chroot configuration", BuildActionResult::Failure, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "no chroot configuration", "The chroot directory is not configured."s, std::get(m_buildAction->resultData)); + + // configure chroot directory + m_setup.building.chrootDir = testDirPath("test-config/chroot-dir"); + + // run with misconfigured destination db + runBuildAction("conduct build with misconfigured destination db (1)"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("failure without destination db (1)", BuildActionResult::Failure, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE("destination db missing (1)", + "Auto-staging is enabled but the staging database \"boost-staging@x86_64\" specified in build-preparation.json can not be found."s, + std::get(m_buildAction->resultData)); + loadTestConfig(); + runBuildAction("conduct build with misconfigured destination db (2)"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("failure without destination db (2)", BuildActionResult::Failure, m_buildAction->result); + TESTUTILS_ASSERT_LIKE("destination db missing (2)", "Destination repository \"repos/boost/os/x86_64\" does not exist.*"s, + std::get(m_buildAction->resultData)); + + // create repositories + const auto reposPath = testDirPath("test-config/repos"); + const auto reposWorkingCopyPath = std::filesystem::path(m_setup.workingDirectory + "/repos"); + std::filesystem::create_directory(reposWorkingCopyPath); + std::filesystem::copy(reposPath, reposWorkingCopyPath, std::filesystem::copy_options::recursive); + + // run without chroot directory + runBuildAction("conduct build without chroot directory"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no chroot directory: results in failure", BuildActionResult::Failure, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no chroot directory: result data states affected packages", "failed to build packages: boost"s, + std::get(m_buildAction->resultData)); + auto *internalData = internalBuildAction(); + TESTUTILS_ASSERT_LIKE("no chroot directory: package-level error message", + "Chroot directory \".*/test-config/chroot-dir/arch-x86_64/root\" is no directory."s, + internalData->m_buildProgress.progressByPackage["boost"].error); + + // create chroot directory + const auto chrootSkelPath = testDirPath("test-config/chroot-skel"); + const auto chrootDirWorkingCopyPath = std::filesystem::path(m_setup.workingDirectory + "/chroot-dir"); + const auto rootChrootWorkingCopyPath = chrootDirWorkingCopyPath / "arch-x86_64/root"; + std::filesystem::create_directory(chrootDirWorkingCopyPath); + std::filesystem::copy(m_setup.building.chrootDir, chrootDirWorkingCopyPath, std::filesystem::copy_options::recursive); + std::filesystem::create_directories(rootChrootWorkingCopyPath); + std::filesystem::copy(chrootSkelPath, rootChrootWorkingCopyPath, std::filesystem::copy_options::recursive); + m_setup.building.chrootDir = chrootDirWorkingCopyPath.string(); // assign the created chroot directory + writeFile(progressFile.native(), progressData); // reset "build-progress.json" so the new chroot directory is actually used + + // run without having makepkg actually producing any packages + runBuildAction("conduct build without producing any packages"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no packages produced: results in failure", BuildActionResult::Failure, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no packages produced: result data states affected packages", "failed to build packages: boost"s, + std::get(m_buildAction->resultData)); + internalData = internalBuildAction(); + TESTUTILS_ASSERT_LIKE("no packages produced: package-level error message", + "not all.*packages exist.*boost-1.73.0-1.src.tar.gz.*boost-libs-1\\.73\\.0-1-x86_64\\.pkg\\.tar\\.zst.*boost-1\\.73\\.0-1-x86_64\\.pkg\\.tar\\.zst"s, + internalData->m_buildProgress.progressByPackage["boost"].error); + CPPUNIT_ASSERT_MESSAGE( + "no packages produced: package considered finished", !internalData->m_buildProgress.progressByPackage["boost"].finished.isNull()); + CPPUNIT_ASSERT_MESSAGE("no packages produced: package not added to repo", !internalData->m_buildProgress.progressByPackage["boost"].addedToRepo); + + // prepare some actual packages + std::filesystem::copy(testFilePath("test-config/fake-build-artefacts/boost-1.73.0-1.src.tar.gz"), + buildDir / "build-data/conduct-build-test/boost/pkg/boost-1.73.0-1.src.tar.gz"); + std::filesystem::copy(testFilePath("test-config/fake-build-artefacts/boost-1.73.0-1-x86_64.pkg.tar.zst"), + buildDir / "build-data/conduct-build-test/boost/pkg/boost-1.73.0-1-x86_64.pkg.tar.zst"); + std::filesystem::copy(testFilePath("test-config/fake-build-artefacts/boost-libs-1.73.0-1-x86_64.pkg.tar.zst"), + buildDir / "build-data/conduct-build-test/boost/pkg/boost-libs-1.73.0-1-x86_64.pkg.tar.zst"); + + // conduct build without staging + runBuildAction("conduct build without staging"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no staging needed: success", BuildActionResult::Success, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE("no staging needed: no result data present", ""s, std::get(m_buildAction->resultData)); + internalData = internalBuildAction(); + CPPUNIT_ASSERT_MESSAGE("no staging needed: rebuild list empty", internalData->m_buildProgress.rebuildList.empty()); + CPPUNIT_ASSERT_MESSAGE( + "no staging needed: package considered finished", !internalData->m_buildProgress.progressByPackage["boost"].finished.isNull()); + CPPUNIT_ASSERT_MESSAGE("no staging needed: package added to repo", internalData->m_buildProgress.progressByPackage["boost"].addedToRepo); + + // check whether log files have been created accordingly + CPPUNIT_ASSERT_EQUAL_MESSAGE("no staging needed: download log", "fake makepkg: -f --nodeps --nobuild --source\n"s, + readFile("building/build-data/conduct-build-test/boost/pkg/download.log")); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "no staging needed: updpkgsums log", "fake updatepkgsums: \n"s, readFile("building/build-data/conduct-build-test/boost/pkg/updpkgsums.log")); + TESTUTILS_ASSERT_LIKE("no staging needed: build log", "fake makechrootpkg: -c -u -C -r .*chroot-dir/arch-x86_64 -l buildservice --\n"s, + readFile("building/build-data/conduct-build-test/boost/pkg/build.log")); + TESTUTILS_ASSERT_LIKE("no staging needed: repo-add log", + "fake repo-add: boost.db.tar.zst boost(-libs)?-1\\.73\\.0-1-x86_64.pkg.tar.zst boost(-libs)?-1\\.73\\.0-1-x86_64.pkg.tar.zst\n"s, + readFile("building/build-data/conduct-build-test/boost/pkg/repo-add.log")); + + // check whether packages have actually been added to repo + CPPUNIT_ASSERT_MESSAGE( + "no staging needed: package added to repo (0)", std::filesystem::is_regular_file("repos/boost/os/src/boost-1.73.0-1.src.tar.gz")); + CPPUNIT_ASSERT_MESSAGE( + "no staging needed: package added to repo (1)", std::filesystem::is_regular_file("repos/boost/os/x86_64/boost-1.73.0-1-x86_64.pkg.tar.zst")); + CPPUNIT_ASSERT_MESSAGE("no staging needed: package added to repo (2)", + std::filesystem::is_regular_file("repos/boost/os/x86_64/boost-libs-1.73.0-1-x86_64.pkg.tar.zst")); + + // add packages needing a rebuild to trigger auto-staging + m_setup.config.loadAllPackages(false); + auto *const boostDb = m_setup.config.findDatabase("boost"sv, "x86_64"sv); + auto *const miscDb = m_setup.config.findDatabase("misc"sv, "x86_64"sv); + CPPUNIT_ASSERT_MESSAGE("boost database present", boostDb); + CPPUNIT_ASSERT_MESSAGE("misc database present", miscDb); + auto &boostLibsPackage = boostDb->packages["boost-libs"]; + boostLibsPackage->libprovides = { "elf-x86_64::libboost_regex.so.1.72.0" }; + boostLibsPackage->libdepends = { "elf-x86_64::libstdc++.so.6" }; + boostDb->forceUpdatePackage(boostLibsPackage); + auto &sourceHighlightPackage = miscDb->packages["source-highlight"]; + sourceHighlightPackage->libprovides = { "elf-x86_64::libsource-highlight.so.4" }; + sourceHighlightPackage->libdepends + = { "elf-x86_64::libboost_regex.so.1.72.0", "elf-x86_64::libsource-highlight.so.4", "elf-x86_64::libstdc++.so.6" }; + miscDb->forceUpdatePackage(sourceHighlightPackage); + m_setup.printDatabases(); + logTestSetup(); + + // conduct build with staging + writeFile(progressFile.native(), progressData); // reset "build-progress.json" so the package is re-considered + runBuildAction("conduct build with staging"); + //CPPUNIT_ASSERT_EQUAL_MESSAGE("staging needed: success", BuildActionResult::Success, m_buildAction->result); + CPPUNIT_ASSERT_EQUAL_MESSAGE("staging needed: no result data present", ""s, std::get(m_buildAction->resultData)); + internalData = internalBuildAction(); + const auto &rebuildList = internalData->m_buildProgress.rebuildList; + const auto rebuildInfoForMisc = rebuildList.find("misc"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("staging needed: rebuild list contains 1 database", 1_st, rebuildList.size()); + CPPUNIT_ASSERT_MESSAGE("staging needed: rebuild info for misc present", rebuildInfoForMisc != rebuildList.end()); + const auto rebuildInfoForSourceHighlight = rebuildInfoForMisc->second.find("source-highlight"); + const auto expectedLibprovides = std::vector{ "elf-x86_64::libboost_regex.so.1.72.0" }; + CPPUNIT_ASSERT_MESSAGE( + "staging needed: rebuild info for source-highlight present", rebuildInfoForSourceHighlight != rebuildInfoForMisc->second.end()); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "staging needed: libprovides for source-highlight present", expectedLibprovides, rebuildInfoForSourceHighlight->second.libprovides); + + // check whether log files have been created accordingly + TESTUTILS_ASSERT_LIKE("no staging needed: repo-add log", + "fake repo-add: boost-staging.db.tar.zst boost(-libs)?-1\\.73\\.0-1-x86_64.pkg.tar.zst boost(-libs)?-1\\.73\\.0-1-x86_64.pkg.tar.zst\n"s, + readFile("building/build-data/conduct-build-test/boost/pkg/repo-add.log")); + + // check whether package have been added to staging repo + CPPUNIT_ASSERT_MESSAGE( + "staging needed: package added to repo (0)", std::filesystem::is_regular_file("repos/boost-staging/os/src/boost-1.73.0-1.src.tar.gz")); + CPPUNIT_ASSERT_MESSAGE("staging needed: package added to repo (1)", + std::filesystem::is_regular_file("repos/boost-staging/os/x86_64/boost-1.73.0-1-x86_64.pkg.tar.zst")); + CPPUNIT_ASSERT_MESSAGE("staging needed: package added to repo (2)", + std::filesystem::is_regular_file("repos/boost-staging/os/x86_64/boost-libs-1.73.0-1-x86_64.pkg.tar.zst")); +} diff --git a/librepomgr/tests/cppunit.cpp b/librepomgr/tests/cppunit.cpp new file mode 100644 index 0000000..67aaee6 --- /dev/null +++ b/librepomgr/tests/cppunit.cpp @@ -0,0 +1 @@ +#include diff --git a/librepomgr/tests/parser_helper.cpp b/librepomgr/tests/parser_helper.cpp new file mode 120000 index 0000000..c41d881 --- /dev/null +++ b/librepomgr/tests/parser_helper.cpp @@ -0,0 +1 @@ +../../libpkg/tests/parser_helper.cpp \ No newline at end of file diff --git a/librepomgr/tests/parser_helper.h b/librepomgr/tests/parser_helper.h new file mode 120000 index 0000000..531042b --- /dev/null +++ b/librepomgr/tests/parser_helper.h @@ -0,0 +1 @@ +../../libpkg/tests/parser_helper.h \ No newline at end of file diff --git a/librepomgr/tests/webapi.cpp b/librepomgr/tests/webapi.cpp new file mode 100644 index 0000000..b98a2d7 --- /dev/null +++ b/librepomgr/tests/webapi.cpp @@ -0,0 +1,151 @@ +#include "../serversetup.h" +#include "../webapi/server.h" +#include "../webclient/session.h" + +#include "../../libpkg/data/config.h" + +#include "resources/config.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; + +using namespace LibRepoMgr; +using namespace LibRepoMgr::WebAPI; + +class WebAPITests : public TestFixture { + CPPUNIT_TEST_SUITE(WebAPITests); + CPPUNIT_TEST(testBasicNetworking); + CPPUNIT_TEST_SUITE_END(); + +public: + WebAPITests(); + void setUp() override; + void tearDown() override; + + void testRoutes(const std::list> &routes); + void testBasicNetworking(); + +private: + ServiceSetup m_setup; + boost::beast::error_code m_lastError; + string m_body; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(WebAPITests); + +unsigned short randomPort() +{ + random_device dev; + default_random_engine engine(dev()); + uniform_int_distribution distri(5000, 25000); + return distri(engine); +} + +WebAPITests::WebAPITests() +{ +} + +void WebAPITests::setUp() +{ + applicationInfo.version = APP_VERSION; + m_setup.webServer.port = randomPort(); +} + +void WebAPITests::tearDown() +{ +} + +void WebAPITests::testRoutes(const std::list> &routes) +{ + // get first route + auto currentRoute = routes.begin(); + if (currentRoute == routes.end()) { + return; + } + + // start server + auto server = std::make_shared(m_setup); + server->run(); + cout << "Test server running under http://" << m_setup.webServer.address << ':' << m_setup.webServer.port << endl; + + // define function to stop server + const auto stopServer = [&] { + if (server->m_socket.is_open()) { + server->m_socket.cancel(); + } + if (server->m_acceptor.is_open()) { + server->m_acceptor.cancel(); + } + m_setup.webServer.ioContext.stop(); + }; + + // define function to request the next route to test + WebClient::Session::Handler handleResponse; + const auto testNextRoute = [&] { + std::make_shared(m_setup.webServer.ioContext, handleResponse) + ->run(m_setup.webServer.address.to_string().data(), numberToString(m_setup.webServer.port).data(), boost::beast::http::verb::get, + currentRoute->first.data(), 11); + }; + + // define function to respond + handleResponse = [&](WebClient::Session &session, const WebClient::HttpClientError &error) { + currentRoute->second(session, error); + if (++currentRoute == routes.end()) { + boost::asio::post(server->m_socket.get_executor(), stopServer); + return; + } + testNextRoute(); + }; + + // start to actually run the tests + testNextRoute(); + m_setup.webServer.ioContext.run(); +} + +void WebAPITests::testBasicNetworking() +{ + testRoutes({ + { "/", + [](const WebClient::Session &session, const WebClient::HttpClientError &error) { + const auto &response = get(session.response); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT(!response.body().empty()); + cout << "index: " << response.body() << endl; + } }, + { "/foo", + [](const WebClient::Session &session, const WebClient::HttpClientError &error) { + const auto &response = get(session.response); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT_EQUAL("text/plain"s, response[boost::beast::http::field::content_type].to_string()); + CPPUNIT_ASSERT_EQUAL("The resource 'route \"GET /foo\"' was not found."s, response.body()); + } }, + { "/api/v0/version", + [](const WebClient::Session &session, const WebClient::HttpClientError &error) { + const auto &response = get(session.response); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT_EQUAL("text/plain"s, response[boost::beast::http::field::content_type].to_string()); + CPPUNIT_ASSERT_EQUAL(string(APP_VERSION), response.body()); + } }, + { "/api/v0/status", + [](const WebClient::Session &session, const WebClient::HttpClientError &error) { + const auto &response = get(session.response); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT(!response.body().empty()); + CPPUNIT_ASSERT_EQUAL("application/json"s, response[boost::beast::http::field::content_type].to_string()); + cout << "status: " << response.body() << endl; + } }, + }); +} diff --git a/librepomgr/webapi/params.cpp b/librepomgr/webapi/params.cpp new file mode 100644 index 0000000..0410acf --- /dev/null +++ b/librepomgr/webapi/params.cpp @@ -0,0 +1,134 @@ +#include "./params.h" +#include "./session.h" + +#include + +using namespace std; +using namespace CppUtilities; + +namespace LibRepoMgr { +namespace WebAPI { + +Url::Url(const Request &request) +{ + const auto target = request.target(); + path = std::string_view(target.data(), target.size()); + + // find hash + const auto hashBegin = path.find('#'); + if (hashBegin != std::string_view::npos) { + hash = path.substr(hashBegin + 1); + path = path.substr(0, hashBegin); + } + + // find params + const auto paramsBegin = path.find('?'); + if (paramsBegin != std::string_view::npos) { + const auto joinedParams(path.substr(paramsBegin + 1)); + const auto paramParts(splitStringSimple>(joinedParams, "&")); + params.reserve(paramParts.size()); + + // split param parts into key-value pairs + for (const auto ¶mPart : paramParts) { + const auto eqBegin(paramPart.find('=')); + if (eqBegin != std::string_view::npos) { + params.emplace_back(paramPart.substr(0, eqBegin), paramPart.substr(eqBegin + 1)); + } else { + params.emplace_back(paramPart, std::string_view()); + } + } + + path = path.substr(0, paramsBegin); + } +} + +bool Url::hasFlag(std::string_view paramName) const +{ + for (const auto ¶m : params) { + if (param.first == paramName) { + return !param.second.empty() && param.second != "0"; + } + } + return false; +} + +std::string_view Url::value(std::string_view paramName) const +{ + for (const auto ¶m : params) { + if (param.first == paramName) { + return param.second; + } + } + return std::string_view(); +} + +std::vector Url::decodeValues(std::string_view paramName) const +{ + vector res; + for (const auto ¶m : params) { + if (param.first == paramName) { + res.emplace_back(Url::decodeValue(param.second)); + } + } + return res; +} + +std::string Url::decodeValue(std::string_view value) +{ + string res; + res.reserve(value.size()); + + enum { + AnyChar, + FirstDigit, + SecondDigit, + } state + = AnyChar; + boost::beast::string_view::value_type encodedValue; + try { + for (const auto c : value) { + switch (state) { + case AnyChar: + switch (c) { + case '%': + state = FirstDigit; + break; + default: + res += c; + } + break; + case FirstDigit: + encodedValue = charToDigit(c, 16) * 16; + state = SecondDigit; + break; + case SecondDigit: + res += encodedValue + charToDigit(c, 16); + state = AnyChar; + break; + } + } + } catch (const ConversionException &) { + throw BadRequest("Decoded URI parameter malformed."); + } + + return res; +} + +std::string Url::encodeValue(std::string_view value) +{ + string res; + res.reserve(value.size()); + for (const auto c : value) { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '-' || c == '_' || c == '.' || c == '~')) { + res += c; + } else { + res += '%'; + res += digitToChar(static_cast((c & 0xF0) >> 4)); + res += digitToChar(static_cast((c & 0x0F) >> 0)); + } + } + return res; +} + +} // namespace WebAPI +} // namespace LibRepoMgr diff --git a/librepomgr/webapi/params.h b/librepomgr/webapi/params.h new file mode 100644 index 0000000..2ee980b --- /dev/null +++ b/librepomgr/webapi/params.h @@ -0,0 +1,90 @@ +#ifndef LIBREPOMGR_WEBAPI_HELPER_H +#define LIBREPOMGR_WEBAPI_HELPER_H + +#include "../global.h" + +#include "./session.h" +#include "./typedefs.h" + +#include + +namespace std { + +template <> struct hash { + std::size_t operator()(boost::beast::string_view str) const noexcept + { + return std::_Hash_impl::hash(str.data(), str.length()); + } +}; + +} // namespace std + +namespace LibRepoMgr { + +struct ServiceSetup; + +namespace WebAPI { + +struct LIBREPOMGR_EXPORT BadRequest : std::runtime_error { + BadRequest(const char *message); +}; + +inline BadRequest::BadRequest(const char *message) + : std::runtime_error(message) +{ +} + +struct LIBREPOMGR_EXPORT Url { + Url(const Request &request); + std::string_view path; + std::string_view hash; + std::vector> params; + + bool hasFlag(std::string_view paramName) const; + bool hasPrettyFlag() const; + std::string_view value(std::string_view paramName) const; + std::vector decodeValues(std::string_view paramName) const; + static std::string decodeValue(std::string_view value); + static std::string encodeValue(std::string_view value); +}; + +inline bool Url::hasPrettyFlag() const +{ + return hasFlag("pretty"); +} + +struct LIBREPOMGR_EXPORT Params { + Params(ServiceSetup &setup, Session &session); + ServiceSetup &setup; + Session &session; + const Url target; + + template boost::beast::string_view headerValue(FieldType field) const; + const Request &request() const; +}; + +inline Params::Params(ServiceSetup &setup, Session &session) + : setup(setup) + , session(session) + , target(session.request()) +{ +} + +inline const Request &Params::request() const +{ + return session.request(); +} + +template boost::beast::string_view Params::headerValue(FieldType field) const +{ + const auto fieldIterator(request().find(field)); + if (fieldIterator != request().cend()) { + return fieldIterator->value(); + } + return boost::beast::string_view(); +} + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_WEBAPI_HELPER_H diff --git a/librepomgr/webapi/render.cpp b/librepomgr/webapi/render.cpp new file mode 100644 index 0000000..d282b98 --- /dev/null +++ b/librepomgr/webapi/render.cpp @@ -0,0 +1,215 @@ +#include "./render.h" + +#include + +#include +#include + +using namespace std; +using namespace boost::beast; +using namespace CppUtilities; + +namespace LibRepoMgr { +namespace WebAPI { +namespace Render { + +string escapeHtml(std::string_view unescapedStr) +{ + // allocate string for result + string::size_type requiredSize = unescapedStr.size(); + for (const auto &c : unescapedStr) { + switch (c) { + case '&': + requiredSize += 4; + break; + case '\"': + case '\'': + requiredSize += 5; + break; + case '<': + case '>': + requiredSize += 3; + break; + default:; + } + } + string escapedStr; + escapedStr.reserve(requiredSize); + + // make escaped string + for (const auto &c : unescapedStr) { + switch (c) { + case '&': + escapedStr += "&"; + break; + case '\"': + escapedStr += """; + break; + case '\'': + escapedStr += "'"; + break; + case '<': + escapedStr += "<"; + break; + case '>': + escapedStr += ">"; + break; + default: + escapedStr += c; + } + } + return escapedStr; +} + +std::shared_ptr makeOptions(const Request &request, std::string_view origin, std::string_view methods, std::string_view headers) +{ + const auto res = make_shared(http::status::ok, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::access_control_allow_origin, boost::beast::string_view(origin.data(), origin.size())); + res->set(http::field::access_control_allow_methods, boost::beast::string_view(methods.data(), methods.size())); + res->set(http::field::access_control_allow_headers, boost::beast::string_view(headers.data(), headers.size())); + res->keep_alive(request.keep_alive()); + res->prepare_payload(); + return res; +} + +std::shared_ptr makeBadRequest(const Request &request, std::string_view why) +{ + const auto res = make_shared(http::status::bad_request, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, "text/plain"); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = why; + res->prepare_payload(); + return res; +} + +std::shared_ptr makeBadRequest(const Request &request, RAPIDJSON_NAMESPACE::StringBuffer &&why) +{ + const auto res = make_shared(http::status::bad_request, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, "application/json"); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body().assign(why.GetString(), why.GetSize()); + res->prepare_payload(); + return res; +} + +std::shared_ptr makeNotFound(const Request &request, std::string_view target) +{ + const auto res = make_shared(http::status::not_found, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, "text/plain"); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = "The resource '" % target + "' was not found."; + res->prepare_payload(); + return res; +} + +std::shared_ptr makeAuthRequired(const Request &request) +{ + const auto res = make_shared(http::status::unauthorized, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, "text/plain"); + res->set(http::field::www_authenticate, "Basic realm=\"auth required\""); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = "Authenticate to access the resource."; + res->prepare_payload(); + return res; +} + +std::shared_ptr makeForbidden(const Request &request) +{ + const auto res = make_shared(http::status::forbidden, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, "text/plain"); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = "Access denied."; + res->prepare_payload(); + return res; +} + +std::shared_ptr makeServerError(const Request &request, std::string_view what) +{ + const auto res = make_shared(http::status::internal_server_error, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, "text/plain"); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = argsToString("An error occurred: '", what, "'"); + res->prepare_payload(); + return res; +} + +std::shared_ptr makeData(const Request &request, const string &buffer, const char *mimeType) +{ + const auto res = make_shared(http::status::ok, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, mimeType); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = buffer; + res->prepare_payload(); + return res; +} + +std::shared_ptr makeData(const Request &request, string &&buffer, const char *mimeType) +{ + const auto res = make_shared(http::status::ok, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, mimeType); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body() = buffer; + res->prepare_payload(); + return res; +} + +std::shared_ptr makeData(const Request &request, rapidjson::StringBuffer &&buffer, const char *mimeType) +{ + const auto res = make_shared(http::status::ok, request.version()); + res->set(http::field::server, BOOST_BEAST_VERSION_STRING); + res->set(http::field::content_type, mimeType); + res->set(http::field::access_control_allow_origin, "*"); + res->keep_alive(request.keep_alive()); + res->body().assign(buffer.GetString(), buffer.GetSize()); + res->prepare_payload(); + return res; +} + +std::shared_ptr makeFile(const Request &request, const char *filePath, const char *mimeType, boost::beast::error_code &ec) +{ + const auto fileResponse = std::make_shared(); + fileResponse->set(http::field::server, BOOST_BEAST_VERSION_STRING); + fileResponse->set(http::field::content_type, mimeType); + fileResponse->set(http::field::access_control_allow_origin, "*"); + fileResponse->keep_alive(request.keep_alive()); + fileResponse->body().open(filePath, file_mode::scan, ec); + return fileResponse; +} + +inline ChunkResponse::ChunkResponse() + : response(boost::beast::http::status::ok, 11) + , serializer(response) +{ + response.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); + response.set(boost::beast::http::field::access_control_allow_origin, "*"); + response.chunked(true); +} + +std::shared_ptr makeChunkResponse(const Request &request, const char *mimeType) +{ + const auto chunkResponse = std::make_shared(); + chunkResponse->response.set(boost::beast::http::field::content_type, mimeType); + chunkResponse->response.keep_alive(request.keep_alive()); + return chunkResponse; +} + +} // namespace Render +} // namespace WebAPI +} // namespace LibRepoMgr diff --git a/librepomgr/webapi/render.h b/librepomgr/webapi/render.h new file mode 100644 index 0000000..caa1f86 --- /dev/null +++ b/librepomgr/webapi/render.h @@ -0,0 +1,104 @@ +#ifndef LIBREPOMGR_RENDER_H +#define LIBREPOMGR_RENDER_H + +#include "./typedefs.h" + +#include + +#include + +#include +#include + +namespace LibRepoMgr { + +namespace WebAPI { + +namespace Render { +struct ChunkResponse { + explicit ChunkResponse(); + boost::beast::http::response response; + boost::beast::http::response_serializer serializer; +}; + +std::string escapeHtml(std::string_view unescapedStr); + +std::shared_ptr makeOptions(const Request &request, std::string_view origin = "*", std::string_view methods = "GET,POST", + std::string_view headers = "Content-Type, Access-Control-Allow-Headers, Authorization, X-API-Key, X-Client-ID"); +std::shared_ptr makeBadRequest(const Request &request, std::string_view why); +std::shared_ptr makeBadRequest(const Request &request, RAPIDJSON_NAMESPACE::StringBuffer &&why); +std::shared_ptr makeNotFound(const Request &request, std::string_view target); +std::shared_ptr makeAuthRequired(const Request &request); +std::shared_ptr makeForbidden(const Request &request); +std::shared_ptr makeServerError(const Request &request, std::string_view what); +std::shared_ptr makeData(const Request &request, const std::string &data, const char *mimeType); +std::shared_ptr makeData(const Request &request, std::string &&data, const char *mimeType); +std::shared_ptr makeData(const Request &request, RAPIDJSON_NAMESPACE::StringBuffer &&buffer, const char *mimeType); +std::shared_ptr makeText(const Request &request, const std::string &text); +std::shared_ptr makeText(const Request &request, std::string &&text); +std::shared_ptr makeJson(const Request &request, std::string &&json); +std::shared_ptr makeFile(const Request &request, const char *filePath, const char *mimeType, boost::beast::error_code &ec); +std::shared_ptr makeChunkResponse(const Request &request, const char *mimeType); + +inline std::shared_ptr makeText(const Request &request, const std::string &text) +{ + return makeData(request, text, "text/plain"); +} + +inline std::shared_ptr makeText(const Request &request, std::string &&text) +{ + return makeData(request, std::move(text), "text/plain"); +} + +inline std::shared_ptr makeHtml(const Request &request, std::string &&text) +{ + return makeData(request, std::move(text), "text/html"); +} + +inline std::shared_ptr makeJson(const Request &request, std::string &&json) +{ + return makeData(request, std::move(json), "application/json"); +} + +inline std::shared_ptr makeJson(const Request &request, RAPIDJSON_NAMESPACE::StringBuffer &&buffer) +{ + return makeData(request, std::move(buffer), "application/json"); +} + +inline std::shared_ptr makeJson(const Request &request, const RAPIDJSON_NAMESPACE::Document &document, bool pretty = true) +{ + RAPIDJSON_NAMESPACE::StringBuffer buffer; + if (pretty) { + RAPIDJSON_NAMESPACE::PrettyWriter writer(buffer); + document.Accept(writer); + } else { + RAPIDJSON_NAMESPACE::Writer writer(buffer); + document.Accept(writer); + } + return makeJson(request, std::move(buffer)); +} + +template , ReflectiveRapidJSON::IsMapOrHash> + * = nullptr> +inline std::shared_ptr makeJson(const Request &request, const Type &serializable, bool pretty = true) +{ + RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); + ReflectiveRapidJSON::JsonReflector::push(serializable, document, document.GetAllocator()); + return makeJson(request, document, pretty); +} + +template > * = nullptr> +inline std::shared_ptr makeJson(const Request &request, const Type &serializable, bool pretty = true) +{ + RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kArrayType); + ReflectiveRapidJSON::JsonReflector::push(serializable, document, document.GetAllocator()); + return makeJson(request, document, pretty); +} + +} // namespace Render + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_RENDER_H diff --git a/librepomgr/webapi/repository.cpp b/librepomgr/webapi/repository.cpp new file mode 100644 index 0000000..ff472c6 --- /dev/null +++ b/librepomgr/webapi/repository.cpp @@ -0,0 +1,11 @@ +#include "./repository.h" + +namespace LibRepoMgr { +namespace WebAPI { + +Repository::Repository() +{ +} + +} // namespace WebAPI +} // namespace LibRepoMgr diff --git a/librepomgr/webapi/repository.h b/librepomgr/webapi/repository.h new file mode 100644 index 0000000..2a308cf --- /dev/null +++ b/librepomgr/webapi/repository.h @@ -0,0 +1,15 @@ +#ifndef REPOSITORY_H +#define REPOSITORY_H + +namespace LibRepoMgr { +namespace WebAPI { + +class Repository { +public: + Repository(); +}; + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // REPOSITORY_H diff --git a/librepomgr/webapi/routeid.h b/librepomgr/webapi/routeid.h new file mode 100644 index 0000000..3b31704 --- /dev/null +++ b/librepomgr/webapi/routeid.h @@ -0,0 +1,48 @@ +#ifndef LIBREPOMGR_ROUTE_IDS_H +#define LIBREPOMGR_ROUTE_IDS_H + +#include "../authentication.h" + +#include "./typedefs.h" + +#include + +#include + +namespace LibRepoMgr { +namespace WebAPI { + +struct RouteId { + boost::beast::http::verb method; + std::string path; + + bool operator==(const RouteId &other) const; +}; + +inline bool RouteId::operator==(const RouteId &other) const +{ + return method == other.method && path == other.path; +} + +struct Route { + RouteHandler handler; + UserPermissions permissions = UserPermissions::None; +}; +using Router = std::unordered_map; + +} // namespace WebAPI +} // namespace LibRepoMgr + +namespace std { + +template <> struct hash { + std::size_t operator()(const LibRepoMgr::WebAPI::RouteId &id) const + { + using VerbType = typename std::underlying_type::type; + return std::hash()(static_cast(id.method)) ^ (std::hash()(id.path) << 1); + } +}; + +} // namespace std + +#endif // LIBREPOMGR_ROUTE_IDS_H diff --git a/librepomgr/webapi/routes.cpp b/librepomgr/webapi/routes.cpp new file mode 100644 index 0000000..c9b8ff4 --- /dev/null +++ b/librepomgr/webapi/routes.cpp @@ -0,0 +1,944 @@ +#include "./routes.h" +#include "./params.h" +#include "./render.h" +#include "./server.h" + +#include "../webclient/aur.h" + +#include "../serversetup.h" + +#include "../../libpkg/data/config.h" +#include "../../libpkg/data/package.h" +#include "../../libpkg/parser/aur.h" + +#include "resources/config.h" + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; +using namespace LibRepoMgr::WebAPI::Render; +using namespace LibPkg; + +namespace LibRepoMgr { +namespace WebAPI { +namespace Routes { + +void getRoot(const Params ¶ms, ResponseHandler &&handler) +{ + static const auto routes([] { + stringstream ss; + ss << "Available routes:\n"; + for (const auto &route : Server::router()) { + const auto method(boost::beast::http::to_string(route.first.method)); + ss << method; + for (auto i = method.size(); i < 5; ++i) { + ss << ' '; + } + ss << route.first.path << '\n'; + } + ss << "\nNote: curl -X GET/POST http://...\n"; + return ss.str(); + }()); + handler(makeText(params.request(), routes)); +} + +void getVersion(const Params ¶ms, ResponseHandler &&handler) +{ + handler(makeText(params.request(), APP_VERSION)); +} + +void getStatus(const Params ¶ms, ResponseHandler &&handler) +{ + auto configLock = params.setup.config.lockToRead(); + auto buildLock = params.setup.building.lockToRead(); + auto metaLock = params.setup.building.metaInfo.lockToRead(); + const auto status = params.setup.computeStatus(); + const auto jsonDoc = status.toJsonDocument(); + metaLock.unlock(); + buildLock.unlock(); + configLock.unlock(); + handler(makeJson(params.request(), jsonDoc, params.target.hasPrettyFlag())); +} + +void getDatabases(const Params ¶ms, ResponseHandler &&handler) +{ + const auto prettyFlag(params.target.hasPrettyFlag()); + const auto dbNames = params.target.decodeValues("name"); + if (dbNames.empty()) { + auto lock = params.setup.config.lockToRead(); + const auto jsonDoc = ReflectiveRapidJSON::JsonReflector::toJsonDocument(params.setup.config.databases); + lock.unlock(); + handler(makeJson(params.request(), jsonDoc, prettyFlag)); + return; + } + + RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kArrayType); + RAPIDJSON_NAMESPACE::Document::Array array(document.GetArray()); + auto lock = params.setup.config.lockToRead(); + for (const auto &dbName : dbNames) { + for (const auto &db : params.setup.config.databases) { + if (db.name == dbName) { + ReflectiveRapidJSON::JsonReflector::push(db, array, document.GetAllocator()); + break; + } + } + } + lock.unlock(); + handler(makeJson(params.request(), document, prettyFlag)); +} + +void getUnresolved(const Params ¶ms, ResponseHandler &&handler) +{ + const auto names(params.target.decodeValues("name")); + if (names.empty()) { + throw BadRequest("parameter name missing"); + } + + namespace JR = ReflectiveRapidJSON::JsonReflector; + RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); + RAPIDJSON_NAMESPACE::Document::Object obj(document.GetObject()); + + auto lock = params.setup.config.lockToRead(); + const auto newPackages = [&] { + const auto names(params.target.decodeValues("add")); + vector> newPackages; + newPackages.reserve(names.size()); + for (const auto &name : names) { + for (auto &package : params.setup.config.findPackages(name)) { + newPackages.emplace_back(move(package.pkg)); + } + } + return newPackages; + }(); + const auto removedPackages = [&] { + DependencySet removedProvides; + for (const auto &dependencyString : params.target.decodeValues("remove")) { + auto dependency(Dependency::fromString(dependencyString.data(), dependencyString.size())); + removedProvides.add(move(dependency.name), DependencyDetail(move(dependency.version), dependency.mode)); + } + return removedProvides; + }(); + + for (const auto &name : names) { + for (auto &db : params.setup.config.databases) { + if (db.name != name) { + continue; + } + const auto unresolvedPackages = db.detectUnresolvedPackages(params.setup.config, newPackages, removedPackages); + auto value = RAPIDJSON_NAMESPACE::Value(RAPIDJSON_NAMESPACE::Type::kObjectType); + auto obj = value.GetObject(); + for (const auto &[package, unresolvedDependencies] : unresolvedPackages) { + JR::push(unresolvedDependencies, package->name.data(), obj, document.GetAllocator()); + } + obj.AddMember(RAPIDJSON_NAMESPACE::StringRef(db.name.data(), JR::rapidJsonSize(db.name.size())), value, document.GetAllocator()); + } + } + + lock.unlock(); + handler(makeJson(params.request(), document, params.target.hasPrettyFlag())); +} + +void getPackages(const Params ¶ms, ResponseHandler &&handler) +{ + // read mode + const auto modes(params.target.decodeValues("mode")); + if (modes.size() > 1) { + throw BadRequest("more than one mode specified"); + } + enum class Mode { + Name, + NameContains, + Regex, + Provides, + Depends, + LibProvides, + LibDepends, + } mode + = Mode::Name; + static const std::unordered_map modeByParamValue{ + { "name", Mode::Name }, + { "name-contains", Mode::NameContains }, + { "regex", Mode::Regex }, + { "provides", Mode::Provides }, + { "depends", Mode::Depends }, + { "libprovides", Mode::LibProvides }, + { "libdepends", Mode::LibDepends }, + }; + if (!modes.empty()) { + const auto modeIterator = modeByParamValue.find(modes.front()); + if (modeIterator == modeByParamValue.end()) { + throw BadRequest("mode must be \"name\", \"name-contains\", \"regex\", \"provides\", \"depends\", \"libprovides\" or \"libdepends\""); + } + mode = modeIterator->second; + } + + // check for details flag + const auto details = params.target.hasFlag("details"); + if (details && mode != Mode::Name) { + throw BadRequest("details flag only supported with mode \"name\""); + } + + // check dbs + const auto dbValues = params.target.decodeValues("db"); + const auto canSearchAur = mode == Mode::Name || mode == Mode::NameContains; + auto dbs = std::unordered_map>(); + auto fromAur = dbValues.empty() && canSearchAur; + auto onlyFromAur = false; + for (const auto &dbValue : dbValues) { + if (dbValue == "any") { + dbs.clear(); + fromAur = canSearchAur; + break; + } + if (dbValue == "aur") { + if (!canSearchAur) { + throw BadRequest("searching AUR is only possible with mode \"name\""); + } + fromAur = true; + onlyFromAur = true; + continue; + } + const auto &[dbName, dbArch] = LibPkg::Config::parseDatabaseDenotation(dbValue); + dbs[dbName].emplace(dbArch); + } + + RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kArrayType); + RAPIDJSON_NAMESPACE::Document::Array array(document.GetArray()); + + const auto pushPackages = [&dbs, &document, &array](auto &&packages) { + for (const auto &package : packages) { + if (!dbs.empty()) { + const auto *const db = std::get(package.db); + const auto dbIterator = dbs.find(db->name); + if (dbIterator == dbs.end() || dbIterator->second.find(db->arch) == dbIterator->second.end()) { + continue; + } + } + ReflectiveRapidJSON::JsonReflector::push(package, array, document.GetAllocator()); + } + }; + + std::vector aurPackages; + std::vector neededAurPackages; + auto lock = params.setup.config.lockToRead(); + auto &aurDb = params.setup.config.aur; + + // add specified packages to JSON array + for (const auto &name : params.target.decodeValues("name")) { + switch (mode) { + case Mode::Name: { + const auto packageDenotation + = LibPkg::Config::parsePackageDenotation(name); // assume names are in the form "repo@arch/pkgname", eg. "core@i686/gcc" + const auto &[dbName, dbArch, packageName] = packageDenotation; + const auto isDbAur = dbName == "aur"; + if (fromAur && (dbName.empty() || isDbAur)) { + auto packageNameStr = std::string(packageName); + if (auto i = aurDb.packages.find(packageNameStr), end = aurDb.packages.end(); + i != end && (!details || i->second->origin != PackageOrigin::AurRpcSearch)) { + aurPackages.emplace_back(PackageSearchResult{ aurDb, i->second }); + } else { + neededAurPackages.emplace_back(std::move(packageNameStr)); + } + } + if (!isDbAur && (!dbs.empty() || !onlyFromAur)) { + auto packages = params.setup.config.findPackages(packageDenotation); + if (details) { + for (const auto &package : packages) { + if (dbs.empty() || dbs.find(std::get(package.db)->name) != dbs.end()) { + ReflectiveRapidJSON::JsonReflector::push(package.pkg, array, document.GetAllocator()); + } + } + } else { + pushPackages(std::move(packages)); + } + } + break; + } + case Mode::NameContains: + pushPackages(params.setup.config.findPackages( + [&dbs, onlyFromAur](const LibPkg::Database &db) { return (dbs.empty() && !onlyFromAur) || dbs.find(db.name) != dbs.end(); }, + [&name](const LibPkg::Database &, const LibPkg::Package &pkg) { return pkg.name.find(name) != std::string::npos; })); + if (fromAur && !name.empty()) { + neededAurPackages.emplace_back(std::move(name)); + } + break; + case Mode::Regex: + // assume names are regexes + pushPackages(params.setup.config.findPackages(std::regex(name.data(), name.size()))); + break; + case Mode::Provides: + case Mode::Depends: + // assume names are dependency notation + pushPackages(params.setup.config.findPackages(Dependency::fromString(name), mode == Mode::Depends)); + break; + case Mode::LibProvides: + case Mode::LibDepends: + // assume names are "normalized" library names with platform prefix + pushPackages(params.setup.config.findPackagesProvidingLibrary(name, mode == Mode::LibDepends)); + break; + default:; + } + } + + // add cached AUR packages + if (details) { + for (auto &package : aurPackages) { + ReflectiveRapidJSON::JsonReflector::push(std::move(package.pkg), array, document.GetAllocator()); + } + } else if (!aurPackages.empty()) { + for (const auto &package : aurPackages) { + ReflectiveRapidJSON::JsonReflector::push(package, array, document.GetAllocator()); + } + } + + // serialize JSON directly if no AUR request required + lock.unlock(); + if (neededAurPackages.empty()) { + handler(makeJson(params.request(), document, params.target.hasPrettyFlag())); + return; + } + + // retrieve packages from AUR + auto log = LogContext(); + auto handleAurResponse + = [handler{ std::move(handler) }, params{ std::move(params) }, document{ make_shared(std::move(document)) }, + details](WebClient::AurQuerySession::ContainerType &&aurPackages) mutable { + std::vector aurPackageSearchResults; + aurPackageSearchResults.reserve(aurPackages.size()); + auto lock = params.setup.config.lockToRead(); + auto array = document->GetArray(); + if (details) { + for (auto &package : aurPackages) { + ReflectiveRapidJSON::JsonReflector::push(std::move(package), array, document->GetAllocator()); + } + } else if (!aurPackages.empty()) { + for (auto &package : aurPackages) { + ReflectiveRapidJSON::JsonReflector::push( + PackageSearchResult{ params.setup.config.aur, std::move(package) }, array, document->GetAllocator()); + } + } + lock.unlock(); + handler(makeJson(params.request(), *document, params.target.hasPrettyFlag())); + }; + if (mode == Mode::Name) { + WebClient::queryAurPackages(log, params.setup, neededAurPackages, params.setup.webServer.ioContext, std::move(handleAurResponse)); + } else { + auto session = WebClient::AurQuerySession::create(params.setup.webServer.ioContext, std::move(handleAurResponse)); + for (const auto &searchTerm : neededAurPackages) { + WebClient::searchAurPackages(log, params.setup, searchTerm, params.setup.webServer.ioContext, session); + } + } +} + +void postLoadPackages(const Params ¶ms, ResponseHandler &&handler) +{ + auto lock = params.setup.config.lockToWrite(); + params.setup.config.loadAllPackages(params.target.hasFlag("with-files")); + lock.unlock(); + handler(makeText(params.request(), "packages loaded")); +} + +void postDumpCacheFile(const Params ¶ms, ResponseHandler &&handler) +{ + auto configLock = params.setup.config.lockToRead(); + auto buildLock = params.setup.building.lockToRead(); + const auto size = params.setup.saveState(); + buildLock.unlock(); + configLock.unlock(); + handler(makeText(params.request(), "cache file written (" % dataSizeToString(size) + ')')); +} + +void postQuit(const Params ¶ms, ResponseHandler &&handler) +{ + cerr << Phrases::SuccessMessage << "Stopping via route /quit" << endl; + Server::stop(); + handler(makeText(params.request(), "stopping")); +} + +void getBuildActions(const Params ¶ms, ResponseHandler &&handler) +{ + RAPIDJSON_NAMESPACE::Document jsonDoc(RAPIDJSON_NAMESPACE::kArrayType); + RAPIDJSON_NAMESPACE::Value::Array array(jsonDoc.GetArray()); + + auto buildActionLock = params.setup.building.lockToRead(); + array.Reserve(ReflectiveRapidJSON::JsonReflector::rapidJsonSize(params.setup.building.actions.size()), jsonDoc.GetAllocator()); + auto configLock = params.setup.config.lockToRead(); // build actions might refer to "config things" like packages + for (const auto &buildAction : params.setup.building.actions) { + if (!buildAction) { + continue; + } + ReflectiveRapidJSON::JsonReflector::push(BuildActionBasicInfo(*buildAction), array, jsonDoc.GetAllocator()); + } + configLock.unlock(); + buildActionLock.unlock(); + + handler(makeJson(params.request(), jsonDoc, params.target.hasPrettyFlag())); +} + +struct BuildActionSearchResult { + struct ActionRef { + std::uint64_t id; + BuildAction *action = nullptr; + }; + std::vector actions; + std::variant, std::unique_lock> lock; + bool ok = false; +}; + +BuildActionSearchResult findBuildActions(const Params ¶ms, ResponseHandler &&handler, bool acquireWriteLock = false, std::size_t maxIDs = 100) +{ + BuildActionSearchResult result; + const auto idParams = params.target.decodeValues("id"); + if (idParams.empty()) { + handler(makeBadRequest(params.request(), "need at least one build action ID")); + return result; + } + if (idParams.size() > maxIDs) { + handler(makeBadRequest(params.request(), "number of specified IDs exceeds limit")); + return result; + } + + result.actions.reserve(idParams.size()); + for (const auto &idString : idParams) { + try { + result.actions.emplace_back(BuildActionSearchResult::ActionRef{ .id = stringToNumber(idString) }); + } catch (const ConversionException &) { + handler(makeBadRequest(params.request(), "all IDs must be unsigned integers")); + return result; + } + } + + if (acquireWriteLock) { + result.lock = params.setup.building.lockToWrite(); + } else { + result.lock = params.setup.building.lockToRead(); + } + + for (auto &actionRef : result.actions) { + if (actionRef.id >= params.setup.building.actions.size()) { + result.lock = std::monostate{}; + handler(makeBadRequest(params.request(), argsToString("no build action with specified ID \"", actionRef.id, "\" exists"))); + return result; + } + actionRef.action = params.setup.building.actions[actionRef.id].get(); + if (!actionRef.action) { + handler(makeBadRequest(params.request(), argsToString("no build action with specified ID \"", actionRef.id, "\" exists"))); + return result; + } + } + result.ok = true; + return result; +} + +void getBuildActionDetails(const Params ¶ms, ResponseHandler &&handler) +{ + RAPIDJSON_NAMESPACE::Document jsonDoc(RAPIDJSON_NAMESPACE::kArrayType); + RAPIDJSON_NAMESPACE::Value::Array array(jsonDoc.GetArray()); + auto buildActionsSearchResult = findBuildActions(params, std::move(handler)); + if (!buildActionsSearchResult.ok) { + return; + } + for (const auto &buildActionRef : buildActionsSearchResult.actions) { + ReflectiveRapidJSON::JsonReflector::push(*buildActionRef.action, array, jsonDoc.GetAllocator()); + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeJson(params.request(), jsonDoc, params.target.hasPrettyFlag())); +} + +void getBuildActionOutput(const Params ¶ms, ResponseHandler &&handler) +{ + const auto offsetParams = params.target.decodeValues("offset"); + std::size_t offset = 0; + if (offsetParams.size() > 1) { + handler(makeBadRequest(params.request(), "the offset parameter must be specified at most once")); + return; + } + try { + offset = stringToNumber(offsetParams.front()); + } catch (const ConversionException &) { + handler(makeBadRequest(params.request(), "the offset must be an unsigned integer")); + return; + } + auto buildActionsSearchResult = findBuildActions(params, std::move(handler), false, 1); + if (!buildActionsSearchResult.ok) { + return; + } + auto buildAction = buildActionsSearchResult.actions.front().action; + if (offset > buildAction->output.size()) { + buildActionsSearchResult.lock = std::monostate{}; + handler(makeBadRequest(params.request(), "the offset must not exceed the output size")); + return; + } + buildActionsSearchResult.lock = std::monostate{}; + buildAction->streamOutput(params, offset); +} + +static std::string readNameParam(const Params ¶ms, ResponseHandler &&handler) +{ + const auto nameParams = params.target.decodeValues("name"); + if (nameParams.size() != 1) { + handler(makeBadRequest(params.request(), "name must be specified exactly one time")); + return std::string(); + } + if (nameParams.front().empty()) { + handler(makeBadRequest(params.request(), "the name parameter must not be empty")); + return std::string(); + } + return nameParams.front(); +} + +static void getBuildActionFile(const Params ¶ms, std::vector BuildAction::*fileList, ResponseHandler &&handler) +{ + const auto name = readNameParam(params, std::move(handler)); + if (name.empty()) { + return; + } + auto buildActionsSearchResult = findBuildActions(params, std::move(handler), false, 1); + if (!buildActionsSearchResult.ok) { + return; + } + + auto *const buildAction = buildActionsSearchResult.actions.front().action; + for (const auto &logFile : buildAction->*fileList) { + if (name == logFile) { + buildAction->streamFile(params, name, "application/octet-stream"); + return; + } + } + handler(makeNotFound(params.request(), name)); +} + +void getBuildActionLogFile(const Params ¶ms, ResponseHandler &&handler) +{ + getBuildActionFile(params, &BuildAction::logfiles, std::move(handler)); +} + +void getBuildActionArtefact(const Params ¶ms, ResponseHandler &&handler) +{ + getBuildActionFile(params, &BuildAction::artefacts, std::move(handler)); +} + +void postBuildAction(const Params ¶ms, ResponseHandler &&handler) +{ + // validate general parameters + static const auto generalParameters = std::unordered_set{ "type", "directory", "start-condition", "start-after-id", + "source-repo", "destination-repo", "package", "pretty" }; + const auto typeParams = params.target.decodeValues("type"); + const auto taskParams = params.target.decodeValues("task"); + if (taskParams.size() + typeParams.size() != 1) { + handler(makeBadRequest(params.request(), "need exactly either one type or one task parameter")); + return; + } + auto directories = params.target.decodeValues("directory"); + if (directories.size() > 1) { + handler(makeBadRequest(params.request(), "at most one directory can be specified")); + return; + } + const auto startConditionValues = params.target.decodeValues("start-condition"); + if (startConditionValues.size() > 1) { + handler(makeBadRequest(params.request(), "at most one start condition can be specified")); + return; + } + const auto startImmediately = startConditionValues.empty() || startConditionValues.front() == "immediately"; + const auto startAfterAnotherAction = !startImmediately && startConditionValues.front() == "after"; + if (!startImmediately && !startAfterAnotherAction && startConditionValues.front() != "manually") { + handler(makeBadRequest(params.request(), "specified start condition is not supported")); + return; + } + const auto startAfterIdValues = params.target.decodeValues("start-after-id"); + auto startAfterIds = std::vector(); + startAfterIds.reserve(startAfterIdValues.size()); + for (const auto &startAfterIdValue : startAfterIdValues) { + if (startAfterIdValue.empty()) { + continue; + } + try { + startAfterIds.emplace_back(stringToNumber(startAfterIdValue)); + } catch (const ConversionException &) { + handler(makeBadRequest(params.request(), "the specified ID to start after is not a valid build action ID")); + return; + } + } + if (startAfterAnotherAction) { + if (startAfterIds.empty()) { + handler(makeBadRequest(params.request(), "start condition is \"after\" but not exactly one \"start-after-id\" specified")); + return; + } + } else if (!startAfterIdValues.empty() && (startAfterIdValues.size() > 1 || !startAfterIdValues.front().empty())) { + handler(makeBadRequest(params.request(), "start condition is not \"after\" but \"start-after-id\" specified")); + return; + } + + // spawn multiple build actions for the specified task + if (!taskParams.empty()) { + postBuildActionsFromTask(params, std::move(handler), taskParams.front(), directories.empty() ? std::string() : directories.front(), + startAfterIds, startImmediately); + return; + } + + // read type and type-specific flags and settings from parameters + auto &metaInfo = params.setup.building.metaInfo; + auto metaInfoLock = metaInfo.lockToRead(); + const auto &typeInfo = metaInfo.typeInfoForName(typeParams.front()); + const auto type = typeInfo.id; + if (type == BuildActionType::Invalid) { + handler(makeBadRequest(params.request(), "specified type is invalid")); + return; + } + const auto &mapping = metaInfo.mappingForId(type); + BuildActionFlagType buildActionFlags = noBuildActionFlags; + std::unordered_map buildActionSettings; + for (const auto &[key, value] : params.target.params) { + if (generalParameters.find(key) != generalParameters.end()) { + continue; // skip general parameters here + } + if (const auto flag = mapping.flagInfoByName.find(key); flag != mapping.flagInfoByName.end()) { + if (!value.empty() && value != "0") { + buildActionFlags += flag->second.get().id; + } + continue; + } + if (mapping.settingInfoByName.find(key) != mapping.settingInfoByName.end()) { + buildActionSettings[WebAPI::Url::decodeValue(key)] = WebAPI::Url::decodeValue(value); + continue; + } + handler(makeBadRequest(params.request(), argsToString("parameter \"", key, "\" is not supported (by specified build action type)"))); + return; + } + metaInfoLock.unlock(); + + // initialize build action + auto buildLock = params.setup.building.lockToWrite(); + const auto id = params.setup.building.allocateBuildActionID(); + auto startsAfterBuildActions = params.setup.building.getBuildActions(startAfterIds); + const auto startNow = startImmediately || BuildAction::haveSucceeded(startsAfterBuildActions); + buildLock.unlock(); + auto buildAction = std::make_shared(id); + if (!directories.empty()) { + buildAction->directory = move(directories.front()); + } + buildAction->type = type; + buildAction->sourceDbs = params.target.decodeValues("source-repo"); + buildAction->destinationDbs = params.target.decodeValues("destination-repo"); + buildAction->packageNames = params.target.decodeValues("package"); + buildAction->startAfter = startAfterIds; + buildAction->flags = buildActionFlags; + buildAction->settings.swap(buildActionSettings); + + // serialize build action + const auto response = buildAction->toJsonDocument(); + + // start build action immediately (no locking required because build action is not part of setup-global list yet) + if (startNow) { + buildAction->start(params.setup); + } + + // add build action to setup-global list and to "follow-up actions" of the build action this one should be started after + auto buildLock2 = params.setup.building.lockToWrite(); + if (!startNow && !startsAfterBuildActions.empty()) { + buildAction->startAfterOtherBuildActions(params.setup, startsAfterBuildActions); + } + params.setup.building.actions[id] = std::move(buildAction); + buildLock2.unlock(); + + handler(makeJson(params.request(), response)); +} + +void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, const std::string &taskName, const std::string &directory, + const std::vector &startAfterIds, bool startImmediately) +{ + static_assert(std::is_same_v); + + // show error when parameters are specified which are only supposed to be specified when starting a single build action + if (!params.target.value("source-repo").empty()) { + handler(makeBadRequest(params.request(), "the source repo can not be explicitely specified when a task is specified")); + return; + } + if (!params.target.value("destination-repo").empty()) { + handler(makeBadRequest(params.request(), "the destination repo can not be explicitely specified when a task is specified")); + return; + } + + // read other parameters + const auto packageNames = params.target.decodeValues("package"); + + // find task info + auto setupLock = params.setup.lockToRead(); + const auto &presets = params.setup.building.presets; + const auto taskIterator = presets.tasks.find(taskName); + if (taskIterator == presets.tasks.end()) { + setupLock.unlock(); + handler(makeBadRequest(params.request(), "the specified task is not configured")); + return; + } + const auto &task = taskIterator->second; + const auto &actionsToCreate = task.actions; + const auto &actionTemplates = presets.templates; + if (task.actions.empty()) { + setupLock.unlock(); + handler(makeBadRequest(params.request(), "the specified task has no actions configured")); + return; + } + + // allocate a vector to store build actions (temporarily) in + auto newBuildActions = std::vector>(); + newBuildActions.reserve(actionsToCreate.size()); + + // copy data from templates into new build actions + auto &metaInfo = params.setup.building.metaInfo; + auto metaInfoLock = metaInfo.lockToRead(); + for (const auto &actionName : actionsToCreate) { + const auto actionTemplateIterator = actionTemplates.find(actionName); + if (actionTemplateIterator == actionTemplates.end()) { + metaInfoLock.unlock(); + auto errorMessage = "the action \"" % actionName + "\" of the specified task is not configured"; + setupLock.unlock(); + handler(makeBadRequest(params.request(), std::move(errorMessage))); + return; + } + const auto &actionTemplate = actionTemplateIterator->second; + const auto buildActionType = actionTemplate.type; + if (!metaInfo.isTypeIdValid(buildActionType)) { + metaInfoLock.unlock(); + auto errorMessage = argsToString( + "the type \"", static_cast(buildActionType), "\" of action \"", actionName, "\" of the specified task is invalid"); + setupLock.unlock(); + handler(makeBadRequest(params.request(), std::move(errorMessage))); + return; + } + const auto &typeInfo = metaInfo.typeInfoForId(actionTemplate.type); + auto &buildAction = newBuildActions.emplace_back(std::make_shared()); // a real ID is set later + buildAction->taskName = taskName; + buildAction->directory = !typeInfo.directory || directory.empty() ? actionTemplate.directory : directory; + buildAction->type = buildActionType; + buildAction->sourceDbs = actionTemplate.sourceDbs; + buildAction->destinationDbs = actionTemplate.destinationDbs; + buildAction->packageNames = !typeInfo.packageNames || packageNames.empty() ? actionTemplate.packageNames : packageNames; + buildAction->flags = actionTemplate.flags; + buildAction->settings = actionTemplate.settings; + } + metaInfoLock.unlock(); + setupLock.unlock(); + + // allocate build action IDs and populate "start after ID" + BuildAction *lastBuildAction = nullptr; + auto &building = params.setup.building; + auto buildLock = building.lockToWrite(); + auto startsAfterBuildActions = building.getBuildActions(startAfterIds); + const auto startNow = startImmediately || BuildAction::haveSucceeded(startsAfterBuildActions); + for (auto &newBuildAction : newBuildActions) { + newBuildAction->id = building.allocateBuildActionID(); + if (lastBuildAction) { + newBuildAction->startAfter.emplace_back(lastBuildAction->id); + } else { + newBuildAction->startAfter = startAfterIds; + } + lastBuildAction = newBuildAction.get(); + } + buildLock.unlock(); + + // serialize build actions + const auto response = ReflectiveRapidJSON::JsonReflector::toJsonDocument(newBuildActions); + + // start first build action immediately + if (startNow) { + newBuildActions.front()->start(params.setup); + } + + // add build actions to setup-global list and populate "follow-up actions" + buildLock = building.lockToWrite(); + lastBuildAction = nullptr; + for (auto &newBuildAction : newBuildActions) { + if (lastBuildAction) { + if (lastBuildAction->hasSucceeded()) { + newBuildAction->start(params.setup); + } else { + lastBuildAction->m_followUpActions.emplace_back(newBuildAction->weak_from_this()); + } + } else if (!startsAfterBuildActions.empty()) { + newBuildAction->startAfterOtherBuildActions(params.setup, startsAfterBuildActions); + } + lastBuildAction = newBuildAction.get(); + building.actions[lastBuildAction->id] = std::move(newBuildAction); + } + buildLock.unlock(); + + handler(makeJson(params.request(), response)); +} + +void deleteBuildActions(const Params ¶ms, ResponseHandler &&handler) +{ + auto buildActionsSearchResult = findBuildActions(params, std::move(handler), true, std::numeric_limits::max()); + if (!buildActionsSearchResult.ok) { + return; + } + for (const auto &actionRef : buildActionsSearchResult.actions) { + if (!actionRef.action->isExecuting()) { + continue; + } + buildActionsSearchResult.lock = std::monostate{}; + handler( + makeBadRequest(params.request(), argsToString("can not delete \"", actionRef.id, "\"; it is still being executed; no actions altered"))); + return; + } + auto &actions = params.setup.building.actions; + auto &invalidActions = params.setup.building.invalidActions; + for (auto &actionRef : buildActionsSearchResult.actions) { + for (auto &maybeFollowUpAction : actionRef.action->m_followUpActions) { + if (auto followUpAction = maybeFollowUpAction.lock()) { + auto &startAfter = followUpAction->startAfter; + startAfter.erase(std::remove(startAfter.begin(), startAfter.end(), actionRef.id)); + } + } + actions[actionRef.id] = nullptr; + invalidActions.emplace(actionRef.id); + } + if (!actions.empty()) { + auto newActionsSize = actions.size(); + for (auto id = newActionsSize - 1;; --id) { + if (actions[id]) { + break; + } + newActionsSize = id; + invalidActions.erase(id); + if (!newActionsSize) { + break; + } + } + actions.resize(newActionsSize); + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeText(params.request(), "ok")); +} + +void postCloneBuildActions(const Params ¶ms, ResponseHandler &&handler) +{ + const auto startConditionValues = params.target.decodeValues("start-condition"); + if (startConditionValues.size() > 1) { + handler(makeBadRequest(params.request(), "at most one start condition can be specified")); + return; + } + const auto startImmediately = startConditionValues.empty() || startConditionValues.front() == "immediately"; + if (!startImmediately && startConditionValues.front() != "manually") { + handler(makeBadRequest(params.request(), "specified start condition is not supported")); + return; + } + auto buildActionsSearchResult = findBuildActions(params, std::move(handler), true); + if (!buildActionsSearchResult.ok) { + return; + } + for (const auto &actionRef : buildActionsSearchResult.actions) { + if (actionRef.action->isDone()) { + continue; + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeBadRequest( + params.request(), argsToString("can not clone \"", actionRef.id, "\"; it is still scheduled or executed; no actions altered"))); + return; + } + std::vector cloneIds; + cloneIds.reserve(buildActionsSearchResult.actions.size()); + for (const auto &actionRef : buildActionsSearchResult.actions) { + const auto orig = actionRef.action; + const auto id = params.setup.building.allocateBuildActionID(); + auto clone = make_shared(id); + clone->directory = orig->directory; + clone->packageNames = orig->packageNames; + clone->sourceDbs = orig->sourceDbs; + clone->destinationDbs = orig->destinationDbs; + clone->extraParams = orig->extraParams; + clone->settings = orig->settings; + clone->flags = orig->flags; + clone->type = orig->type; + // transfer any follow-up actions which haven't already started yet from the original build action to the new one + // TODO: It would be cool to have a "recursive flag" which would allow restarting follow-ups. + clone->m_followUpActions.reserve(orig->m_followUpActions.size()); + for (auto &maybeOrigFollowUp : orig->m_followUpActions) { + auto origFollowUp = maybeOrigFollowUp.lock(); + if (!origFollowUp || !origFollowUp->isScheduled()) { + continue; + } + for (auto &startAfterId : origFollowUp->startAfter) { + if (startAfterId == orig->id) { + startAfterId = id; + } + } + clone->m_followUpActions.emplace_back(origFollowUp); + } + orig->m_followUpActions.clear(); + if (startImmediately) { + clone->start(params.setup); + } + params.setup.building.actions[id] = move(clone); + cloneIds.emplace_back(id); + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeJson(params.request(), cloneIds)); +} + +void postStartBuildActions(const Params ¶ms, ResponseHandler &&handler) +{ + auto buildActionsSearchResult = findBuildActions(params, std::move(handler), true); + if (!buildActionsSearchResult.ok) { + return; + } + for (const auto &actionRef : buildActionsSearchResult.actions) { + if (actionRef.action->isScheduled()) { + continue; + } + buildActionsSearchResult.lock = std::monostate{}; + handler( + makeBadRequest(params.request(), argsToString("can not start \"", actionRef.id, "\"; it has already been started; no actions altered"))); + return; + } + for (auto &actionRef : buildActionsSearchResult.actions) { + actionRef.action->start(params.setup); + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeText(params.request(), "ok")); +} + +void postStopBuildActions(const Params ¶ms, ResponseHandler &&handler) +{ + auto buildActionsSearchResult = findBuildActions(params, std::move(handler), true); + if (!buildActionsSearchResult.ok) { + return; + } + for (const auto &actionRef : buildActionsSearchResult.actions) { + if (actionRef.action->isExecuting()) { + continue; + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeBadRequest( + params.request(), argsToString("can not stop/decline \"", actionRef.id, "\"; it is not being executed; no actions altered"))); + return; + } + for (auto &actionRef : buildActionsSearchResult.actions) { + actionRef.action->abort(); + if (actionRef.action->status == BuildActionStatus::Running) { + // can not immediately stop a running action; the action needs to terminate itself acknowledging the aborted flag + continue; + } + actionRef.action->status = BuildActionStatus::Finished; + if (actionRef.action->status == BuildActionStatus::AwaitingConfirmation) { + actionRef.action->result = BuildActionResult::ConfirmationDeclined; + } else { + actionRef.action->result = BuildActionResult::Aborted; + } + } + buildActionsSearchResult.lock = std::monostate{}; + handler(makeText(params.request(), "ok")); +} + +} // namespace Routes +} // namespace WebAPI +} // namespace LibRepoMgr diff --git a/librepomgr/webapi/routes.h b/librepomgr/webapi/routes.h new file mode 100644 index 0000000..2dc5b3a --- /dev/null +++ b/librepomgr/webapi/routes.h @@ -0,0 +1,38 @@ +#ifndef LIBREPOMGR_ROUTES_H +#define LIBREPOMGR_ROUTES_H + +#include "./typedefs.h" + +#include "../buildactions/buildactionfwd.h" + +namespace LibRepoMgr { +namespace WebAPI { + +namespace Routes { +void getRoot(const Params ¶ms, ResponseHandler &&handler); +void getVersion(const Params ¶ms, ResponseHandler &&handler); +void getStatus(const Params ¶ms, ResponseHandler &&handler); +void getDatabases(const Params ¶ms, ResponseHandler &&handler); +void getUnresolved(const Params ¶ms, ResponseHandler &&handler); +void getPackages(const Params ¶ms, ResponseHandler &&handler); +void getBuildActions(const Params ¶ms, ResponseHandler &&handler); +void getBuildActionDetails(const Params ¶ms, ResponseHandler &&handler); +void getBuildActionOutput(const Params ¶ms, ResponseHandler &&handler); +void getBuildActionLogFile(const Params ¶ms, ResponseHandler &&handler); +void getBuildActionArtefact(const Params ¶ms, ResponseHandler &&handler); +void postLoadPackages(const Params ¶ms, ResponseHandler &&handler); +void postDumpCacheFile(const Params ¶ms, ResponseHandler &&handler); +void postBuildAction(const Params ¶ms, ResponseHandler &&handler); +void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, const std::string &taskName, const std::string &directory, + const std::vector &startAfterIds, bool startImmediately); +void deleteBuildActions(const Params ¶ms, ResponseHandler &&handler); +void postCloneBuildActions(const Params ¶ms, ResponseHandler &&handler); +void postStartBuildActions(const Params ¶ms, ResponseHandler &&handler); +void postStopBuildActions(const Params ¶ms, ResponseHandler &&handler); +void postQuit(const Params ¶ms, ResponseHandler &&handler); +} // namespace Routes + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_ROUTES_H diff --git a/librepomgr/webapi/server.cpp b/librepomgr/webapi/server.cpp new file mode 100644 index 0000000..8918737 --- /dev/null +++ b/librepomgr/webapi/server.cpp @@ -0,0 +1,132 @@ +#include "./server.h" +#include "./routes.h" +#include "./session.h" + +#include "../serversetup.h" + +#include +#include + +#include + +#include +#include + +using namespace std; +using namespace boost::asio; +using namespace boost::beast; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { +namespace WebAPI { + +// clang-format off +const Router Server::s_router = { + { { http::verb::get, "/" }, Route{&Routes::getRoot} }, + { { http::verb::get, "/api/v0/databases" }, Route{&Routes::getDatabases} }, + { { http::verb::get, "/api/v0/unresolved" }, Route{&Routes::getUnresolved} }, + { { http::verb::get, "/api/v0/packages" }, Route{&Routes::getPackages} }, + { { http::verb::get, "/api/v0/version" }, Route{&Routes::getVersion} }, + { { http::verb::get, "/api/v0/status" }, Route{&Routes::getStatus} }, + { { http::verb::post, "/api/v0/load/packages" }, Route{&Routes::postLoadPackages, UserPermissions::PerformAdminActions} }, + { { http::verb::get, "/api/v0/build-action" }, Route{&Routes::getBuildActions} }, + { { http::verb::delete_, "/api/v0/build-action" }, Route{&Routes::deleteBuildActions, UserPermissions::ModifyBuildActions} }, + { { http::verb::get, "/api/v0/build-action/details" }, Route{&Routes::getBuildActionDetails, UserPermissions::ReadBuildActionsDetails} }, + { { http::verb::get, "/api/v0/build-action/output" }, Route{&Routes::getBuildActionOutput, UserPermissions::ReadBuildActionsDetails} }, + { { http::verb::get, "/api/v0/build-action/logfile" }, Route{&Routes::getBuildActionLogFile, UserPermissions::ReadBuildActionsDetails} }, + { { http::verb::get, "/api/v0/build-action/artefact" }, Route{&Routes::getBuildActionArtefact, UserPermissions::ReadBuildActionsDetails} }, + { { http::verb::post, "/api/v0/build-action" }, Route{&Routes::postBuildAction, UserPermissions::ModifyBuildActions} }, + { { http::verb::post, "/api/v0/build-action/clone" }, Route{&Routes::postCloneBuildActions, UserPermissions::ModifyBuildActions} }, + { { http::verb::post, "/api/v0/build-action/start" }, Route{&Routes::postStartBuildActions, UserPermissions::ModifyBuildActions} }, + { { http::verb::post, "/api/v0/build-action/stop" }, Route{&Routes::postStopBuildActions, UserPermissions::ModifyBuildActions} }, + { { http::verb::post, "/api/v0/dump/cache-file" }, Route{&Routes::postDumpCacheFile, UserPermissions::PerformAdminActions} }, + { { http::verb::post, "/api/v0/quit" }, Route{&Routes::postQuit, UserPermissions::PerformAdminActions} }, +}; +// clang-format on + +Server::Server(ServiceSetup &setup) + : m_acceptor(setup.webServer.ioContext) + , m_socket(setup.webServer.ioContext) + , m_setup(setup) +{ + // open the acceptor + const auto endpoint = boost::asio::ip::tcp::endpoint{ setup.webServer.address, setup.webServer.port }; + m_acceptor.open(endpoint.protocol()); + // allow to reuse the address immediately (TODO: make this configurable) + m_acceptor.set_option(socket_base::reuse_address(true)); + m_acceptor.set_option(ip::tcp::acceptor::reuse_address(true)); + // bind to the server address + m_acceptor.bind(endpoint); + // start listening for connections + m_acceptor.listen(boost::asio::socket_base::max_listen_connections); +} + +std::shared_ptr Server::s_instance = nullptr; + +void Server::serve(ServiceSetup &setup) +{ + // create and launch listener + s_instance = std::make_shared(setup); + s_instance->run(); + + // stop gracefully when receiving SIGINT or SIGTERM + boost::asio::signal_set signalSet(setup.webServer.ioContext, SIGINT, SIGTERM, SIGQUIT); + signalSet.async_wait([](const boost::system::error_code &error, int signalNumber) { + CPP_UTILITIES_UNUSED(signalNumber) + if (!error) { + Server::stop(); + } + }); + + // run the IO service on the requested number of threads + const auto additionalThreads = ThreadPool("Additional web server thread", setup.webServer.ioContext, setup.webServer.threadCount - 1); + setup.webServer.ioContext.run(); +} + +void Server::stop() +{ + if (!s_instance) { + return; + } + boost::asio::post(s_instance->m_socket.get_executor(), [] { + if (!s_instance) { + return; + } + if (s_instance->m_socket.is_open()) { + s_instance->m_socket.cancel(); + } + if (s_instance->m_acceptor.is_open()) { + s_instance->m_acceptor.cancel(); + } + s_instance->m_setup.webServer.ioContext.stop(); + }); +} + +void Server::run() +{ + if (!m_acceptor.is_open()) { + return; + } + accept(); +} + +void Server::accept() +{ + m_acceptor.async_accept(m_socket, bind(&Server::handleAccepted, shared_from_this(), placeholders::_1)); +} + +void Server::handleAccepted(boost::system::error_code ec) +{ + if (ec) { + cerr << Phrases::WarningMessage << "Failed to accept new connection: " << ec.message() << Phrases::EndFlush; + } else { + // create session and run it + std::make_shared(move(m_socket), m_setup)->receive(); + } + // accept next connection + accept(); +} + +} // namespace WebAPI +} // namespace LibRepoMgr diff --git a/librepomgr/webapi/server.h b/librepomgr/webapi/server.h new file mode 100644 index 0000000..72895ff --- /dev/null +++ b/librepomgr/webapi/server.h @@ -0,0 +1,54 @@ +#ifndef LIBREPOMGR_SERVER_H +#define LIBREPOMGR_SERVER_H + +#include "./routeid.h" + +#include "../global.h" + +#include +#include + +#include +#include +#include + +class WebAPITests; + +namespace LibRepoMgr { + +struct ServiceSetup; + +namespace WebAPI { + +class LIBREPOMGR_EXPORT Server : public std::enable_shared_from_this { + friend WebAPITests; + +public: + Server(ServiceSetup &setup); + + static void serve(ServiceSetup &setup); + static void stop(); + static const Router &router(); + static boost::asio::io_context *context(); + + void run(); + void accept(); + void handleAccepted(boost::system::error_code ec); + +private: + boost::asio::ip::tcp::acceptor m_acceptor; + boost::asio::ip::tcp::socket m_socket; + ServiceSetup &m_setup; + static std::shared_ptr s_instance; + static const Router s_router; +}; + +inline const Router &Server::router() +{ + return s_router; +} + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_SERVER_H diff --git a/librepomgr/webapi/session.cpp b/librepomgr/webapi/session.cpp new file mode 100644 index 0000000..f7ab724 --- /dev/null +++ b/librepomgr/webapi/session.cpp @@ -0,0 +1,191 @@ +#include "./session.h" + +#include "./params.h" +#include "./render.h" +#include "./routes.h" +#include "./server.h" + +#include "../serversetup.h" + +#include +#include +#include + +#include + +using namespace std; +using namespace boost::asio; +using namespace boost::beast; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { +namespace WebAPI { + +void Session::receive() +{ + m_parser = make_unique(); + m_parser->header_limit(0x10000); + boost::beast::http::async_read(m_socket, m_buffer, *m_parser, + boost::asio::bind_executor(m_strand, std::bind(&Session::received, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); +} + +void Session::received(boost::system::error_code ec, size_t bytesTransferred) +{ + boost::ignore_unused(bytesTransferred); + + // this means they closed the connection + if (ec == http::error::end_of_stream) { + return close(); + } + + if (ec) { + cerr << Phrases::WarningMessage << "Failed to read request:" << Phrases::End << " " << ec.message() << endl; + return close(); + } + + // parse request + auto &request = m_parser->get(); + auto params = Params{ m_setup, *this }; + const auto &router = Server::router(); + const auto path = params.target.path; + + // allow overriding method via query parameter (for easier debugging) + auto method = request.method(); + if (const auto methodStr = params.target.value("method"); !methodStr.empty()) { + if (methodStr == "get") { + method = boost::beast::http::verb::get; + } else if (methodStr == "post") { + method = boost::beast::http::verb::post; + } else if (methodStr == "put") { + method = boost::beast::http::verb::put; + } else if (methodStr == "delete") { + method = boost::beast::http::verb::delete_; + } + } + + // find route's controller and invoke it + if (const auto routing(router.find(RouteId{ method, std::string(path) })); routing != router.cend()) { + const Route &route = routing->second; + const auto requiredPermissions = route.permissions; + if (requiredPermissions != UserPermissions::None) { + const auto authInfo = request.find(boost::beast::http::field::authorization); + if (authInfo == request.end()) { + respond(Render::makeAuthRequired(request)); + return; + } + const auto userPermissions = m_setup.auth.authenticate(std::string_view(authInfo->value().data(), authInfo->value().size())); + using PermissionFlags = std::underlying_type_t; + if (static_cast(userPermissions) & static_cast(UserPermissions::TryAgain)) { + // send the 401 response again if credentials are 'try again' to show the password prompt for the XMLHttpRequest again + // note: This is kind of a hack. Maybe there's a better solution to make XMLHttpRequest forget wrongly entered credentials + // and instead show the login prompt again? + respond(Render::makeAuthRequired(request)); + return; + } + if ((static_cast(requiredPermissions) & static_cast(userPermissions)) + != static_cast(requiredPermissions)) { + respond(Render::makeForbidden(request)); + return; + } + } + try { + route.handler(move(params), + std::bind( + static_cast &&)>(&Session::respond), shared_from_this(), std::placeholders::_1)); + } catch (const BadRequest &badRequest) { + respond(Render::makeBadRequest(request, badRequest.what())); + } + return; + } + + // handle requests to static files (intended for development only; use NGINX in production) + if (!m_setup.webServer.staticFilesPath.empty() && (path.find("../") == string::npos || path.find("..\\") == string::npos)) { + const auto filePath = argsToString(m_setup.webServer.staticFilesPath, params.target.path); + const auto *mimeType = "text/plain"; + if (endsWith(params.target.path, ".html")) { + mimeType = "text/html"; + } else if (endsWith(params.target.path, ".json")) { + mimeType = "application/json"; + } else if (endsWith(params.target.path, ".js")) { + mimeType = "text/javascript"; + } else if (endsWith(params.target.path, ".css")) { + mimeType = "text/css"; + } else if (endsWith(params.target.path, ".svg")) { + mimeType = "image/svg+xml"; + } + respond(filePath.data(), mimeType, params.target.path); + return; + } + + // handle requests for non-existent routes +#ifdef CPP_UTILITIES_DEBUG_BUILD + cerr << Phrases::Info << "Invalid request: " << TextAttribute::Reset << request.method_string() << ' ' << request.target() << '\n'; + if (!request.body().empty()) { + cerr << "body: " << request.body(); + } + cerr << flush; +#endif + respond(Render::makeNotFound(request, "route \"" % request.method_string().to_string() % ' ' % request.target().to_string() + '\"')); +} + +void Session::respond(std::shared_ptr &&response) +{ + // write the response + http::async_write(m_socket, *response, + boost::asio::bind_executor( + m_strand, std::bind(&Session::responded, shared_from_this(), std::placeholders::_1, std::placeholders::_2, response->need_eof()))); + + // keep message alive as long as the session exists + m_res = move(response); +} + +void Session::respond(const char *localFilePath, const char *mimeType, std::string_view urlPath) +{ + // make response with file body + auto ec = boost::beast::error_code{}; + auto response = Render::makeFile(m_parser->get(), localFilePath, mimeType, ec); + if (ec.failed()) { + respond(Render::makeNotFound(m_parser->get(), urlPath)); + return; + } + + // write the response + http::async_write(m_socket, *response, + boost::asio::bind_executor( + m_strand, std::bind(&Session::responded, shared_from_this(), std::placeholders::_1, std::placeholders::_2, response->need_eof()))); + + // keep message alive as long as the session exists + m_res = response; +} + +void Session::responded(boost::system::error_code ec, std::size_t bytesTransferred, bool shouldClose) +{ + boost::ignore_unused(bytesTransferred); + if (ec) { + cerr << Phrases::WarningMessage << "Failed to write response:" << Phrases::End << " " << ec.message() << endl; + } + + if (shouldClose) { + // this means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return close(); + } + + // we're done with the response so delete it + m_res = nullptr; + + // read another request + receive(); +} + +void Session::close() +{ + // send a TCP shutdown + boost::system::error_code ec; + m_socket.shutdown(ip::tcp::socket::shutdown_send, ec); + // at this point the connection is closed gracefully +} + +} // namespace WebAPI +} // namespace LibRepoMgr diff --git a/librepomgr/webapi/session.h b/librepomgr/webapi/session.h new file mode 100644 index 0000000..54d339d --- /dev/null +++ b/librepomgr/webapi/session.h @@ -0,0 +1,61 @@ +#ifndef LIBREPOMGR_SESSION_H +#define LIBREPOMGR_SESSION_H + +#include "./typedefs.h" + +#include +#include + +#include +#include +#include + +namespace LibRepoMgr { + +struct ServiceSetup; + +namespace WebAPI { + +class Session : public std::enable_shared_from_this { +public: + Session(boost::asio::ip::tcp::socket socket, ServiceSetup &config); + + void receive(); + void respond(std::shared_ptr &&response); + void respond(const char *localFilePath, const char *mimeType, std::string_view urlPath); + void close(); + const Request &request() const; + boost::asio::ip::tcp::socket &socket(); + void received(boost::system::error_code ec, std::size_t bytesTransferred); + void responded(boost::system::error_code ec, std::size_t bytesTransferred, bool shouldClose); + +private: + boost::asio::ip::tcp::socket m_socket; + boost::asio::strand m_strand; + boost::beast::flat_buffer m_buffer; + std::unique_ptr m_parser; + ServiceSetup &m_setup; + std::shared_ptr m_res; +}; + +inline Session::Session(boost::asio::ip::tcp::socket socket, ServiceSetup &setup) + : m_socket(std::move(socket)) + , m_strand(m_socket.get_executor()) + , m_setup(setup) +{ +} + +inline const Request &Session::request() const +{ + return m_parser->get(); +} + +inline boost::asio::ip::tcp::socket &Session::socket() +{ + return m_socket; +} + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_SESSION_H diff --git a/librepomgr/webapi/typedefs.h b/librepomgr/webapi/typedefs.h new file mode 100644 index 0000000..6cb18ee --- /dev/null +++ b/librepomgr/webapi/typedefs.h @@ -0,0 +1,34 @@ +#ifndef LIBREPOMGR_TYPEDEFS_H +#define LIBREPOMGR_TYPEDEFS_H + +#include +#include +#include +#include +#include + +#include +#include + +namespace LibPkg { +struct Config; +} + +namespace LibRepoMgr { +namespace WebAPI { + +struct RouteId; +struct Params; + +using Response = boost::beast::http::response; +using ResponseHandler = std::function &&)>; +using RouteHandler = std::function; +using FileResponse = boost::beast::http::response; +using FileStreamResponse = boost::beast::http::response_serializer; +using Request = boost::beast::http::request; +using RequestParser = boost::beast::http::request_parser; + +} // namespace WebAPI +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_TYPEDEFS_H diff --git a/librepomgr/webclient/aur.cpp b/librepomgr/webclient/aur.cpp new file mode 100644 index 0000000..861c27c --- /dev/null +++ b/librepomgr/webclient/aur.cpp @@ -0,0 +1,298 @@ +#include "./aur.h" + +#include "../logging.h" + +#include "../webapi/params.h" +#include "../webapi/server.h" + +#include "../json.h" +#include "../multisession.h" + +#include "../../libpkg/data/package.h" +#include "../../libpkg/parser/aur.h" + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; +using namespace LibPkg; + +namespace LibRepoMgr { + +namespace WebClient { + +/// \cond +constexpr auto aurHost = "aur.archlinux.org"; +constexpr auto aurPort = "443"; +/// \endcond + +/*! + * \brief Searches the AUR for packages which name contains the specified \a searchTerm. + */ +void searchAurPackages(LogContext &log, ServiceSetup &setup, const std::string &searchTerm, boost::asio::io_context &ioContext, + std::shared_ptr &multiSession) +{ + auto session = std::make_shared(ioContext, setup.webServer.sslContext, + [&log, &setup, multiSession](WebClient::SslSession &session, const WebClient::HttpClientError &error) mutable { + if (error.errorCode != boost::beast::errc::success && error.errorCode != boost::asio::ssl::error::stream_truncated) { + log(Phrases::ErrorMessage, "Failed to search AUR: ", error.what(), '\n'); + return; + } + + // parse retrieved JSON + const auto &body = get(session.response).body(); + try { + const auto packages = Package::fromAurRpcJson(body.data(), body.size(), PackageOrigin::AurRpcSearch); + + // cache the AUR packages + auto lock = setup.config.lockToWrite(); + for (auto &package : packages) { + setup.config.aur.updatePackage(package); + } + lock.unlock(); + + multiSession->addResponses(packages); + } catch (const RAPIDJSON_NAMESPACE::ParseResult &e) { + log(Phrases::ErrorMessage, "Unable to parse AUR search result: ", serializeParseError(e), '\n'); + } + }); + session->run(aurHost, aurPort, boost::beast::http::verb::get, ("/rpc/?v=5&type=search&arg=" + WebAPI::Url::encodeValue(searchTerm)).data(), 11); +} + +/// \cond +const std::string &packageNameFromIterator(std::vector::const_iterator i) +{ + return *i; +} + +const std::string &packageNameFromIterator(std::unordered_map>::const_iterator i) +{ + return i->first; +} + +template +std::shared_ptr queryAurPackagesInternal(LogContext &log, ServiceSetup &setup, const PackageCollection &packages, + boost::asio::io_context &ioContext, typename AurQuerySession::HandlerType &&handler) +{ + log("Retrieving ", packages.size(), " packages from the AUR\n"); + + constexpr auto packagesPerQuery = 100; + auto multiSession = AurQuerySession::create(ioContext, move(handler)); + + for (auto i = packages.cbegin(), end = packages.cend(); i != end;) { + auto session = make_shared(ioContext, setup.webServer.sslContext, + [&log, &setup, multiSession](WebClient::SslSession &session, const WebClient::HttpClientError &error) mutable { + if (error.errorCode != boost::beast::errc::success && error.errorCode != boost::asio::ssl::error::stream_truncated) { + log(Phrases::ErrorMessage, "Failed to retrieve AUR packages from RPC: ", error.what(), '\n'); + return; + } + + // parse retrieved JSON + const auto &body = get(session.response).body(); + try { + const auto packages = Package::fromAurRpcJson(body.data(), body.size()); + + // cache the AUR packages + auto lock = setup.config.lockToWrite(); + for (auto &package : packages) { + setup.config.aur.updatePackage(package); + } + lock.unlock(); + + multiSession->addResponses(packages); + } catch (const RAPIDJSON_NAMESPACE::ParseResult &e) { + log(Phrases::ErrorMessage, "Unable to parse AUR package from RPC: ", serializeParseError(e), '\n'); + } + }); + + const auto url = [&] { + stringstream url; + url << "/rpc/?v=5&type=info"; + for (size_t batchIndex = 0; batchIndex != packagesPerQuery && i != end; ++batchIndex, ++i) { + url << "&arg[]="; + url << WebAPI::Url::encodeValue(packageNameFromIterator(i)); + } + return url.str(); + }(); + + session->run(aurHost, aurPort, boost::beast::http::verb::get, url.data(), 11); + } + return multiSession; +} +/// \endcond + +/*! + * \brief Queries the AUR asnychronously to populate \a setup.config.aur.packages for all specified \a packages. + * \remarks When results are available, a write-lock is acquired to insert the results. When this is done, the write-lock is released and \a handler + * is called. + */ +std::shared_ptr queryAurPackages(LogContext &log, ServiceSetup &setup, const std::vector &packages, + boost::asio::io_context &ioContext, typename AurQuerySession::HandlerType &&handler) +{ + return queryAurPackagesInternal(log, setup, packages, ioContext, move(handler)); +} + +/*! + * \brief Queries the AUR asnychronously to populate \a setup.config.aur.packages for all package names within the specified map of \a packages. + * \remarks When results are available, a write-lock is acquired to insert the results. When this is done, the write-lock is released and \a handler + * is called. + */ +std::shared_ptr queryAurPackages(LogContext &log, ServiceSetup &setup, + const std::unordered_map> &packages, boost::asio::io_context &ioContext, + typename AurQuerySession::HandlerType &&handler) +{ + return queryAurPackagesInternal(log, setup, packages, ioContext, move(handler)); +} + +/*! + * \brief Queries the AUR asnychronously to populate \a setup.config.aur.packages for all package names contained by \a database. + * \returns Returns true if all these packages are already present and no async operation needed to be started. Returns false when an async + * operation has been started. + * \remarks If the current thread already holds a read-only lock to the config one must pass it via \a configReadLock. Otherwise this function + * will attempt to acquire its own lock (which makes no sense if already locked). In case the function returns false the lock is released. + * When results are available, a write-lock is acquired to insert the results. When this is done, the write-lock is released and \a handler + * is called. + */ +std::shared_ptr queryAurPackagesForDatabase(LogContext &log, ServiceSetup &setup, boost::asio::io_context &ioContext, + std::shared_lock *configReadLock, LibPkg::Database &database, typename AurQuerySession::HandlerType &&handler) +{ + vector missingPackages; + std::shared_lock ownConfigReadLock; + if (!configReadLock) { + ownConfigReadLock = setup.config.lockToRead(); + configReadLock = &ownConfigReadLock; + } + const auto &aurPackages = setup.config.aur.packages; + for (const auto &package : database.packages) { + if (aurPackages.find(package.first) == aurPackages.end()) { + missingPackages.emplace_back(package.first); + } + } + if (missingPackages.empty()) { + return nullptr; + } + configReadLock->unlock(); + return queryAurPackages(log, setup, missingPackages, ioContext, move(handler)); +} + +/*! + * \brief Queries the AUR asynchronously to get the latest snapshot for the specified \a packageNames. + */ +void queryAurSnapshots(LogContext &log, ServiceSetup &setup, const std::vector &queryParams, + boost::asio::io_context &ioContext, std::shared_ptr &multiSession) +{ + CPP_UTILITIES_UNUSED(log) + for (const auto ¶ms : queryParams) { + auto session = std::make_shared(ioContext, setup.webServer.sslContext, + [multiSession, params](WebClient::SslSession &session, const WebClient::HttpClientError &error) mutable { + if (error.errorCode != boost::beast::errc::success && error.errorCode.message() != "stream truncated") { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = *params.packageName, + .error = "Unable to retrieve AUR snapshot tarball for package " % *params.packageName % ": " + error.what() }); + return; + } + + // parse retrieved archive + const auto &response = get(session.response); + if (response.result() != boost::beast::http::status::ok) { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = *params.packageName, + .error + = "Unable to retrieve AUR snapshot tarball for package " % *params.packageName % ": AUR returned " % response.result_int() + + " response" }); + return; + } + const auto &body = response.body(); + auto snapshotFiles = FileMap{}; + try { + snapshotFiles = extractFilesFromBuffer(body, *params.packageName, [](const char *, const char *, mode_t) { return true; }); + } catch (const runtime_error &error) { + multiSession->addResponse(WebClient::AurSnapshotResult{ .packageName = *params.packageName, + .error = "Unable to extract AUR snapshot tarball for package " % *params.packageName % ": " + error.what() }); + return; + } + auto result = AurSnapshotResult{ .packageName = *params.packageName }; + auto haveSrcFileInfo = false, havePkgbuild = false; + for (const auto &directory : snapshotFiles) { + // parse .SRCINFO and check for presence of PKGBUILD + if (!startsWith(directory.first, *params.packageName)) { + continue; + } + const auto directoryPath = string_view{ directory.first }.substr(params.packageName->size()); + if (directoryPath.empty()) { + for (const auto &rootFile : directory.second) { + if (rootFile.name == ".SRCINFO") { + result.packages = Package::fromInfo(rootFile.content, false); + haveSrcFileInfo = true; + } else if (rootFile.name == "PKGBUILD") { + havePkgbuild = true; + } + if (haveSrcFileInfo && havePkgbuild) { + continue; + } + } + } + + // store files in target directory + const auto targetDir = *params.targetDirectory % '/' + directoryPath; + try { + filesystem::create_directories(targetDir); + } catch (const filesystem::filesystem_error &error) { + multiSession->addResponse(WebClient::AurSnapshotResult{ + .packageName = *params.packageName, .error = "Unable to make directory " % targetDir % ": " + error.what() }); + return; + } + for (const auto &file : directory.second) { + const auto targetPath = directory.first.empty() ? (*params.targetDirectory % '/' + file.name) + : (*params.targetDirectory % '/' % directoryPath % '/' + file.name); + if (file.type == ArchiveFileType::Link) { + try { + filesystem::create_symlink(file.content, targetPath); + } catch (const filesystem::filesystem_error &error) { + multiSession->addResponse(WebClient::AurSnapshotResult{ + .packageName = *params.packageName, .error = "Unable to make symlink " % targetPath % ": " + error.what() }); + return; + } + continue; + } + try { + writeFile(targetPath, file.content); + setLastModified(targetPath, file.modificationTime); + } catch (const std::ios_base::failure &failure) { + multiSession->addResponse(WebClient::AurSnapshotResult{ + .packageName = *params.packageName, .error = "Unable to write " % targetPath % ": " + failure.what() }); + return; + } + } + } + + // validate what we've got and add response + if (!haveSrcFileInfo) { + result.error = ".SRCINFO is missing"; + } + if (!havePkgbuild) { + result.error = "PKGINFO is missing"; + } + if (result.packages.empty() || result.packages.front()->name.empty()) { + result.error = "Unable to parse .SRCINFO: no package name present"; + } else if (!(result.sourceInfo = result.packages.front()->sourceInfo)) { + result.error = "Unable to parse .SRCINFO: no source info present"; + } + multiSession->addResponse(move(result)); + }); + + // run query, e.g. https: //aur.archlinux.org/cgit/aur.git/snapshot/mingw-w64-configure.tar.gz + const auto url = "/cgit/aur.git/snapshot/" % WebAPI::Url::encodeValue(*params.packageName) + ".tar.gz"; + session->run("aur.archlinux.org", "443", boost::beast::http::verb::get, url.data(), 11); + } +} + +} // namespace WebClient + +} // namespace LibRepoMgr diff --git a/librepomgr/webclient/aur.h b/librepomgr/webclient/aur.h new file mode 100644 index 0000000..eb2ce06 --- /dev/null +++ b/librepomgr/webclient/aur.h @@ -0,0 +1,52 @@ +#ifndef LIBREPOMGR_AUR_H +#define LIBREPOMGR_AUR_H + +#include "../multisession.h" +#include "../serversetup.h" + +#include "./session.h" + +#include +#include + +#include + +namespace LibRepoMgr { + +struct LogContext; + +namespace WebClient { +struct AurSnapshotResult { + std::string packageName; + std::string errorOutput; + std::shared_ptr sourceInfo; + std::vector> packages; + std::string error; +}; +struct AurSnapshotQueryParams { + const std::string *packageName; + const std::string *targetDirectory; +}; + +using AurQuerySession = MultiSession>; +using AurSnapshotQuerySession = MultiSession; + +void searchAurPackages(LogContext &log, ServiceSetup &setup, const std::string &searchTerms, boost::asio::io_context &ioContext, + std::shared_ptr &multiSession); + +std::shared_ptr queryAurPackages(LogContext &log, ServiceSetup &setup, const std::vector &packages, + boost::asio::io_context &ioContext, typename AurQuerySession::HandlerType &&handler); +std::shared_ptr queryAurPackages(LogContext &log, ServiceSetup &setup, + const std::unordered_map> &packages, boost::asio::io_context &ioContext, + typename AurQuerySession::HandlerType &&handler); +std::shared_ptr queryAurPackagesForDatabase(LogContext &log, ServiceSetup &setup, boost::asio::io_context &ioContext, + std::shared_lock *configReadLock, LibPkg::Database &database, typename AurQuerySession::HandlerType &&handler); + +void queryAurSnapshots(LogContext &log, ServiceSetup &setup, const std::vector &queryParams, + boost::asio::io_context &ioContext, std::shared_ptr &multiSession); + +} // namespace WebClient + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_AUR_H diff --git a/librepomgr/webclient/database.cpp b/librepomgr/webclient/database.cpp new file mode 100644 index 0000000..a0f6152 --- /dev/null +++ b/librepomgr/webclient/database.cpp @@ -0,0 +1,226 @@ +#include "./database.h" +#include "./logging.h" + +#include +#include + +#include +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; +using namespace LibPkg; + +namespace LibRepoMgr { + +namespace WebClient { + +DatabaseQuery prepareDatabaseQuery(LogContext &log, const std::vector &dbs, bool withFiles) +{ + DatabaseQuery query; + query.queryParamsForDbs.reserve(dbs.size()); + + for (auto *const db : dbs) { + const auto &destinationFilePath = withFiles ? db->filesPath : db->path; + if (destinationFilePath.empty()) { + log(Phrases::ErrorMessage, "No local path configured database \"", db->name, '@', db->arch, "\" (for ", + withFiles ? "files" : "regular db", ')', '\n'); + continue; + } + if (db->mirrors.empty()) { + log(Phrases::ErrorMessage, "No mirrors configured for database \"", db->name, '@', db->arch, '\"', '\n'); + continue; + } + const auto destinationDirPath = std::filesystem::path(destinationFilePath).parent_path(); + try { + if (!std::filesystem::exists(destinationDirPath)) { + std::filesystem::create_directories(destinationDirPath); + } + } catch (const std::filesystem::filesystem_error &e) { + log(Phrases::ErrorMessage, "Unable to create directory \"", destinationDirPath, "\" for database \"", db->name, '@', db->arch, + "\": ", e.what(), '\n'); + query.failedDbs.emplace_back(db->name); + continue; + } + query.queryParamsForDbs.emplace_back( + DatabaseQueryParams{ db->name, db->arch, (db->mirrors.front() % '/' % db->name) + (withFiles ? ".files" : ".db"), destinationFilePath }); + } + return query; +} + +static int matchToInt(const std::sub_match &match) +{ + const auto sv = std::string_view(match.first, static_cast(match.length())); + return stringToNumber(sv); +} + +void queryDatabases( + LogContext &log, ServiceSetup &setup, std::vector &&dbQueries, std::shared_ptr &dbQuerySession) +{ + for (auto &query : dbQueries) { + log(Phrases::InfoMessage, "Retrieving \"", query.databaseName, "\" from mirror: ", query.url, '\n'); + auto session = runSessionFromUrl( + setup.building.ioContext, setup.webServer.sslContext, query.url, + [&log, &setup, dbName = std::move(query.databaseName), dbArch = std::move(query.databaseArch), dbQuerySession]( + SessionData data, const WebClient::HttpClientError &error) mutable { + if (error.errorCode != boost::beast::errc::success && error.errorCode != boost::asio::ssl::error::stream_truncated) { + log(Phrases::ErrorMessage, "Error retrieving database file \"", data.destinationFilePath, "\" for ", dbName, ": ", error.what(), + '\n'); + dbQuerySession->addResponse(std::move(dbName)); + return; + } + + const auto &response = get(data.response); + const auto &message = response.get(); + if (message.result() != boost::beast::http::status::ok) { + log(Phrases::ErrorMessage, "Error retrieving database file \"", data.destinationFilePath, "\" for ", dbName, ": mirror returned ", + message.result_int(), " response\n"); + dbQuerySession->addResponse(std::move(dbName)); + return; + } + + // find last modification time + auto lastModified = DateTime(); + const auto lastModifiedHeader = message.find(boost::beast::http::field::last_modified); + if (lastModifiedHeader != message.cend()) { + // parse "Last-Modified" header which should be something like ", :: GMT" + const auto lastModifiedStr = lastModifiedHeader->value(); + static const auto lastModifiedPattern = regex("..., (\\d\\d) (...) (\\d\\d\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) GMT"); + static const auto months = unordered_map{ { "Jan", 1 }, { "Feb", 2 }, { "Mar", 3 }, { "Apr", 4 }, { "May", 5 }, + { "Jun", 6 }, { "Jul", 7 }, { "Aug", 8 }, { "Sep", 9 }, { "Oct", 10 }, { "Nov", 11 }, { "Dec", 12 } }; + try { + cmatch match; + if (!regex_search(lastModifiedStr.cbegin(), lastModifiedStr.cend(), match, lastModifiedPattern)) { + throw ConversionException("date/time denotation not in expected format"); + } + const auto month = months.find(match[2]); + if (month == months.cend()) { + throw ConversionException("month is invalid"); + } + lastModified = DateTime::fromDateAndTime(matchToInt(match[3]), month->second, matchToInt(match[1]), matchToInt(match[4]), + matchToInt(match[5]), matchToInt(match[6])); + } catch (const ConversionException &e) { + log(Phrases::WarningMessage, "Unable to parse \"Last-Modified\" header for database ", dbName, ": ", e.what(), + " (last modification time was \"", string_view(lastModifiedStr.data(), lastModifiedStr.size()), "\")\n"); + } + } + + // log + if (lastModified.isNull()) { + log(Phrases::InfoMessage, "Loading database \"", dbName, "\" from mirror response\n"); + lastModified = DateTime::gmtNow(); + } else { + log(Phrases::InfoMessage, "Loading database \"", dbName, + "\" from mirror response; last modification time: ", lastModified.toString(), '\n'); + } + + try { + // load packages + auto files = extractFiles(data.destinationFilePath, &Database::isFileRelevant); + auto packages = Package::fromDatabaseFile(move(files)); + + // insert packages + auto lock = setup.config.lockToWrite(); + auto db = setup.config.findDatabase(dbName, dbArch); + if (!db) { + log(Phrases::ErrorMessage, "Retrieved database file for \"", dbName, '@', dbArch, "\" but it no longer exists; discarding\n"); + return; + } + db->replacePackages(packages, lastModified); + } catch (const std::runtime_error &e) { + log(Phrases::ErrorMessage, "Unable to parse retrieved database file for \"", dbName, '@', dbArch, "\": ", e.what(), '\n'); + dbQuerySession->addResponse(std::move(dbName)); + } + }, + move(query.destinationFilePath)); + } +} + +std::shared_ptr queryDatabases( + LogContext &log, ServiceSetup &setup, std::vector &&urls, DatabaseQuerySession::HandlerType &&handler) +{ + auto dbQuerySession = DatabaseQuerySession::create(setup.building.ioContext, move(handler)); + queryDatabases(log, setup, move(urls), dbQuerySession); + return dbQuerySession; +} + +std::shared_ptr queryDatabases(LogContext &log, ServiceSetup &setup, std::shared_lock *configReadLock, + const std::vector &dbs, DatabaseQuerySession::HandlerType &&handler) +{ + shared_lock ownConfigReadLock; + auto query = prepareDatabaseQuery(log, dbs, setup.building.loadFilesDbs); + configReadLock->unlock(); + return queryDatabases(log, setup, std::move(query.queryParamsForDbs), move(handler)); +} + +PackageCachingSession::PackageCachingSession(PackageCachingDataForSession &data, boost::asio::io_context &ioContext, + boost::asio::ssl::context &sslContext, MultiSession::HandlerType &&handler) + : MultiSession(ioContext, std::move(handler)) + , m_sslContext(sslContext) + , m_data(data) + , m_dbIterator(data.begin()) + , m_dbEnd(data.end()) + , m_packageIterator(m_dbIterator != m_dbEnd ? m_dbIterator->second.begin() : decltype(m_packageIterator)()) + , m_packageEnd(m_dbIterator != m_dbEnd ? m_dbIterator->second.end() : decltype(m_packageEnd)()) +{ +} + +void PackageCachingSession::selectNextPackage() +{ + if (++m_packageIterator != m_packageEnd) { + return; + } + if (++m_dbIterator == m_dbEnd) { + return; + } + m_packageIterator = m_dbIterator->second.begin(); + m_packageEnd = m_dbIterator->second.end(); +} + +PackageCachingDataForPackage *PackageCachingSession::getCurrentDataAndSelectNext() +{ + std::lock_guard lock(m_mutex); + if (m_packageIterator == m_packageEnd) { + return nullptr; + } + auto *const data = &m_packageIterator->second; + selectNextPackage(); + return data; +} + +void cachePackages(LogContext &log, std::shared_ptr &&packageCachingSession, std::size_t maxParallelDownloads) +{ + for (std::size_t startedDownloads = 0; startedDownloads < maxParallelDownloads; ++startedDownloads) { + auto *const cachingData = packageCachingSession->getCurrentDataAndSelectNext(); + if (!cachingData) { + return; + } + log(Phrases::InfoMessage, "Downloading \"", cachingData->url, "\" to \"", cachingData->destinationFilePath, "\"\n"); + runSessionFromUrl( + packageCachingSession->ioContext(), packageCachingSession->m_sslContext, cachingData->url, + [&log, packageCachingSession, cachingData](SessionData data, const WebClient::HttpClientError &error) mutable { + if (error.errorCode != boost::beast::errc::success && error.errorCode != boost::asio::ssl::error::stream_truncated) { + const auto msg = std::make_tuple( + "Error downloading \"", cachingData->url, "\" to \"", cachingData->destinationFilePath, "\": ", error.what()); + cachingData->error = tupleToString(msg); + log(Phrases::ErrorMessage, msg, '\n'); + } + const auto &response = get(data.response); + const auto &message = response.get(); + if (message.result() != boost::beast::http::status::ok) { + const auto msg = std::make_tuple("Error downloading \"", cachingData->url, "\" to \"", cachingData->destinationFilePath, + "\": mirror returned ", message.result_int(), " response"); + cachingData->error = tupleToString(msg); + log(Phrases::ErrorMessage, msg, '\n'); + } + cachePackages(log, std::move(packageCachingSession), 1); + }, + std::string(cachingData->destinationFilePath)); + } +} + +} // namespace WebClient + +} // namespace LibRepoMgr diff --git a/librepomgr/webclient/database.h b/librepomgr/webclient/database.h new file mode 100644 index 0000000..5c8db4f --- /dev/null +++ b/librepomgr/webclient/database.h @@ -0,0 +1,71 @@ +#ifndef LIBREPOMGR_DATABASE_H +#define LIBREPOMGR_DATABASE_H + +#include "../multisession.h" +#include "../serversetup.h" + +#include "./session.h" + +#include +#include + +namespace LibRepoMgr { + +struct LogContext; + +namespace WebClient { +using DatabaseQuerySession = MultiSession; + +struct DatabaseQueryParams { + std::string databaseName; + std::string databaseArch; + std::string url; + std::string destinationFilePath; +}; +struct DatabaseQuery { + std::vector queryParamsForDbs; + std::vector failedDbs; +}; + +[[nodiscard]] DatabaseQuery prepareDatabaseQuery(LogContext &log, const std::vector &dbs, bool withFiles); +std::shared_ptr queryDatabases( + LogContext &log, ServiceSetup &setup, std::vector &&urls, DatabaseQuerySession::HandlerType &&handler); +std::shared_ptr queryDatabases(LogContext &log, ServiceSetup &setup, std::shared_lock *configReadLock, + const std::vector &dbs, DatabaseQuerySession::HandlerType &&handler); +void queryDatabases( + LogContext &log, ServiceSetup &setup, std::vector &&urls, std::shared_ptr &dbQuerySession); + +struct PackageCachingDataForPackage { + std::string_view url; + std::string_view destinationFilePath; + std::string error; +}; +using PackageCachingDataForDatabase = std::unordered_map; +using PackageCachingDataForSession = std::unordered_map; + +struct PackageCachingSession; +void cachePackages(LogContext &log, std::shared_ptr &&packageCachingSession, std::size_t maxParallelDownloads = 8); + +struct PackageCachingSession : public MultiSession { + friend void cachePackages(LogContext &, std::shared_ptr &&, std::size_t); + using SharedPointerType = std::shared_ptr; + + explicit PackageCachingSession( + PackageCachingDataForSession &data, boost::asio::io_context &ioContext, boost::asio::ssl::context &sslContext, HandlerType &&handler); + +private: + void selectNextPackage(); + PackageCachingDataForPackage *getCurrentDataAndSelectNext(); + + boost::asio::ssl::context &m_sslContext; + PackageCachingDataForSession &m_data; + std::mutex m_mutex; + PackageCachingDataForSession::iterator m_dbIterator, m_dbEnd; + PackageCachingDataForDatabase::iterator m_packageIterator, m_packageEnd; +}; + +} // namespace WebClient + +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_DATABASE_H diff --git a/librepomgr/webclient/session.cpp b/librepomgr/webclient/session.cpp new file mode 100644 index 0000000..a9f34d5 --- /dev/null +++ b/librepomgr/webclient/session.cpp @@ -0,0 +1,295 @@ +#include "./session.h" + +#include "../serversetup.h" + +#include "resources/config.h" + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace boost::asio; +using namespace boost::beast; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; + +namespace LibRepoMgr { +namespace WebClient { + +HttpClientError::HttpClientError(const char *context, boost::beast::error_code errorCode) + : std::runtime_error(argsToString(context, ':', ' ', errorCode.message())) + , context(context) + , errorCode(errorCode) +{ +} + +void Session::run(const char *host, const char *port, http::verb verb, const char *target, unsigned int version) +{ + // set up an HTTP request message + request.version(version); + request.method(verb); + request.target(target); + request.set(http::field::host, host); + request.set(http::field::user_agent, APP_NAME " " APP_VERSION); + + // setup a file response + if (!destinationFilePath.empty()) { + auto &fileResponse = response.emplace(); + boost::beast::error_code errorCode; + fileResponse.body_limit(100 * 1024 * 1024); + fileResponse.get().body().open(destinationFilePath.data(), file_mode::write, errorCode); + if (errorCode != boost::beast::errc::success) { + m_handler(*this, HttpClientError("opening output file", errorCode)); + return; + } + } + + // look up the domain name + m_resolver.async_resolve(host, port, + boost::asio::ip::tcp::resolver::canonical_name | boost::asio::ip::tcp::resolver::passive | boost::asio::ip::tcp::resolver::all_matching, + std::bind(&Session::resolved, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +} + +void Session::resolved(boost::beast::error_code ec, ip::tcp::resolver::results_type results) +{ + if (ec) { + m_handler(*this, HttpClientError("resolving", ec)); + return; + } + + // make the connection on the IP address we get from a lookup + boost::asio::async_connect(m_socket, results.begin(), results.end(), std::bind(&Session::connected, shared_from_this(), std::placeholders::_1)); +} + +void Session::connected(boost::beast::error_code ec) +{ + if (ec) { + m_handler(*this, HttpClientError("connecting", ec)); + return; + } + + // perform the SSL handshake + http::async_write(m_socket, request, std::bind(&Session::requested, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +} + +void Session::requested(boost::beast::error_code ec, std::size_t bytesTransferred) +{ + boost::ignore_unused(bytesTransferred); + if (ec) { + m_handler(*this, HttpClientError("sending request", ec)); + return; + } + + // receive the HTTP response + std::visit( + [this](auto &&response) { + http::async_read( + m_socket, m_buffer, response, std::bind(&Session::received, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + }, + response); +} + +void Session::received(boost::beast::error_code ec, std::size_t bytesTransferred) +{ + boost::ignore_unused(bytesTransferred); + if (ec) { + m_handler(*this, HttpClientError("receiving response", ec)); + return; + } + + // close the stream gracefully + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + + if (ec && ec != boost::beast::errc::not_connected) { + m_handler(*this, HttpClientError("closing connection", ec)); + return; + } + // if we get here then the connection is closed gracefully + m_handler(*this, HttpClientError()); +} + +void SslSession::run(const char *host, const char *port, http::verb verb, const char *target, unsigned int version) +{ + // set SNI Hostname (many hosts need this to handshake successfully) + if (!SSL_set_tlsext_host_name(m_stream.native_handle(), const_cast(host))) { + m_handler(*this, + HttpClientError( + "setting SNI hostname", boost::beast::error_code{ static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category() })); + return; + } + + // setup an HTTP request message + request.version(version); + request.method(verb); + request.target(target); + request.set(http::field::host, host); + request.set(http::field::user_agent, APP_NAME " " APP_VERSION); + + // setup a file response + if (!destinationFilePath.empty()) { + auto &fileResponse = response.emplace(); + boost::beast::error_code errorCode; + fileResponse.body_limit(100 * 1024 * 1024); + fileResponse.get().body().open(destinationFilePath.data(), file_mode::write, errorCode); + if (errorCode != boost::beast::errc::success) { + m_handler(*this, HttpClientError("opening output file", errorCode)); + return; + } + } + + // look up the domain name + m_resolver.async_resolve(host, port, + boost::asio::ip::tcp::resolver::canonical_name | boost::asio::ip::tcp::resolver::passive | boost::asio::ip::tcp::resolver::all_matching, + std::bind(&SslSession::resolved, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +} + +void SslSession::resolved(boost::beast::error_code ec, ip::tcp::resolver::results_type results) +{ + if (ec) { + m_handler(*this, HttpClientError("resolving", ec)); + return; + } + + // make the connection on the IP address we get from a lookup + boost::asio::async_connect( + m_stream.next_layer(), results.begin(), results.end(), std::bind(&SslSession::connected, shared_from_this(), std::placeholders::_1)); +} + +void SslSession::connected(boost::beast::error_code ec) +{ + if (ec) { + m_handler(*this, HttpClientError("connecting", ec)); + return; + } + + // perform the SSL handshake + m_stream.async_handshake(ssl::stream_base::client, std::bind(&SslSession::handshakeDone, shared_from_this(), std::placeholders::_1)); +} + +void SslSession::handshakeDone(boost::beast::error_code ec) +{ + if (ec) { + m_handler(*this, HttpClientError("SSL handshake", ec)); + return; + } + + // send the HTTP request to the remote host + boost::beast::http::async_write( + m_stream, request, std::bind(&SslSession::requested, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +} + +void SslSession::requested(boost::beast::error_code ec, std::size_t bytesTransferred) +{ + boost::ignore_unused(bytesTransferred); + if (ec) { + m_handler(*this, HttpClientError("sending request", ec)); + return; + } + + // receive the HTTP response + std::visit( + [this](auto &&response) { + http::async_read( + m_stream, m_buffer, response, std::bind(&SslSession::received, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + }, + response); +} + +void SslSession::received(boost::beast::error_code ec, std::size_t bytesTransferred) +{ + boost::ignore_unused(bytesTransferred); + if (ec) { + m_handler(*this, HttpClientError("receiving response", ec)); + return; + } + + // close the stream gracefully + m_stream.async_shutdown(std::bind(&SslSession::closed, shared_from_this(), std::placeholders::_1)); +} + +void SslSession::closed(boost::beast::error_code ec) +{ + if (ec == boost::asio::error::eof) { + // rationale: http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if (ec) { + m_handler(*this, HttpClientError("closing connection", ec)); + return; + } + // if we get here then the connection is closed gracefully + m_handler(*this, HttpClientError()); +} + +template +std::variant, std::shared_ptr> runSession(const std::string &host, const std::string &port, + const std::string &target, std::function &&handler, std::string &&destinationPath, + std::string_view userName, std::string_view password, ArgType &&...args) +{ + auto session = make_shared(args..., [handler{ move(handler) }](auto &session, const HttpClientError &error) mutable { + handler(SessionData{ session.shared_from_this(), session.request, session.response, session.destinationFilePath }, error); + }); + if (!userName.empty()) { + const auto authInfo = userName % ":" + password; + session->request.set(boost::beast::http::field::authorization, + "Basic " + encodeBase64(reinterpret_cast(authInfo.data()), authInfo.size())); + } + session->destinationFilePath = move(destinationPath); + session->run(host.data(), port.data(), http::verb::get, target.data()); + return move(session); +} + +std::variant, std::shared_ptr> runSessionFromUrl(boost::asio::io_context &ioContext, + boost::asio::ssl::context &sslContext, std::string_view url, std::function &&handler, + std::string &&destinationPath, std::string_view userName, std::string_view password) +{ + string host, port, target; + auto ssl = false; + + if (startsWith(url, "http:")) { + url = url.substr(5); + } else if (startsWith(url, "https:")) { + url = url.substr(6); + ssl = true; + } else { + return "db mirror for database has unsupported protocol"; + } + + auto urlParts = splitStringSimple>(url, "/"); + target.reserve(url.size()); + for (const auto &part : urlParts) { + if (part.empty()) { + continue; + } + if (host.empty()) { + host = part; + continue; + } + target += '/'; + target += part; + } + + if (const auto lastColon = host.find_last_of(':'); lastColon != std::string_view::npos) { + port = host.substr(lastColon + 1); + host = host.substr(0, lastColon); + } + if (port.empty()) { + port = ssl ? "443" : "80"; + } + + if (ssl) { + return runSession(host, port, target, move(handler), move(destinationPath), userName, password, ioContext, sslContext); + } else { + return runSession(host, port, target, move(handler), move(destinationPath), userName, password, ioContext); + } +} + +} // namespace WebClient +} // namespace LibRepoMgr diff --git a/librepomgr/webclient/session.h b/librepomgr/webclient/session.h new file mode 100644 index 0000000..af8d63a --- /dev/null +++ b/librepomgr/webclient/session.h @@ -0,0 +1,147 @@ +#ifndef LIBREPOMGR_CLIENT_SESSION_H +#define LIBREPOMGR_CLIENT_SESSION_H + +#include "../global.h" +#include "../webapi/typedefs.h" + +#include +#include + +#include +#include + +#include +#include +#include + +namespace LibRepoMgr { + +struct ServiceSetup; + +namespace WebClient { + +struct LIBREPOMGR_EXPORT HttpClientError : std::runtime_error { + explicit HttpClientError(); + explicit HttpClientError(const char *context, boost::beast::error_code errorCode); + operator bool() const; + + const char *const context; + const boost::beast::error_code errorCode; +}; + +inline HttpClientError::HttpClientError() + : std::runtime_error(std::string()) + , context(nullptr) + , errorCode(boost::beast::error_code()) +{ +} + +inline LibRepoMgr::WebClient::HttpClientError::operator bool() const +{ + return errorCode ? true : false; +} + +using Response = WebAPI::Response; +using FileResponse = boost::beast::http::response_parser; +using MultiResponse = std::variant; +using Request = boost::beast::http::request; +struct LIBREPOMGR_EXPORT SessionData { + std::shared_ptr session; + Request &request; + MultiResponse &response; + std::string &destinationFilePath; +}; + +class LIBREPOMGR_EXPORT Session : public std::enable_shared_from_this { +public: + using Handler = std::function; + + template explicit Session(boost::asio::io_context &ioContext, const Handler &handler = Handler()); + + void run(const char *host, const char *port, boost::beast::http::verb verb, const char *target, unsigned int version = 11); + +private: + void resolved(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results); + void connected(boost::beast::error_code ec); + void requested(boost::beast::error_code ec, std::size_t bytesTransferred); + void received(boost::beast::error_code ec, std::size_t bytesTransferred); + +public: + Request request; + MultiResponse response; + std::string destinationFilePath; + +private: + boost::asio::ip::tcp::resolver m_resolver; + boost::asio::ip::tcp::socket m_socket; + boost::beast::flat_buffer m_buffer; + Handler m_handler; +}; + +template +inline Session::Session(boost::asio::io_context &ioContext, const Handler &handler) + : response(ResponseType{}) + , m_resolver(ioContext) + , m_socket(ioContext) + , m_handler(handler) +{ +} + +class LIBREPOMGR_EXPORT SslSession : public std::enable_shared_from_this { +public: + using Handler = std::function; + + template + explicit SslSession(boost::asio::io_context &ioContext, boost::asio::ssl::context &sslContext, const Handler &handler = Handler()); + template + explicit SslSession(boost::asio::io_context &ioContext, boost::asio::ssl::context &sslContext, Handler &&handler); + + void run(const char *host, const char *port, boost::beast::http::verb verb, const char *target, unsigned int version = 11); + +private: + void resolved(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results); + void connected(boost::beast::error_code ec); + void handshakeDone(boost::beast::error_code ec); + void requested(boost::beast::error_code ec, std::size_t bytesTransferred); + void received(boost::beast::error_code ec, std::size_t bytesTransferred); + void closed(boost::beast::error_code ec); + +public: + Request request; + MultiResponse response; + std::string destinationFilePath; + +private: + boost::asio::ip::tcp::resolver m_resolver; + boost::asio::ssl::stream m_stream; + boost::beast::flat_buffer m_buffer; + Handler m_handler; +}; + +template +inline SslSession::SslSession(boost::asio::io_context &ioContext, boost::asio::ssl::context &sslContext, const Handler &handler) + : response(ResponseType{}) + , m_resolver(ioContext) + , m_stream(ioContext, sslContext) + , m_handler(handler) +{ +} + +template +inline SslSession::SslSession(boost::asio::io_context &ioContext, boost::asio::ssl::context &sslContext, Handler &&handler) + : response(ResponseType{}) + , m_resolver(ioContext) + , m_stream(ioContext, sslContext) + , m_handler(handler) +{ +} + +LIBREPOMGR_EXPORT std::variant, std::shared_ptr> runSessionFromUrl( + boost::asio::io_context &ioContext, boost::asio::ssl::context &sslContext, std::string_view url, + std::function &&handler, std::string &&destinationPath = std::string(), + std::string_view userName = std::string_view(), std::string_view password = std::string_view()); + +} // namespace WebClient +} // namespace LibRepoMgr + +#endif // LIBREPOMGR_CLIENT_SESSION_H diff --git a/pacfind/CMakeLists.txt b/pacfind/CMakeLists.txt new file mode 100644 index 0000000..d30df8b --- /dev/null +++ b/pacfind/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +# add project files +set(HEADER_FILES) +set(SRC_FILES main.cpp) +set(TEST_HEADER_FILES) +set(TEST_SRC_FILES tests/cppunit.cpp tests/check.cpp) + +# meta data +set(META_PROJECT_NAME pacfind) +set(META_PROJECT_TYPE application) +set(META_PROJECT_VARNAME REPO_CLEAN) +set(META_APP_NAME "Package finder") +set(META_APP_AUTHOR "Martchus") +set(META_APP_DESCRIPTION "Tool to find the package containing a certain file") +set(META_VERSION_MAJOR 0) +set(META_VERSION_MINOR 0) +set(META_VERSION_PATCH 1) + +# find c++utilities +set(CONFIGURATION_PACKAGE_SUFFIX + "" + CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities") +find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED) +use_cpp_utilities() + +# find backend libraries +find_package(libpkg ${META_APP_VERSION} REQUIRED) +use_libpkg() + +list(APPEND PRIVATE_LIBRARIES pthread) + +# include modules to apply configuration +include(BasicConfig) +include(WindowsResources) +include(AppTarget) +include(TestTarget) +include(ShellCompletion) +include(ConfigHeader) diff --git a/pacfind/main.cpp b/pacfind/main.cpp new file mode 100644 index 0000000..1d71b58 --- /dev/null +++ b/pacfind/main.cpp @@ -0,0 +1,156 @@ +#include "../libpkg/data/config.h" + +#include "resources/config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace LibPkg; +using namespace CppUtilities; + +int main(int argc, const char *argv[]) +{ + SET_APPLICATION_INFO; + + // read cli args + ArgumentParser parser; + Argument dbFileArg("db-files", 'd', "specifies the repository database files"); + dbFileArg.setRequiredValueCount(Argument::varValueCount); + dbFileArg.setValueNames({ "path" }); + Argument loadPacmanConfigArg("pacman-config", 'c', "specifies whether to load all databases from pacman config"); + loadPacmanConfigArg.setCombinable(true); + ConfigValueArgument pacmanConfigPathArg("pacman-config-path", '\0', "specifies the path of the pacman config file", { "path" }); + loadPacmanConfigArg.setSubArguments({ &pacmanConfigPathArg }); + ConfigValueArgument fileNameArg("file-name", 'f', "specifies the file name to seach for", { "name" }); + fileNameArg.setImplicit(true); + fileNameArg.setRequired(true); + Argument regexArg("regex", 'r', "use regex to match the file name (--file-name is considered a regex)"); + regexArg.setCombinable(true); + Argument negateArg("negate", 'n', "lists only packages which do NOT contain --file-name"); + negateArg.setCombinable(true); + OperationArgument searchArg("search", '\0', "searches for packages containing the specified file"); + searchArg.setImplicit(true); + searchArg.setSubArguments({ &fileNameArg, ®exArg, &negateArg, &dbFileArg, &loadPacmanConfigArg }); + HelpArgument helpArg(parser); + OperationArgument listArg("list", '\0', "lists the files contained within the specified package"); + ConfigValueArgument packageArg("package", '\0', "the name of the package", { "name" }); + packageArg.setImplicit(true); + listArg.setSubArguments({ &packageArg, &dbFileArg, &loadPacmanConfigArg }); + parser.setMainArguments({ &searchArg, &listArg, &helpArg }); + parser.setDefaultArgument(&helpArg); + parser.parseArgs(argc, argv); + + // init config from pacman config to get relevant dbs + Config cfg; + if (loadPacmanConfigArg.isPresent()) { + try { + cfg.loadPacmanConfig("/etc/pacman.conf"); + } catch (const runtime_error &e) { + cerr << "Unable to load pacman config." << endl; + exit(1); + } + } + + // allow adding custom db paths + if (dbFileArg.isPresent()) { + for (const char *dbPath : dbFileArg.values(0)) { + Database &db = cfg.databases.emplace_back(string(), dbPath); + db.name = std::filesystem::path(db.path).stem().string(); + db.localPkgDir = directory(db.path); + } + } + + if (cfg.databases.empty()) { + cerr << "No databases configured." << endl; + exit(2); + } + + // load all packages for the dbs + for (Database &db : cfg.databases) { + try { + if (endsWith(db.path, ".files")) { + db.filesPath = db.path; + } else { + db.filesPath = db.filesPathFromRegularPath(); + } + db.loadPackages(true); + } catch (const runtime_error &e) { + cerr << "Unable to load database \"" << db.name << "\": " << e.what() << '\n'; + } + } + + // print file list for certain package + if (packageArg.isPresent()) { + const auto pkgs = cfg.findPackages(packageArg.firstValue()); + for (const auto &pkg : pkgs) { + if (const auto *const db = std::get(pkg.db); !db->name.empty()) { + cout << db->name << '/'; + } + cout << pkg.pkg->name; + if (!pkg.pkg->packageInfo) { + cout << '\n'; + continue; + } + for (const auto &path : pkg.pkg->packageInfo->files) { + cout << "\n - " << path; + } + cout << '\n'; + } + return 0; + } + + // search databases for relevant packages + const char *const searchTerm = fileNameArg.firstValue(); + const auto negate = negateArg.isPresent(); + auto regex = std::optional(); + if (regexArg.isPresent()) { + regex = std::regex(searchTerm, std::regex::egrep); + } + for (const Database &db : cfg.databases) { + for (const auto &pkg : db.packages) { + const auto &pkgInfo = pkg.second->packageInfo; + if (!pkgInfo) { + continue; + } + auto foundOne = false; + for (const string &file : pkgInfo->files) { + const auto found = regex.has_value() ? std::regex_match(file, regex.value()) : file.find(searchTerm) != string::npos; + if (negate) { + if (found) { + foundOne = true; + break; + } else { + continue; + } + } + if (!found) { + continue; + } + if (!foundOne) { + if (!db.name.empty()) { + cout << db.name << '/'; + } + cout << pkg.first << '\n'; + foundOne = true; + } + cout << " - " << file << '\n'; + } + if (negate && !foundOne) { + if (!db.name.empty()) { + cout << db.name << '/'; + } + cout << pkg.first << '\n'; + } + } + } + + return 0; +} diff --git a/pacfind/tests/check.cpp b/pacfind/tests/check.cpp new file mode 100644 index 0000000..b68f2bb --- /dev/null +++ b/pacfind/tests/check.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include + +using namespace std; +using namespace CPPUNIT_NS; +using namespace CppUtilities; + +class PacfindTests : public TestFixture { + CPPUNIT_TEST_SUITE(PacfindTests); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + +public: + PacfindTests(); + void setUp() override; + void tearDown() override; + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(PacfindTests); + +PacfindTests::PacfindTests() +{ +} + +void PacfindTests::setUp() +{ +} + +void PacfindTests::tearDown() +{ +} + +void PacfindTests::test() +{ +} diff --git a/pacfind/tests/cppunit.cpp b/pacfind/tests/cppunit.cpp new file mode 100644 index 0000000..67aaee6 --- /dev/null +++ b/pacfind/tests/cppunit.cpp @@ -0,0 +1 @@ +#include diff --git a/srv/CMakeLists.txt b/srv/CMakeLists.txt new file mode 100644 index 0000000..748cbb3 --- /dev/null +++ b/srv/CMakeLists.txt @@ -0,0 +1,107 @@ +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) + +# add project files +set(HEADER_FILES) +set(SRC_FILES main.cpp) +set(TEST_HEADER_FILES) +set(TEST_SRC_FILES) +set(HTML_FILES static/index.html static/log.html) +set(JAVA_SCRIPT_FILES + static/js/ajaxhelper.js + static/js/buildactionspage.js + static/js/customrendering.js + static/js/genericrendering.js + static/js/globalstatuspage.js + static/js/log.js + static/js/packagedetailspage.js + static/js/packagesearchpage.js + static/js/singlepage.js + static/js/terminal.js + static/js/utils.js + static/node_modules/xterm/lib/xterm.js + static/node_modules/xterm-addon-search/out/SearchAddon.js) +set(CSS_FILES static/css/basics.css static/css/genericrendering.css static/css/layout.css static/css/log.css + static/css/specifics.css static/node_modules/xterm/css/xterm.css) +set(IMG_FILES + static/img/logo.svg + static/img/icon/content-save.svg + static/img/icon/power.svg + static/img/icon/play.svg + static/img/icon/magnify.svg + static/img/icon/select-all.svg + static/img/icon/select-off.svg + static/img/icon/plus.svg + static/img/icon/delete.svg + static/img/icon/undo-variant.svg + static/img/icon/restart.svg + static/img/icon/reload.svg + static/img/icon/dock-window.svg + static/img/icon/download.svg + static/img/icon/stop.svg + static/img/icon/database-white.svg + static/img/icon/magnify-white.svg + static/img/icon/package-variant-white.svg + static/img/icon/cogs-white.svg + static/img/icon/console-line-white.svg + static/img/icon/table-refresh.svg) +set(CMAKE_MODULE_FILES cmake/modules/SystemdService.cmake) +set(CMAKE_TEMPLATE_FILES cmake/templates/systemd.service.in cmake/templates/sysusers.conf.in + cmake/templates/tmpfiles.conf.in) + +# meta data +set(META_PROJECT_NAME buildservice) +set(META_PROJECT_TYPE application) +set(META_PROJECT_VARNAME BUILD_SERVICE) +set(META_APP_NAME "Repo manager and package builder for Arch Linux") +set(META_APP_AUTHOR "Martchus") +set(META_APP_DESCRIPTION + "Daemon and web application for building Arch Linux packages and managing custom Arch Linux repositories") +set(META_VERSION_MAJOR 0) +set(META_VERSION_MINOR 0) +set(META_VERSION_PATCH 1) + +# include CMake modules from own project directory +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "${CMAKE_MODULE_PATH}") + +# find c++utilities +set(CONFIGURATION_PACKAGE_SUFFIX + "" + CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities") +find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.4.0 REQUIRED) +use_cpp_utilities() + +# find backend libraries +find_package(librepomgr ${META_APP_VERSION} REQUIRED) +use_librepomgr() + +# apply basic configuration +include(BasicConfig) +include(WindowsResources) +include(AppTarget) +include(ShellCompletion) +include(ConfigHeader) + +# add install target for web files +set(WEB_FILES ${HTML_FILES} ${JAVA_SCRIPT_FILES} ${CSS_FILES} ${IMG_FILES}) +set(WEB_FILES_INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/${META_DATA_DIR}/web") +foreach (WEB_FILE ${WEB_FILES}) + get_filename_component(WEB_DIR "${WEB_FILE}" DIRECTORY) + install( + FILES "${WEB_FILE}" + DESTINATION "${WEB_FILES_INSTALL_DESTINATION}/${WEB_DIR}" + COMPONENT web-files) +endforeach () +if (NOT TARGET install-web-files) + add_custom_target(install-web-files COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=web-files -P + "${CMAKE_BINARY_DIR}/cmake_install.cmake") +endif () + +# add installs for config file template +install( + FILES doc/server-config-example.conf doc/nginx-config-example.conf + DESTINATION "${CMAKE_INSTALL_PREFIX}/${META_DATA_DIR}/skel" + COMPONENT config) + +# make files for systemd service +include(SystemdService) +add_systemd_service(ARGUMENTS run -c "/etc/${META_PROJECT_NAME}${META_CONFIG_SUFFIX}/server.conf") diff --git a/srv/cmake/modules/SystemdService.cmake b/srv/cmake/modules/SystemdService.cmake new file mode 100644 index 0000000..e227bda --- /dev/null +++ b/srv/cmake/modules/SystemdService.cmake @@ -0,0 +1,107 @@ +cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) + +# prevent multiple inclusion +if (DEFINED SYSTEMD_SERVICE_LOADED) + return() +endif () +set(SYSTEMD_SERVICE_LOADED YES) + +include(CMakeParseArguments) +include(TemplateFinder) +include(ListToString) + +function (add_systemd_service) + # parse arguments + set(OPTIONAL_ARGS + SERVICE_NAME + SERVICE_DESCRIPTION + USER_NAME + USER_ID + USER_DISPLAY_NAME + EXECUTABLE + SERVICE_TEMPLATE + SYSUSERS_TEMPLATE + TMPFILES_TEMPLATE) + set(ONE_VALUE_ARGS) + set(MULTI_VALUE_ARGS ARGUMENTS) + cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) + if (NOT ARGS_SERVICE_NAME) + set(ARGS_SERVICE_NAME "${META_PROJECT_NAME}${META_CONFIG_SUFFIX}") + endif () + if (NOT ARGS_SERVICE_DESCRIPTION) + if (META_APP_DESCRIPTION) + set(ARGS_SERVICE_DESCRIPTION "${META_APP_DESCRIPTION}") + else () + set(ARGS_SERVICE_DESCRIPTION "${ARGS_SERVICE_NAME} service") + endif () + endif () + if (NOT ARGS_USER_NAME) + set(ARGS_USER_NAME "${ARGS_SERVICE_NAME}") + endif () + if (NOT ARGS_USER_ID) + set(ARGS_USER_ID "-") + endif () + if (NOT ARGS_USER_DISPLAY_NAME) + if (META_APP_NAME) + set(ARGS_USER_DISPLAY_NAME "${META_APP_NAME}") + else () + set(ARGS_USER_DISPLAY_NAME "${ARGS_SERVICE_NAME} user") + endif () + endif () + if (NOT ARGS_EXECUTABLE) + set(ARGS_EXECUTABLE "${TARGET_EXECUTABLE}") + endif () + if (NOT ARGS_SERVICE_TEMPLATE) + set(ARGS_SERVICE_TEMPLATE "systemd.service") + endif () + if (NOT ARGS_SYSUSERS_TEMPLATE) + set(ARGS_SYSUSERS_TEMPLATE "sysusers.conf") + endif () + if (NOT ARGS_TMPFILES_TEMPLATE) + set(ARGS_TMPFILES_TEMPLATE "tmpfiles.conf") + endif () + + # find templates, make paths for outputs + find_template_file("${ARGS_SERVICE_TEMPLATE}" ${META_PROJECT_VARNAME} SERVICE_TEMPLATE_FILE) + find_template_file("${ARGS_SYSUSERS_TEMPLATE}" ${META_PROJECT_VARNAME} SYSUSERS_TEMPLATE_FILE) + find_template_file("${ARGS_TMPFILES_TEMPLATE}" ${META_PROJECT_VARNAME} TMPFILES_TEMPLATE_FILE) + set(SERVICE_TARGET_FILE "${CMAKE_CURRENT_BINARY_DIR}/resources/systemd/system/${ARGS_SERVICE_NAME}.service") + set(SYSUSERS_TARGET_FILE "${CMAKE_CURRENT_BINARY_DIR}/resources/sysusers.d/${ARGS_SERVICE_NAME}.conf") + set(TMPFILES_TARGET_FILE "${CMAKE_CURRENT_BINARY_DIR}/resources/tmpfiles.d/${ARGS_SERVICE_NAME}.conf") + + # set variables used in templates + set(SYSTEMD_SERVICE_NAME "${ARGS_SERVICE_NAME}") + set(SYSTEMD_SERVICE_DESCRIPTION "${ARGS_SERVICE_DESCRIPTION}") + set(SYSTEMD_SERVICE_USER_NAME "${ARGS_USER_NAME}") + set(SYSTEMD_SERVICE_USER_ID "${ARGS_USER_ID}") + set(SYSTEMD_SERVICE_USER_DISPLAY_NAME "${ARGS_USER_DISPLAY_NAME}") + if (ARGS_ARGUMENTS) + list_to_string(" " "" "" "${ARGS_ARGUMENTS}" SYSTEMD_SERVICE_EXEC) + set(SYSTEMD_SERVICE_EXEC "${ARGS_EXECUTABLE} ${SYSTEMD_SERVICE_EXEC}") + else () + set(SYSTEMD_SERVICE_EXEC "${ARGS_EXECUTABLE}") + endif () + + # configure files + configure_file("${SERVICE_TEMPLATE_FILE}" "${SERVICE_TARGET_FILE}") + configure_file("${SYSUSERS_TEMPLATE_FILE}" "${SYSUSERS_TARGET_FILE}") + configure_file("${TMPFILES_TEMPLATE_FILE}" "${TMPFILES_TARGET_FILE}") + + # add installs + install( + FILES "${SERVICE_TARGET_FILE}" + DESTINATION lib/systemd/system + COMPONENT service) + install( + FILES "${SYSUSERS_TARGET_FILE}" + DESTINATION lib/sysusers.d + COMPONENT service) + install( + FILES "${TMPFILES_TARGET_FILE}" + DESTINATION lib/tmpfiles.d + COMPONENT service) + if (NOT TARGET install-service) + add_custom_target(install-service COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=service -P + "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif () +endfunction () diff --git a/srv/cmake/templates/systemd.service.in b/srv/cmake/templates/systemd.service.in new file mode 100644 index 0000000..70b7784 --- /dev/null +++ b/srv/cmake/templates/systemd.service.in @@ -0,0 +1,12 @@ +[Unit] +Description=@SYSTEMD_SERVICE_DESCRIPTION@ +After=network.target + +[Service] +User=@SYSTEMD_SERVICE_USER_NAME@ +ExecStart=@SYSTEMD_SERVICE_EXEC@ +KillSignal=SIGQUIT +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/srv/cmake/templates/sysusers.conf.in b/srv/cmake/templates/sysusers.conf.in new file mode 100644 index 0000000..a5e10fb --- /dev/null +++ b/srv/cmake/templates/sysusers.conf.in @@ -0,0 +1 @@ +u @SYSTEMD_SERVICE_USER_NAME@ @SYSTEMD_SERVICE_USER_ID@ "@SYSTEMD_SERVICE_USER_DISPLAY_NAME@" /var/lib/@SYSTEMD_SERVICE_NAME@ /bin/bash diff --git a/srv/cmake/templates/tmpfiles.conf.in b/srv/cmake/templates/tmpfiles.conf.in new file mode 100644 index 0000000..ec4342c --- /dev/null +++ b/srv/cmake/templates/tmpfiles.conf.in @@ -0,0 +1,3 @@ +d /var/lib/@SYSTEMD_SERVICE_NAME@ 0755 @SYSTEMD_SERVICE_USER_NAME@ @SYSTEMD_SERVICE_USER_NAME@ +d /var/log/@SYSTEMD_SERVICE_NAME@ 0755 @SYSTEMD_SERVICE_USER_NAME@ @SYSTEMD_SERVICE_USER_NAME@ +d /etc/@SYSTEMD_SERVICE_NAME@ 0775 root @SYSTEMD_SERVICE_USER_NAME@ diff --git a/srv/doc/nginx-config-example.conf b/srv/doc/nginx-config-example.conf new file mode 100644 index 0000000..842a61a --- /dev/null +++ b/srv/doc/nginx-config-example.conf @@ -0,0 +1,14 @@ +proxy_http_version 1.1; # this is essential for chunked responses to work +proxy_read_timeout 1d; # prevent timeouts when serving live stream + +location /buildservice/api/ { + proxy_buffering off; # for live streaming of logfiles + proxy_pass http://localhost:8090/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; +} +location ~ ^(?!/buildservice/api/)/buildservice/(.*)$ { + alias /run/media/devel/projects/c++/cmake/auto-makepkg/buildservice/static/$1; +} diff --git a/srv/doc/server-config-example.conf b/srv/doc/server-config-example.conf new file mode 100644 index 0000000..a7fc0d3 --- /dev/null +++ b/srv/doc/server-config-example.conf @@ -0,0 +1,195 @@ +pacman_config_file_path = /etc/pacman.conf +working_directory = /var/lib/buildservice + +[webserver] +static_files = /usr/share/buildservice/web +threads = 4 + +[user/martchus] +password_sha512 = ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff +permissions = read_build_actions_details modify_build_actions perform_admin_actions + +[building] +load_files_dbs = off +working_directory = /var/lib/buildservice/building +local_pkgbuilds_dir = /run/media/devel/projects/pkgbuilds +ignore_local_pkgbuilds_regex = ^(some-package-name|yet-another-package-name)$ +chroot_dir = /directory/with/enough/space/for/chroot +#ccache_dir = /the/ccache/directory +#package_cache_dir = /var/cache/pacman/pkg + +[definitions] +sync_db_path = sync-dbs +local_db_path = /var/lib/buildservice/repos +default_mirror = http://mirror.23media.de/archlinux +fallback_mirror = https://ftp.fau.de/archlinux +default_mirror_32 = https://de.mirror.archlinux32.org +fallback_mirror_32 = https://mirror.archlinux32.org +local_mirror = file://$local_db_path + +[database/staging] +depends = testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/testing] +depends = core extra +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/core] +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/extra] +depends = core +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/community-staging] +depends = community-testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/community-testing] +depends = community +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/community] +depends = extra +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/multilib-staging] +depends = multilib-testing staging community-staging +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/multilib-testing] +depends = multilib testing community-testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/multilib] +depends = core extra community +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/kde-unstable] +depends = testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/gnome-unstable] +depends = testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror/$repo/os/$arch +mirror = $fallback_mirror/$repo/os/$arch + +[database/ownstuff] +depends = core extra community multilib +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch + +[database/ownstuff-testing] +depends = testing community-testing multilib-testing ownstuff +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch + +[database/ownstuff-staging] +depends = staging community-staging multilib-staging ownstuff-testing +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch + +[database/ownstuff-protected] +depends = ownstuff +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch + +[database/ownstuff-protected-testing] +depends = ownstuff-protected ownstuff-testing +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch + +[database/ownstuff-experimental] +depends = ownstuff-testing +pkgdir = $local_db_path/$repo/os/$arch +dbdir = $local_db_path/$repo/os/$arch +mirror = $local_mirror/$repo/os/$arch + +[database/staging@i686] +depends = testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror_32/$arch/$repo + +[database/testing@i686] +depends = core extra +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror/$repo/os/$arch + +[database/core@i686] +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror_32/$arch/$repo + +[database/extra@i686] +depends = core +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror_32/$arch/$repo + +[database/community-staging@i686] +depends = community-testing +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror_32/$arch/$repo + +[database/community-testing@i686] +depends = community +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror_32/$arch/$repo + +[database/community@i686] +depends = extra +sync_from_mirror = on +dbdir = $sync_db_path/$arch +mirror = $default_mirror_32/$arch/$repo +mirror = $fallback_mirror_32/$arch/$repo diff --git a/srv/main.cpp b/srv/main.cpp new file mode 100644 index 0000000..8b179c7 --- /dev/null +++ b/srv/main.cpp @@ -0,0 +1,47 @@ +#include "../librepomgr/serversetup.h" + +#include "../libpkg/data/config.h" + +#include "resources/config.h" + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace CppUtilities; +using namespace CppUtilities::EscapeCodes; +using namespace LibRepoMgr; +using namespace LibPkg; + +int main(int argc, const char *argv[]) +{ + SET_APPLICATION_INFO; + + // define default server setup + ServiceSetup setup; + + // read cli args + ArgumentParser parser; + OperationArgument runArg("run", 'r', "runs the server"); + ConfigValueArgument configFileArg("config-file", 'c', "specifies the path of the config file", { "path" }); + configFileArg.setEnvironmentVariable(PROJECT_VARNAME_UPPER "_CONFIG_FILE"); + runArg.setSubArguments({ &configFileArg }); + runArg.setImplicit(true); + runArg.setCallback([&setup, &configFileArg](const ArgumentOccurrence &) { + if (configFileArg.firstValue()) { + setup.configFilePath = configFileArg.firstValue(); + } + setup.run(); + }); + HelpArgument helpArg(parser); + NoColorArgument noColorArg; + parser.setMainArguments({ &runArg, &noColorArg, &helpArg }); + parser.setDefaultArgument(&runArg); + parser.parseArgs(argc, argv); + return 0; +} diff --git a/srv/static/css/basics.css b/srv/static/css/basics.css new file mode 100644 index 0000000..dc6b1d0 --- /dev/null +++ b/srv/static/css/basics.css @@ -0,0 +1,60 @@ +/* style for basic HTML elements */ + +html, body { + background: white; + color: #222; + font: normal 16px sans-serif; + margin: 0px; + padding: 0px; +} + +h1, h2, h3, h4, h5, h6 { + padding: 0px; +} +h1 { + font-size: 150%; + margin: 15px 0px; +} +h2 { + font-size: 130%; + margin: 10px 0px; +} +h3 { + font-size: 110%; + margin: 10px 0px; +} + +a:link { + color: #07b; + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +a:visited { + color: #666; +} + +pre { + border: 1px solid #aaa; + background-color: white; + margin: 5px; + padding: 5px; + white-space: pre-wrap; + overflow-x: auto; +} + +fieldset { + border: 1px dotted #999; + background: #f6f9fc; + font-size: .846em; +} +fieldset legend { + font-weight: bold; + margin-bottom: 5px; +} + +button, input[type="button"] { + border: 1px solid #aaa; + padding: 3px; +} \ No newline at end of file diff --git a/srv/static/css/genericrendering.css b/srv/static/css/genericrendering.css new file mode 100644 index 0000000..4579e38 --- /dev/null +++ b/srv/static/css/genericrendering.css @@ -0,0 +1,116 @@ +/* style for elements rendered via genericrendering.js */ + +.table-from-json { + border-top: 1px dotted #999; + border-bottom: 1px dotted #999; + border-spacing: 0px; + background-color: white; + margin-bottom: 10px; + font-size: .846em; +} +.table-from-json-array thead th { + position: sticky; + background-color: white; + top: -1px; +} +@media (min-width: 900px) { + .table-from-json-array thead th { + top: 60px; + } +} +.table-from-json-array th { + border-bottom: 1px solid #999; +} +.table-from-json tr:nth-child(even), .table-from-json .table-from-json tbody tr:nth-child(even):hover { + background: #e4eeff; +} +.table-from-json tr:nth-child(odd), .table-from-json .table-from-json tbody tr:nth-child(odd):hover { + background: #fff; +} +.table-from-json-array td { + padding: 1px; +} +.table-from-json tbody tr:hover, .pagination a:hover, .table-row-highlighted { + background: #ffd !important; +} +.table-from-json-array th, .table-from-json-object th, .table-from-json-object td { + padding: 3px; + vertical-align: middle; +} +.table-from-json-array a { + text-decoration: none; + color: inherit; +} +.table-from-json-array tbody a:hover { + border-bottom: 1px dotted #aaa; +} +.table-from-json-object th { + text-align: right; + vertical-align: top; +} +.table-row-actions { + display: block; + text-align: center; +} +.table-row-actions a { + font-size: 105%; + padding: 2px; +} +.table-row-actions a:hover { + border-bottom: none; + font-weight: bold; +} + +.pagination { + position: sticky; + border-top: 1px solid #999; + background-color: white; + bottom: 0px; + text-align: center; + white-space: nowrap; + overflow-x: auto; + max-width: 0px; +} +.pagination a.prev { + position: sticky; + margin-left: 0px; + left: -1px; +} +.pagination a.next { + position: sticky; + margin-right: 0px; + right: 0px; +} +.pagination a, .pagination span, .pagination input[type="number"] { + display: inline-block; + padding: 5pt; + border: 1px solid #ddd; + background-color: #f6f9fc; + margin: 0.20em; + min-width: 12pt; + height: 12pt; + user-select: none; +} +.pagination a { + cursor: pointer; +} +.pagination a.current, .pagination a.current:hover { + font-weight: bold; + background: #e4eeff; + cursor: default; +} +.pagination span { + margin-left: 0px; + border-left: none; +} +.pagination input[type="number"] { + margin-right: 0px; + padding-right: 0px; + border-right: none; +} + +.compact-heading { + margin: 0px; + margin-bottom: 3px; + padding: 0px; +} \ No newline at end of file diff --git a/srv/static/css/layout.css b/srv/static/css/layout.css new file mode 100644 index 0000000..253c19e --- /dev/null +++ b/srv/static/css/layout.css @@ -0,0 +1,102 @@ +/* style for defining the overall layout of the page (header, navigation, main container) */ + +ul { + margin: 0px; +} + +header nav { + margin: 0px; + display: block; + background-color: #333; + border-bottom: 5px #08c solid!important; + color: #fff; + cursor: default; + width: 100%; + z-index: 1000; +} +header nav > div { + box-sizing: border-box; + list-style-type: none; + width: 100%; + padding-top: 8px; + margin-bottom: 10px; + padding-left: 0em; +} +header nav > div > span { + font-size: 90%; + color: #999; +} +header nav > div img { + float: left; + height: 40px; + margin-left: 15px; + margin-right: 10px; +} +header nav ul { + padding: 0px; + margin: 0px; + border-top: 1px solid #222; +} +header nav li { + display: inline-block; + margin: 0px; + padding: 0px; + background-repeat: no-repeat; + background-size: auto 45%; + background-position: center 20%; + opacity: .6; +} +header nav ul li a, header nav ul li a:link, header nav ul li a:visited { + display: inline-block; + box-sizing: border-box; + text-decoration: none; + padding-left: 20px; + padding-top: 35px; + padding-bottom: 5px; + padding-right: 20px; + color: #999; + font-weight: bold; + font-size: 90%; +} +header nav ul li a:hover, header nav ul li.active a { + color: #fff; + text-decoration: none; +} +header nav li.active { + background-color: #000; +} +header nav ul li:hover { + opacity: .9; +} +@media (min-width: 900px) { + header nav { + position: fixed; + } + header nav ul { + float: right; + border-top: none; + } + header nav > div { + position: absolute; + top: 50%; + transform: translate(0%, -50%); + margin: 0px; + padding: 0px; + z-index: -1000; + } +} + +main { + padding-left: 0.5em; + padding-right: 0.5em; +} +@media (min-width: 900px) { + main { + padding-top: 55px; + } +} + +section { + background-color: white; + padding: 10px; +} \ No newline at end of file diff --git a/srv/static/css/log.css b/srv/static/css/log.css new file mode 100644 index 0000000..aafb921 --- /dev/null +++ b/srv/static/css/log.css @@ -0,0 +1,6 @@ +/* style for standalone log-view */ + +html, body, main { + maring: 0px; + padding: 0px; +} \ No newline at end of file diff --git a/srv/static/css/specifics.css b/srv/static/css/specifics.css new file mode 100644 index 0000000..4f13d42 --- /dev/null +++ b/srv/static/css/specifics.css @@ -0,0 +1,190 @@ +/* style for page-specific elements */ + +/* icons of buttons within navigation */ +#global-nav-link { + background-image: url(../img/icon/database-white.svg); +} +#package-search-nav-link { + background-image: url(../img/icon/magnify-white.svg); +} +#package-details-nav-link { + background-image: url(../img/icon/package-variant-white.svg); +} +#build-action-nav-link { + background-image: url(../img/icon/cogs-white.svg); +} +#build-action-details-nav-link { + background-image: url(../img/icon/console-line-white.svg); +} + +/* elements for package search */ +#package-search-db { + height: 100px !important; +} +#package-search-form table input[type="text"], #package-search-form table select { + width: 100%; +} +#package-search-form table td, #package-search-form table th { + vertical-align: top; +} +#package-search-form table th { + text-align: right; + font-weight: normal; + font-size: 90%; +} +#package-search-section table, #build-action-section > table { + width: 100%; +} +#package-search-form input, #package-search-form select { + box-sizing: border-box; +} + +/* elements for build actions page */ +#build-action-form select[multiple="multiple"] { + height: 150px; +} +#build-actions-list-form { + margin-bottom: 1em; +} +#build-actions-list tbody td:first-child { + border-left: 10px solid #93bafb; +} +#build-action-settings { + width: fit-content; +} + +/* common styling for certain forms */ +.form-row { + margin-bottom: 5px; +} +.form-split-50 { + display: inline-block; + box-sizing: border-box; + width: 40%; +} +#build-actions-list-form fieldset input[type="button"], +#package-results-form fieldset input[type="button"] { + box-sizing: border-box; + height: 2.0em; + line-height: 0em; +} + +/* build action details */ +.streaming-link { + display: inline-block; + margin-left: 0.5em; +} +.streaming-link:hover { + text-decoration: none; +} +.package-details-table ul, .update-info-form ul { + list-style-type: none; + margin: 0px; + padding: 0px; +} +.update-info-form tbody tr:hover { + background-color: inherit; +} +.update-info-form tbody tr:nth-child(even) { + background-color: inherit; +} + +/* about dialog */ +#about-dialog { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + margin: auto; + box-sizing: border-box; + background-color: #f6f9fc; + border: 1px solid #999; + box-shadow: inset 0 1px 3px rgba(169, 169, 169, 0.7); + text-align: center; + z-index: 5000; +} +#about-text { + padding: 1em; +} +#about-text img { + width: 20em; + margin: 2em; +} +#about-dialog .close-button { + text-decoration: none; + background-color: #333; + border: 1px solid #999; + border-top: none; + border-right: none; + color: #fff; + padding: .3em; + float: right; + width: 1em; + height: 1em; + font-size: 1.5em; +} + +/* icons rendered via customrendering.js */ +.icon-button { + background-repeat: no-repeat; + background-size: contain; + padding-left: 25px; +} +.icon-link { + opacity: .5; +} +.icon-link:hover { + opacity: 1; + border-bottom: none!important; +} +.icon-link span { + display: inline-block; + width: 16px; + height: 16px; + background-size: 16px 16px; +} +.icon-magnify { + background-image: url(../img/icon/magnify.svg); +} +.icon-table-refresh { + background-image: url(../img/icon/table-refresh.svg); +} +.icon-restart { + background-image: url(../img/icon/restart.svg); +} +.icon-reload { + background-image: url(../img/icon/reload.svg); +} +.icon-plus { + background-image: url(../img/icon/plus.svg); +} +.icon-delete { + background-image: url(../img/icon/delete.svg); +} +.icon-stop { + background-image: url(../img/icon/stop.svg); +} +.icon-play { + background-image: url(../img/icon/play.svg); +} +.icon-dock-window { + background-image: url(../img/icon/dock-window.svg); +} +.icon-download { + background-image: url(../img/icon/download.svg); +} + +/* actions shown on top-right corner of build actions and build action details */ +.heading-actions { + float: right; + margin-bottom: -10px; + background: #ecf2f5; + border: 1px solid #bcd; + padding: 2px; +} +.heading-actions span { + margin-bottom: -5px; + width: 28px; + height: 28px; + background-size: 28px 28px; +} \ No newline at end of file diff --git a/srv/static/img/icon/chevron-left.svg b/srv/static/img/icon/chevron-left.svg new file mode 100644 index 0000000..13317b9 --- /dev/null +++ b/srv/static/img/icon/chevron-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/chevron-right.svg b/srv/static/img/icon/chevron-right.svg new file mode 100644 index 0000000..8e7175d --- /dev/null +++ b/srv/static/img/icon/chevron-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/close-box.svg b/srv/static/img/icon/close-box.svg new file mode 100644 index 0000000..28ca200 --- /dev/null +++ b/srv/static/img/icon/close-box.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/close-circle-outline.svg b/srv/static/img/icon/close-circle-outline.svg new file mode 100644 index 0000000..dad58cf --- /dev/null +++ b/srv/static/img/icon/close-circle-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/close.svg b/srv/static/img/icon/close.svg new file mode 100644 index 0000000..18691d7 --- /dev/null +++ b/srv/static/img/icon/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/cogs-white.svg b/srv/static/img/icon/cogs-white.svg new file mode 100644 index 0000000..5ac260b --- /dev/null +++ b/srv/static/img/icon/cogs-white.svg @@ -0,0 +1 @@ + diff --git a/srv/static/img/icon/cogs.svg b/srv/static/img/icon/cogs.svg new file mode 100644 index 0000000..e4f47c8 --- /dev/null +++ b/srv/static/img/icon/cogs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/console-line-white.svg b/srv/static/img/icon/console-line-white.svg new file mode 100644 index 0000000..f664ce4 --- /dev/null +++ b/srv/static/img/icon/console-line-white.svg @@ -0,0 +1 @@ + diff --git a/srv/static/img/icon/console-line.svg b/srv/static/img/icon/console-line.svg new file mode 100644 index 0000000..83ddbb2 --- /dev/null +++ b/srv/static/img/icon/console-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/console.svg b/srv/static/img/icon/console.svg new file mode 100644 index 0000000..77b5d13 --- /dev/null +++ b/srv/static/img/icon/console.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/content-save.svg b/srv/static/img/icon/content-save.svg new file mode 100644 index 0000000..bbd8d59 --- /dev/null +++ b/srv/static/img/icon/content-save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/database-search.svg b/srv/static/img/icon/database-search.svg new file mode 100644 index 0000000..0e6ac62 --- /dev/null +++ b/srv/static/img/icon/database-search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/database-white.svg b/srv/static/img/icon/database-white.svg new file mode 100644 index 0000000..a84e861 --- /dev/null +++ b/srv/static/img/icon/database-white.svg @@ -0,0 +1 @@ + diff --git a/srv/static/img/icon/database.svg b/srv/static/img/icon/database.svg new file mode 100644 index 0000000..3e9512e --- /dev/null +++ b/srv/static/img/icon/database.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/delete.svg b/srv/static/img/icon/delete.svg new file mode 100644 index 0000000..f9daefe --- /dev/null +++ b/srv/static/img/icon/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/dock-window.svg b/srv/static/img/icon/dock-window.svg new file mode 100644 index 0000000..15be877 --- /dev/null +++ b/srv/static/img/icon/dock-window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/download.svg b/srv/static/img/icon/download.svg new file mode 100644 index 0000000..67c9440 --- /dev/null +++ b/srv/static/img/icon/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/eye.svg b/srv/static/img/icon/eye.svg new file mode 100644 index 0000000..e3cdcf4 --- /dev/null +++ b/srv/static/img/icon/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/factory-white.svg b/srv/static/img/icon/factory-white.svg new file mode 100644 index 0000000..759fb10 --- /dev/null +++ b/srv/static/img/icon/factory-white.svg @@ -0,0 +1 @@ + diff --git a/srv/static/img/icon/factory.svg b/srv/static/img/icon/factory.svg new file mode 100644 index 0000000..1582b99 --- /dev/null +++ b/srv/static/img/icon/factory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/magnify-white.svg b/srv/static/img/icon/magnify-white.svg new file mode 100644 index 0000000..c2a563f --- /dev/null +++ b/srv/static/img/icon/magnify-white.svg @@ -0,0 +1 @@ + diff --git a/srv/static/img/icon/magnify.svg b/srv/static/img/icon/magnify.svg new file mode 100644 index 0000000..eb42571 --- /dev/null +++ b/srv/static/img/icon/magnify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/package-variant-white.svg b/srv/static/img/icon/package-variant-white.svg new file mode 100644 index 0000000..ad0190a --- /dev/null +++ b/srv/static/img/icon/package-variant-white.svg @@ -0,0 +1 @@ + diff --git a/srv/static/img/icon/package-variant.svg b/srv/static/img/icon/package-variant.svg new file mode 100644 index 0000000..1ca1671 --- /dev/null +++ b/srv/static/img/icon/package-variant.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/page-first.svg b/srv/static/img/icon/page-first.svg new file mode 100644 index 0000000..c683d5d --- /dev/null +++ b/srv/static/img/icon/page-first.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/page-last.svg b/srv/static/img/icon/page-last.svg new file mode 100644 index 0000000..b54ce9f --- /dev/null +++ b/srv/static/img/icon/page-last.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/pause.svg b/srv/static/img/icon/pause.svg new file mode 100644 index 0000000..37ed32d --- /dev/null +++ b/srv/static/img/icon/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/play-pause.svg b/srv/static/img/icon/play-pause.svg new file mode 100644 index 0000000..5b58b8a --- /dev/null +++ b/srv/static/img/icon/play-pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/play.svg b/srv/static/img/icon/play.svg new file mode 100644 index 0000000..87a70f2 --- /dev/null +++ b/srv/static/img/icon/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/plus-box-multiple.svg b/srv/static/img/icon/plus-box-multiple.svg new file mode 100644 index 0000000..aa57d4e --- /dev/null +++ b/srv/static/img/icon/plus-box-multiple.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/plus-thick.svg b/srv/static/img/icon/plus-thick.svg new file mode 100644 index 0000000..41a18f8 --- /dev/null +++ b/srv/static/img/icon/plus-thick.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/plus.svg b/srv/static/img/icon/plus.svg new file mode 100644 index 0000000..2c21839 --- /dev/null +++ b/srv/static/img/icon/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/power.svg b/srv/static/img/icon/power.svg new file mode 100644 index 0000000..343d1ba --- /dev/null +++ b/srv/static/img/icon/power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/reload.svg b/srv/static/img/icon/reload.svg new file mode 100644 index 0000000..71cad29 --- /dev/null +++ b/srv/static/img/icon/reload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/restart.svg b/srv/static/img/icon/restart.svg new file mode 100644 index 0000000..2d36f8a --- /dev/null +++ b/srv/static/img/icon/restart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/select-all.svg b/srv/static/img/icon/select-all.svg new file mode 100644 index 0000000..ddb5f28 --- /dev/null +++ b/srv/static/img/icon/select-all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/select-off.svg b/srv/static/img/icon/select-off.svg new file mode 100644 index 0000000..a3593e2 --- /dev/null +++ b/srv/static/img/icon/select-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/shape-circle-plus.svg b/srv/static/img/icon/shape-circle-plus.svg new file mode 100644 index 0000000..3b6dcb4 --- /dev/null +++ b/srv/static/img/icon/shape-circle-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/stop.svg b/srv/static/img/icon/stop.svg new file mode 100644 index 0000000..99d345d --- /dev/null +++ b/srv/static/img/icon/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/table-refresh-white.svg b/srv/static/img/icon/table-refresh-white.svg new file mode 100644 index 0000000..0818cba --- /dev/null +++ b/srv/static/img/icon/table-refresh-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/table-refresh.svg b/srv/static/img/icon/table-refresh.svg new file mode 100644 index 0000000..272ccf5 --- /dev/null +++ b/srv/static/img/icon/table-refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/timer-off-outline.svg b/srv/static/img/icon/timer-off-outline.svg new file mode 100644 index 0000000..b50ff61 --- /dev/null +++ b/srv/static/img/icon/timer-off-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/timer-outline.svg b/srv/static/img/icon/timer-outline.svg new file mode 100644 index 0000000..3fe0f49 --- /dev/null +++ b/srv/static/img/icon/timer-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/icon/undo-variant.svg b/srv/static/img/icon/undo-variant.svg new file mode 100644 index 0000000..5b3ec9c --- /dev/null +++ b/srv/static/img/icon/undo-variant.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/srv/static/img/logo.svg b/srv/static/img/logo.svg new file mode 100644 index 0000000..5f002a8 --- /dev/null +++ b/srv/static/img/logo.svg @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/srv/static/index.html b/srv/static/index.html new file mode 100644 index 0000000..04eafb9 --- /dev/null +++ b/srv/static/index.html @@ -0,0 +1,274 @@ + + + + + Build service + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+

Databases

+
+
+
+
+

Package search

+
+ + + + + + + + + + + + + + + +
Package name:Database: + +
Mode: + +
+ +
+
+
+
+
+
+ Actions +
+ + + +
+
+
+
+
+

Package details

+
+

No package selected. Use the "Search" to find and select a package.

+
+
+
+

+ Build actions + + Save state manually + Save state manually + +

+
+
+

Loading build actions ...

+
+
+ Modify selected actions +
+ + + + +
+
+
+
+
+ Start new build action +
+
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ Flags: +
+
+
+
+
+ Settings: +
+
+
+
+
+ Start condition: + + + + + + + +
+
+
+
+ + +
+
+
+
+
+

Build action details

+
+

No build action has been selected yet. Start or select a build action under "Actions" first.

+
+
+
+ + + \ No newline at end of file diff --git a/srv/static/js/ajaxhelper.js b/srv/static/js/ajaxhelper.js new file mode 100644 index 0000000..e9412ca --- /dev/null +++ b/srv/static/js/ajaxhelper.js @@ -0,0 +1,128 @@ +const apiPrefix = 'api/v0'; +let authError = false; + +/// \brief Makes an AJAX query with basic error handling. +function queryRoute(method, path, callback) +{ + const ajaxRequest = new XMLHttpRequest(); + ajaxRequest.onreadystatechange = function() { + if (this.readyState === 4) { + const status = this.status; + authError = status === 403; + switch (status) { + case 401: + return queryRoute(method, path, callback); + case 403: + return window.alert('Authentication failed. Try again.'); + default: + try { + return callback(this, status === 200); + } catch (e) { + window.alert('Unable to process server response: ' + e); + throw e; + } + } + } + }; + const args = [method, apiPrefix + path, true]; + if (authError) { + args.push('try', 'again'); + } + ajaxRequest.open(...args); + ajaxRequest.send(); + return ajaxRequest; +} + +/// \brief Makes an AJAX query for the specified form. +function startFormQuery(formId, handler) +{ + const form = document.getElementById(formId); + return queryRoute(form.method, form.getAttribute('action') + makeFormQueryParameter(form), handler); +} + +/// \brief Returns the query parameter for the specified \a form element. +function makeFormQueryParameter(form) +{ + const params = []; + const formElements = form.elements; + for (let i = 0, count = formElements.length; i != count; ++i) { + const formElement = formElements[i]; + if (formElement.disabled || formElement.style.display === 'none') { + continue; // if we disable a form element or hide it via CSS we also don't want to submit its data + } + const type = formElement.type; + if ((type === 'checkbox' || type === 'radio') && !formElement.checked) { + continue; + } + const name = formElement.name; + if (name === undefined || name.length < 1) { + continue; + } + else if (name === 'package-names') { + const packageNames = formElement.value.split(/[,\s]+/); + packageNames.forEach(function (packageName) { + if (packageName.length <= 0) { + return; + } + params.push(['package', encodeURIComponent(packageName)].join("=")); + }); + continue; + } + const values = []; + const options = formElement.options; + if (options !== undefined) { + for (let i = 0, end = options.length; i != end; ++i) { + const option = options[i]; + if (option.selected && option.dataset.ignore) { + continue; + } + const value = option.value; + if (option.selected && value !== undefined && value !== 'none' && value !== 'None') { + values.push(value); + } + } + } else if (formElement.value !== undefined) { + values.push(formElement.value); + } + values.forEach(function(value) { + params.push([encodeURIComponent(name), encodeURIComponent(value)].join("=")); + }); + } + if (params.length < 1) { + return ""; + } + return "?" + params.join("&"); +} + +function makeIdParams(ids) +{ + if (!Array.isArray(ids)) { + ids = [ids]; + } + return ids.map(id => 'id=' + encodeURIComponent(id)).join('&'); +} + +/// \brief Shows an alert for the specified AJAX request. +function showAjaxError(xhr, action) +{ + let errorMessage; + try { + errorMessage = JSON.parse(xhr.responseText).error; + } catch (e) { + errorMessage = xhr.responseText; + } + if (!errorMessage) { + errorMessage = 'unknown error'; + } + window.alert('Unable to ' + action + ': ' + errorMessage); +} + +/// \brief Returns whether the specified AJAX request failed and shows an alert it it did. +function checkForAjaxError(xhr, action) +{ + if (xhr.status === 200) { + return false; + } + showAjaxError(xhr, action); + return true; +} \ No newline at end of file diff --git a/srv/static/js/buildactionspage.js b/srv/static/js/buildactionspage.js new file mode 100644 index 0000000..7fc5e84 --- /dev/null +++ b/srv/static/js/buildactionspage.js @@ -0,0 +1,891 @@ +function initBuildActionsForm() +{ + const buildActionsForm = document.getElementById('build-action-form'); + if (buildActionsForm.dataset.initialized) { + return true; + } + queryBuildActions(); + handleBuildActionTypeChange(); + buildActionsForm.dataset.initialized = true; + return true; +} + +function queryBuildActions() +{ + queryRoute('GET', '/build-action', showBuildActions); + return true; +} + +function queryBuildActionDetails(ids) +{ + queryRoute('GET', '/build-action/details?' + makeIdParams(ids), showBuildActionDetails); + return true; +} + +function cloneBuildAction(ids) +{ + queryRoute('POST', '/build-action/clone?' + makeIdParams(ids), function (xhr, success) { + if (!success) { + return showAjaxError(xhr, 'clone build action'); + } + const cloneIDs = JSON.parse(xhr.responseText); + if (cloneIDs.length === 1 && typeof cloneIDs[0] === 'number') { + if (window.confirm('Build action cloned as ' + cloneIDs[0] + '. Show cloned action?')) { + queryBuildActionDetails(cloneIDs[0]); + } + } else { + window.alert('Build action(s) have been cloned.'); + } + }); + return true; +} + +function deleteBuildAction(ids) +{ + queryRoute('DELETE', '/build-action?' + makeIdParams(ids), function (xhr, success) { + success ? window.alert('Build action has been deleted.') : showAjaxError(xhr, 'delete build action'); + }); + return true; +} + +function stopBuildAction(ids) +{ + queryRoute('POST', '/build-action/stop?' + makeIdParams(ids), function (xhr, success) { + success ? window.alert('Build action has been stopped.') : showAjaxError(xhr, 'stop build action'); + }); + return true; +} + +function startBuildAction(ids) +{ + queryRoute('POST', '/build-action/start?' + makeIdParams(ids), function (xhr, success) { + success ? window.alert('Build action has been started.') : showAjaxError(xhr, 'start build action'); + }); + return true; +} + +function selectOptions(selectElement, values) +{ + Array.from(selectElement.options).forEach(function(option) { + option.selected = values.includes(option.value); + }); +} + +function prepareBuildActionBasedOnExistingOne(existingBuildAction) +{ + if (!globalInfo) { + window.alert('Global info needs to be retrieved first.'); + return; + } + const typeId = existingBuildAction.type; + const typeInfo = globalInfo.buildActionTypes[typeId]; + const typeName = typeInfo.type; + document.getElementById('build-action-task-none').selected = true; + document.getElementById('build-action-type').value = typeName; + handleBuildActionPresetChange(); + handleBuildActionTypeChange(); + document.getElementById('build-action-directory').value = existingBuildAction.directory; + document.getElementById('build-action-package-names').value = existingBuildAction.packageNames.join(', '); + selectOptions(document.getElementById('build-action-source-repo'), existingBuildAction.sourceDbs); + selectOptions(document.getElementById('build-action-destination-repo'), existingBuildAction.destinationDbs); + const presentFlags = existingBuildAction.flags; + typeInfo.flags.forEach(function (flag) { + const input = document.getElementById('build-action-' + typeName + '-' + flag.param); + input.checked = parseInt(input.dataset.id) & presentFlags; + }); + typeInfo.settings.forEach(function (setting) { + document.getElementById('build-action-' + typeName + '-' + setting.param).value = existingBuildAction.settings[setting.param] || ''; + }); +} + +function handleBuildActionTypeChange() +{ + if (!globalInfo) { + return; + } + const buildActionType = document.getElementById('build-action-type').value; + const typeInfo = globalInfo.buildActionTypesByParam[buildActionType]; + document.getElementById('build-action-directory').disabled = !typeInfo.directory; + document.getElementById('build-action-source-repo').disabled = !typeInfo.sourceDb; + document.getElementById('build-action-destination-repo').disabled = !typeInfo.destinationDb; + document.getElementById('build-action-package-names').disabled = !typeInfo.packageNames; + Array.from(document.getElementById('build-action-flags').getElementsByTagName('label')).forEach(function(label) { + const relevantType = label.dataset.relevantType; + if (!relevantType) { + return; + } + label.control.style.display = label.style.display = buildActionType === relevantType ? 'inline' : 'none'; + }); + Array.from(document.getElementById('build-action-settings').getElementsByTagName('tr')).forEach(function(tr) { + const relevantType = tr.dataset.relevantType; + if (!relevantType) { + return; + } + const isRelevant = buildActionType === relevantType; + tr.style.display = isRelevant ? 'table-row' : 'none'; + Array.from(tr.getElementsByTagName('input')).forEach(function(input) { + input.style.display = isRelevant ? 'inline' : 'none'; + }); + }); +} + +function handleBuildActionPresetChange() +{ + if (!globalInfo) { + return; + } + const task = document.getElementById('build-action-task').value; + const taskInfo = globalInfo.presets.tasks[task]; + const taskInfoElement = getAndEmptyElement('build-action-task-info'); + const actionSelect = document.getElementById('build-action-type'); + if (!taskInfo) { + actionSelect.disabled = false; + taskInfoElement.style.fontStyle = 'italic'; + taskInfoElement.appendChild(document.createTextNode('Start a single action (no predefined task selected)')); + handleBuildActionTypeChange(); + return; + } + actionSelect.disabled = true; + taskInfoElement.style.fontStyle = 'normal'; + taskInfoElement.appendChild(document.createTextNode(taskInfo.desc || taskInfo.name)); + document.getElementById('build-action-directory').disabled = false; + document.getElementById('build-action-source-repo').disabled = true; + document.getElementById('build-action-destination-repo').disabled = true; + document.getElementById('build-action-package-names').disabled = false; + Array.from(document.getElementById('build-action-flags').getElementsByTagName('label')).forEach(function(label) { + label.control.style.display = label.style.display = 'none'; + }); + Array.from(document.getElementById('build-action-settings').getElementsByTagName('tr')).forEach(function(tr) { + tr.style.display = 'none'; + Array.from(tr.getElementsByTagName('input')).forEach(function(input) { + input.style.display = 'none'; + }); + }); +} + +function renderBuildActionActions(actionValue, buildAction, refresh) +{ + const container = document.createElement('span'); + if (!refresh) { + container.className = 'table-row-actions'; + } + const id = buildAction.id; + container.appendChild(renderIconLink(refresh ? 'table-refresh' : 'magnify', buildAction, function() { + queryBuildActionDetails(id); + return false; + }, refresh ? 'Refresh details table' : 'Show details', undefined, '#build-action-details-section&' + id)); + container.appendChild(renderIconLink('restart', buildAction, function() { + if (window.confirm('Do you really want to clone/restart action ' + id + '?')) { + cloneBuildAction(id); + } + }, 'Clone ' + id)); + container.appendChild(renderIconLink('plus', buildAction, function() { + prepareBuildActionBasedOnExistingOne(buildAction); + switchToBuildActions(); + }, 'Create new build action based on ' + id)); + container.appendChild(renderIconLink('delete', buildAction, function() { + if (window.confirm('Do you really want to delete action ' + id + '?')) { + deleteBuildAction(id); + } + }, 'Delete ' + id)); + if (buildAction.status !== 0 && buildAction.status !== 4) { + container.appendChild(renderIconLink('stop', buildAction, function() { + if (window.confirm('Do you really want to stop/decline action ' + id + '?')) { + stopBuildAction(id); + } + }, 'Stop/decline ' + id)); + } + if (buildAction.status === 0) { + container.appendChild(renderIconLink('play', buildAction, function() { + if (window.confirm('Do you really want to start action ' + id + '?')) { + startBuildAction(id); + } + }, 'Start ' + id)); + } + return container; +} + +function showBuildActions(ajaxRequest) +{ + if (!window.globalInfo) { + window.functionsPostponedUntilGlobalInfo.push(showBuildActions.bind(this, ...arguments)); + return; + } + const buildActionsList = getAndEmptyElement('build-actions-list'); + if (ajaxRequest.status !== 200) { + buildActionsList.appendChild(document.createTextNode('Unable to load build actions: ' + ajaxRequest.responseText)); + buildActionsList.appendChild(renderReloadButton(queryBuildActions)); + return; + } + const responseJson = JSON.parse(ajaxRequest.responseText); + if (!Array.isArray(responseJson)) { + buildActionsList.appendChild(document.createTextNode('Unable to load build actions: response is no array')); + buildActionsList.appendChild(renderReloadButton(queryBuildActions)); + return; + } + + // compute clusters of actions which run in sequence + const buildActionsById = {}; + responseJson.forEach(function(buildAction) { + buildActionsById[buildAction.id] = buildAction; + }); + responseJson.forEach(function(buildAction) { + if (!Array.isArray(buildAction.startAfter)) { + return; + } + const currentActionId = buildAction.id; + let clusterId = buildAction._clusterId; + let clusterActions = buildAction._clusterActions; + buildAction.startAfter.forEach(function(previousActionId) { + const previousAction = buildActionsById[previousActionId]; + if (!previousAction) { + return; + } + const previousActionClusterId = previousAction._clusterId; + const previousClusterActions = previousAction._clusterActions; + if (clusterId !== undefined) { + if (previousActionClusterId === undefined) { + clusterActions.splice(clusterActions.indexOf(buildAction), 0, previousAction); + previousAction._clusterId = clusterId; + previousAction._clusterActions = clusterActions; + } else if (previousActionClusterId !== clusterId) { + clusterActions.splice(clusterActions.indexOf(buildAction), 0, ...previousClusterActions); + previousClusterActions.forEach(function(previousClusterAction) { + previousClusterAction._clusterId = clusterId; + previousClusterAction._clusterActions = clusterActions; + }); + } + } else if (previousActionClusterId !== undefined) { + clusterId = buildAction._clusterId = previousAction._clusterId; + clusterActions = buildAction._clusterActions = previousAction._clusterActions; + clusterActions.push(buildAction); + } else { + clusterId = buildAction._clusterId = previousAction._clusterId = currentActionId; + clusterActions = buildAction._clusterActions = previousAction._clusterActions = [previousAction, buildAction]; + } + }); + }); + + // compute cluster offsets and make up a color for each cluster + responseJson.forEach(function(buildAction) { + const clusterActions = buildAction._clusterActions; + const clusterOffset = buildAction._clusterOffset = clusterActions ? clusterActions.indexOf(buildAction) : 0; + buildAction._cc = buildAction.created + ':' + clusterOffset.toString().padStart(4, '0'); + if (!clusterActions) { return; } + if (buildAction._clusterColor) { + return; + } + const clusterColor = '#' + Math.floor((Math.abs(Math.sin(buildAction._clusterId) * 16777215)) % 16777215).toString(16); + clusterActions.forEach(a => a._clusterColor = clusterColor); + }); + + // define function to toggle a row + const toggleRow = function(trElement) { + trElement.cells[0].getElementsByTagName('input')[0].click(); + }; + + // render the table + const container = renderTableFromJsonArray({ + rows: responseJson, + rowsPerPage: 10, + columnHeaders: ['', 'ID', 'Task', 'Type', 'Status', 'Result', 'Created', 'Started', 'Runtime', 'Directory', 'Source repo', 'Destination repo', 'Packages', 'Actions'], + columnAccessors: ['checkbox', 'id', 'taskName', 'type', 'status', 'result', 'created', 'started', 'finished', 'directory', 'sourceDbs', 'destinationDbs', 'packageNames', 'actions'], + columnSortAccessors: [null, null, null, null, null, null, '_cc'], + customRenderer: { + checkbox: function(value, row) { + return renderCheckBoxForTableRow(value, row, function(row) { + return row.name; + }); + }, + actions: renderBuildActionActions, + id: function(value, row) { + return renderLink(value, row, function() { + queryBuildActionDetails(row.id); + return false; + }, 'Show details', undefined, '#build-action-details-section&' + row.id); + }, + taskName: function (value) { + if (!value) { + return renderNoneInGrey(); + } + return document.createTextNode(getProperty(globalInfo.presets.tasks[value], 'name', value)); + }, + status: function(value) { + return document.createTextNode(getProperty(globalInfo.buildActionStates[value], 'name', 'Invalid/unknown')); + }, + result: function(value) { + return renderNoneInGrey(getProperty(globalInfo.buildActionResults[value], 'name', 'Invalid/unknown')); + }, + type: function(value) { + return document.createTextNode(getProperty(globalInfo.buildActionTypes[value], 'name', 'Invalid/debugging')); + }, + created: renderShortTimeStamp, + started: renderShortTimeStamp, + finished: function (value, row) { + return renderTimeSpan(row.started, value); + }, + sourceDbs: renderArrayElidedAsCommaSeparatedString, + destinationDbs: renderArrayElidedAsCommaSeparatedString, + packageNames: function(value) { + return renderNoneInGrey(!Array.isArray(value) || value.length <= 0 ? 'none' : value.length); + }, + note: function(rows) { + const note = document.createElement('p'); + note.appendChild(document.createTextNode(rows.length + ' build actions present ')); + note.appendChild(renderReloadButton(queryBuildActions)); + return note; + }, + }, + rowHandler: function(row, tr) { + // toggle checkbox on click; toggle checkbox for whole cluster when clicking edge + const dataset = tr.dataset; + dataset.id = row.id; + if (!dataset.initialized) { + dataset.initialized = true; + tr.onclick = function(e) { + if (e.defaultPrevented || e.target.type === 'checkbox') { return; } + const dataset = tr.dataset; + const clusterClass = e.clientX <= 30 ? dataset.clusterClass : undefined; + if (!clusterClass) { return toggleRow(tr); } + const relevantAction = buildActionsById[dataset.id]; + const newSelected = !relevantAction.selected; + const clusterActions = relevantAction._clusterActions || []; + clusterActions.forEach(clusterAction => clusterAction.selected = newSelected); + Array.from(tr.parentElement.getElementsByClassName(clusterClass)).forEach(toggleRow); + }; + } + + // apply highlighting of clusters + const oldClusterClass = dataset.clusterClass; + const clusterId = row._clusterId; + const className = clusterId === undefined ? undefined : 'ba-tr-cluster-' + clusterId; + if (oldClusterClass === className) { + return; + } + if (oldClusterClass) { + delete tr.dataset.clusterClass; + tr.classList.remove(oldClusterClass); + tr.firstChild.style.borderLeft = ''; + } + if (clusterId === undefined) { + return; + } + tr.dataset.clusterClass = className; + tr.classList.add(className); + tr.firstChild.style.borderLeft = '10px solid ' + row._clusterColor; + if (oldClusterClass) { + return; + } + const updateRowHighlighting = function(row, highlight) { + const clusterClass = row.dataset.clusterClass; + if (!clusterClass) { + return; + } + const fn = highlight ? 'add' : 'remove'; + Array.from(document.getElementsByClassName(clusterClass)).forEach(function(tr) { + tr.classList[fn]('table-row-highlighted'); + }); + } + tr.addEventListener('mouseover', function() { updateRowHighlighting(this, true); }); + tr.addEventListener('mouseout', function() { updateRowHighlighting(this, false); }); + }, + }); + container.table.sort('_cc', true); + buildActionsList.appendChild(container); +} + +function switchToBuildActionDetails(buildActionIds) +{ + sections['build-action-details'].state.ids = buildActionIds; + window.preventSectionInitializer = true; + if (!Array.isArray(buildActionIds) || buildActionIds.length === 0) { + window.location.hash = '#build-action-details-section'; + } else { + window.location.hash = '#build-action-details-section&' + encodeURIComponent(buildActionIds.join(',')); + } + window.preventSectionInitializer = false; +} + +function switchToBuildActions() +{ + window.preventSectionInitializer = true; + window.location.hash = '#build-action-section'; + window.preventSectionInitializer = false; +} + +function showBuildActionDetails(ajaxRequest) +{ + if (!window.globalInfo) { + window.functionsPostponedUntilGlobalInfo.push(showBuildActionDetails.bind(this, ...arguments)); + return; + } + if (checkForAjaxError(ajaxRequest, 'show build action')) { + return; + } + const responseJSON = JSON.parse(ajaxRequest.responseText); + return showBuildActionDetails2(Array.isArray(responseJSON) ? responseJSON : [responseJSON]); +} + +function showBuildActionDetails2(buildActions) +{ + const buildActionResults = getAndEmptyElement('build-action-results'); + let buildActionActions = getAndEmptyElement('build-action-details-actions'); + buildActions.forEach(function (buildActionDetails) { + if (!buildActionActions) { + buildActionActions = document.createElement('span'); + buildActionActions.className = 'heading-actions'; + buildActionResults.appendChild(buildActionActions); + } + buildActionResults.appendChild(renderBuildActionDetailsTable(buildActionDetails)); + buildActionActions.appendChild(renderBuildActionActions(undefined, buildActionDetails, true)); + buildActionActions = undefined; + }); + switchToBuildActionDetails(buildActions.map(buildAction => buildAction.id)); +} + +function renderBuildActionDetailsTable(buildActionDetails) +{ + return renderTableFromJsonObject({ + data: buildActionDetails, + displayLabels: ['ID', 'Task', 'Type', 'Status', 'Result', 'Result data', 'Created', 'Started', 'Finished', 'Start after', 'Directory', 'Source repo', 'Destination repo', 'Packages', 'Flags', 'Settings', 'Log files', 'Artefacts', 'Output'], + fieldAccessors: ['id', 'taskName', 'type', 'status', 'result', 'resultData', 'created', 'started', 'finished', 'startAfter', 'directory', 'sourceDbs', 'destinationDbs', 'packageNames', 'flags', 'settings', 'logfiles', 'artefacts', 'output'], + customRenderer: { + taskName: function (value) { + if (!value) { + return renderNoneInGrey(); + } + return document.createTextNode(getProperty(globalInfo.presets.tasks[value], 'name', value)); + }, + status: function(value) { + return document.createTextNode(getProperty(globalInfo.buildActionStates[value], 'name', 'Invalid/unknown')); + }, + result: function(value) { + return renderNoneInGrey(getProperty(globalInfo.buildActionResults[value], 'name', 'Invalid/unknown')); + }, + type: function(value) { + return document.createTextNode(getProperty(globalInfo.buildActionTypes[value], 'name', 'Invalid/debugging')); + }, + created: renderTimeStamp, + started: renderTimeStamp, + finished: renderTimeStamp, + startAfter: renderArrayAsCommaSeparatedString, + sourceDbs: renderArrayAsCommaSeparatedString, + destinationDbs: renderArrayAsCommaSeparatedString, + packageNames: renderArrayAsCommaSeparatedString, + flags: function(value, row) { + const flagNames = []; + const typeInfo = globalInfo.buildActionTypes[row.type]; + typeInfo.flags.forEach(function(flag) { + if (flag.id & value) { + flagNames.push(flag.name); + } + }); + return renderArrayAsCommaSeparatedString(flagNames); + }, + settings: function(value, row) { + const typeInfo = globalInfo.buildActionTypes[row.type]; + if (typeInfo.settingNames.length === 0) { + return renderNoneInGrey(); + } + return renderTableFromJsonObject({ + data: value, + displayLabels: typeInfo.settingNames, + fieldAccessors: typeInfo.settingParams, + defaultRenderer: function (arg1, arg2, arg3) { + return renderNoneInGrey(arg1, arg2, arg3, 'default/none'); + }, + }); + }, + resultData: function(value, row) { + switch(value.index) { + case 3: // update info + const formElement = document.createElement('form'); + formElement.className = 'update-info-form'; + formElement.appendChild(renderTableFromJsonObject({ + data: value.data, + relatedRow: row, + displayLabels: ['Version updates', 'Package updates', 'Downgrades', 'Orphans'], + fieldAccessors: ['versionUpdates', 'packageUpdates', 'downgrades', 'orphans'], + customRenderer: { + orphans: renderOrphanPackage, + versionUpdates: renderUpdateOrDowngrade, + packageUpdates: renderUpdateOrDowngrade, + downgrades: renderUpdateOrDowngrade, + }, + })); + const addSelectedInput = document.createElement('input'); + addSelectedInput.type = 'button'; + addSelectedInput.value = 'Add selected packages for new build action'; + addSelectedInput.onclick = function () { + const buildActionForm = document.getElementById('build-action-form'); + const packageNamesTextArea = buildActionForm['package-names']; + const elements = formElement.elements; + const packageNames = []; + for (let i = 0, count = elements.length; i != count; ++i) { + const element = elements[i]; + if (element.type === 'checkbox' && element.checked) { + packageNames.push(element.value); + } + } + if (packageNamesTextArea.value !== '') { + packageNamesTextArea.value += ' '; + } + packageNamesTextArea.value += packageNames.join(' '); + }; + formElement.appendChild(addSelectedInput); + return formElement; + case 4: // build preparation info + return renderTableFromJsonObject({ + data: value.data, + displayLabels: ['Error', 'Warnings', 'Database config', 'Batches', 'Cyclic leftovers', 'Build data'], + fieldAccessors: ['error', 'warnings', 'dbConfig', 'batches', 'cyclicLeftovers', 'buildData'], + customRenderer: { + buildData: renderBuildPreparationResultData, + cyclicLeftovers: renderArrayAsCommaSeparatedString, + batches: function(batch) { + return renderCustomList(batch, renderArrayAsCommaSeparatedString); + }, + }, + }); + case 7: // reporitory problems + const container = document.createElement('div'); + container.className = 'repo-problems'; + for (const [database, problems] of Object.entries(value.data)) { + const table = renderTableFromJsonArray({ + rows: problems, + columnHeaders: ['Related package', 'Problem description'], + columnAccessors: ['pkg', 'desc'], + customRenderer: { + note: problems.length + ' problems in reporitory ' + database, + desc: function(value) { + switch(value.index) { + case 1: + return renderTableFromJsonObject({ + data: value.data, + displayLabels: ['Missing dependencies', 'Missing libraries'], + fieldAccessors: ['deps', 'libs'], + customRenderer: { + deps: renderDependency, + }, + }); + default: + return renderStandardTableCell(value.data); + } + }, + }, + }); + table.table.sort('pkg', false); + container.appendChild(table); + } + return container; + default: + return renderStandardTableCell(value.data); + } + }, + logfiles: renderBuildActionLogFiles, + artefacts: renderBuildActionArtefacts, + output: function(value, row) { + const isFinished = row.status === 4; + if (!value && isFinished) { + return renderNoneInGrey(); + } + const targetElement = document.createElement('div'); + if (isFinished) { + const terminal = makeTerminal(); + setupTerminalLater(terminal, targetElement, value); + return targetElement; + } + const streamingSetup = setupTerminalForStreaming({ + id: 'output-' + row.id, + targetElement: targetElement, + path: '/build-action/output?id=' + encodeURIComponent(row.id) + '&offset=' + encodeURIComponent(value.length), + }); + streamingSetup.startStreaming(); + const terminal = streamingSetup.terminal(); + terminal.setOption('convertEol', true); + terminal.write(value); + return streamingSetup.elements; + }, + }, + }); +} + +function setupTerminalForStreaming(args) +{ + const id = args.id; // a page-unique ID to make up DOM element IDs as needed + const targetElement = args.targetElement; // the DOM element to stream contents into + const path = args.path; // the path to GET contents from via streamRouteIntoTerminal() + let terminal; + let ajaxRequest; + return { + elements: [targetElement], + startStreaming: function () { + terminal = makeTerminal(); + ajaxRequest = streamRouteIntoTerminal('GET', path, terminal); + setupTerminalLater(terminal, targetElement); + }, + stopStreaming: function () { + ajaxRequest.abort(); + ajaxRequest = undefined; + terminal.dispose(); + terminal = undefined; + }, + isStreaming: function () { + return ajaxRequest !== undefined; + }, + terminal: function () { + return terminal; + }, + }; +} + +function fillBuildActionFromPackageSearch() +{ + const packageNamesTextArea = document.getElementById('build-action-form')['package-names']; + const data = getFormTableData('package-results-form'); + if (data === undefined) { + return; + } + packageNamesTextArea.value = getSelectedRowProperties(data, 'name').join(' '); + location.hash = '#build-action-section'; +} + +function submitBuildAction() +{ + startFormQuery('build-action-form', handleBuildActionResponse); +} + +function handleBuildActionResponse(ajaxRequest) +{ + const results = getAndEmptyElement('build-action-results'); + if (ajaxRequest.status !== 200) { + results.appendChild(document.createTextNode('unable to create build action: ' + ajaxRequest.responseText)); + switchToBuildActionDetails(); + return; + } + showBuildActionDetails(ajaxRequest); +} + +function renderBuildActionLogFiles(array, obj) +{ + return renderCustomList(array, function(arrayElement) { + const params = 'id=' + encodeURIComponent(obj.id) + '&name=' + encodeURIComponent(arrayElement); + const logFilePath = '/build-action/logfile?' + params; + const newWindowPath = 'log.html#' + params; + const targetElement = document.createElement('div'); + const streamingSetup = setupTerminalForStreaming({ + id: 'logfile-' + obj.id + '-' + arrayElement, + targetElement: targetElement, + path: logFilePath, + }); + const basicElements = streamingSetup.elements; + const openInNewWindowLinkElement = renderIconLink('dock-window', obj, function() { window.open(newWindowPath); }, 'Open in new window'); + const downloadLinkElement = renderIconLink('download', obj, function() { window.open(apiPrefix + logFilePath); }, 'Download log'); + const stopStreamingLinkElement = renderIconLink('stop', obj, function() { + if (!streamingSetup.isStreaming()) { + return; + } + streamingSetup.stopStreaming(); + targetElement.style.display = stopStreamingLinkElement.style.display = 'none'; + emptyDomElement(targetElement); + }, 'Close log'); + const startStreamingLinkElement = renderLink(arrayElement, obj, function() { + if (streamingSetup.isStreaming()) { + return; + } + emptyDomElement(targetElement); + streamingSetup.startStreaming(); + targetElement.style.display = 'block'; + stopStreamingLinkElement.style.display = 'inline-block'; + }, 'Show log file', apiPrefix + logFilePath); + targetElement.style.display = stopStreamingLinkElement.style.display = 'none'; + [downloadLinkElement, stopStreamingLinkElement].forEach(function(element) { + element.classList.add('streaming-link'); + }); + return [startStreamingLinkElement, stopStreamingLinkElement, downloadLinkElement, openInNewWindowLinkElement, ...basicElements]; + }); +} + +function renderBuildActionArtefacts(array, obj) +{ + return renderCustomList(array, function(arrayElement) { + const path = apiPrefix + '/build-action/artefact?id=' + encodeURIComponent(obj.id) + '&name=' + encodeURIComponent(arrayElement); + return renderLink(arrayElement, obj, function() { + window.open(path); + }, 'Download artefact', path); + }); +} + +function isBuildActionSourceAur(buildActionInfo) +{ + const sourceDbs = typeof buildActionInfo === 'object' ? buildActionInfo.sourceDbs : undefined; + return Array.isArray(sourceDbs) && sourceDbs.length === 1 && sourceDbs[0] == 'aur'; +} + +function renderUpdateInfoWithCheckbox(id, packageName, versionInfo, sourceFromAur) +{ + const inputElement = document.createElement('input'); + inputElement.type = 'checkbox'; + inputElement.id = id; + inputElement.value = packageName; + const labelElement = document.createElement('label'); + labelElement.htmlFor = id; + if (sourceFromAur) { + const packageNameLink = document.createElement('a'); + packageNameLink.href = 'https://aur.archlinux.org/packages/' + packageName; + packageNameLink.target = '_blank'; + packageNameLink.appendChild(document.createTextNode(packageName)); + labelElement.appendChild(packageNameLink); + labelElement.appendChild(document.createTextNode(': ' + versionInfo)); + } else { + labelElement.appendChild(document.createTextNode(packageName + ': ' + versionInfo)); + } + return [inputElement, labelElement]; +} + +function renderPackageList(packageList) +{ + return renderCustomList(packageList, renderPackage); +} + +function renderBuildPreparationBuildData(buildDataForPackage) +{ + return renderTableFromJsonObject({ + data: buildDataForPackage, + displayLabels: ['Error', 'Warnings', 'Has source', 'Source directory', 'Original/local source directory', 'New packages', 'Existing packages', 'Specified index'], + fieldAccessors: ['error', 'warnings', 'hasSource', 'sourceDirectory', 'originalSourceDirectory', 'packages', 'existingPackages', 'specifiedIndex'], + customRenderer: { + packages: renderPackageList, + existingPackages: renderPackageList, + }, + }); +} + +function makeVersionsString(packages) +{ + const versions = packages.map(package => package.version); + if (versions.length === 0) { + return '?'; + } else if (versions.length === 1) { + return versions[0]; + } else { + return '(' + versions.join(', ') + ')'; + } +} + +function renderBuildPreparationResultData(buildPreparationData) +{ + const elements = []; + for (const [packageName, buildDataForPackage] of Object.entries(buildPreparationData)) { + const heading = document.createElement('h4'); + let table; + heading.className = 'compact-heading'; + heading.appendChild(renderLink(packageName, undefined, function() { + if (table === undefined) { + table = renderBuildPreparationBuildData(buildDataForPackage); + heading.insertAdjacentElement('afterEnd', table); + } else { + table.style.display = table.style.display === 'none' ? 'table' : 'none'; + } + })); + const versionSpan = document.createElement('span'); + const newVersions = makeVersionsString(buildDataForPackage.packages); + const existingVersions = makeVersionsString(buildDataForPackage.existingPackages); + versionSpan.style.fontWeight = 'lighter'; + versionSpan.style.float = 'right'; + versionSpan.appendChild(document.createTextNode(' ' + existingVersions + ' → ' + newVersions)); + heading.appendChild(versionSpan); + elements.push(heading); + } + return elements; +} + +function renderOrphanPackage(value, obj, level, row) +{ + return renderCustomList(value, function(package) { + const packageName = package.name; + return renderUpdateInfoWithCheckbox( + 'update-info-checkbox-' + packageName + '-' + package.version, + packageName, + package.version + ); + return document.createTextNode(); + }, function(package1, package2) { + return package1.name.localeCompare(package2.name); + }); +} + +function renderUpdateOrDowngrade(value, obj, level, row) +{ + const sourceFromAur = isBuildActionSourceAur(row); + return renderCustomList(value, function(updateInfo) { + const oldVersion = updateInfo.oldVersion; + const newVersion = updateInfo.newVersion; + const packageName = oldVersion.name; + return renderUpdateInfoWithCheckbox( + 'update-info-checkbox-' + packageName + '-' + oldVersion.version + '-' + newVersion.version, + packageName, + oldVersion.version + ' → ' + newVersion.version, + sourceFromAur + ); + }, function(updateInfo1, updateInfo2) { + return updateInfo1.oldVersion.name.localeCompare(updateInfo2.oldVersion.name); + }); +} + +function triggerToolbarAction(toolbarElement) +{ + const confirmQuestion = toolbarElement.dataset.confirmation; + if (confirmQuestion !== undefined && !window.confirm(confirmQuestion)) { + return false; + } + toolbarElement.disabled = true; + queryRoute(toolbarElement.dataset.method, toolbarElement.dataset.action, function(ajaxRequest) { + window.alert(ajaxRequest.responseText); + toolbarElement.disabled = false; + }); + return false; +} + +function deleteSelectedActions() +{ + const data = getFormTableData('build-actions-list'); + if (data === undefined) { + return; + } + const ids = getSelectedRowProperties(data, 'id'); + if (ids.length && window.confirm('Do you really want to delete the build action(s) ' + ids.join(', ') + '?')) { + deleteBuildAction(ids); + } +} + +function showSelectedActions() +{ + const data = getFormTableData('build-actions-list'); + if (data === undefined) { + return; + } + const ids = getSelectedRowProperties(data, 'id'); + if (ids.length) { + queryBuildActionDetails(ids); + } +} + +function initBuildActionDetails(sectionElement, sectionData, newHashParts) +{ + const currentBuildActionIds = sectionData.state.ids; + const hasCurrentlyBuildActions = Array.isArray(currentBuildActionIds) && currentBuildActionIds.length !== 0; + if (!newHashParts.length) { + if (hasCurrentlyBuildActions) { + window.preventHandlingHashChange = true; + window.location.hash = '#build-action-details-section&' + encodeURIComponent(currentBuildActionIds.join(',')); + window.preventHandlingHashChange = false; + } + return true; + } + const newBuildActionIds = newHashParts[0].split(','); + if (!hasCurrentlyBuildActions || newBuildActionIds.some(id => !currentBuildActionIds.find(currentId => id == currentId))) { // possible type conversion wanted + queryBuildActionDetails(newBuildActionIds); + } + return true; +} \ No newline at end of file diff --git a/srv/static/js/customrendering.js b/srv/static/js/customrendering.js new file mode 100644 index 0000000..49f096d --- /dev/null +++ b/srv/static/js/customrendering.js @@ -0,0 +1,56 @@ +/// \brief Renders a dependency object. +function renderDependency(value) +{ + if (value.length < 1) { + return renderArrayAsCommaSeparatedString(value); + } + const list = document.createElement('ul'); + list.className = 'dependency-list'; + value.forEach(function (dependency) { + const item = document.createElement('li'); + let res = dependency.name; + if (dependency.version) { + const modes = [undefined, undefined, '=', '>=', '<=', '>', '<']; + const mode = modes[dependency.mode]; + if (mode !== undefined) { + res += mode + dependency.version; + } + } + item.appendChild(document.createTextNode(res)); + if (dependency.description) { + const descriptionSpan = document.createElement('span'); + descriptionSpan.appendChild(document.createTextNode(' - ' + dependency.description)); + descriptionSpan.style.fontStyle = 'italics'; + item.appendChild(descriptionSpan); + } + list.appendChild(item); + }); + return list; +} + +/// \brief Renders a "Reload" button invoking the specified \a handler when clicked. +function renderReloadButton(handler) +{ + const reloadButton = document.createElement('button'); + reloadButton.className = 'icon-button icon-reload'; + reloadButton.type = 'button'; + reloadButton.onclick = handler; + reloadButton.appendChild(document.createTextNode('Reload')); + return reloadButton; +} + +/// \brief Renders an icon. +function renderIcon(iconName) +{ + const icon = document.createElement('span'); + icon.className = 'icon icon-' + iconName; + return icon; +} + +/// \brief Renders an icon link which will invoke the specified \a handler when clicked. +function renderIconLink(value, row, handler, tooltip, href, middleClickHref) +{ + const link = renderLink(renderIcon(value), row, handler, tooltip, href, middleClickHref); + link.className = 'icon-link'; + return link; +} \ No newline at end of file diff --git a/srv/static/js/genericrendering.js b/srv/static/js/genericrendering.js new file mode 100644 index 0000000..209bf14 --- /dev/null +++ b/srv/static/js/genericrendering.js @@ -0,0 +1,632 @@ +/// \brief Renders the specified \a value as text or a grey 'none' if the value is 'none' or empty. +function renderNoneInGrey(value, row, elementName, noneText) +{ + const noValue = value === undefined || value === null || value === 'none' || value === 'None' || + value === '' || value === 18446744073709552000; + const element = document.createElement((noValue || elementName === undefined) ? 'span' : elementName); + if (noValue) { + element.appendChild(document.createTextNode(noneText || 'none')); + element.style.color = 'grey'; + element.dataset.isNone = true; + } else if (typeof value === 'boolean') { + element.appendChild(document.createTextNode(value ? 'yes' : 'no')); + } else { + element.appendChild(document.createTextNode(value)); + } + return element; +} + +/// \brief Renders a standard table cell. +/// \remarks This is the default renderer used by renderTableFromJsonArray() and renderTableFromJsonObject(). +function renderStandardTableCell(data, allData, level) +{ + const dataType = typeof data; + if (dataType !== 'object') { + return renderNoneInGrey(data); + } + if (!Array.isArray(data)) { + if (level !== undefined && level > 1) { + return renderNoneInGrey('', data, undefined, 'rendering stopped at this level') + } + return renderTableFromJsonObject({data: data, level: level !== undefined ? level + 1 : 1}); + } + if (data.length === 0) { + return renderNoneInGrey('', data, undefined, 'empty array'); + } + const ul = document.createElement('ul'); + data.forEach(function(element) { + const li = document.createElement('li'); + li.appendChild(renderStandardTableCell(element)); + ul.appendChild(li); + }); + return ul; +} + +/// \brief Renders a custom list. +function renderCustomList(array, customRenderer, compareFunction) +{ + if (!Array.isArray(array) || array.length < 1) { + return renderNoneInGrey(); + } + if (compareFunction !== undefined) { + array.sort(compareFunction); + } + const ul = document.createElement('ul'); + array.forEach(function(arrayElement) { + const li = document.createElement('li'); + const renderedDomElements = customRenderer(arrayElement); + if (Array.isArray(renderedDomElements)) { + renderedDomElements.forEach(function(renderedDomElement) { + li.appendChild(renderedDomElement); + }); + } else { + li.appendChild(renderedDomElements); + } + ul.appendChild(li); + }); + return ul; +} + +/// \brief Renders a list of links. +function renderLinkList(array, obj, handler) +{ + return renderCustomList(array, function(arrayElement) { + return renderLink(array, obj, function() { + handler(arrayElement, array, obj); + }); + }); +} + +/// \brief Returns a 'time ago' string used by the time stamp rendering functions. +function formatTimeAgoString(date) +{ + const seconds = Math.floor((new Date() - date) / 1000); + let interval = Math.floor(seconds / 31536000); + if (interval > 1) { + return interval + ' years ago'; + } + interval = Math.floor(seconds / 2592000); + if (interval > 1) { + return interval + ' months ago'; + } + interval = Math.floor(seconds / 86400); + if (interval > 1) { + return interval + ' days ago'; + } + interval = Math.floor(seconds / 3600); + if (interval > 1) { + return interval + ' hours ago'; + } + interval = Math.floor(seconds / 60); + if (interval > 1) { + return interval + ' minutes ago'; + } + return Math.floor(seconds) + ' seconds ago'; +} + +/// \brief Returns a Date object from the specified time stamp. +function dateFromTimeStamp(timeStamp) +{ + return new Date(timeStamp + 'Z'); +} + +/// \brief Renders a short time stamp, e.g. "12 hours ago" with the exact date as tooltip. +function renderShortTimeStamp(timeStamp) +{ + const date = dateFromTimeStamp(timeStamp); + if (date.getFullYear() === 1) { + return document.createTextNode('not yet'); + } + const span = document.createElement('span'); + span.appendChild(document.createTextNode(formatTimeAgoString(date))); + span.title = timeStamp; + return span; +} + +/// \brief Renders a time stamp, e.g. "12 hours ago" with the exact date in brackets. +function renderTimeStamp(timeStamp) +{ + const date = dateFromTimeStamp(timeStamp); + if (date.getFullYear() === 1) { + return document.createTextNode('not yet'); + } + return document.createTextNode(formatTimeAgoString(date) + ' (' + timeStamp + ')'); +} + +/// \brief Renders a time delta from 2 time stamps. +function renderTimeSpan(startTimeStamp, endTimeStamp) +{ + const startDate = dateFromTimeStamp(startTimeStamp); + if (startDate.getFullYear() === 1) { + return document.createTextNode('not started yet'); + } + let endDate = dateFromTimeStamp(endTimeStamp); + if (endDate.getFullYear() === 1) { + endDate = Date.now(); + } + const elapsedMilliseconds = endDate - startDate; + let text; + if (elapsedMilliseconds >= (1000 * 60 * 60)) { + text = Math.floor(elapsedMilliseconds / 1000 / 60 / 60) + ' h'; + } else if (elapsedMilliseconds >= (1000 * 60)) { + text = Math.floor(elapsedMilliseconds / 1000 / 60) + ' min'; + } else if (elapsedMilliseconds >= (1000)) { + text = Math.floor(elapsedMilliseconds / 1000) + ' s'; + } else { + text = '< 1 s'; + } + return document.createTextNode(text); +} + +/// \brief Renders a link which will invoke the specified \a handler when clicked. +function renderLink(value, row, handler, tooltip, href, middleClickHref) +{ + const linkElement = document.createElement('a'); + const linkText = typeof value === 'object' ? value : document.createTextNode(value); + linkElement.appendChild(linkText); + linkElement.href = middleClickHref || href || '#'; + if (tooltip !== undefined) { + linkElement.title = tooltip; + } + linkElement.onclick = function () { + handler(value, row); + return false; + }; + linkElement.onmouseup = function (e) { + // treat middle-click as regular click + if (e.which !== 2) { + return true; + } + e.preventDefault(); + e.stopPropagation(); + if (!middleClickHref) { + handler(value, row); + } + return false; + }; + return linkElement; +} + +/// \brief Renders the specified array as comma-separated string or 'none' if the array is empty. +function renderArrayAsCommaSeparatedString(value) +{ + return renderNoneInGrey(!Array.isArray(value) || value.length <= 0 ? 'none' : value.join(', ')); +} + +/// \brief Renders the specified array as a possibly elided comma-separated string or 'none' if the array is empty. +function renderArrayElidedAsCommaSeparatedString(value) +{ + if (!Array.isArray(value) || value.length <= 0) { + return renderNoneInGrey('none'); + } + return renderTextPossiblyElidingTheEnd(value.join(', ')); +} + +/// \brief Renders the specified value, possibly eliding the end. +function renderTextPossiblyElidingTheEnd(value) +{ + const limit = 50; + if (value.length < limit) { + return document.createTextNode(value); + } + const element = document.createElement('span'); + const remainingText = document.createTextNode(value.substr(limit)); + const elipses = document.createTextNode('…'); + let expaned = false; + element.appendChild(document.createTextNode(value.substr(0, limit))); + element.appendChild(elipses); + element.onclick = function () { + element.removeChild(element.lastChild); + (expaned = !expaned) ? element.appendChild(remainingText) : element.appendChild(elipses); + }; + return element; +} + +/// \brief Renders the specified \a sizeInByte using an appropriate unit. +function renderDataSize(sizeInByte, row, includeBytes) +{ + if (typeof(sizeInByte) !== 'number') { + return renderNoneInGrey('none'); + } + let res; + if (sizeInByte < 1024) { + res = sizeInByte << " bytes"; + } else if (sizeInByte < 1048576) { + res = (sizeInByte / 1024.0) + " KiB"; + } else if (sizeInByte < 1073741824) { + res = (sizeInByte / 1048576.0) + " MiB"; + } else if (sizeInByte < 1099511627776) { + res = (sizeInByte / 1073741824.0) + " GiB"; + } else { + res = (sizeInByte / 1099511627776.0) + " TiB"; + } + if (includeBytes && sizeInByte > 1024) { + res += ' (' + sizeInByte + " byte)"; + } + return document.createTextNode(res); +} + +// \brief Accesses the property of the specified \a object denoted by \a accessor which is a string like 'foo.bar'. +// \returns Returns the propertie's value or undefined if it doesn't exist. +function accessProperty(object, accessor) +{ + if (accessor === undefined) { + return; + } + const propertyNames = accessor.split("."); + for (let i = 0, count = propertyNames.length; i !== count; ++i) { + object = object[propertyNames[i]]; + if (object === undefined || object === null) { + return; + } + } + return object; +} + +/// \brief Renders a checkbox for selecting a table row. +function renderCheckBoxForTableRow(value, row, computeCheckBoxValue) +{ + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.checked = row.selected; + checkbox.value = computeCheckBoxValue(row); + checkbox.onchange = function () { row.selected = this.checked }; + return checkbox; +} + +/// \brief Returns a table for the specified JSON array. +function renderTableFromJsonArray(args) +{ + // handle arguments + const rows = args.rows; + const columnHeaders = args.columnHeaders; + const columnAccessors = args.columnAccessors; + const columnSortAccessors = args.columnSortAccessors || []; + const defaultRenderer = args.defaultRenderer || renderStandardTableCell; + const customRenderer = args.customRenderer || {}; + const rowHandler = args.rowHandler; + const maxPageButtons = args.maxPageButtons || 5; + + const container = document.createElement("div"); + + // render note + let noteRenderer = customRenderer.note; + if (noteRenderer === undefined || typeof noteRenderer === 'string') { + const note = document.createElement("p"); + note.appendChild(document.createTextNode(noteRenderer || "Showing " + rows.length + " results")); + container.appendChild(note); + } else { + const note = noteRenderer(rows); + if (note !== undefined) { + container.appendChild(note); + } + } + + // add table + const rowsPerPage = args.rowsPerPage; + const table = document.createElement("table"); + table.data = rows; + table.sortedData = rows; + table.className = "table-from-json table-from-json-array"; + + // define pagination stuff + if (rowsPerPage !== undefined && rowsPerPage > 0) { + table.hasPagination = true; + table.rowsPerPage = args.rowsPerPage; + table.currentPage = args.currentPage = 1; + table.pageCount = Math.ceil(rows.length / rowsPerPage); + } + table.pageInfo = function() { + if (!this.hasPagination) { + return {begin: 0, end: this.data.length}; // no pagination + } + if (this.currentPage === undefined || this.currentPage <= 0) { + return {begin: 0, end: 0}; // invalid page + } + return { + begin: Math.min((this.currentPage - 1) * this.rowsPerPage, this.data.length), + end: Math.min(this.currentPage * this.rowsPerPage, this.data.length), + }; + }; + table.forEachRowOnPage = function(callback) { + const pageInfo = this.pageInfo(); + if (isNaN(pageInfo.begin) || isNaN(pageInfo.end)) { + return; + } + for (let i = pageInfo.begin, end = pageInfo.end; i != end; ++i) { + const row = this.data[i]; + if (row === null || row === undefined) { + continue; + } + callback(row, i, pageInfo, this); + } + }; + + // render column header + const thead = document.createElement("thead"); + const tr = document.createElement("tr"); + columnHeaders.forEach(function (columnHeader) { + const th = document.createElement("th"); + const columnIndex = tr.children.length; + th.columnAccessor = columnSortAccessors[columnIndex] || columnAccessors[columnIndex]; + th.descending = true; + th.onclick = function () { + table.sort(this.columnAccessor, this.descending = !this.descending); + }; + th.style.cursor = "pointer"; + th.appendChild(document.createTextNode(columnHeader)); + tr.appendChild(th); + }); + thead.appendChild(tr); + table.appendChild(thead); + + // add pagination + if (table.hasPagination) { + const td = document.createElement("td"); + td.className = "pagination"; + td.colSpan = columnAccessors.length; + const previousA = document.createElement("a"); + previousA.appendChild(document.createTextNode("<")); + previousA.className = "prev"; + previousA.onclick = function() { + const currentA = table.currentA; + if (currentA) { + const a = currentA.previousSibling; + if (a !== undefined && a !== previousA) { + a.onclick(); + } + } + const pageNumInput = table.pageNumInput; + if (pageNumInput && table.currentPage > 1) { + pageNumInput.value = table.currentPage - 1; + pageNumInput.onchange(); + } + }; + const nextA = document.createElement("a"); + nextA.appendChild(document.createTextNode(">")); + nextA.className = "next"; + nextA.onclick = function() { + const currentA = table.currentA; + if (currentA) { + const a = currentA.nextSibling; + if (a !== undefined && a !== nextA) { + a.onclick(); + } + } + const pageNumInput = table.pageNumInput; + if (pageNumInput && table.currentPage < table.pageCount) { + pageNumInput.value = table.currentPage + 1; + pageNumInput.onchange(); + } + }; + + td.appendChild(previousA); + + if (table.pageCount <= maxPageButtons) { + for (let pageNumber = 1; pageNumber <= table.pageCount; ++pageNumber) { + const a = document.createElement("a"); + a.appendChild(document.createTextNode(pageNumber)); + a.onclick = function () { + table.currentA.className = ''; + table.currentA = this; + table.currentA.className = 'current'; + table.currentPage = pageNumber; + table.rerender(); + }; + if (pageNumber === table.currentPage) { + table.currentA = a; + a.className = 'current'; + } + td.appendChild(a); + } + } else { + const pageNumInput = document.createElement("input"); + pageNumInput.type = "number"; + pageNumInput.value = table.currentPage; + pageNumInput.min = 1; + pageNumInput.max = table.pageCount; + pageNumInput.onchange = function() { + const selectedPage = parseInt(this.value); + if (!isNaN(selectedPage) && selectedPage) { + table.currentPage = selectedPage; + table.rerender(); + } + }; + table.pageNumInput = pageNumInput; + td.appendChild(pageNumInput); + const totalSpan = document.createElement("span"); + totalSpan.appendChild(document.createTextNode(" of " + table.pageCount)); + td.appendChild(totalSpan); + } + + td.appendChild(nextA); + + const tr = document.createElement("tr"); + const tfoot = document.createElement("tfoot"); + tr.appendChild(td); + tfoot.appendChild(tr); + table.appendChild(tfoot); + } + + // render table contents + const tbody = document.createElement("tbody"); + const renderNewRow = function (row) { + const tr = document.createElement("tr"); + columnAccessors.forEach(function (columnAccessor) { + const td = document.createElement("td"); + const renderer = customRenderer[columnAccessor]; + let data = accessProperty(row, columnAccessor); + if (data === undefined) { + data = "?"; + } + const content = renderer ? renderer(data, row) : defaultRenderer(data, row); + td.appendChild(content); + tr.appendChild(td); + }); + if (rowHandler) { + rowHandler(row, tr); + } + tbody.appendChild(tr); + }; + table.forEachRowOnPage(renderNewRow); + table.appendChild(tbody); + + // define function to re-render the table's contents + table.rerender = function() { + const sortedData = this.sortedData; + const trs = tbody.getElementsByTagName("tr"); + const pageInfo = table.pageInfo(); + let dataIndex = pageInfo.begin, dataEnd = pageInfo.end; + for (let tr = tbody.firstChild; tr; ++dataIndex) { + if (dataIndex >= dataEnd) { + const nextTr = tr.nextSibling; + tbody.removeChild(tr); + tr = nextTr; + continue; + } + const tds = tr.getElementsByTagName("td"); + const row = sortedData[dataIndex]; + for (let td = tr.firstChild, i = 0; td; td = td.nextSibling, ++i) { + const columnAccessor = columnAccessors[i]; + while (td.firstChild) { + td.removeChild(td.firstChild); + } + const renderer = customRenderer[columnAccessor]; + const data = accessProperty(row, columnAccessor); + const content = renderer !== undefined ? renderer(data, row) : defaultRenderer(data, row); + td.appendChild(content); + } + if (rowHandler) { + rowHandler(row, tr); + } + tr = tr.nextSibling; + } + for (; dataIndex < dataEnd; ++dataIndex) { + renderNewRow(sortedData[dataIndex]); + } + }; + + // define function to re-sort according to a specific column + table.sort = function(columnAccessor, descending) { + // sort the rows according to the column + table.sortedData = rows.sort(function (a, b) { + let aValue = accessProperty(a, columnAccessor); + let bValue = accessProperty(b, columnAccessor); + let aType = typeof aValue; + let bType = typeof bValue; + + // handle undefined/null + if (aValue === undefined || aValue === null) { + return -1; + } + if (bValue === undefined || bValue === null) { + return 1; + } + + // handle numbers + if (aType === "number" && bType === "number") { + if (aValue < bValue) { + return descending ? 1 : -1; + } else if (aValue > bValue) { + return descending ? -1 : 1; + } else { + return 0; + } + } + + // handle arrays (sort them by length) + if (aType === "array" && bType === "array") { + if (aValue.length < bValue.length) { + return descending ? 1 : -1; + } else if (aValue > bValue) { + return descending ? -1 : 1; + } else { + return 0; + } + } + + // handle non-strings + if (aType !== "string" || bType !== "string") { + aValue = aValue.toString(); + bValue = bValue.toString(); + aType = bType = "string"; + } + + // compare strings + return descending ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue); + }); + + // re-render the table's contents + table.rerender(); + }; + + // FIXME: implement filter + + container.appendChild(table); + container.table = table; + return container; +} + +/// \brief Returns a table for the specified JSON object. +function renderTableFromJsonObject(args) +{ + // handle arguments + const data = args.data; + const relatedRow = args.relatedRow; + const displayLabels = args.displayLabels || []; + const fieldAccessors = args.fieldAccessors || Object.getOwnPropertyNames(data); + const level = args.level; + const defaultRenderer = args.defaultRenderer || renderStandardTableCell; + const customRenderer = args.customRenderer || {}; + + const container = document.createElement("div"); + + // render table + const table = document.createElement("table"); + table.className = "table-from-json table-from-json-object"; + + // render table contents + const tbody = document.createElement("tbody"); + fieldAccessors.forEach(function (fieldAccessor) { + const tr = document.createElement("tr"); + const th = document.createElement("th"); + const displayLabel = displayLabels[tbody.children.length] || fieldAccessor; + if (displayLabel !== undefined) { + th.appendChild(document.createTextNode(displayLabel)); + } + tr.appendChild(th); + const td = document.createElement("td"); + const renderer = customRenderer[fieldAccessor]; + let fieldData = accessProperty(data, fieldAccessor); + if (fieldData === undefined) { + fieldData = "?"; + } + const content = renderer ? renderer(fieldData, data, level, relatedRow) : defaultRenderer(fieldData, data, level, relatedRow); + if (Array.isArray(content)) { + content.forEach(function(contentElement) { + td.appendChild(contentElement); + }); + } else { + td.appendChild(content); + } + tr.appendChild(td); + tbody.appendChild(tr); + }); + table.appendChild(tbody); + + container.appendChild(table); + return container; +} + +/// \brief Returns a heading for each key and values via renderStandardTableCell(). +function renderObjectWithHeadings(object, row, level) +{ + const elements = []; + for (const [key, value] of Object.entries(object)) { + const heading = document.createElement('h4'); + heading.className = 'compact-heading'; + heading.appendChild(document.createTextNode(key)); + elements.push(heading, renderStandardTableCell(value, object, level)); + } + return elements; +} \ No newline at end of file diff --git a/srv/static/js/globalstatuspage.js b/srv/static/js/globalstatuspage.js new file mode 100644 index 0000000..d722b8d --- /dev/null +++ b/srv/static/js/globalstatuspage.js @@ -0,0 +1,189 @@ +function queryGlobalStatus() +{ + queryRoute('GET', '/status', handleGlobalStatusUpdate); + return true; +} + +function handleGlobalStatusUpdate(ajaxRequest) +{ + const globalStatus = getAndEmptyElement('global-status'); + let responseText = ajaxRequest.responseText; + if (ajaxRequest.status === 500) { + responseText = 'internal server error'; + } + if (ajaxRequest.status !== 200) { + globalStatus.appendChild(document.createTextNode('unable to load global status: ' + responseText)); + return; + } + const responseJson = JSON.parse(responseText); + const applicationVersion = responseJson.version; + if (applicationVersion) { + getAndEmptyElement('application-version').appendChild(document.createTextNode(applicationVersion)); + } + const dbStats = responseJson.config.dbStats; + const table = renderTableFromJsonArray({ + rows: dbStats, + columnHeaders: ['Arch', 'Database', 'Package count', 'Last update', 'Synced from mirror'], + columnAccessors: ['arch', 'name', 'packageCount', 'lastUpdate', 'syncFromMirror'], + customRenderer: { + name: function (value, row) { + return renderLink(value, row, showRepository); + }, + lastUpdate: renderShortTimeStamp, + note: function(rows) { + const note = document.createElement('div'); + const totalPackageCount = rows.reduce((acc, cur) => acc + cur.packageCount, 0); + note.className = 'form-row'; + note.appendChild(document.createTextNode(rows.length + ' databases and ' + totalPackageCount + ' packages ')); + note.appendChild(renderReloadButton(queryGlobalStatus)); + return note; + }, + }, + }); + globalStatus.appendChild(table); + + // update database selections + const repoSelections = [ + getAndEmptyElement('build-action-source-repo', {'build-action-source-repo-none': 'keep'}), + getAndEmptyElement('build-action-destination-repo', {'build-action-destination-repo-none': 'keep'}), + getAndEmptyElement('package-search-db', {'package-search-db-any': 'keep'}), + ]; + status.repoNames = []; + dbStats.forEach(function (dbInfo) { + const repoName = makeRepoName(dbInfo.name, dbInfo.arch); + status.repoNames.push(repoName); + repoSelections.forEach(function (selection) { + const option = document.createElement('option'); + option.appendChild(document.createTextNode(repoName)); + selection.appendChild(option); + }); + }); + + // update lists of build action states, results and types + const globalInfo = window.globalInfo = {}; + const actions = responseJson.actions; + [{jsonKey: 'states', variable: 'buildActionStates'}, + {jsonKey: 'results', variable: 'buildActionResults'}, + {jsonKey: 'types', variable: 'buildActionTypes'}, + {jsonKey: 'types', variable: 'buildActionTypesByParam', field: 'type'}, + ].forEach(function (mapping) { + const variable = globalInfo[mapping.variable] = {}; + actions[mapping.jsonKey].forEach(function(entry) { + variable[entry[mapping.field || 'id']] = entry; + }); + }); + + // update build action form and make settingNames/settingParams arrays for build action details + const buildActionTypeSelect = getAndEmptyElement('build-action-type'); + const buildActionFlagsContainer = getAndEmptyElement('build-action-flags'); + const buildActionSettingsTable = getAndEmptyElement('build-action-settings'); + let optgroupElements = {}; + const makeOrReuseOptgroup = function (label) { + const existingOptgroupElement = optgroupElements[label]; + if (existingOptgroupElement) { + return existingOptgroupElement; + } + const optgroupElement = document.createElement('optgroup'); + optgroupElement.label = label; + return optgroupElements[label] = optgroupElement; + }; + actions.types.forEach(function (typeInfo) { + const typeId = typeInfo.id; + if (typeId === 0) { + return; // skip invalid type + } + const type = typeInfo.type; + const category = typeInfo.category || 'Misc'; + const optionElement = document.createElement('option'); + optionElement.value = type; + optionElement.dataset.id = typeId; + optionElement.appendChild(document.createTextNode(typeInfo.name)); + const optgroupElement = makeOrReuseOptgroup(category); + optgroupElement.appendChild(optionElement); + if (!optgroupElement.parentElement) { + buildActionTypeSelect.appendChild(optgroupElement); + } + typeInfo.flags.forEach(function (flag) { + const param = flag.param; + const input = document.createElement('input'); + input.id = 'build-action-' + type + '-' + param; + input.type = 'checkbox'; + input.name = param; + input.value = 1; + input.dataset.id = flag.id; + const label = document.createElement('label'); + label.htmlFor = input.id; + label.dataset.relevantType = type; + label.appendChild(document.createTextNode(flag.name)); + label.title = flag.desc; + buildActionFlagsContainer.appendChild(input); + buildActionFlagsContainer.appendChild(label); + }); + const settingNames = typeInfo.settingNames = []; + const settingParams = typeInfo.settingParams = []; + typeInfo.settings.forEach(function (setting) { + const name = setting.name; + const param = setting.param; + const label = document.createElement('label'); + const input = document.createElement('input'); + input.id = 'build-action-' + type + '-' + param; + input.type = 'text'; + input.name = param; + label.htmlFor = input.id; + label.appendChild(document.createTextNode(name + ': ')); + label.title = setting.desc; + const labelTh = document.createElement('th'); + labelTh.style.fontWeight = 'normal'; + labelTh.appendChild(label); + const inputTd = document.createElement('td'); + inputTd.appendChild(input); + const tr = document.createElement('tr'); + tr.appendChild(labelTh); + tr.appendChild(inputTd); + tr.dataset.relevantType = type; + buildActionSettingsTable.appendChild(tr); + settingNames.push(name); + settingParams.push(param); + }); + }); + + // update presets/tasks form + const buildActionPresetSelect = getAndEmptyElement('build-action-task', {'build-action-task-none': 'keep'}); + const presets = responseJson.presets; + globalInfo.presets = presets; + optgroupElements = {}; + for (const [presetId, presetInfo] of Object.entries(presets.tasks)) { + const category = presetInfo.category || 'Misc'; + const optionElement = document.createElement('option'); + optionElement.value = presetId; + optionElement.appendChild(document.createTextNode(presetInfo.name)); + const optgroupElement = makeOrReuseOptgroup(category); + optgroupElement.appendChild(optionElement); + if (!optgroupElement.parentElement) { + buildActionPresetSelect.appendChild(optgroupElement); + } + } + + window.functionsPostponedUntilGlobalInfo.forEach(function(f) { f(); }); + window.functionsPostponedUntilGlobalInfo = []; + + handleBuildActionTypeChange(); + handleBuildActionPresetChange(); +} + +function showRepository(dbName, dbInfo) +{ + const mirror = dbInfo.mainMirror; + if (!mirror) { + window.alert('No mirror configured for ' + dbName + '.'); + } else { + window.open(mirror); + } +} + +window.globalInfo = undefined; +window.functionsPostponedUntilGlobalInfo = []; +window.hasGlobalStatus = false; +window.buildActionStates = {}; +window.buildActionResults = {}; +window.buildActionTypes = {}; \ No newline at end of file diff --git a/srv/static/js/log.js b/srv/static/js/log.js new file mode 100644 index 0000000..1412b54 --- /dev/null +++ b/srv/static/js/log.js @@ -0,0 +1,21 @@ +function initLog() +{ + const hashParts = hashAsObject(); + const id = hashParts.id; + const name = hashParts.name; + const mainElement = getAndEmptyElement('log-container'); + if (id === undefined || id === '' || name === undefined || name === '') { + document.title += ' - logfile'; + mainElement.appendChild(document.createTextNode('id or name invalid')); + return; + } + const path = '/build-action/logfile?id=' + encodeURIComponent(id) + '&name=' + encodeURIComponent(name); + const terminal = makeTerminal(); + const ajaxRequest = streamRouteIntoTerminal('GET', path, terminal); + document.title += ' - ' + name; + terminal.resize(180, 500); + window.setTimeout(function() { + addSearchToTerminal(terminal, mainElement); + terminal.open(mainElement); + }); +} \ No newline at end of file diff --git a/srv/static/js/packagedetailspage.js b/srv/static/js/packagedetailspage.js new file mode 100644 index 0000000..af53ab1 --- /dev/null +++ b/srv/static/js/packagedetailspage.js @@ -0,0 +1,117 @@ +function initPackageDetails(sectionElement, sectionData, newPackages) +{ + const currentPackage = sectionData.state.package; + const hasNewPackages = newPackages.length >= 1; + if (!hasNewPackages) { + if (currentPackage !== undefined) { + window.preventHandlingHashChange = true; + window.location.hash = '#package-details-section&' + encodeURIComponent(currentPackage); + window.preventHandlingHashChange = false; + } + return true; + } + const packageStr = newPackages[0]; + if (currentPackage === packageStr) { + return true; + } + const packageParts = packageStr.split('/'); + const package = {db: packageParts[0], name: packageParts[1]}; + queryRoute('GET', '/packages?details=1&name=' + encodeURIComponent(packageStr), function(ajaxRequest) { + showPackageDetails(ajaxRequest, package); + }); + return true; +} + +function makePackageID(row) +{ + return row.db + (row.dbArch ? '@' + row.dbArch : '') + '/' + row.name; +} + +function queryPackageDetails(value, row) +{ + queryRoute('GET', '/packages?details=1&name=' + encodeURIComponent(makePackageID(row)), function(ajaxRequest) { + showPackageDetails(ajaxRequest, row); + }); +} + +function switchToPackageDetails(packageID) +{ + sections['package-details'].state.package = packageID; + window.preventSectionInitializer = true; + window.location.hash = '#package-details-section&' + encodeURIComponent(packageID); + window.preventSectionInitializer = false; +} + +function showPackageDetails(ajaxRequest, row) +{ + const packageID = makePackageID(row); + const packageDetailsContainer = getAndEmptyElement('package-details-container'); + if (ajaxRequest.status !== 200) { + packageDetailsContainer.appendChild(document.createTextNode('unable query package details: ' + ajaxRequest.responseText)); + return; + } + const responseJson = JSON.parse(ajaxRequest.responseText); + if (!Array.isArray(responseJson) || responseJson.length !== 1) { + switchToPackageDetails(packageID); + packageDetailsContainer.appendChild(document.createTextNode('unable query package details: package not present')); + return; + } + const package = responseJson[0]; + const heading = document.createElement('h3'); + heading.appendChild(document.createTextNode(package.name)); + heading.appendChild(document.createTextNode(' ' + package.version)); + packageDetailsContainer.appendChild(heading); + package.db = row.db; + package.dbArch = row.dbArch; + packageDetailsContainer.appendChild(renderPackage(package, true)); + + switchToPackageDetails(packageID); +} + +const labelsWithoutBasics = ['Architecture', 'Repository', 'Description', 'Upstream URL', 'License(s)', 'Groups', 'Package size', 'Installed size', 'Packager', 'Build date', 'Dependencies', 'Optional dependencies', 'Make dependencies', 'Check dependencies', 'Provides', 'Replaces', 'Conflicts', 'Contained libraries', 'Needed libraries', 'Files']; +const fieldsWithoutBasics = ['packageInfo.arch', 'db', 'description', 'upstreamUrl', 'licenses', 'groups', 'packageInfo.size', 'installInfo.installedSize', 'packageInfo.packager', 'packageInfo.buildDate', 'dependencies', 'optionalDependencies', 'sourceInfo.makeDependencies', 'sourceInfo.checkDependencies', 'provides', 'replaces', 'conflicts', 'libprovides', 'libdepends', 'packageInfo.files']; +const labelsWithBasics = ['Name', 'Version', ...labelsWithoutBasics]; +const fieldsWithBasics = ['name', 'version', ...fieldsWithoutBasics]; + +function renderPackage(package, withoutBasics) +{ + const table = renderTableFromJsonObject({ + data: package, + displayLabels: withoutBasics ? labelsWithoutBasics : labelsWithBasics, + fieldAccessors: withoutBasics ? fieldsWithoutBasics : fieldsWithBasics, + customRenderer: { + db: function(value, row) { + return document.createTextNode(makeRepoName(value, row.dbArch)); + }, + upstreamUrl: function (value, row) { + return renderLink(value, row, function (value) { + window.open(value); + }); + }, + licenses: renderArrayAsCommaSeparatedString, + groups: renderArrayAsCommaSeparatedString, + dependencies: renderDependency, + optionalDependencies: renderDependency, + provides: renderDependency, + replaces: renderDependency, + conflicts: renderDependency, + libprovides: renderArrayAsCommaSeparatedString, + libdepends: renderArrayAsCommaSeparatedString, + 'sourceInfo.makeDependencies': renderDependency, + 'sourceInfo.checkDependencies': renderDependency, + 'packageInfo.arch': function (value, row) { + const sourceInfo = row.sourceInfo; + const sourceArchs = sourceInfo !== undefined ? sourceInfo.archs : undefined; + if (Array.isArray(sourceArchs) && sourceArchs.length) { + return renderArrayAsCommaSeparatedString(sourceArchs); + } else { + return renderNoneInGrey(value); + } + }, + 'packageInfo.size': renderDataSize, + 'installInfo.installedSize': renderDataSize, + }, + }); + table.className = 'package-details-table'; + return table; +} \ No newline at end of file diff --git a/srv/static/js/packagesearchpage.js b/srv/static/js/packagesearchpage.js new file mode 100644 index 0000000..6448b47 --- /dev/null +++ b/srv/static/js/packagesearchpage.js @@ -0,0 +1,37 @@ +function searchForPackages() +{ + return startFormQuery('package-search-form', showPackageSearchResults); +} + +function showPackageSearchResults(ajaxRequest) +{ + const packageSearchResults = getAndEmptyElement('package-search-results'); + if (ajaxRequest.status !== 200) { + packageSearchResults.appendChild(document.createTextNode('unable search for packages: ' + ajaxRequest.responseText)); + return; + } + const responseJson = JSON.parse(ajaxRequest.responseText); + const table = renderTableFromJsonArray({ + rows: responseJson, + columnHeaders: ['', 'Arch', 'Repo', 'Name', 'Version', 'Description', 'Build date'], + columnAccessors: ['checkbox', 'arch', 'db', 'name', 'version', 'description', 'buildDate'], + rowsPerPage: 40, + customRenderer: { + name: function (value, row) { + return renderLink(value, row, queryPackageDetails, 'Show package details', undefined, + '#package-details-section&' + encodeURIComponent(row.db + (row.dbArch ? '@' + row.dbArch : '') + '/' + value)); + }, + checkbox: function(value, row) { + return renderCheckBoxForTableRow(value, row, function(row) { + return [row.db, row.name].join('/'); + }); + }, + note: function (rows) { + const note = document.createElement("p"); + note.appendChild(document.createTextNode("Found " + rows.length + " packages")); + return note; + }, + }, + }); + packageSearchResults.appendChild(table); +} \ No newline at end of file diff --git a/srv/static/js/singlepage.js b/srv/static/js/singlepage.js new file mode 100644 index 0000000..ed19db9 --- /dev/null +++ b/srv/static/js/singlepage.js @@ -0,0 +1,62 @@ +/// \brief 'main()' function which initializes the single page app. +function initPage() +{ + handleHashChange(); + queryGlobalStatus(); +} + +/// \brief Shows the current section and hides other sections. +function handleHashChange() +{ + if (window.preventHandlingHashChange) { + return; + } + + const hashParts = splitHashParts(); + const currentSectionName = hashParts.shift() || 'global-section'; + if (!currentSectionName.endsWith('-section')) { + return; + } + + sectionNames.forEach(function (sectionName) { + const sectionData = sections[sectionName]; + const sectionElement = document.getElementById(sectionName + '-section'); + if (sectionElement.id === currentSectionName) { + const sectionInitializer = sectionData.initializer; + if (sectionInitializer === undefined || window.preventSectionInitializer || sectionInitializer(sectionElement, sectionData, hashParts)) { + sectionElement.style.display = 'block'; + } + } else { + const sectionDestructor = sectionData.destructor; + if (sectionDestructor === undefined || sectionDestructor(sectionElement, sectionData, hashParts)) { + sectionElement.style.display = 'none'; + } + } + const navLinkElement = document.getElementById(sectionName + '-nav-link'); + if (sectionElement.id === currentSectionName) { + navLinkElement.classList.add('active'); + } else { + navLinkElement.classList.remove('active'); + } + }); +} + +const sections = { + 'global': { + }, + 'package-search': { + }, + 'package-details': { + initializer: initPackageDetails, + state: {package: undefined}, + }, + 'build-action': { + initializer: initBuildActionsForm, + }, + 'build-action-details': { + initializer: initBuildActionDetails, + state: {id: undefined}, + }, +}; +const sectionNames = Object.keys(sections); +const status = {repoNames: undefined}; diff --git a/srv/static/js/terminal.js b/srv/static/js/terminal.js new file mode 100644 index 0000000..bceb63b --- /dev/null +++ b/srv/static/js/terminal.js @@ -0,0 +1,78 @@ +/// \brief Returns a new terminal created via xterm.js. +function makeTerminal() +{ + const terminal = new Terminal({ + disableStdin: true, + convertEol: true, + scrollback: 500000, + cols: 120, + }); + return terminal; +} + +/// \brief Adds a search for the specified \a terminal to the specified \a targetElement. +function addSearchToTerminal(terminal, targetElement) +{ + const searchAddon = new SearchAddon(); + // FIXME: import the search addon correctly + //import('../node_modules/xterm-addon-search/lib/xterm-addon-search.js').then(function(module) { + // const searchAddon = new module.SearchAddon(); + // terminal.loadAddon(searchAddon); + //}); + terminal.loadAddon(searchAddon); + + const searchInput = document.createElement('input'); + searchInput.placeholder = 'Search'; + searchInput.style.width = '100%'; + searchInput.style.boxSizing = 'border-box'; + searchInput.onkeyup = function(event) { + event.preventDefault(); + if (event.keyCode === 13) { + const res = (event.shiftKey ? searchAddon.findPrevious(searchInput.value) : searchAddon.findNext(searchInput.value)); + searchInput.style.backgroundColor = (res ? '#83d883' : '#d5aaaf'); + } + }; + targetElement.appendChild(searchInput); + + return searchAddon; +} + +/// \brief Initializes the specified \a terminal within the specified \a targetElement writing the specified initial \a value +/// to the terminal. +/// \remarks This initialization only works if \a targetElement is already part of the rendered HTML page. Hence this function +/// uses window.setTimeout to ensure \a targetElement is rendered. +function setupTerminalLater(terminal, targetElement, value) +{ + window.setTimeout(function() { + terminal.open(targetElement); + if (value !== undefined) { + terminal.write(value); + } + addSearchToTerminal(terminal, targetElement); + // ensure the scroll bar on the right side is not clipped + targetElement.style.minWidth = terminal.element.scrollWidth + 20 + 'px'; + }, 100); +} + +/// \brief Makes an AJAX query and writes the received data to the specified \a terminal. +/// \remarks If the server responds in chunks, each chunk is written as soon as it arrives. +function streamRouteIntoTerminal(method, path, terminal) +{ + const ajaxRequest = new XMLHttpRequest(); + let responseWritten = 0; + ajaxRequest.onreadystatechange = function() { + const response = this.response; + if (this.readyState === 3 || this.readyState === 4) { + terminal.write(response.substr(responseWritten)); + responseWritten = response.length; + } + if (this.readyState === 4) { + if (this.status !== 200) { + terminal.write('\r\nUnable to query ' + path + ': ' + this.status + ' response'); + } + } + }; + ajaxRequest.open(method, apiPrefix + path, true); + ajaxRequest.send(); + return ajaxRequest; +} \ No newline at end of file diff --git a/srv/static/js/utils.js b/srv/static/js/utils.js new file mode 100644 index 0000000..4a2cc0f --- /dev/null +++ b/srv/static/js/utils.js @@ -0,0 +1,118 @@ +function splitHashParts() +{ + const currentHash = location.hash.substr(1); + const hashParts = currentHash.split('&'); + for (let i = 0, len = hashParts.length; i != len; ++i) { + hashParts[i] = decodeURIComponent(hashParts[i]); + } + return hashParts; +} + +function hashAsObject() +{ + const hashObject = {}; + location.hash.substr(1).split('&').forEach(function(hashPart) { + const parts = hashPart.split('=', 2); + if (parts.length < 1) { + return; + } + hashObject[decodeURIComponent(parts[0])] = parts.length > 1 ? decodeURIComponent(parts[1]) : undefined; + }); + return hashObject; +} + +function getAndEmptyElement(elementId, specialActionsById) +{ + return emptyDomElement(document.getElementById(elementId), specialActionsById); +} + +function emptyDomElement(domElement, specialActionsById) +{ + let child = domElement.firstChild; + while (child) { + let specialAction = specialActionsById ? specialActionsById[child.id] : undefined; + let nextSibling = child.nextSibling; + if (specialAction !== 'keep') { + domElement.removeChild(child); + } + child = nextSibling; + } + return domElement; +} + +function alterFormSelection(form, command) +{ + // modify form elements + const elements = form.elements; + for (let i = 0, len = elements.length; i != len; ++i) { + const element = elements[i]; + if (element.type !== 'checkbox') { + continue; + } + switch (command) { + case 'uncheck-all': + element.checked = false; + break; + case 'check-all': + element.checked = true; + break; + } + } + // modify the actual data + const tables = form.getElementsByTagName('table'); + for (let i = 0, len = tables.length; i != len; ++i) { + const data = tables[i].data; + if (!Array.isArray(data)) { + return; + } + data.forEach(function (row) { + switch (command) { + case 'uncheck-all': + row.selected = false; + break; + case 'check-all': + row.selected = true; + break; + } + }); + }; +} + +function getProperty(object, property, fallback) +{ + if (typeof object !== 'object') { + return fallback; + } + const value = object[property]; + return value !== undefined ? value : fallback; +} + +function makeRepoName(dbName, dbArch) +{ + return dbArch && dbArch !== 'x86_64' ? dbName + '@' + dbArch : dbName; +} + +/// \brief Returns the table row data for the table within the element with the specified ID. +function getFormTableData(formId) +{ + const formElement = document.getElementById(formId); + const tableElement = formElement.getElementsByTagName('table')[0]; + if (tableElement === undefined) { + return; + } + const data = tableElement.data; + return Array.isArray(data) ? data : undefined; +} + +/// \brief Returns the cell values of selected rows. +/// \remarks The row data needs to be passed. The cell is determined by the specified \a propertyName. +function getSelectedRowProperties(data, propertyName) +{ + const propertyValues = []; + data.forEach(function (row) { + if (row.selected) { + propertyValues.push(row[propertyName]); + } + }); + return propertyValues; +} \ No newline at end of file diff --git a/srv/static/log.html b/srv/static/log.html new file mode 100644 index 0000000..8a17ff4 --- /dev/null +++ b/srv/static/log.html @@ -0,0 +1,24 @@ + + + + + Build service + + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/srv/static/node_modules/xterm-addon-search/LICENSE b/srv/static/node_modules/xterm-addon-search/LICENSE new file mode 100644 index 0000000..e597698 --- /dev/null +++ b/srv/static/node_modules/xterm-addon-search/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/srv/static/node_modules/xterm-addon-search/out/SearchAddon.js b/srv/static/node_modules/xterm-addon-search/out/SearchAddon.js new file mode 100644 index 0000000..e795563 --- /dev/null +++ b/srv/static/node_modules/xterm-addon-search/out/SearchAddon.js @@ -0,0 +1,260 @@ +"use strict"; +var NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\;:"\',./<>?'; +var LINES_CACHE_TIME_TO_LIVE = 15 * 1000; + + function SearchAddon() { + this._linesCacheTimeoutId = 0; + } + SearchAddon.prototype.activate = function (terminal) { + this._terminal = terminal; + }; + SearchAddon.prototype.dispose = function () { }; + SearchAddon.prototype.findNext = function (term, searchOptions) { + if (!this._terminal) { + throw new Error('Cannot use addon until it has been loaded'); + } + if (!term || term.length === 0) { + this._terminal.clearSelection(); + return false; + } + var startCol = 0; + var startRow = 0; + var currentSelection; + if (this._terminal.hasSelection()) { + var incremental = searchOptions ? searchOptions.incremental : false; + currentSelection = this._terminal.getSelectionPosition(); + startRow = incremental ? currentSelection.startRow : currentSelection.endRow; + startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn; + } + this._initLinesCache(); + var searchPosition = { + startRow: startRow, + startCol: startCol + }; + var result = this._findInLine(term, searchPosition, searchOptions); + if (!result) { + for (var y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) { + searchPosition.startRow = y; + searchPosition.startCol = 0; + result = this._findInLine(term, searchPosition, searchOptions); + if (result) { + break; + } + } + } + if (!result && startRow !== 0) { + for (var y = 0; y < startRow; y++) { + searchPosition.startRow = y; + searchPosition.startCol = 0; + result = this._findInLine(term, searchPosition, searchOptions); + if (result) { + break; + } + } + } + if (!result && currentSelection) + return true; + return this._selectResult(result); + }; + SearchAddon.prototype.findPrevious = function (term, searchOptions) { + if (!this._terminal) { + throw new Error('Cannot use addon until it has been loaded'); + } + if (!term || term.length === 0) { + this._terminal.clearSelection(); + return false; + } + var isReverseSearch = true; + var startRow = this._terminal.buffer.active.baseY + this._terminal.rows; + var startCol = this._terminal.cols; + var result; + var incremental = searchOptions ? searchOptions.incremental : false; + var currentSelection; + if (this._terminal.hasSelection()) { + currentSelection = this._terminal.getSelectionPosition(); + startRow = currentSelection.startRow; + startCol = currentSelection.startColumn; + } + this._initLinesCache(); + var searchPosition = { + startRow: startRow, + startCol: startCol + }; + if (incremental) { + result = this._findInLine(term, searchPosition, searchOptions, false); + if (!(result && result.row === startRow && result.col === startCol)) { + result = this._findInLine(term, searchPosition, searchOptions, true); + } + } + else { + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); + } + if (!result) { + searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols); + for (var y = startRow - 1; y >= 0; y--) { + searchPosition.startRow = y; + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); + if (result) { + break; + } + } + } + if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) { + for (var y = (this._terminal.buffer.active.baseY + this._terminal.rows); y > startRow; y--) { + searchPosition.startRow = y; + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); + if (result) { + break; + } + } + } + if (!result && currentSelection) + return true; + return this._selectResult(result); + }; + SearchAddon.prototype._initLinesCache = function () { + var _this = this; + var terminal = this._terminal; + if (!this._linesCache) { + this._linesCache = new Array(terminal.buffer.active.length); + this._cursorMoveListener = terminal.onCursorMove(function () { return _this._destroyLinesCache(); }); + this._resizeListener = terminal.onResize(function () { return _this._destroyLinesCache(); }); + } + window.clearTimeout(this._linesCacheTimeoutId); + this._linesCacheTimeoutId = window.setTimeout(function () { return _this._destroyLinesCache(); }, LINES_CACHE_TIME_TO_LIVE); + }; + SearchAddon.prototype._destroyLinesCache = function () { + this._linesCache = undefined; + if (this._cursorMoveListener) { + this._cursorMoveListener.dispose(); + this._cursorMoveListener = undefined; + } + if (this._resizeListener) { + this._resizeListener.dispose(); + this._resizeListener = undefined; + } + if (this._linesCacheTimeoutId) { + window.clearTimeout(this._linesCacheTimeoutId); + this._linesCacheTimeoutId = 0; + } + }; + SearchAddon.prototype._isWholeWord = function (searchIndex, line, term) { + return (((searchIndex === 0) || (NON_WORD_CHARACTERS.indexOf(line[searchIndex - 1]) !== -1)) && + (((searchIndex + term.length) === line.length) || (NON_WORD_CHARACTERS.indexOf(line[searchIndex + term.length]) !== -1))); + }; + SearchAddon.prototype._findInLine = function (term, searchPosition, searchOptions, isReverseSearch) { + if (searchOptions === void 0) { searchOptions = {}; } + if (isReverseSearch === void 0) { isReverseSearch = false; } + var terminal = this._terminal; + var row = searchPosition.startRow; + var col = searchPosition.startCol; + var firstLine = terminal.buffer.active.getLine(row); + if (firstLine && firstLine.isWrapped) { + if (isReverseSearch) { + searchPosition.startCol += terminal.cols; + return; + } + searchPosition.startRow--; + searchPosition.startCol += terminal.cols; + return this._findInLine(term, searchPosition, searchOptions); + } + var stringLine = this._linesCache ? this._linesCache[row] : void 0; + if (stringLine === void 0) { + stringLine = this._translateBufferLineToStringWithWrap(row, true); + if (this._linesCache) { + this._linesCache[row] = stringLine; + } + } + var searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase(); + var searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase(); + var resultIndex = -1; + if (searchOptions.regex) { + var searchRegex = RegExp(searchTerm, 'g'); + var foundTerm = void 0; + if (isReverseSearch) { + while (foundTerm = searchRegex.exec(searchStringLine.slice(0, col))) { + resultIndex = searchRegex.lastIndex - foundTerm[0].length; + term = foundTerm[0]; + searchRegex.lastIndex -= (term.length - 1); + } + } + else { + foundTerm = searchRegex.exec(searchStringLine.slice(col)); + if (foundTerm && foundTerm[0].length > 0) { + resultIndex = col + (searchRegex.lastIndex - foundTerm[0].length); + term = foundTerm[0]; + } + } + } + else { + if (isReverseSearch) { + if (col - searchTerm.length >= 0) { + resultIndex = searchStringLine.lastIndexOf(searchTerm, col - searchTerm.length); + } + } + else { + resultIndex = searchStringLine.indexOf(searchTerm, col); + } + } + if (resultIndex >= 0) { + if (resultIndex >= terminal.cols) { + row += Math.floor(resultIndex / terminal.cols); + resultIndex = resultIndex % terminal.cols; + } + if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) { + return; + } + var line = terminal.buffer.active.getLine(row); + if (line) { + for (var i = 0; i < resultIndex; i++) { + var cell = line.getCell(i); + if (!cell) { + break; + } + var char = cell.getChars(); + if (char.length > 1) { + resultIndex -= char.length - 1; + } + var charWidth = cell.getWidth(); + if (charWidth === 0) { + resultIndex++; + } + } + } + return { + term: term, + col: resultIndex, + row: row + }; + } + }; + SearchAddon.prototype._translateBufferLineToStringWithWrap = function (lineIndex, trimRight) { + var terminal = this._terminal; + var lineString = ''; + var lineWrapsToNext; + do { + var nextLine = terminal.buffer.active.getLine(lineIndex + 1); + lineWrapsToNext = nextLine ? nextLine.isWrapped : false; + var line = terminal.buffer.active.getLine(lineIndex); + if (!line) { + break; + } + lineString += line.translateToString(!lineWrapsToNext && trimRight).substring(0, terminal.cols); + lineIndex++; + } while (lineWrapsToNext); + return lineString; + }; + SearchAddon.prototype._selectResult = function (result) { + var terminal = this._terminal; + if (!result) { + terminal.clearSelection(); + return false; + } + terminal.select(result.col, result.row, result.term.length); + if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) { + var scroll_1 = result.row - terminal.buffer.active.viewportY; + scroll_1 = scroll_1 - Math.floor(terminal.rows / 2); + terminal.scrollLines(scroll_1); + } + return true; + }; diff --git a/srv/static/node_modules/xterm/LICENSE b/srv/static/node_modules/xterm/LICENSE new file mode 100644 index 0000000..4472336 --- /dev/null +++ b/srv/static/node_modules/xterm/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) +Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com) +Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/srv/static/node_modules/xterm/css/xterm.css b/srv/static/node_modules/xterm/css/xterm.css new file mode 100644 index 0000000..b3d8d4f --- /dev/null +++ b/srv/static/node_modules/xterm/css/xterm.css @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2014 The xterm.js authors. All rights reserved. + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * @license MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */ + +/** + * Default styles for xterm.js + */ + +.xterm { + font-feature-settings: "liga" 0; + position: relative; + user-select: none; + -ms-user-select: none; + -webkit-user-select: none; +} + +.xterm.focus, +.xterm:focus { + outline: none; +} + +.xterm .xterm-helpers { + position: absolute; + top: 0; + /** + * The z-index of the helpers must be higher than the canvases in order for + * IMEs to appear on top. + */ + z-index: 5; +} + +.xterm .xterm-helper-textarea { + /* + * HACK: to fix IE's blinking cursor + * Move textarea out of the screen to the far left, so that the cursor is not visible. + */ + position: absolute; + opacity: 0; + left: -9999em; + top: 0; + width: 0; + height: 0; + z-index: -5; + /** Prevent wrapping so the IME appears against the textarea at the correct position */ + white-space: nowrap; + overflow: hidden; + resize: none; +} + +.xterm .composition-view { + /* TODO: Composition position got messed up somewhere */ + background: #000; + color: #FFF; + display: none; + position: absolute; + white-space: nowrap; + z-index: 1; +} + +.xterm .composition-view.active { + display: block; +} + +.xterm .xterm-viewport { + /* On OS X this is required in order for the scroll bar to appear fully opaque */ + background-color: #000; + overflow-y: scroll; + cursor: default; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; +} + +.xterm .xterm-screen { + position: relative; +} + +.xterm .xterm-screen canvas { + position: absolute; + left: 0; + top: 0; +} + +.xterm .xterm-scroll-area { + visibility: hidden; +} + +.xterm-char-measure-element { + display: inline-block; + visibility: hidden; + position: absolute; + top: 0; + left: -9999em; + line-height: normal; +} + +.xterm { + cursor: text; +} + +.xterm.enable-mouse-events { + /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ + cursor: default; +} + +.xterm.xterm-cursor-pointer { + cursor: pointer; +} + +.xterm.column-select.focus { + /* Column selection mode */ + cursor: crosshair; +} + +.xterm .xterm-accessibility, +.xterm .xterm-message { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + z-index: 10; + color: transparent; +} + +.xterm .live-region { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + +.xterm-dim { + opacity: 0.5; +} + +.xterm-underline { + text-decoration: underline; +} diff --git a/srv/static/node_modules/xterm/lib/xterm.js b/srv/static/node_modules/xterm/lib/xterm.js new file mode 100644 index 0000000..08b6688 --- /dev/null +++ b/srv/static/node_modules/xterm/lib/xterm.js @@ -0,0 +1,2 @@ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var r=t();for(var i in r)("object"==typeof exports?exports:e)[i]=r[i]}}(window,(function(){return function(e){var t={};function r(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{}};return e[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(i,n,function(t){return e[t]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=32)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this._listeners=[],this._disposed=!1}return Object.defineProperty(e.prototype,"event",{get:function(){var e=this;return this._event||(this._event=function(t){return e._listeners.push(t),{dispose:function(){if(!e._disposed)for(var r=0;r>22},t.prototype.getChars=function(){return 2097152&this.content?this.combinedData:2097151&this.content?o.stringFromCodePoint(2097151&this.content):""},t.prototype.getCode=function(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content},t.prototype.setFromCharData=function(e){this.fg=e[s.CHAR_DATA_ATTR_INDEX],this.bg=0;var t=!1;if(e[s.CHAR_DATA_CHAR_INDEX].length>2)t=!0;else if(2===e[s.CHAR_DATA_CHAR_INDEX].length){var r=e[s.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=r&&r<=56319){var i=e[s.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=i&&i<=57343?this.content=1024*(r-55296)+i-56320+65536|e[s.CHAR_DATA_WIDTH_INDEX]<<22:t=!0}else t=!0}else this.content=e[s.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|e[s.CHAR_DATA_WIDTH_INDEX]<<22;t&&(this.combinedData=e[s.CHAR_DATA_CHAR_INDEX],this.content=2097152|e[s.CHAR_DATA_WIDTH_INDEX]<<22)},t.prototype.getAsCharData=function(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]},t}(r(6).AttributeData);t.CellData=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this.fg=0,this.bg=0}return e.toColorRGB=function(e){return[e>>>16&255,e>>>8&255,255&e]},e.fromColorRGB=function(e){return(255&e[0])<<16|(255&e[1])<<8|255&e[2]},e.prototype.clone=function(){var t=new e;return t.fg=this.fg,t.bg=this.bg,t},e.prototype.isInverse=function(){return 67108864&this.fg},e.prototype.isBold=function(){return 134217728&this.fg},e.prototype.isUnderline=function(){return 268435456&this.fg},e.prototype.isBlink=function(){return 536870912&this.fg},e.prototype.isInvisible=function(){return 1073741824&this.fg},e.prototype.isItalic=function(){return 67108864&this.bg},e.prototype.isDim=function(){return 134217728&this.bg},e.prototype.getFgColorMode=function(){return 50331648&this.fg},e.prototype.getBgColorMode=function(){return 50331648&this.bg},e.prototype.isFgRGB=function(){return 50331648==(50331648&this.fg)},e.prototype.isBgRGB=function(){return 50331648==(50331648&this.bg)},e.prototype.isFgPalette=function(){return 16777216==(50331648&this.fg)||33554432==(50331648&this.fg)},e.prototype.isBgPalette=function(){return 16777216==(50331648&this.bg)||33554432==(50331648&this.bg)},e.prototype.isFgDefault=function(){return 0==(50331648&this.fg)},e.prototype.isBgDefault=function(){return 0==(50331648&this.bg)},e.prototype.isAttributeDefault=function(){return 0===this.fg&&0===this.bg},e.prototype.getFgColor=function(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}},e.prototype.getBgColor=function(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}},e}();t.AttributeData=i},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.stringFromCodePoint=function(e){return e>65535?(e-=65536,String.fromCharCode(55296+(e>>10))+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)},t.utf32ToString=function(e,t,r){void 0===t&&(t=0),void 0===r&&(r=e.length);for(var i="",n=t;n65535?(o-=65536,i+=String.fromCharCode(55296+(o>>10))+String.fromCharCode(o%1024+56320)):i+=String.fromCharCode(o)}return i};var i=function(){function e(){this._interim=0}return e.prototype.clear=function(){this._interim=0},e.prototype.decode=function(e,t){var r=e.length;if(!r)return 0;var i=0,n=0;this._interim&&(56320<=(a=e.charCodeAt(n++))&&a<=57343?t[i++]=1024*(this._interim-55296)+a-56320+65536:(t[i++]=this._interim,t[i++]=a),this._interim=0);for(var o=n;o=r)return this._interim=s,i;var a;56320<=(a=e.charCodeAt(o))&&a<=57343?t[i++]=1024*(s-55296)+a-56320+65536:(t[i++]=s,t[i++]=a)}else t[i++]=s}return i},e}();t.StringToUtf32=i;var n=function(){function e(){this.interim=new Uint8Array(3)}return e.prototype.clear=function(){this.interim.fill(0)},e.prototype.decode=function(e,t){var r=e.length;if(!r)return 0;var i,n,o,s,a=0,c=0,l=0;if(this.interim[0]){var h=!1,u=this.interim[0];u&=192==(224&u)?31:224==(240&u)?15:7;for(var f=0,_=void 0;(_=63&this.interim[++f])&&f<4;)u<<=6,u|=_;for(var d=192==(224&this.interim[0])?2:224==(240&this.interim[0])?3:4,p=d-f;l=r)return 0;if(128!=(192&(_=e[l++]))){l--,h=!0;break}this.interim[f++]=_,u<<=6,u|=63&_}h||(2===d?u<128?l--:t[a++]=u:3===d?u<2048||u>=55296&&u<=57343||(t[a++]=u):u<65536||u>1114111||(t[a++]=u)),this.interim.fill(0)}for(var v=r-4,g=l;g=r)return this.interim[0]=i,a;if(128!=(192&(n=e[g++]))){g--;continue}if((c=(31&i)<<6|63&n)<128){g--;continue}t[a++]=c}else if(224==(240&i)){if(g>=r)return this.interim[0]=i,a;if(128!=(192&(n=e[g++]))){g--;continue}if(g>=r)return this.interim[0]=i,this.interim[1]=n,a;if(128!=(192&(o=e[g++]))){g--;continue}if((c=(15&i)<<12|(63&n)<<6|63&o)<2048||c>=55296&&c<=57343)continue;t[a++]=c}else if(240==(248&i)){if(g>=r)return this.interim[0]=i,a;if(128!=(192&(n=e[g++]))){g--;continue}if(g>=r)return this.interim[0]=i,this.interim[1]=n,a;if(128!=(192&(o=e[g++]))){g--;continue}if(g>=r)return this.interim[0]=i,this.interim[1]=n,this.interim[2]=o,a;if(128!=(192&(s=e[g++]))){g--;continue}if((c=(7&i)<<18|(63&n)<<12|(63&o)<<6|63&s)<65536||c>1114111)continue;t[a++]=c}}return a},e}();t.Utf8ToUtf32=n},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.addDisposableDomListener=function(e,t,r,i){e.addEventListener(t,r,i);var n=!1;return{dispose:function(){n||(n=!0,e.removeEventListener(t,r,i))}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.INVERTED_DEFAULT_COLOR=257,t.DIM_OPACITY=.5,t.CHAR_ATLAS_CELL_SPACING=1},function(e,t,r){"use strict";var i,n,o,s;function a(e){var t=e.toString(16);return t.length<2?"0"+t:t}function c(e,t){return e>>0}}(i=t.channels||(t.channels={})),(n=t.color||(t.color={})).blend=function(e,t){var r=(255&t.rgba)/255;if(1===r)return{css:t.css,rgba:t.rgba};var n=t.rgba>>24&255,o=t.rgba>>16&255,s=t.rgba>>8&255,a=e.rgba>>24&255,c=e.rgba>>16&255,l=e.rgba>>8&255,h=a+Math.round((n-a)*r),u=c+Math.round((o-c)*r),f=l+Math.round((s-l)*r);return{css:i.toCss(h,u,f),rgba:i.toRgba(h,u,f)}},n.ensureContrastRatio=function(e,t,r){var i=s.ensureContrastRatio(e.rgba,t.rgba,r);if(i)return s.toColor(i>>24&255,i>>16&255,i>>8&255)},n.opaque=function(e){var t=(255|e.rgba)>>>0,r=s.toChannels(t),n=r[0],o=r[1],a=r[2];return{css:i.toCss(n,o,a),rgba:t}},(t.css||(t.css={})).toColor=function(e){return{css:e,rgba:(parseInt(e.slice(1),16)<<8|255)>>>0}},function(e){function t(e,t,r){var i=e/255,n=t/255,o=r/255;return.2126*(i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4))+.7152*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))+.0722*(o<=.03928?o/12.92:Math.pow((o+.055)/1.055,2.4))}e.relativeLuminance=function(e){return t(e>>16&255,e>>8&255,255&e)},e.relativeLuminance2=t}(o=t.rgb||(t.rgb={})),function(e){function t(e,t,r){for(var i=e>>24&255,n=e>>16&255,s=e>>8&255,a=t>>24&255,l=t>>16&255,h=t>>8&255,u=c(o.relativeLuminance2(a,h,l),o.relativeLuminance2(i,n,s));u0||l>0||h>0);)a-=Math.max(0,Math.ceil(.1*a)),l-=Math.max(0,Math.ceil(.1*l)),h-=Math.max(0,Math.ceil(.1*h)),u=c(o.relativeLuminance2(a,h,l),o.relativeLuminance2(i,n,s));return(a<<24|l<<16|h<<8|255)>>>0}function r(e,t,r){for(var i=e>>24&255,n=e>>16&255,s=e>>8&255,a=t>>24&255,l=t>>16&255,h=t>>8&255,u=c(o.relativeLuminance2(a,h,l),o.relativeLuminance2(i,n,s));u>>0}e.ensureContrastRatio=function(e,i,n){var s=o.relativeLuminance(e>>8),a=o.relativeLuminance(i>>8);if(c(s,a)>24&255,e>>16&255,e>>8&255,255&e]},e.toColor=function(e,t,r){return{css:i.toCss(e,t,r),rgba:i.toRgba(e,t,r)}}}(s=t.rgba||(t.rgba={})),t.toPaddedHex=a,t.contrastRatio=c},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i="undefined"==typeof navigator,n=i?"node":navigator.userAgent,o=i?"node":navigator.platform;function s(e,t){return e.indexOf(t)>=0}t.isFirefox=!!~n.indexOf("Firefox"),t.isSafari=/^((?!chrome|android).)*safari/i.test(n),t.isMac=s(["Macintosh","MacIntel","MacPPC","Mac68K"],o),t.isIpad="iPad"===o,t.isIphone="iPhone"===o,t.isWindows=s(["Windows","Win16","Win32","WinCE"],o),t.isLinux=o.indexOf("Linux")>=0},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){e.NUL="\0",e.SOH="",e.STX="",e.ETX="",e.EOT="",e.ENQ="",e.ACK="",e.BEL="",e.BS="\b",e.HT="\t",e.LF="\n",e.VT="\v",e.FF="\f",e.CR="\r",e.SO="",e.SI="",e.DLE="",e.DC1="",e.DC2="",e.DC3="",e.DC4="",e.NAK="",e.SYN="",e.ETB="",e.CAN="",e.EM="",e.SUB="",e.ESC="",e.FS="",e.GS="",e.RS="",e.US="",e.SP=" ",e.DEL=""}(t.C0||(t.C0={})),function(e){e.PAD="€",e.HOP="",e.BPH="‚",e.NBH="ƒ",e.IND="„",e.NEL="…",e.SSA="†",e.ESA="‡",e.HTS="ˆ",e.HTJ="‰",e.VTS="Š",e.PLD="‹",e.PLU="Œ",e.RI="",e.SS2="Ž",e.SS3="",e.DCS="",e.PU1="‘",e.PU2="’",e.STS="“",e.CCH="”",e.MW="•",e.SPA="–",e.EPA="—",e.SOS="˜",e.SGCI="™",e.SCI="š",e.CSI="›",e.ST="œ",e.OSC="",e.PM="ž",e.APC="Ÿ"}(t.C1||(t.C1={}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(3),n=r(9),o=r(23),s=r(6),a=r(26),c=r(10),l=function(){function e(e,t,r,i,n,o,s,a){this._container=e,this._alpha=i,this._colors=n,this._rendererId=o,this._bufferService=s,this._optionsService=a,this._scaledCharWidth=0,this._scaledCharHeight=0,this._scaledCellWidth=0,this._scaledCellHeight=0,this._scaledCharLeft=0,this._scaledCharTop=0,this._currentGlyphIdentifier={chars:"",code:0,bg:0,fg:0,bold:!1,dim:!1,italic:!1},this._canvas=document.createElement("canvas"),this._canvas.classList.add("xterm-"+t+"-layer"),this._canvas.style.zIndex=r.toString(),this._initCanvas(),this._container.appendChild(this._canvas)}return e.prototype.dispose=function(){var e;this._container.removeChild(this._canvas),null===(e=this._charAtlas)||void 0===e||e.dispose()},e.prototype._initCanvas=function(){this._ctx=a.throwIfFalsy(this._canvas.getContext("2d",{alpha:this._alpha})),this._alpha||this._clearAll()},e.prototype.onOptionsChanged=function(){},e.prototype.onBlur=function(){},e.prototype.onFocus=function(){},e.prototype.onCursorMove=function(){},e.prototype.onGridChanged=function(e,t){},e.prototype.onSelectionChanged=function(e,t,r){void 0===r&&(r=!1)},e.prototype.setColors=function(e){this._refreshCharAtlas(e)},e.prototype._setTransparency=function(e){if(e!==this._alpha){var t=this._canvas;this._alpha=e,this._canvas=this._canvas.cloneNode(),this._initCanvas(),this._container.replaceChild(this._canvas,t),this._refreshCharAtlas(this._colors),this.onGridChanged(0,this._bufferService.rows-1)}},e.prototype._refreshCharAtlas=function(e){this._scaledCharWidth<=0&&this._scaledCharHeight<=0||(this._charAtlas=o.acquireCharAtlas(this._optionsService.options,this._rendererId,e,this._scaledCharWidth,this._scaledCharHeight),this._charAtlas.warmUp())},e.prototype.resize=function(e){this._scaledCellWidth=e.scaledCellWidth,this._scaledCellHeight=e.scaledCellHeight,this._scaledCharWidth=e.scaledCharWidth,this._scaledCharHeight=e.scaledCharHeight,this._scaledCharLeft=e.scaledCharLeft,this._scaledCharTop=e.scaledCharTop,this._canvas.width=e.scaledCanvasWidth,this._canvas.height=e.scaledCanvasHeight,this._canvas.style.width=e.canvasWidth+"px",this._canvas.style.height=e.canvasHeight+"px",this._alpha||this._clearAll(),this._refreshCharAtlas(this._colors)},e.prototype._fillCells=function(e,t,r,i){this._ctx.fillRect(e*this._scaledCellWidth,t*this._scaledCellHeight,r*this._scaledCellWidth,i*this._scaledCellHeight)},e.prototype._fillBottomLineAtCells=function(e,t,r){void 0===r&&(r=1),this._ctx.fillRect(e*this._scaledCellWidth,(t+1)*this._scaledCellHeight-window.devicePixelRatio-1,r*this._scaledCellWidth,window.devicePixelRatio)},e.prototype._fillLeftLineAtCell=function(e,t,r){this._ctx.fillRect(e*this._scaledCellWidth,t*this._scaledCellHeight,window.devicePixelRatio*r,this._scaledCellHeight)},e.prototype._strokeRectAtCell=function(e,t,r,i){this._ctx.lineWidth=window.devicePixelRatio,this._ctx.strokeRect(e*this._scaledCellWidth+window.devicePixelRatio/2,t*this._scaledCellHeight+window.devicePixelRatio/2,r*this._scaledCellWidth-window.devicePixelRatio,i*this._scaledCellHeight-window.devicePixelRatio)},e.prototype._clearAll=function(){this._alpha?this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height):(this._ctx.fillStyle=this._colors.background.css,this._ctx.fillRect(0,0,this._canvas.width,this._canvas.height))},e.prototype._clearCells=function(e,t,r,i){this._alpha?this._ctx.clearRect(e*this._scaledCellWidth,t*this._scaledCellHeight,r*this._scaledCellWidth,i*this._scaledCellHeight):(this._ctx.fillStyle=this._colors.background.css,this._ctx.fillRect(e*this._scaledCellWidth,t*this._scaledCellHeight,r*this._scaledCellWidth,i*this._scaledCellHeight))},e.prototype._fillCharTrueColor=function(e,t,r){this._ctx.font=this._getFont(!1,!1),this._ctx.textBaseline="middle",this._clipRow(r),this._ctx.fillText(e.getChars(),t*this._scaledCellWidth+this._scaledCharLeft,r*this._scaledCellHeight+this._scaledCharTop+this._scaledCharHeight/2)},e.prototype._drawChars=function(e,t,r){var o,s,a=this._getContrastColor(e);a||e.isFgRGB()||e.isBgRGB()?this._drawUncachedChars(e,t,r,a):(e.isInverse()?(o=e.isBgDefault()?n.INVERTED_DEFAULT_COLOR:e.getBgColor(),s=e.isFgDefault()?n.INVERTED_DEFAULT_COLOR:e.getFgColor()):(s=e.isBgDefault()?i.DEFAULT_COLOR:e.getBgColor(),o=e.isFgDefault()?i.DEFAULT_COLOR:e.getFgColor()),o+=this._optionsService.options.drawBoldTextInBrightColors&&e.isBold()&&o<8?8:0,this._currentGlyphIdentifier.chars=e.getChars()||i.WHITESPACE_CELL_CHAR,this._currentGlyphIdentifier.code=e.getCode()||i.WHITESPACE_CELL_CODE,this._currentGlyphIdentifier.bg=s,this._currentGlyphIdentifier.fg=o,this._currentGlyphIdentifier.bold=!!e.isBold(),this._currentGlyphIdentifier.dim=!!e.isDim(),this._currentGlyphIdentifier.italic=!!e.isItalic(),this._charAtlas&&this._charAtlas.draw(this._ctx,this._currentGlyphIdentifier,t*this._scaledCellWidth+this._scaledCharLeft,r*this._scaledCellHeight+this._scaledCharTop)||this._drawUncachedChars(e,t,r))},e.prototype._drawUncachedChars=function(e,t,r,i){if(this._ctx.save(),this._ctx.font=this._getFont(!!e.isBold(),!!e.isItalic()),this._ctx.textBaseline="middle",e.isInverse())if(i)this._ctx.fillStyle=i.css;else if(e.isBgDefault())this._ctx.fillStyle=c.color.opaque(this._colors.background).css;else if(e.isBgRGB())this._ctx.fillStyle="rgb("+s.AttributeData.toColorRGB(e.getBgColor()).join(",")+")";else{var o=e.getBgColor();this._optionsService.options.drawBoldTextInBrightColors&&e.isBold()&&o<8&&(o+=8),this._ctx.fillStyle=this._colors.ansi[o].css}else if(i)this._ctx.fillStyle=i.css;else if(e.isFgDefault())this._ctx.fillStyle=this._colors.foreground.css;else if(e.isFgRGB())this._ctx.fillStyle="rgb("+s.AttributeData.toColorRGB(e.getFgColor()).join(",")+")";else{var a=e.getFgColor();this._optionsService.options.drawBoldTextInBrightColors&&e.isBold()&&a<8&&(a+=8),this._ctx.fillStyle=this._colors.ansi[a].css}this._clipRow(r),e.isDim()&&(this._ctx.globalAlpha=n.DIM_OPACITY),this._ctx.fillText(e.getChars(),t*this._scaledCellWidth+this._scaledCharLeft,r*this._scaledCellHeight+this._scaledCharTop+this._scaledCharHeight/2),this._ctx.restore()},e.prototype._clipRow=function(e){this._ctx.beginPath(),this._ctx.rect(0,e*this._scaledCellHeight,this._bufferService.cols*this._scaledCellWidth,this._scaledCellHeight),this._ctx.clip()},e.prototype._getFont=function(e,t){return(t?"italic":"")+" "+(e?this._optionsService.options.fontWeightBold:this._optionsService.options.fontWeight)+" "+this._optionsService.options.fontSize*window.devicePixelRatio+"px "+this._optionsService.options.fontFamily},e.prototype._getContrastColor=function(e){if(1!==this._optionsService.options.minimumContrastRatio){var t=this._colors.contrastCache.getColor(e.bg,e.fg);if(void 0!==t)return t||void 0;var r=e.getFgColor(),i=e.getFgColorMode(),n=e.getBgColor(),o=e.getBgColorMode(),s=!!e.isInverse(),a=!!e.isInverse();if(s){var l=r;r=n,n=l;var h=i;i=o,o=h}var u=this._resolveBackgroundRgba(o,n,s),f=this._resolveForegroundRgba(i,r,s,a),_=c.rgba.ensureContrastRatio(u,f,this._optionsService.options.minimumContrastRatio);if(_){var d={css:c.channels.toCss(_>>24&255,_>>16&255,_>>8&255),rgba:_};return this._colors.contrastCache.setColor(e.bg,e.fg,d),d}this._colors.contrastCache.setColor(e.bg,e.fg,null)}},e.prototype._resolveBackgroundRgba=function(e,t,r){switch(e){case 16777216:case 33554432:return this._colors.ansi[t].rgba;case 50331648:return t<<8;case 0:default:return r?this._colors.foreground.rgba:this._colors.background.rgba}},e.prototype._resolveForegroundRgba=function(e,t,r,i){switch(e){case 16777216:case 33554432:return this._optionsService.options.drawBoldTextInBrightColors&&i&&t<8&&(t+=8),this._colors.ansi[t].rgba;case 50331648:return t<<8;case 0:default:return r?this._colors.background.rgba:this._colors.foreground.rgba}},e}();t.BaseRenderLayer=l},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});function i(e,t,r){t.di$target===t?t.di$dependencies.push({id:e,index:r}):(t.di$dependencies=[{id:e,index:r}],t.di$target=t)}t.serviceRegistry=new Map,t.getServiceDependencies=function(e){return e.di$dependencies||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);var r=function(e,t,n){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");i(r,e,n)};return r.toString=function(){return e},t.serviceRegistry.set(e,r),r}},function(e,t,r){"use strict";function i(e,t,r,i){if(void 0===r&&(r=0),void 0===i&&(i=e.length),r>=e.length)return e;r=(e.length+r)%e.length,i=i>=e.length?e.length:(e.length+i)%e.length;for(var n=r;n>22,2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):r]},e.prototype.set=function(e,t){this._data[3*e+1]=t[n.CHAR_DATA_ATTR_INDEX],t[n.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[e]=t[1],this._data[3*e+0]=2097152|e|t[n.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*e+0]=t[n.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|t[n.CHAR_DATA_WIDTH_INDEX]<<22},e.prototype.getWidth=function(e){return this._data[3*e+0]>>22},e.prototype.hasWidth=function(e){return 12582912&this._data[3*e+0]},e.prototype.getFg=function(e){return this._data[3*e+1]},e.prototype.getBg=function(e){return this._data[3*e+2]},e.prototype.hasContent=function(e){return 4194303&this._data[3*e+0]},e.prototype.getCodePoint=function(e){var t=this._data[3*e+0];return 2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):2097151&t},e.prototype.isCombined=function(e){return 2097152&this._data[3*e+0]},e.prototype.getString=function(e){var t=this._data[3*e+0];return 2097152&t?this._combined[e]:2097151&t?i.stringFromCodePoint(2097151&t):""},e.prototype.loadCell=function(e,t){var r=3*e;return t.content=this._data[r+0],t.fg=this._data[r+1],t.bg=this._data[r+2],2097152&t.content&&(t.combinedData=this._combined[e]),t},e.prototype.setCell=function(e,t){2097152&t.content&&(this._combined[e]=t.combinedData),this._data[3*e+0]=t.content,this._data[3*e+1]=t.fg,this._data[3*e+2]=t.bg},e.prototype.setCellFromCodePoint=function(e,t,r,i,n){this._data[3*e+0]=t|r<<22,this._data[3*e+1]=i,this._data[3*e+2]=n},e.prototype.addCodepointToCell=function(e,t){var r=this._data[3*e+0];2097152&r?this._combined[e]+=i.stringFromCodePoint(t):(2097151&r?(this._combined[e]=i.stringFromCodePoint(2097151&r)+i.stringFromCodePoint(t),r&=-2097152,r|=2097152):r=t|1<<22,this._data[3*e+0]=r)},e.prototype.insertCells=function(e,t,r,i){if((e%=this.length)&&2===this.getWidth(e-1)&&this.setCellFromCodePoint(e-1,0,1,(null==i?void 0:i.fg)||0,(null==i?void 0:i.bg)||0),t=0;--s)this.setCell(e+t+s,this.loadCell(e+s,n));for(s=0;sthis.length){var r=new Uint32Array(3*e);this.length&&(3*e=e&&delete this._combined[o]}}else this._data=new Uint32Array(0),this._combined={};this.length=e}},e.prototype.fill=function(e){this._combined={};for(var t=0;t=0;--e)if(4194303&this._data[3*e+0])return e+(this._data[3*e+0]>>22);return 0},e.prototype.copyCellsFrom=function(e,t,r,i,n){var o=e._data;if(n)for(var s=i-1;s>=0;s--)for(var a=0;a<3;a++)this._data[3*(r+s)+a]=o[3*(t+s)+a];else for(s=0;s=t&&(this._combined[l-t+r]=e._combined[l])}},e.prototype.translateToString=function(e,t,r){void 0===e&&(e=!1),void 0===t&&(t=0),void 0===r&&(r=this.length),e&&(r=Math.min(r,this.getTrimmedLength()));for(var o="";t>22||1}return o},e}();t.BufferLine=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.promptLabel="Terminal input",t.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CHARSETS={},t.DEFAULT_CHARSET=t.CHARSETS.B,t.CHARSETS[0]={"`":"◆",a:"▒",b:"␉",c:"␌",d:"␍",e:"␊",f:"°",g:"±",h:"␤",i:"␋",j:"┘",k:"┐",l:"┌",m:"└",n:"┼",o:"⎺",p:"⎻",q:"─",r:"⎼",s:"⎽",t:"├",u:"┤",v:"┴",w:"┬",x:"│",y:"≤",z:"≥","{":"π","|":"≠","}":"£","~":"·"},t.CHARSETS.A={"#":"£"},t.CHARSETS.B=null,t.CHARSETS[4]={"#":"£","@":"¾","[":"ij","\\":"½","]":"|","{":"¨","|":"f","}":"¼","~":"´"},t.CHARSETS.C=t.CHARSETS[5]={"[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS.R={"#":"£","@":"à","[":"°","\\":"ç","]":"§","{":"é","|":"ù","}":"è","~":"¨"},t.CHARSETS.Q={"@":"à","[":"â","\\":"ç","]":"ê","^":"î","`":"ô","{":"é","|":"ù","}":"è","~":"û"},t.CHARSETS.K={"@":"§","[":"Ä","\\":"Ö","]":"Ü","{":"ä","|":"ö","}":"ü","~":"ß"},t.CHARSETS.Y={"#":"£","@":"§","[":"°","\\":"ç","]":"é","`":"ù","{":"à","|":"ò","}":"è","~":"ì"},t.CHARSETS.E=t.CHARSETS[6]={"@":"Ä","[":"Æ","\\":"Ø","]":"Å","^":"Ü","`":"ä","{":"æ","|":"ø","}":"å","~":"ü"},t.CHARSETS.Z={"#":"£","@":"§","[":"¡","\\":"Ñ","]":"¿","{":"°","|":"ñ","}":"ç"},t.CHARSETS.H=t.CHARSETS[7]={"@":"É","[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS["="]={"#":"ù","@":"à","[":"é","\\":"ç","]":"ê","^":"î",_:"è","`":"ô","{":"ä","|":"ö","}":"ü","~":"û"}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){if(void 0===e&&(e=32),void 0===t&&(t=32),this.maxLength=e,this.maxSubParamsLength=t,t>256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}return e.fromArray=function(t){var r=new e;if(!t.length)return r;for(var i=t[0]instanceof Array?1:0;i>8,i=255&this._subParamsIdx[t];i-r>0&&e.push(Array.prototype.slice.call(this._subParams,r,i))}return e},e.prototype.reset=function(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1},e.prototype.addParam=function(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>2147483647?2147483647:e}},e.prototype.addSubParam=function(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=e>2147483647?2147483647:e,this._subParamsIdx[this.length-1]++}},e.prototype.hasSubParams=function(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0},e.prototype.getSubParams=function(e){var t=this._subParamsIdx[e]>>8,r=255&this._subParamsIdx[e];return r-t>0?this._subParams.subarray(t,r):null},e.prototype.getSubParamsAll=function(){for(var e={},t=0;t>8,i=255&this._subParamsIdx[t];i-r>0&&(e[t]=this._subParams.slice(r,i))}return e},e.prototype.addDigit=function(e){var t;if(!(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)){var r=this._digitIsSub?this._subParams:this.params,i=r[t-1];r[t-1]=~i?Math.min(10*i+e,2147483647):e}},e}();t.Params=i},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(21),n=r(7),o=function(){function e(){this._state=0,this._id=-1,this._handlers=Object.create(null),this._handlerFb=function(){}}return e.prototype.addHandler=function(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);var r=this._handlers[e];return r.push(t),{dispose:function(){var e=r.indexOf(t);-1!==e&&r.splice(e,1)}}},e.prototype.setHandler=function(e,t){this._handlers[e]=[t]},e.prototype.clearHandler=function(e){this._handlers[e]&&delete this._handlers[e]},e.prototype.setHandlerFallback=function(e){this._handlerFb=e},e.prototype.dispose=function(){this._handlers=Object.create(null),this._handlerFb=function(){}},e.prototype.reset=function(){2===this._state&&this.end(!1),this._id=-1,this._state=0},e.prototype._start=function(){var e=this._handlers[this._id];if(e)for(var t=e.length-1;t>=0;t--)e[t].start();else this._handlerFb(this._id,"START")},e.prototype._put=function(e,t,r){var i=this._handlers[this._id];if(i)for(var o=i.length-1;o>=0;o--)i[o].put(e,t,r);else this._handlerFb(this._id,"PUT",n.utf32ToString(e,t,r))},e.prototype._end=function(e){var t=this._handlers[this._id];if(t){for(var r=t.length-1;r>=0&&!1===t[r].end(e);r--);for(r--;r>=0;r--)t[r].end(!1)}else this._handlerFb(this._id,"END",e)},e.prototype.start=function(){this.reset(),this._id=-1,this._state=1},e.prototype.put=function(e,t,r){if(3!==this._state){if(1===this._state)for(;t0&&this._put(e,t,r)}},e.prototype.end=function(e){0!==this._state&&(3!==this._state&&(1===this._state&&this._start(),this._end(e)),this._id=-1,this._state=0)},e}();t.OscParser=o;var s=function(){function e(e){this._handler=e,this._data="",this._hitLimit=!1}return e.prototype.start=function(){this._data="",this._hitLimit=!1},e.prototype.put=function(e,t,r){this._hitLimit||(this._data+=n.utf32ToString(e,t,r),this._data.length>i.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))},e.prototype.end=function(e){var t;return this._hitLimit?t=!1:e&&(t=this._handler(this._data)),this._data="",this._hitLimit=!1,t},e}();t.OscHandler=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=1e7},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(7),n=r(19),o=r(21),s=[],a=function(){function e(){this._handlers=Object.create(null),this._active=s,this._ident=0,this._handlerFb=function(){}}return e.prototype.dispose=function(){this._handlers=Object.create(null),this._handlerFb=function(){}},e.prototype.addHandler=function(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);var r=this._handlers[e];return r.push(t),{dispose:function(){var e=r.indexOf(t);-1!==e&&r.splice(e,1)}}},e.prototype.setHandler=function(e,t){this._handlers[e]=[t]},e.prototype.clearHandler=function(e){this._handlers[e]&&delete this._handlers[e]},e.prototype.setHandlerFallback=function(e){this._handlerFb=e},e.prototype.reset=function(){this._active.length&&this.unhook(!1),this._active=s,this._ident=0},e.prototype.hook=function(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||s,this._active.length)for(var r=this._active.length-1;r>=0;r--)this._active[r].hook(t);else this._handlerFb(this._ident,"HOOK",t)},e.prototype.put=function(e,t,r){if(this._active.length)for(var n=this._active.length-1;n>=0;n--)this._active[n].put(e,t,r);else this._handlerFb(this._ident,"PUT",i.utf32ToString(e,t,r))},e.prototype.unhook=function(e){if(this._active.length){for(var t=this._active.length-1;t>=0&&!1===this._active[t].unhook(e);t--);for(t--;t>=0;t--)this._active[t].unhook(!1)}else this._handlerFb(this._ident,"UNHOOK",e);this._active=s,this._ident=0},e}();t.DcsParser=a;var c=function(){function e(e){this._handler=e,this._data="",this._hitLimit=!1}return e.prototype.hook=function(e){this._params=e.clone(),this._data="",this._hitLimit=!1},e.prototype.put=function(e,t,r){this._hitLimit||(this._data+=i.utf32ToString(e,t,r),this._data.length>o.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))},e.prototype.unhook=function(e){var t;return this._hitLimit?t=!1:e&&(t=this._handler(this._data,this._params?this._params:new n.Params)),this._params=void 0,this._data="",this._hitLimit=!1,t},e}();t.DcsHandler=c},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(24),n=r(42),o=[];t.acquireCharAtlas=function(e,t,r,s,a){for(var c=i.generateConfig(s,a,e,r),l=0;l=0){if(i.configEquals(u.config,c))return u.atlas;1===u.ownedBy.length?(u.atlas.dispose(),o.splice(l,1)):u.ownedBy.splice(h,1);break}}for(l=0;l1)for(var u=this._getJoinedRanges(i,a,o,t,n),f=0;f1)for(u=this._getJoinedRanges(i,a,o,t,n),f=0;f=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new i.CellData)},e.prototype.translateToString=function(e,t,r){return this._line.translateToString(e,t,r)},e}(),f=function(){function e(e){this._core=e}return e.prototype.registerCsiHandler=function(e,t){return this._core.addCsiHandler(e,(function(e){return t(e.toArray())}))},e.prototype.addCsiHandler=function(e,t){return this.registerCsiHandler(e,t)},e.prototype.registerDcsHandler=function(e,t){return this._core.addDcsHandler(e,(function(e,r){return t(e,r.toArray())}))},e.prototype.addDcsHandler=function(e,t){return this.registerDcsHandler(e,t)},e.prototype.registerEscHandler=function(e,t){return this._core.addEscHandler(e,t)},e.prototype.addEscHandler=function(e,t){return this.registerEscHandler(e,t)},e.prototype.registerOscHandler=function(e,t){return this._core.addOscHandler(e,t)},e.prototype.addOscHandler=function(e,t){return this.registerOscHandler(e,t)},e}(),_=function(){function e(e){this._core=e}return e.prototype.register=function(e){this._core.unicodeService.register(e)},Object.defineProperty(e.prototype,"versions",{get:function(){return this._core.unicodeService.versions},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"activeVersion",{get:function(){return this._core.unicodeService.activeVersion},set:function(e){this._core.unicodeService.activeVersion=e},enumerable:!0,configurable:!0}),e}()},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=r(34),s=r(35),a=r(36),c=r(12),l=r(37),h=r(39),u=r(49),f=r(50),_=r(11),d=r(8),p=r(17),v=r(53),g=r(54),y=r(55),b=r(56),m=r(58),S=r(0),C=r(16),w=r(59),E=r(25),L=r(60),A=r(1),k=r(61),R=r(4),x=r(62),D=r(63),T=r(2),M=r(69),O=r(70),P=r(71),H=r(72),I=r(73),B=r(74),F=r(75),j=r(76),W=r(77),q=r(78),U=r(80),N="undefined"!=typeof window?window.document:null,z=function(e){function t(t){void 0===t&&(t={});var r=e.call(this)||this;return r.browser=_,r.mouseEvents=0,r._keyDownHandled=!1,r._blankLine=null,r._onCursorMove=new S.EventEmitter,r._onData=new S.EventEmitter,r._onBinary=new S.EventEmitter,r._onKey=new S.EventEmitter,r._onLineFeed=new S.EventEmitter,r._onRender=new S.EventEmitter,r._onResize=new S.EventEmitter,r._onScroll=new S.EventEmitter,r._onSelectionChange=new S.EventEmitter,r._onTitleChange=new S.EventEmitter,r._onFocus=new S.EventEmitter,r._onBlur=new S.EventEmitter,r.onA11yCharEmitter=new S.EventEmitter,r.onA11yTabEmitter=new S.EventEmitter,r._instantiationService=new I.InstantiationService,r.optionsService=new k.OptionsService(t),r._instantiationService.setService(A.IOptionsService,r.optionsService),r._bufferService=r._instantiationService.createInstance(D.BufferService),r._instantiationService.setService(A.IBufferService,r._bufferService),r._logService=r._instantiationService.createInstance(P.LogService),r._instantiationService.setService(A.ILogService,r._logService),r._coreService=r._instantiationService.createInstance(O.CoreService,(function(){return r.scrollToBottom()})),r._instantiationService.setService(A.ICoreService,r._coreService),r._coreService.onData((function(e){return r._onData.fire(e)})),r._coreService.onBinary((function(e){return r._onBinary.fire(e)})),r._coreMouseService=r._instantiationService.createInstance(B.CoreMouseService),r._instantiationService.setService(A.ICoreMouseService,r._coreMouseService),r._dirtyRowService=r._instantiationService.createInstance(H.DirtyRowService),r._instantiationService.setService(A.IDirtyRowService,r._dirtyRowService),r.unicodeService=r._instantiationService.createInstance(q.UnicodeService),r._instantiationService.setService(A.IUnicodeService,r.unicodeService),r._charsetService=r._instantiationService.createInstance(U.CharsetService),r._instantiationService.setService(A.ICharsetService,r._charsetService),r._setupOptionsListeners(),r._setup(),r._writeBuffer=new F.WriteBuffer((function(e){return r._inputHandler.parse(e)})),r}return n(t,e),Object.defineProperty(t.prototype,"options",{get:function(){return this.optionsService.options},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"cols",{get:function(){return this._bufferService.cols},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"rows",{get:function(){return this._bufferService.rows},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onCursorMove",{get:function(){return this._onCursorMove.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onData",{get:function(){return this._onData.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onBinary",{get:function(){return this._onBinary.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onKey",{get:function(){return this._onKey.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onLineFeed",{get:function(){return this._onLineFeed.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onRender",{get:function(){return this._onRender.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onResize",{get:function(){return this._onResize.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onScroll",{get:function(){return this._onScroll.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onSelectionChange",{get:function(){return this._onSelectionChange.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onTitleChange",{get:function(){return this._onTitleChange.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onFocus",{get:function(){return this._onFocus.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onBlur",{get:function(){return this._onBlur.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onA11yChar",{get:function(){return this.onA11yCharEmitter.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onA11yTab",{get:function(){return this.onA11yTabEmitter.event},enumerable:!0,configurable:!0}),t.prototype.dispose=function(){var t,r,i,n;this._isDisposed||(e.prototype.dispose.call(this),null===(t=this._windowsMode)||void 0===t||t.dispose(),this._windowsMode=void 0,null===(r=this._renderService)||void 0===r||r.dispose(),this._customKeyEventHandler=null,this.write=function(){},null===(n=null===(i=this.element)||void 0===i?void 0:i.parentNode)||void 0===n||n.removeChild(this.element))},t.prototype._setup=function(){var e=this;this._customKeyEventHandler=null,this.insertMode=!1,this.bracketedPasteMode=!1,this._userScrolling=!1,this._inputHandler?this._inputHandler.reset():(this._inputHandler=new l.InputHandler(this,this._bufferService,this._charsetService,this._coreService,this._dirtyRowService,this._logService,this.optionsService,this._coreMouseService,this.unicodeService,this._instantiationService),this._inputHandler.onRequestBell((function(){return e.bell()})),this._inputHandler.onRequestRefreshRows((function(t,r){return e.refresh(t,r)})),this._inputHandler.onRequestReset((function(){return e.reset()})),this._inputHandler.onCursorMove((function(){return e._onCursorMove.fire()})),this._inputHandler.onLineFeed((function(){return e._onLineFeed.fire()})),this.register(this._inputHandler)),this.linkifier||(this.linkifier=new u.Linkifier(this._bufferService,this._logService,this.optionsService,this.unicodeService)),this.linkifier2||(this.linkifier2=new j.Linkifier2(this._bufferService)),this.options.windowsMode&&this._enableWindowsMode()},t.prototype._enableWindowsMode=function(){var e=this;if(!this._windowsMode){var t=[];t.push(this.onLineFeed(w.updateWindowsModeWrappedState.bind(null,this._bufferService))),t.push(this.addCsiHandler({final:"H"},(function(){return w.updateWindowsModeWrappedState(e._bufferService),!1}))),this._windowsMode={dispose:function(){t.forEach((function(e){return e.dispose()}))}}}},Object.defineProperty(t.prototype,"buffer",{get:function(){return this.buffers.active},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"buffers",{get:function(){return this._bufferService.buffers},enumerable:!0,configurable:!0}),t.prototype.focus=function(){this.textarea&&this.textarea.focus({preventScroll:!0})},t.prototype._setupOptionsListeners=function(){var e=this;this.optionsService.onOptionChange((function(t){var r,i,n,o,s;switch(t){case"fontFamily":case"fontSize":null===(r=e._renderService)||void 0===r||r.clear(),null===(i=e._charSizeService)||void 0===i||i.measure();break;case"cursorBlink":case"cursorStyle":e.refresh(e.buffer.y,e.buffer.y);break;case"drawBoldTextInBrightColors":case"letterSpacing":case"lineHeight":case"fontWeight":case"fontWeightBold":case"minimumContrastRatio":e._renderService&&(e._renderService.clear(),e._renderService.onResize(e.cols,e.rows),e.refresh(0,e.rows-1));break;case"rendererType":e._renderService&&(e._renderService.setRenderer(e._createRenderer()),e._renderService.onResize(e.cols,e.rows));break;case"scrollback":e.buffers.resize(e.cols,e.rows),null===(n=e.viewport)||void 0===n||n.syncScrollArea();break;case"screenReaderMode":e.optionsService.options.screenReaderMode?!e._accessibilityManager&&e._renderService&&(e._accessibilityManager=new y.AccessibilityManager(e,e._renderService)):(null===(o=e._accessibilityManager)||void 0===o||o.dispose(),e._accessibilityManager=null);break;case"tabStopWidth":e.buffers.setupTabStops();break;case"theme":e._setTheme(e.optionsService.options.theme);break;case"windowsMode":e.optionsService.options.windowsMode?e._enableWindowsMode():(null===(s=e._windowsMode)||void 0===s||s.dispose(),e._windowsMode=void 0)}}))},t.prototype._onTextAreaFocus=function(e){this.sendFocus&&this._coreService.triggerDataEvent(c.C0.ESC+"[I"),this.updateCursorStyle(e),this.element.classList.add("focus"),this.showCursor(),this._onFocus.fire()},t.prototype.blur=function(){return this.textarea.blur()},t.prototype._onTextAreaBlur=function(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.sendFocus&&this._coreService.triggerDataEvent(c.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()},t.prototype._initGlobal=function(){var e=this;this._bindKeys(),this.register(d.addDisposableDomListener(this.element,"copy",(function(t){e.hasSelection()&&a.copyHandler(t,e._selectionService)})));var t=function(t){return a.handlePasteEvent(t,e.textarea,e.bracketedPasteMode,e._coreService)};this.register(d.addDisposableDomListener(this.textarea,"paste",t)),this.register(d.addDisposableDomListener(this.element,"paste",t)),_.isFirefox?this.register(d.addDisposableDomListener(this.element,"mousedown",(function(t){2===t.button&&a.rightClickHandler(t,e.textarea,e.screenElement,e._selectionService,e.options.rightClickSelectsWord)}))):this.register(d.addDisposableDomListener(this.element,"contextmenu",(function(t){a.rightClickHandler(t,e.textarea,e.screenElement,e._selectionService,e.options.rightClickSelectsWord)}))),_.isLinux&&this.register(d.addDisposableDomListener(this.element,"auxclick",(function(t){1===t.button&&a.moveTextAreaUnderMouseCursor(t,e.textarea,e.screenElement)})))},t.prototype._bindKeys=function(){var e=this;this.register(d.addDisposableDomListener(this.textarea,"keyup",(function(t){return e._keyUp(t)}),!0)),this.register(d.addDisposableDomListener(this.textarea,"keydown",(function(t){return e._keyDown(t)}),!0)),this.register(d.addDisposableDomListener(this.textarea,"keypress",(function(t){return e._keyPress(t)}),!0)),this.register(d.addDisposableDomListener(this.textarea,"compositionstart",(function(){return e._compositionHelper.compositionstart()}))),this.register(d.addDisposableDomListener(this.textarea,"compositionupdate",(function(t){return e._compositionHelper.compositionupdate(t)}))),this.register(d.addDisposableDomListener(this.textarea,"compositionend",(function(){return e._compositionHelper.compositionend()}))),this.register(this.onRender((function(){return e._compositionHelper.updateCompositionElements()}))),this.register(this.onRender((function(t){return e._queueLinkification(t.start,t.end)})))},t.prototype.open=function(e){var t=this;if(!e)throw new Error("Terminal requires a parent element.");N.body.contains(e)||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),this._document=e.ownerDocument,this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),this.element.setAttribute("tabindex","0"),e.appendChild(this.element);var r=N.createDocumentFragment();this._viewportElement=N.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),r.appendChild(this._viewportElement),this._viewportScrollArea=N.createElement("div"),this._viewportScrollArea.classList.add("xterm-scroll-area"),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=N.createElement("div"),this.screenElement.classList.add("xterm-screen"),this._helperContainer=N.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),r.appendChild(this.screenElement),this.textarea=N.createElement("textarea"),this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",p.promptLabel),this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this.register(d.addDisposableDomListener(this.textarea,"focus",(function(e){return t._onTextAreaFocus(e)}))),this.register(d.addDisposableDomListener(this.textarea,"blur",(function(){return t._onTextAreaBlur()}))),this._helperContainer.appendChild(this.textarea);var i=this._instantiationService.createInstance(W.CoreBrowserService,this.textarea);this._instantiationService.setService(R.ICoreBrowserService,i),this._charSizeService=this._instantiationService.createInstance(x.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(R.ICharSizeService,this._charSizeService),this._compositionView=N.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(o.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this.element.appendChild(r),this._theme=this.options.theme||this._theme,this.options.theme=void 0,this._colorManager=new E.ColorManager(N,this.options.allowTransparency),this.optionsService.onOptionChange((function(e){return t._colorManager.onOptionsChange(e)})),this._colorManager.setTheme(this._theme);var n=this._createRenderer();this._renderService=this._instantiationService.createInstance(L.RenderService,n,this.rows,this.screenElement),this._instantiationService.setService(R.IRenderService,this._renderService),this._renderService.onRender((function(e){return t._onRender.fire(e)})),this.onResize((function(e){return t._renderService.resize(e.cols,e.rows)})),this._soundService=this._instantiationService.createInstance(v.SoundService),this._instantiationService.setService(R.ISoundService,this._soundService),this._mouseService=this._instantiationService.createInstance(M.MouseService),this._instantiationService.setService(R.IMouseService,this._mouseService),this.viewport=this._instantiationService.createInstance(s.Viewport,(function(e,r){return t.scrollLines(e,r)}),this._viewportElement,this._viewportScrollArea),this.viewport.onThemeChange(this._colorManager.colors),this.register(this.viewport),this.register(this.onCursorMove((function(){return t._renderService.onCursorMove()}))),this.register(this.onResize((function(){return t._renderService.onResize(t.cols,t.rows)}))),this.register(this.onBlur((function(){return t._renderService.onBlur()}))),this.register(this.onFocus((function(){return t._renderService.onFocus()}))),this.register(this._renderService.onDimensionsChange((function(){return t.viewport.syncScrollArea()}))),this._selectionService=this._instantiationService.createInstance(f.SelectionService,(function(e,r){return t.scrollLines(e,r)}),this.element,this.screenElement),this._instantiationService.setService(R.ISelectionService,this._selectionService),this.register(this._selectionService.onSelectionChange((function(){return t._onSelectionChange.fire()}))),this.register(this._selectionService.onRedrawRequest((function(e){return t._renderService.onSelectionChanged(e.start,e.end,e.columnSelectMode)}))),this.register(this._selectionService.onLinuxMouseSelection((function(e){t.textarea.value=e,t.textarea.focus(),t.textarea.select()}))),this.register(this.onScroll((function(){t.viewport.syncScrollArea(),t._selectionService.refresh()}))),this.register(d.addDisposableDomListener(this._viewportElement,"scroll",(function(){return t._selectionService.refresh()}))),this._mouseZoneManager=this._instantiationService.createInstance(g.MouseZoneManager,this.element,this.screenElement),this.register(this._mouseZoneManager),this.register(this.onScroll((function(){return t._mouseZoneManager.clearAll()}))),this.linkifier.attachToDom(this.element,this._mouseZoneManager),this.linkifier2.attachToDom(this.element,this._mouseService,this._renderService),this.register(d.addDisposableDomListener(this.element,"mousedown",(function(e){return t._selectionService.onMouseDown(e)}))),this.mouseEvents?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager=new y.AccessibilityManager(this,this._renderService)),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()},t.prototype._createRenderer=function(){switch(this.options.rendererType){case"canvas":return this._instantiationService.createInstance(h.Renderer,this._colorManager.colors,this.screenElement,this.linkifier,this.linkifier2);case"dom":return this._instantiationService.createInstance(b.DomRenderer,this._colorManager.colors,this.element,this.screenElement,this._viewportElement,this.linkifier,this.linkifier2);default:throw new Error('Unrecognized rendererType "'+this.options.rendererType+'"')}},t.prototype._setTheme=function(e){var t,r,i;this._theme=e,null===(t=this._colorManager)||void 0===t||t.setTheme(e),null===(r=this._renderService)||void 0===r||r.setColors(this._colorManager.colors),null===(i=this.viewport)||void 0===i||i.onThemeChange(this._colorManager.colors)},t.prototype.bindMouse=function(){var e=this,t=this,r=this.element;function i(e){var r,i,n=t._mouseService.getRawByteCoords(e,t.screenElement,t.cols,t.rows);if(!n)return!1;switch(e.overrideType||e.type){case"mousemove":i=32,void 0===e.buttons?(r=3,void 0!==e.button&&(r=e.button<3?e.button:3)):r=1&e.buttons?0:4&e.buttons?1:2&e.buttons?2:3;break;case"mouseup":i=0,r=e.button<3?e.button:3;break;case"mousedown":i=1,r=e.button<3?e.button:3;break;case"wheel":0!==e.deltaY&&(i=e.deltaY<0?0:1),r=4;break;default:return!1}return!(void 0===i||void 0===r||r>4)&&t._coreMouseService.triggerMouseEvent({col:n.x-33,row:n.y-33,button:r,action:i,ctrl:e.ctrlKey,alt:e.altKey,shift:e.shiftKey})}var n={mouseup:null,wheel:null,mousedrag:null,mousemove:null},o=function(t){return i(t),t.buttons||(e._document.removeEventListener("mouseup",n.mouseup),n.mousedrag&&e._document.removeEventListener("mousemove",n.mousedrag)),e.cancel(t)},s=function(t){return i(t),t.preventDefault(),e.cancel(t)},a=function(e){e.buttons&&i(e)},l=function(e){e.buttons||i(e)};this._coreMouseService.onProtocolChange((function(t){e.mouseEvents=t,t?("debug"===e.optionsService.options.logLevel&&e._logService.debug("Binding to mouse events:",e._coreMouseService.explainEvents(t)),e.element.classList.add("enable-mouse-events"),e._selectionService.disable()):(e._logService.debug("Unbinding from mouse events."),e.element.classList.remove("enable-mouse-events"),e._selectionService.enable()),8&t?n.mousemove||(r.addEventListener("mousemove",l),n.mousemove=l):(r.removeEventListener("mousemove",n.mousemove),n.mousemove=null),16&t?n.wheel||(r.addEventListener("wheel",s),n.wheel=s):(r.removeEventListener("wheel",n.wheel),n.wheel=null),2&t?n.mouseup||(n.mouseup=o):(e._document.removeEventListener("mouseup",n.mouseup),n.mouseup=null),4&t?n.mousedrag||(n.mousedrag=a):(e._document.removeEventListener("mousemove",n.mousedrag),n.mousedrag=null)})),this._coreMouseService.activeProtocol=this._coreMouseService.activeProtocol,this.register(d.addDisposableDomListener(r,"mousedown",(function(t){if(t.preventDefault(),e.focus(),e.mouseEvents&&!e._selectionService.shouldForceSelection(t))return i(t),n.mouseup&&e._document.addEventListener("mouseup",n.mouseup),n.mousedrag&&e._document.addEventListener("mousemove",n.mousedrag),e.cancel(t)}))),this.register(d.addDisposableDomListener(r,"wheel",(function(t){if(n.wheel);else if(!e.buffer.hasScrollback){var r=e.viewport.getLinesScrolled(t);if(0===r)return;for(var i=c.C0.ESC+(e._coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(t.deltaY<0?"A":"B"),o="",s=0;s=this.buffer.ybase&&(this._userScrolling=!1);var r=this.buffer.ydisp;this.buffer.ydisp=Math.max(Math.min(this.buffer.ydisp+e,this.buffer.ybase),0),r!==this.buffer.ydisp&&(t||this._onScroll.fire(this.buffer.ydisp),this.refresh(0,this.rows-1))},t.prototype.scrollPages=function(e){this.scrollLines(e*(this.rows-1))},t.prototype.scrollToTop=function(){this.scrollLines(-this.buffer.ydisp)},t.prototype.scrollToBottom=function(){this.scrollLines(this.buffer.ybase-this.buffer.ydisp)},t.prototype.scrollToLine=function(e){var t=e-this.buffer.ydisp;0!==t&&this.scrollLines(t)},t.prototype.paste=function(e){a.paste(e,this.textarea,this.bracketedPasteMode,this._coreService)},t.prototype.attachCustomKeyEventHandler=function(e){this._customKeyEventHandler=e},t.prototype.addEscHandler=function(e,t){return this._inputHandler.addEscHandler(e,t)},t.prototype.addDcsHandler=function(e,t){return this._inputHandler.addDcsHandler(e,t)},t.prototype.addCsiHandler=function(e,t){return this._inputHandler.addCsiHandler(e,t)},t.prototype.addOscHandler=function(e,t){return this._inputHandler.addOscHandler(e,t)},t.prototype.registerLinkMatcher=function(e,t,r){var i=this.linkifier.registerLinkMatcher(e,t,r);return this.refresh(0,this.rows-1),i},t.prototype.deregisterLinkMatcher=function(e){this.linkifier.deregisterLinkMatcher(e)&&this.refresh(0,this.rows-1)},t.prototype.registerLinkProvider=function(e){return this.linkifier2.registerLinkProvider(e)},t.prototype.registerCharacterJoiner=function(e){var t=this._renderService.registerCharacterJoiner(e);return this.refresh(0,this.rows-1),t},t.prototype.deregisterCharacterJoiner=function(e){this._renderService.deregisterCharacterJoiner(e)&&this.refresh(0,this.rows-1)},Object.defineProperty(t.prototype,"markers",{get:function(){return this.buffer.markers},enumerable:!0,configurable:!0}),t.prototype.addMarker=function(e){if(this.buffer===this.buffers.normal)return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+e)},t.prototype.hasSelection=function(){return!!this._selectionService&&this._selectionService.hasSelection},t.prototype.select=function(e,t,r){this._selectionService.setSelection(e,t,r)},t.prototype.getSelection=function(){return this._selectionService?this._selectionService.selectionText:""},t.prototype.getSelectionPosition=function(){if(this._selectionService.hasSelection)return{startColumn:this._selectionService.selectionStart[0],startRow:this._selectionService.selectionStart[1],endColumn:this._selectionService.selectionEnd[0],endRow:this._selectionService.selectionEnd[1]}},t.prototype.clearSelection=function(){var e;null===(e=this._selectionService)||void 0===e||e.clearSelection()},t.prototype.selectAll=function(){var e;null===(e=this._selectionService)||void 0===e||e.selectAll()},t.prototype.selectLines=function(e,t){var r;null===(r=this._selectionService)||void 0===r||r.selectLines(e,t)},t.prototype._keyDown=function(e){if(this._keyDownHandled=!1,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(!this._compositionHelper.keydown(e))return this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;var t=m.evaluateKeyboardEvent(e,this._coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(e),3===t.type||2===t.type){var r=this.rows-1;return this.scrollLines(2===t.type?-r:r),this.cancel(e,!0)}return 1===t.type&&this.selectAll(),!!this._isThirdLevelShift(this.browser,e)||(t.cancel&&this.cancel(e,!0),!t.key||(t.key!==c.C0.ETX&&t.key!==c.C0.CR||(this.textarea.value=""),this._onKey.fire({key:t.key,domEvent:e}),this.showCursor(),this._coreService.triggerDataEvent(t.key,!0),this.optionsService.options.screenReaderMode?void(this._keyDownHandled=!0):this.cancel(e,!0)))},t.prototype._isThirdLevelShift=function(e,t){var r=e.isMac&&!this.options.macOptionIsMeta&&t.altKey&&!t.ctrlKey&&!t.metaKey||e.isWindows&&t.altKey&&t.ctrlKey&&!t.metaKey;return"keypress"===t.type?r:r&&(!t.keyCode||t.keyCode>47)},t.prototype._keyUp=function(e){this._customKeyEventHandler&&!1===this._customKeyEventHandler(e)||(function(e){return 16===e.keyCode||17===e.keyCode||18===e.keyCode}(e)||this.focus(),this.updateCursorStyle(e))},t.prototype._keyPress=function(e){var t;if(this._keyDownHandled)return!1;if(this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(this.cancel(e),e.charCode)t=e.charCode;else if(null===e.which||void 0===e.which)t=e.keyCode;else{if(0===e.which||0===e.charCode)return!1;t=e.which}return!(!t||(e.altKey||e.ctrlKey||e.metaKey)&&!this._isThirdLevelShift(this.browser,e))&&(t=String.fromCharCode(t),this._onKey.fire({key:t,domEvent:e}),this.showCursor(),this._coreService.triggerDataEvent(t,!0),!0)},t.prototype.bell=function(){var e=this;this._soundBell()&&this._soundService.playBellSound(),this._visualBell()&&(this.element.classList.add("visual-bell-active"),clearTimeout(this._visualBellTimer),this._visualBellTimer=window.setTimeout((function(){e.element.classList.remove("visual-bell-active")}),200))},t.prototype.resize=function(e,t){var r,i;isNaN(e)||isNaN(t)||(e!==this.cols||t!==this.rows?(e=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(4),s=r(1),a=function(){function e(e,t,r,i,n,o){this._textarea=e,this._compositionView=t,this._bufferService=r,this._optionsService=i,this._charSizeService=n,this._coreService=o,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0}}return e.prototype.compositionstart=function(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._compositionView.classList.add("active")},e.prototype.compositionupdate=function(e){var t=this;this._compositionView.textContent=e.data,this.updateCompositionElements(),setTimeout((function(){t._compositionPosition.end=t._textarea.value.length}),0)},e.prototype.compositionend=function(){this._finalizeComposition(!0)},e.prototype.keydown=function(e){if(this._isComposing||this._isSendingComposition){if(229===e.keyCode)return!1;if(16===e.keyCode||17===e.keyCode||18===e.keyCode)return!1;this._finalizeComposition(!1)}return 229!==e.keyCode||(this._handleAnyTextareaChanges(),!1)},e.prototype._finalizeComposition=function(e){var t=this;if(this._compositionView.classList.remove("active"),this._isComposing=!1,this._clearTextareaPosition(),e){var r={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout((function(){if(t._isSendingComposition){t._isSendingComposition=!1;var e=void 0;e=t._isComposing?t._textarea.value.substring(r.start,r.end):t._textarea.value.substring(r.start),t._coreService.triggerDataEvent(e,!0)}}),0)}else{this._isSendingComposition=!1;var i=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(i,!0)}},e.prototype._handleAnyTextareaChanges=function(){var e=this,t=this._textarea.value;setTimeout((function(){if(!e._isComposing){var r=e._textarea.value.replace(t,"");r.length>0&&e._coreService.triggerDataEvent(r,!0)}}),0)},e.prototype.updateCompositionElements=function(e){var t=this;if(this._isComposing){if(this._bufferService.buffer.isCursorInViewport){var r=Math.ceil(this._charSizeService.height*this._optionsService.options.lineHeight),i=this._bufferService.buffer.y*r,n=this._bufferService.buffer.x*this._charSizeService.width;this._compositionView.style.left=n+"px",this._compositionView.style.top=i+"px",this._compositionView.style.height=r+"px",this._compositionView.style.lineHeight=r+"px",this._compositionView.style.fontFamily=this._optionsService.options.fontFamily,this._compositionView.style.fontSize=this._optionsService.options.fontSize+"px";var o=this._compositionView.getBoundingClientRect();this._textarea.style.left=n+"px",this._textarea.style.top=i+"px",this._textarea.style.width=o.width+"px",this._textarea.style.height=o.height+"px",this._textarea.style.lineHeight=o.height+"px"}e||setTimeout((function(){return t.updateCompositionElements(!0)}),0)}},e.prototype._clearTextareaPosition=function(){this._textarea.style.left="",this._textarea.style.top=""},e=i([n(2,s.IBufferService),n(3,s.IOptionsService),n(4,o.ICharSizeService),n(5,s.ICoreService)],e)}();t.CompositionHelper=a},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),o=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(2),c=r(8),l=r(4),h=r(1),u=function(e){function t(t,r,i,n,o,s,a){var l=e.call(this)||this;return l._scrollLines=t,l._viewportElement=r,l._scrollArea=i,l._bufferService=n,l._optionsService=o,l._charSizeService=s,l._renderService=a,l.scrollBarWidth=0,l._currentRowHeight=0,l._lastRecordedBufferLength=0,l._lastRecordedViewportHeight=0,l._lastRecordedBufferHeight=0,l._lastTouchY=0,l._lastScrollTop=0,l._wheelPartialScroll=0,l._refreshAnimationFrame=null,l._ignoreNextScrollEvent=!1,l.scrollBarWidth=l._viewportElement.offsetWidth-l._scrollArea.offsetWidth||15,l.register(c.addDisposableDomListener(l._viewportElement,"scroll",l._onScroll.bind(l))),setTimeout((function(){return l.syncScrollArea()}),0),l}return n(t,e),t.prototype.onThemeChange=function(e){this._viewportElement.style.backgroundColor=e.background.css},t.prototype._refresh=function(e){var t=this;if(e)return this._innerRefresh(),void(null!==this._refreshAnimationFrame&&cancelAnimationFrame(this._refreshAnimationFrame));null===this._refreshAnimationFrame&&(this._refreshAnimationFrame=requestAnimationFrame((function(){return t._innerRefresh()})))},t.prototype._innerRefresh=function(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderService.dimensions.scaledCellHeight/window.devicePixelRatio,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;var e=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderService.dimensions.canvasHeight);this._lastRecordedBufferHeight!==e&&(this._lastRecordedBufferHeight=e,this._scrollArea.style.height=this._lastRecordedBufferHeight+"px")}var t=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==t&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=t),this._refreshAnimationFrame=null},t.prototype.syncScrollArea=function(e){if(void 0===e&&(e=!1),this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(e);if(this._lastRecordedViewportHeight===this._renderService.dimensions.canvasHeight){var t=this._bufferService.buffer.ydisp*this._currentRowHeight;this._lastScrollTop===t&&this._lastScrollTop===this._viewportElement.scrollTop&&this._renderService.dimensions.scaledCellHeight/window.devicePixelRatio===this._currentRowHeight||this._refresh(e)}else this._refresh(e)},t.prototype._onScroll=function(e){if(this._lastScrollTop=this._viewportElement.scrollTop,this._viewportElement.offsetParent)if(this._ignoreNextScrollEvent)this._ignoreNextScrollEvent=!1;else{var t=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._scrollLines(t,!0)}},t.prototype._bubbleScroll=function(e,t){var r=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(t<0&&0!==this._viewportElement.scrollTop||t>0&&r0?1:-1),this._wheelPartialScroll%=1):e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(t*=this._bufferService.rows),t},t.prototype._applyScrollModifier=function(e,t){var r=this._optionsService.options.fastScrollModifier;return"alt"===r&&t.altKey||"ctrl"===r&&t.ctrlKey||"shift"===r&&t.shiftKey?e*this._optionsService.options.fastScrollSensitivity*this._optionsService.options.scrollSensitivity:e*this._optionsService.options.scrollSensitivity},t.prototype.onTouchStart=function(e){this._lastTouchY=e.touches[0].pageY},t.prototype.onTouchMove=function(e){var t=this._lastTouchY-e.touches[0].pageY;return this._lastTouchY=e.touches[0].pageY,0!==t&&(this._viewportElement.scrollTop+=t,this._bubbleScroll(e,t))},t=o([s(3,h.IBufferService),s(4,h.IOptionsService),s(5,l.ICharSizeService),s(6,l.IRenderService)],t)}(a.Disposable);t.Viewport=u},function(e,t,r){"use strict";function i(e){return e.replace(/\r?\n/g,"\r")}function n(e,t){return t?"[200~"+e+"[201~":e}function o(e,t,r,o){e=n(e=i(e),r),o.triggerDataEvent(e,!0),t.value=""}function s(e,t,r){var i=r.getBoundingClientRect(),n=e.clientX-i.left-10,o=e.clientY-i.top-10;t.style.position="absolute",t.style.width="20px",t.style.height="20px",t.style.left=n+"px",t.style.top=o+"px",t.style.zIndex="1000",t.focus(),setTimeout((function(){t.style.position="",t.style.width="",t.style.height="",t.style.left="",t.style.top="",t.style.zIndex=""}),200)}Object.defineProperty(t,"__esModule",{value:!0}),t.prepareTextForTerminal=i,t.bracketTextForPaste=n,t.copyHandler=function(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()},t.handlePasteEvent=function(e,t,r,i){e.stopPropagation(),e.clipboardData&&o(e.clipboardData.getData("text/plain"),t,r,i)},t.paste=o,t.moveTextAreaUnderMouseCursor=s,t.rightClickHandler=function(e,t,r,i,n){s(e,t,r),n&&!i.isClickInSelection(e)&&i.selectWordAtCursor(e),t.value=i.selectionText,t.select()}},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=r(12),s=r(18),a=r(38),c=r(2),l=r(15),h=r(7),u=r(16),f=r(0),_=r(3),d=r(5),p=r(6),v=r(20),g=r(22),y=r(4),b={"(":0,")":1,"*":2,"+":3,"-":1,".":2};function m(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return!!t.restoreWin;case 2:return!!t.minimizeWin;case 3:return!!t.setWinPosition;case 4:return!!t.setWinSizePixels;case 5:return!!t.raiseWin;case 6:return!!t.lowerWin;case 7:return!!t.refreshWin;case 8:return!!t.setWinSizeChars;case 9:return!!t.maximizeWin;case 10:return!!t.fullscreenWin;case 11:return!!t.getWinState;case 13:return!!t.getWinPosition;case 14:return!!t.getWinSizePixels;case 15:return!!t.getScreenSizePixels;case 16:return!!t.getCellSizePixels;case 18:return!!t.getWinSizeChars;case 19:return!!t.getScreenSizeChars;case 20:return!!t.getIconTitle;case 21:return!!t.getWinTitle;case 22:return!!t.pushTitle;case 23:return!!t.popTitle;case 24:return!!t.setWinLines}return!1}var S=function(){function e(e,t,r,i){this._bufferService=e,this._coreService=t,this._logService=r,this._optionsService=i,this._data=new Uint32Array(0)}return e.prototype.hook=function(e){this._data=new Uint32Array(0)},e.prototype.put=function(e,t,r){this._data=l.concat(this._data,e.subarray(t,r))},e.prototype.unhook=function(e){if(e){var t=h.utf32ToString(this._data);switch(this._data=new Uint32Array(0),t){case'"q':return this._coreService.triggerDataEvent(o.C0.ESC+'P1$r0"q'+o.C0.ESC+"\\");case'"p':return this._coreService.triggerDataEvent(o.C0.ESC+'P1$r61;1"p'+o.C0.ESC+"\\");case"r":var r=this._bufferService.buffer.scrollTop+1+";"+(this._bufferService.buffer.scrollBottom+1)+"r";return this._coreService.triggerDataEvent(o.C0.ESC+"P1$r"+r+o.C0.ESC+"\\");case"m":return this._coreService.triggerDataEvent(o.C0.ESC+"P1$r0m"+o.C0.ESC+"\\");case" q":var i={block:2,underline:4,bar:6}[this._optionsService.options.cursorStyle];return i-=this._optionsService.options.cursorBlink?1:0,this._coreService.triggerDataEvent(o.C0.ESC+"P1$r"+i+" q"+o.C0.ESC+"\\");default:this._logService.debug("Unknown DCS $q %s",t),this._coreService.triggerDataEvent(o.C0.ESC+"P0$r"+o.C0.ESC+"\\")}}else this._data=new Uint32Array(0)},e}(),C=function(e){function t(t,r,i,n,c,l,_,p,g,y,b){void 0===b&&(b=new a.EscapeSequenceParser);var m=e.call(this)||this;m._terminal=t,m._bufferService=r,m._charsetService=i,m._coreService=n,m._dirtyRowService=c,m._logService=l,m._optionsService=_,m._coreMouseService=p,m._unicodeService=g,m._instantiationService=y,m._parser=b,m._parseBuffer=new Uint32Array(4096),m._stringDecoder=new h.StringToUtf32,m._utf8Decoder=new h.Utf8ToUtf32,m._workCell=new d.CellData,m._windowTitle="",m._iconName="",m._windowTitleStack=[],m._iconNameStack=[],m._curAttrData=u.DEFAULT_ATTR_DATA.clone(),m._eraseAttrDataInternal=u.DEFAULT_ATTR_DATA.clone(),m._onRequestRefreshRows=new f.EventEmitter,m._onRequestReset=new f.EventEmitter,m._onRequestBell=new f.EventEmitter,m._onCursorMove=new f.EventEmitter,m._onLineFeed=new f.EventEmitter,m._onScroll=new f.EventEmitter,m.register(m._parser),m._parser.setCsiHandlerFallback((function(e,t){m._logService.debug("Unknown CSI code: ",{identifier:m._parser.identToString(e),params:t.toArray()})})),m._parser.setEscHandlerFallback((function(e){m._logService.debug("Unknown ESC code: ",{identifier:m._parser.identToString(e)})})),m._parser.setExecuteHandlerFallback((function(e){m._logService.debug("Unknown EXECUTE code: ",{code:e})})),m._parser.setOscHandlerFallback((function(e,t,r){m._logService.debug("Unknown OSC code: ",{identifier:e,action:t,data:r})})),m._parser.setDcsHandlerFallback((function(e,t,r){"HOOK"===t&&(r=r.toArray()),m._logService.debug("Unknown DCS code: ",{identifier:m._parser.identToString(e),action:t,payload:r})})),m._parser.setPrintHandler((function(e,t,r){return m.print(e,t,r)})),m._parser.setCsiHandler({final:"@"},(function(e){return m.insertChars(e)})),m._parser.setCsiHandler({intermediates:" ",final:"@"},(function(e){return m.scrollLeft(e)})),m._parser.setCsiHandler({final:"A"},(function(e){return m.cursorUp(e)})),m._parser.setCsiHandler({intermediates:" ",final:"A"},(function(e){return m.scrollRight(e)})),m._parser.setCsiHandler({final:"B"},(function(e){return m.cursorDown(e)})),m._parser.setCsiHandler({final:"C"},(function(e){return m.cursorForward(e)})),m._parser.setCsiHandler({final:"D"},(function(e){return m.cursorBackward(e)})),m._parser.setCsiHandler({final:"E"},(function(e){return m.cursorNextLine(e)})),m._parser.setCsiHandler({final:"F"},(function(e){return m.cursorPrecedingLine(e)})),m._parser.setCsiHandler({final:"G"},(function(e){return m.cursorCharAbsolute(e)})),m._parser.setCsiHandler({final:"H"},(function(e){return m.cursorPosition(e)})),m._parser.setCsiHandler({final:"I"},(function(e){return m.cursorForwardTab(e)})),m._parser.setCsiHandler({final:"J"},(function(e){return m.eraseInDisplay(e)})),m._parser.setCsiHandler({prefix:"?",final:"J"},(function(e){return m.eraseInDisplay(e)})),m._parser.setCsiHandler({final:"K"},(function(e){return m.eraseInLine(e)})),m._parser.setCsiHandler({prefix:"?",final:"K"},(function(e){return m.eraseInLine(e)})),m._parser.setCsiHandler({final:"L"},(function(e){return m.insertLines(e)})),m._parser.setCsiHandler({final:"M"},(function(e){return m.deleteLines(e)})),m._parser.setCsiHandler({final:"P"},(function(e){return m.deleteChars(e)})),m._parser.setCsiHandler({final:"S"},(function(e){return m.scrollUp(e)})),m._parser.setCsiHandler({final:"T"},(function(e){return m.scrollDown(e)})),m._parser.setCsiHandler({final:"X"},(function(e){return m.eraseChars(e)})),m._parser.setCsiHandler({final:"Z"},(function(e){return m.cursorBackwardTab(e)})),m._parser.setCsiHandler({final:"`"},(function(e){return m.charPosAbsolute(e)})),m._parser.setCsiHandler({final:"a"},(function(e){return m.hPositionRelative(e)})),m._parser.setCsiHandler({final:"b"},(function(e){return m.repeatPrecedingCharacter(e)})),m._parser.setCsiHandler({final:"c"},(function(e){return m.sendDeviceAttributesPrimary(e)})),m._parser.setCsiHandler({prefix:">",final:"c"},(function(e){return m.sendDeviceAttributesSecondary(e)})),m._parser.setCsiHandler({final:"d"},(function(e){return m.linePosAbsolute(e)})),m._parser.setCsiHandler({final:"e"},(function(e){return m.vPositionRelative(e)})),m._parser.setCsiHandler({final:"f"},(function(e){return m.hVPosition(e)})),m._parser.setCsiHandler({final:"g"},(function(e){return m.tabClear(e)})),m._parser.setCsiHandler({final:"h"},(function(e){return m.setMode(e)})),m._parser.setCsiHandler({prefix:"?",final:"h"},(function(e){return m.setModePrivate(e)})),m._parser.setCsiHandler({final:"l"},(function(e){return m.resetMode(e)})),m._parser.setCsiHandler({prefix:"?",final:"l"},(function(e){return m.resetModePrivate(e)})),m._parser.setCsiHandler({final:"m"},(function(e){return m.charAttributes(e)})),m._parser.setCsiHandler({final:"n"},(function(e){return m.deviceStatus(e)})),m._parser.setCsiHandler({prefix:"?",final:"n"},(function(e){return m.deviceStatusPrivate(e)})),m._parser.setCsiHandler({intermediates:"!",final:"p"},(function(e){return m.softReset(e)})),m._parser.setCsiHandler({intermediates:" ",final:"q"},(function(e){return m.setCursorStyle(e)})),m._parser.setCsiHandler({final:"r"},(function(e){return m.setScrollRegion(e)})),m._parser.setCsiHandler({final:"s"},(function(e){return m.saveCursor(e)})),m._parser.setCsiHandler({final:"t"},(function(e){return m.windowOptions(e)})),m._parser.setCsiHandler({final:"u"},(function(e){return m.restoreCursor(e)})),m._parser.setCsiHandler({intermediates:"'",final:"}"},(function(e){return m.insertColumns(e)})),m._parser.setCsiHandler({intermediates:"'",final:"~"},(function(e){return m.deleteColumns(e)})),m._parser.setExecuteHandler(o.C0.BEL,(function(){return m.bell()})),m._parser.setExecuteHandler(o.C0.LF,(function(){return m.lineFeed()})),m._parser.setExecuteHandler(o.C0.VT,(function(){return m.lineFeed()})),m._parser.setExecuteHandler(o.C0.FF,(function(){return m.lineFeed()})),m._parser.setExecuteHandler(o.C0.CR,(function(){return m.carriageReturn()})),m._parser.setExecuteHandler(o.C0.BS,(function(){return m.backspace()})),m._parser.setExecuteHandler(o.C0.HT,(function(){return m.tab()})),m._parser.setExecuteHandler(o.C0.SO,(function(){return m.shiftOut()})),m._parser.setExecuteHandler(o.C0.SI,(function(){return m.shiftIn()})),m._parser.setExecuteHandler(o.C1.IND,(function(){return m.index()})),m._parser.setExecuteHandler(o.C1.NEL,(function(){return m.nextLine()})),m._parser.setExecuteHandler(o.C1.HTS,(function(){return m.tabSet()})),m._parser.setOscHandler(0,new v.OscHandler((function(e){m.setTitle(e),m.setIconName(e)}))),m._parser.setOscHandler(1,new v.OscHandler((function(e){return m.setIconName(e)}))),m._parser.setOscHandler(2,new v.OscHandler((function(e){return m.setTitle(e)}))),m._parser.setEscHandler({final:"7"},(function(){return m.saveCursor()})),m._parser.setEscHandler({final:"8"},(function(){return m.restoreCursor()})),m._parser.setEscHandler({final:"D"},(function(){return m.index()})),m._parser.setEscHandler({final:"E"},(function(){return m.nextLine()})),m._parser.setEscHandler({final:"H"},(function(){return m.tabSet()})),m._parser.setEscHandler({final:"M"},(function(){return m.reverseIndex()})),m._parser.setEscHandler({final:"="},(function(){return m.keypadApplicationMode()})),m._parser.setEscHandler({final:">"},(function(){return m.keypadNumericMode()})),m._parser.setEscHandler({final:"c"},(function(){return m.fullReset()})),m._parser.setEscHandler({final:"n"},(function(){return m.setgLevel(2)})),m._parser.setEscHandler({final:"o"},(function(){return m.setgLevel(3)})),m._parser.setEscHandler({final:"|"},(function(){return m.setgLevel(3)})),m._parser.setEscHandler({final:"}"},(function(){return m.setgLevel(2)})),m._parser.setEscHandler({final:"~"},(function(){return m.setgLevel(1)})),m._parser.setEscHandler({intermediates:"%",final:"@"},(function(){return m.selectDefaultCharset()})),m._parser.setEscHandler({intermediates:"%",final:"G"},(function(){return m.selectDefaultCharset()}));var C=function(e){w._parser.setEscHandler({intermediates:"(",final:e},(function(){return m.selectCharset("("+e)})),w._parser.setEscHandler({intermediates:")",final:e},(function(){return m.selectCharset(")"+e)})),w._parser.setEscHandler({intermediates:"*",final:e},(function(){return m.selectCharset("*"+e)})),w._parser.setEscHandler({intermediates:"+",final:e},(function(){return m.selectCharset("+"+e)})),w._parser.setEscHandler({intermediates:"-",final:e},(function(){return m.selectCharset("-"+e)})),w._parser.setEscHandler({intermediates:".",final:e},(function(){return m.selectCharset("."+e)})),w._parser.setEscHandler({intermediates:"/",final:e},(function(){return m.selectCharset("/"+e)}))},w=this;for(var E in s.CHARSETS)C(E);return m._parser.setEscHandler({intermediates:"#",final:"8"},(function(){return m.screenAlignmentPattern()})),m._parser.setErrorHandler((function(e){return m._logService.error("Parsing error: ",e),e})),m._parser.setDcsHandler({intermediates:"$",final:"q"},new S(m._bufferService,m._coreService,m._logService,m._optionsService)),m}return n(t,e),Object.defineProperty(t.prototype,"onRequestRefreshRows",{get:function(){return this._onRequestRefreshRows.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onRequestReset",{get:function(){return this._onRequestReset.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onRequestBell",{get:function(){return this._onRequestBell.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onCursorMove",{get:function(){return this._onCursorMove.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onLineFeed",{get:function(){return this._onLineFeed.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onScroll",{get:function(){return this._onScroll.event},enumerable:!0,configurable:!0}),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.parse=function(e){var t=this._bufferService.buffer,r=t.x,i=t.y;if(this._logService.debug("parsing data",e),this._parseBuffer.length131072)for(var n=0;n0&&2===d.getWidth(o.x-1)&&d.setCellFromCodePoint(o.x-1,0,1,f.fg,f.bg);for(var p=t;p=c)if(l)o.x=0,o.y++,o.y===o.scrollBottom+1?(o.y--,this._terminal.scroll(this._eraseAttrData(),!0)):(o.y>=this._bufferService.rows&&(o.y=this._bufferService.rows-1),o.lines.get(o.y).isWrapped=!0),d=o.lines.get(o.y+o.ybase);else if(o.x=c-1,2===n)continue;if(u&&(d.insertCells(o.x,n,o.getNullCell(f),f),2===d.getWidth(c-1)&&d.setCellFromCodePoint(c-1,_.NULL_CELL_CODE,_.NULL_CELL_WIDTH,f.fg,f.bg)),d.setCellFromCodePoint(o.x++,i,n,f.fg,f.bg),n>0)for(;--n;)d.setCellFromCodePoint(o.x++,0,0,f.fg,f.bg)}else d.getWidth(o.x-1)?d.addCodepointToCell(o.x-1,i):d.addCodepointToCell(o.x-2,i)}r-t>0&&(d.loadCell(o.x-1,this._workCell),2===this._workCell.getWidth()||this._workCell.getCode()>65535?this._parser.precedingCodepoint=0:this._workCell.isCombined()?this._parser.precedingCodepoint=this._workCell.getChars().charCodeAt(0):this._parser.precedingCodepoint=this._workCell.content),o.x0&&0===d.getWidth(o.x)&&!d.hasContent(o.x)&&d.setCellFromCodePoint(o.x,0,1,f.fg,f.bg),this._dirtyRowService.markDirty(o.y)},t.prototype.addCsiHandler=function(e,t){var r=this;return"t"!==e.final||e.prefix||e.intermediates?this._parser.addCsiHandler(e,t):this._parser.addCsiHandler(e,(function(e){return!m(e.params[0],r._optionsService.options.windowOptions)||t(e)}))},t.prototype.addDcsHandler=function(e,t){return this._parser.addDcsHandler(e,new g.DcsHandler(t))},t.prototype.addEscHandler=function(e,t){return this._parser.addEscHandler(e,t)},t.prototype.addOscHandler=function(e,t){return this._parser.addOscHandler(e,new v.OscHandler(t))},t.prototype.bell=function(){this._onRequestBell.fire()},t.prototype.lineFeed=function(){var e=this._bufferService.buffer;this._dirtyRowService.markDirty(e.y),this._optionsService.options.convertEol&&(e.x=0),e.y++,e.y===e.scrollBottom+1?(e.y--,this._terminal.scroll(this._eraseAttrData())):e.y>=this._bufferService.rows&&(e.y=this._bufferService.rows-1),e.x>=this._bufferService.cols&&e.x--,this._dirtyRowService.markDirty(e.y),this._onLineFeed.fire()},t.prototype.carriageReturn=function(){this._bufferService.buffer.x=0},t.prototype.backspace=function(){this._restrictCursor(),this._bufferService.buffer.x>0&&this._bufferService.buffer.x--},t.prototype.tab=function(){if(!(this._bufferService.buffer.x>=this._bufferService.cols)){var e=this._bufferService.buffer.x;this._bufferService.buffer.x=this._bufferService.buffer.nextStop(),this._optionsService.options.screenReaderMode&&this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x-e)}},t.prototype.shiftOut=function(){this._charsetService.setgLevel(1)},t.prototype.shiftIn=function(){this._charsetService.setgLevel(0)},t.prototype._restrictCursor=function(){this._bufferService.buffer.x=Math.min(this._bufferService.cols-1,Math.max(0,this._bufferService.buffer.x)),this._bufferService.buffer.y=this._coreService.decPrivateModes.origin?Math.min(this._bufferService.buffer.scrollBottom,Math.max(this._bufferService.buffer.scrollTop,this._bufferService.buffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._bufferService.buffer.y)),this._dirtyRowService.markDirty(this._bufferService.buffer.y)},t.prototype._setCursor=function(e,t){this._dirtyRowService.markDirty(this._bufferService.buffer.y),this._coreService.decPrivateModes.origin?(this._bufferService.buffer.x=e,this._bufferService.buffer.y=this._bufferService.buffer.scrollTop+t):(this._bufferService.buffer.x=e,this._bufferService.buffer.y=t),this._restrictCursor(),this._dirtyRowService.markDirty(this._bufferService.buffer.y)},t.prototype._moveCursor=function(e,t){this._restrictCursor(),this._setCursor(this._bufferService.buffer.x+e,this._bufferService.buffer.y+t)},t.prototype.cursorUp=function(e){var t=this._bufferService.buffer.y-this._bufferService.buffer.scrollTop;t>=0?this._moveCursor(0,-Math.min(t,e.params[0]||1)):this._moveCursor(0,-(e.params[0]||1))},t.prototype.cursorDown=function(e){var t=this._bufferService.buffer.scrollBottom-this._bufferService.buffer.y;t>=0?this._moveCursor(0,Math.min(t,e.params[0]||1)):this._moveCursor(0,e.params[0]||1)},t.prototype.cursorForward=function(e){this._moveCursor(e.params[0]||1,0)},t.prototype.cursorBackward=function(e){this._moveCursor(-(e.params[0]||1),0)},t.prototype.cursorNextLine=function(e){this.cursorDown(e),this._bufferService.buffer.x=0},t.prototype.cursorPrecedingLine=function(e){this.cursorUp(e),this._bufferService.buffer.x=0},t.prototype.cursorCharAbsolute=function(e){this._setCursor((e.params[0]||1)-1,this._bufferService.buffer.y)},t.prototype.cursorPosition=function(e){this._setCursor(e.length>=2?(e.params[1]||1)-1:0,(e.params[0]||1)-1)},t.prototype.charPosAbsolute=function(e){this._setCursor((e.params[0]||1)-1,this._bufferService.buffer.y)},t.prototype.hPositionRelative=function(e){this._moveCursor(e.params[0]||1,0)},t.prototype.linePosAbsolute=function(e){this._setCursor(this._bufferService.buffer.x,(e.params[0]||1)-1)},t.prototype.vPositionRelative=function(e){this._moveCursor(0,e.params[0]||1)},t.prototype.hVPosition=function(e){this.cursorPosition(e)},t.prototype.tabClear=function(e){var t=e.params[0];0===t?delete this._bufferService.buffer.tabs[this._bufferService.buffer.x]:3===t&&(this._bufferService.buffer.tabs={})},t.prototype.cursorForwardTab=function(e){if(!(this._bufferService.buffer.x>=this._bufferService.cols))for(var t=e.params[0]||1;t--;)this._bufferService.buffer.x=this._bufferService.buffer.nextStop()},t.prototype.cursorBackwardTab=function(e){if(!(this._bufferService.buffer.x>=this._bufferService.cols))for(var t=e.params[0]||1,r=this._bufferService.buffer;t--;)r.x=r.prevStop()},t.prototype._eraseInBufferLine=function(e,t,r,i){void 0===i&&(i=!1);var n=this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase+e);n.replaceCells(t,r,this._bufferService.buffer.getNullCell(this._eraseAttrData()),this._eraseAttrData()),i&&(n.isWrapped=!1)},t.prototype._resetBufferLine=function(e){var t=this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase+e);t.fill(this._bufferService.buffer.getNullCell(this._eraseAttrData())),t.isWrapped=!1},t.prototype.eraseInDisplay=function(e){var t;switch(this._restrictCursor(),e.params[0]){case 0:for(t=this._bufferService.buffer.y,this._dirtyRowService.markDirty(t),this._eraseInBufferLine(t++,this._bufferService.buffer.x,this._bufferService.cols,0===this._bufferService.buffer.x);t=this._bufferService.cols&&(this._bufferService.buffer.lines.get(t+1).isWrapped=!1);t--;)this._resetBufferLine(t);this._dirtyRowService.markDirty(0);break;case 2:for(t=this._bufferService.rows,this._dirtyRowService.markDirty(t-1);t--;)this._resetBufferLine(t);this._dirtyRowService.markDirty(0);break;case 3:var r=this._bufferService.buffer.lines.length-this._bufferService.rows;r>0&&(this._bufferService.buffer.lines.trimStart(r),this._bufferService.buffer.ybase=Math.max(this._bufferService.buffer.ybase-r,0),this._bufferService.buffer.ydisp=Math.max(this._bufferService.buffer.ydisp-r,0),this._onScroll.fire(0))}},t.prototype.eraseInLine=function(e){switch(this._restrictCursor(),e.params[0]){case 0:this._eraseInBufferLine(this._bufferService.buffer.y,this._bufferService.buffer.x,this._bufferService.cols);break;case 1:this._eraseInBufferLine(this._bufferService.buffer.y,0,this._bufferService.buffer.x+1);break;case 2:this._eraseInBufferLine(this._bufferService.buffer.y,0,this._bufferService.cols)}this._dirtyRowService.markDirty(this._bufferService.buffer.y)},t.prototype.insertLines=function(e){this._restrictCursor();var t=e.params[0]||1,r=this._bufferService.buffer;if(!(r.y>r.scrollBottom||r.yr.scrollBottom||r.yt.scrollBottom||t.yt.scrollBottom||t.yt.scrollBottom||t.yt.scrollBottom||t.y0||(this._terminal.is("xterm")||this._terminal.is("rxvt-unicode")||this._terminal.is("screen")?this._coreService.triggerDataEvent(o.C0.ESC+"[?1;2c"):this._terminal.is("linux")&&this._coreService.triggerDataEvent(o.C0.ESC+"[?6c"))},t.prototype.sendDeviceAttributesSecondary=function(e){e.params[0]>0||(this._terminal.is("xterm")?this._coreService.triggerDataEvent(o.C0.ESC+"[>0;276;0c"):this._terminal.is("rxvt-unicode")?this._coreService.triggerDataEvent(o.C0.ESC+"[>85;95;0c"):this._terminal.is("linux")?this._coreService.triggerDataEvent(e.params[0]+"c"):this._terminal.is("screen")&&this._coreService.triggerDataEvent(o.C0.ESC+"[>83;40003;0c"))},t.prototype.setMode=function(e){for(var t=0;t=2||2===i[1]&&o+n>=5)break;i[1]&&(n=1)}while(++o+t=30&&t<=37?(i.fg&=-50331904,i.fg|=16777216|t-30):t>=40&&t<=47?(i.bg&=-50331904,i.bg|=16777216|t-40):t>=90&&t<=97?(i.fg&=-50331904,i.fg|=16777224|t-90):t>=100&&t<=107?(i.bg&=-50331904,i.bg|=16777224|t-100):0===t?(i.fg=u.DEFAULT_ATTR_DATA.fg,i.bg=u.DEFAULT_ATTR_DATA.bg):1===t?i.fg|=134217728:3===t?i.bg|=67108864:4===t?i.fg|=268435456:5===t?i.fg|=536870912:7===t?i.fg|=67108864:8===t?i.fg|=1073741824:2===t?i.bg|=134217728:22===t?(i.fg&=-134217729,i.bg&=-134217729):23===t?i.bg&=-67108865:24===t?i.fg&=-268435457:25===t?i.fg&=-536870913:27===t?i.fg&=-67108865:28===t?i.fg&=-1073741825:39===t?(i.fg&=-67108864,i.fg|=16777215&u.DEFAULT_ATTR_DATA.fg):49===t?(i.bg&=-67108864,i.bg|=16777215&u.DEFAULT_ATTR_DATA.bg):38===t||48===t?n+=this._extractColor(e,n,i):100===t?(i.fg&=-67108864,i.fg|=16777215&u.DEFAULT_ATTR_DATA.fg,i.bg&=-67108864,i.bg|=16777215&u.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",t)},t.prototype.deviceStatus=function(e){switch(e.params[0]){case 5:this._coreService.triggerDataEvent(o.C0.ESC+"[0n");break;case 6:var t=this._bufferService.buffer.y+1,r=this._bufferService.buffer.x+1;this._coreService.triggerDataEvent(o.C0.ESC+"["+t+";"+r+"R")}},t.prototype.deviceStatusPrivate=function(e){switch(e.params[0]){case 6:var t=this._bufferService.buffer.y+1,r=this._bufferService.buffer.x+1;this._coreService.triggerDataEvent(o.C0.ESC+"[?"+t+";"+r+"R")}},t.prototype.softReset=function(e){var t;this._coreService.isCursorHidden=!1,this._terminal.insertMode=!1,null===(t=this._terminal.viewport)||void 0===t||t.syncScrollArea(),this._bufferService.buffer.scrollTop=0,this._bufferService.buffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=u.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._bufferService.buffer.savedX=0,this._bufferService.buffer.savedY=this._bufferService.buffer.ybase,this._bufferService.buffer.savedCurAttrData.fg=this._curAttrData.fg,this._bufferService.buffer.savedCurAttrData.bg=this._curAttrData.bg,this._bufferService.buffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1},t.prototype.setCursorStyle=function(e){var t=e.params[0]||1;switch(t){case 1:case 2:this._optionsService.options.cursorStyle="block";break;case 3:case 4:this._optionsService.options.cursorStyle="underline";break;case 5:case 6:this._optionsService.options.cursorStyle="bar"}var r=t%2==1;this._optionsService.options.cursorBlink=r},t.prototype.setScrollRegion=function(e){var t,r=e.params[0]||1;(e.length<2||(t=e.params[1])>this._bufferService.rows||0===t)&&(t=this._bufferService.rows),t>r&&(this._bufferService.buffer.scrollTop=r-1,this._bufferService.buffer.scrollBottom=t-1,this._setCursor(0,0))},t.prototype.windowOptions=function(e){if(m(e.params[0],this._optionsService.options.windowOptions)){var t=e.length>1?e.params[1]:0,r=this._instantiationService.getService(y.IRenderService);switch(e.params[0]){case 14:if(r&&2!==t){console.log(r.dimensions);var i=r.dimensions.scaledCanvasWidth.toFixed(0),n=r.dimensions.scaledCanvasHeight.toFixed(0);this._coreService.triggerDataEvent(o.C0.ESC+"[4;"+n+";"+i+"t")}break;case 16:if(r){i=r.dimensions.scaledCellWidth.toFixed(0),n=r.dimensions.scaledCellHeight.toFixed(0);this._coreService.triggerDataEvent(o.C0.ESC+"[6;"+n+";"+i+"t")}break;case 18:this._bufferService&&this._coreService.triggerDataEvent(o.C0.ESC+"[8;"+this._bufferService.rows+";"+this._bufferService.cols+"t");break;case 22:0!==t&&2!==t||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),0!==t&&1!==t||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:0!==t&&2!==t||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),0!==t&&1!==t||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}}},t.prototype.saveCursor=function(e){this._bufferService.buffer.savedX=this._bufferService.buffer.x,this._bufferService.buffer.savedY=this._bufferService.buffer.ybase+this._bufferService.buffer.y,this._bufferService.buffer.savedCurAttrData.fg=this._curAttrData.fg,this._bufferService.buffer.savedCurAttrData.bg=this._curAttrData.bg,this._bufferService.buffer.savedCharset=this._charsetService.charset},t.prototype.restoreCursor=function(e){this._bufferService.buffer.x=this._bufferService.buffer.savedX||0,this._bufferService.buffer.y=Math.max(this._bufferService.buffer.savedY-this._bufferService.buffer.ybase,0),this._curAttrData.fg=this._bufferService.buffer.savedCurAttrData.fg,this._curAttrData.bg=this._bufferService.buffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._bufferService.buffer.savedCharset&&(this._charsetService.charset=this._bufferService.buffer.savedCharset),this._restrictCursor()},t.prototype.setTitle=function(e){this._windowTitle=e,this._terminal.handleTitle(e)},t.prototype.setIconName=function(e){this._iconName=e},t.prototype.nextLine=function(){this._bufferService.buffer.x=0,this.index()},t.prototype.keypadApplicationMode=function(){var e;this._logService.debug("Serial port requested application keypad."),this._coreService.decPrivateModes.applicationKeypad=!0,null===(e=this._terminal.viewport)||void 0===e||e.syncScrollArea()},t.prototype.keypadNumericMode=function(){var e;this._logService.debug("Switching back to normal keypad."),this._coreService.decPrivateModes.applicationKeypad=!1,null===(e=this._terminal.viewport)||void 0===e||e.syncScrollArea()},t.prototype.selectDefaultCharset=function(){this._charsetService.setgLevel(0),this._charsetService.setgCharset(0,s.DEFAULT_CHARSET)},t.prototype.selectCharset=function(e){2===e.length?"/"!==e[0]&&this._charsetService.setgCharset(b[e[0]],s.CHARSETS[e[1]]||s.DEFAULT_CHARSET):this.selectDefaultCharset()},t.prototype.index=function(){this._restrictCursor();var e=this._bufferService.buffer;this._bufferService.buffer.y++,e.y===e.scrollBottom+1?(e.y--,this._terminal.scroll(this._eraseAttrData())):e.y>=this._bufferService.rows&&(e.y=this._bufferService.rows-1),this._restrictCursor()},t.prototype.tabSet=function(){this._bufferService.buffer.tabs[this._bufferService.buffer.x]=!0},t.prototype.reverseIndex=function(){this._restrictCursor();var e=this._bufferService.buffer;if(e.y===e.scrollTop){var t=e.scrollBottom-e.scrollTop;e.lines.shiftElements(e.y+e.ybase,t,1),e.lines.set(e.y+e.ybase,e.getBlankLine(this._eraseAttrData())),this._dirtyRowService.markRangeDirty(e.scrollTop,e.scrollBottom)}else e.y--,this._restrictCursor()},t.prototype.fullReset=function(){this._parser.reset(),this._onRequestReset.fire()},t.prototype.reset=function(){this._curAttrData=u.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=u.DEFAULT_ATTR_DATA.clone()},t.prototype._eraseAttrData=function(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal},t.prototype.setgLevel=function(e){this._charsetService.setgLevel(e)},t.prototype.screenAlignmentPattern=function(){var e=new d.CellData;e.content=1<<22|"E".charCodeAt(0),e.fg=this._curAttrData.fg,e.bg=this._curAttrData.bg;var t=this._bufferService.buffer;this._setCursor(0,0);for(var r=0;r1)throw new Error("only one byte as prefix supported");if((r=e.prefix.charCodeAt(0))&&60>r||r>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(e.intermediates){if(e.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(var i=0;in||n>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");r<<=8,r|=n}}if(1!==e.final.length)throw new Error("final must be a single byte");var o=e.final.charCodeAt(0);if(t[0]>o||o>t[1])throw new Error("final must be in range "+t[0]+" .. "+t[1]);return r<<=8,r|=o},r.prototype.identToString=function(e){for(var t=[];e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join("")},r.prototype.dispose=function(){this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null),this._oscParser.dispose(),this._dcsParser.dispose()},r.prototype.setPrintHandler=function(e){this._printHandler=e},r.prototype.clearPrintHandler=function(){this._printHandler=this._printHandlerFb},r.prototype.addEscHandler=function(e,t){var r=this._identifier(e,[48,126]);void 0===this._escHandlers[r]&&(this._escHandlers[r]=[]);var i=this._escHandlers[r];return i.push(t),{dispose:function(){var e=i.indexOf(t);-1!==e&&i.splice(e,1)}}},r.prototype.setEscHandler=function(e,t){this._escHandlers[this._identifier(e,[48,126])]=[t]},r.prototype.clearEscHandler=function(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]},r.prototype.setEscHandlerFallback=function(e){this._escHandlerFb=e},r.prototype.setExecuteHandler=function(e,t){this._executeHandlers[e.charCodeAt(0)]=t},r.prototype.clearExecuteHandler=function(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]},r.prototype.setExecuteHandlerFallback=function(e){this._executeHandlerFb=e},r.prototype.addCsiHandler=function(e,t){var r=this._identifier(e);void 0===this._csiHandlers[r]&&(this._csiHandlers[r]=[]);var i=this._csiHandlers[r];return i.push(t),{dispose:function(){var e=i.indexOf(t);-1!==e&&i.splice(e,1)}}},r.prototype.setCsiHandler=function(e,t){this._csiHandlers[this._identifier(e)]=[t]},r.prototype.clearCsiHandler=function(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]},r.prototype.setCsiHandlerFallback=function(e){this._csiHandlerFb=e},r.prototype.addDcsHandler=function(e,t){return this._dcsParser.addHandler(this._identifier(e),t)},r.prototype.setDcsHandler=function(e,t){this._dcsParser.setHandler(this._identifier(e),t)},r.prototype.clearDcsHandler=function(e){this._dcsParser.clearHandler(this._identifier(e))},r.prototype.setDcsHandlerFallback=function(e){this._dcsParser.setHandlerFallback(e)},r.prototype.addOscHandler=function(e,t){return this._oscParser.addHandler(e,t)},r.prototype.setOscHandler=function(e,t){this._oscParser.setHandler(e,t)},r.prototype.clearOscHandler=function(e){this._oscParser.clearHandler(e)},r.prototype.setOscHandlerFallback=function(e){this._oscParser.setHandlerFallback(e)},r.prototype.setErrorHandler=function(e){this._errorHandler=e},r.prototype.clearErrorHandler=function(){this._errorHandler=this._errorHandlerFb},r.prototype.reset=function(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingCodepoint=0},r.prototype.parse=function(e,t){for(var r=0,i=0,n=this.currentState,o=this._oscParser,s=this._dcsParser,a=this._collect,c=this._params,l=this._transitions.table,h=0;h>4){case 2:for(var u=h+1;;++u){if(u>=t||(r=e[u])<32||r>126&&r<160){this._printHandler(e,h,u),h=u-1;break}if(++u>=t||(r=e[u])<32||r>126&&r<160){this._printHandler(e,h,u),h=u-1;break}if(++u>=t||(r=e[u])<32||r>126&&r<160){this._printHandler(e,h,u),h=u-1;break}if(++u>=t||(r=e[u])<32||r>126&&r<160){this._printHandler(e,h,u),h=u-1;break}}break;case 3:this._executeHandlers[r]?this._executeHandlers[r]():this._executeHandlerFb(r),this.precedingCodepoint=0;break;case 0:break;case 1:if(this._errorHandler({position:h,code:r,currentState:n,collect:a,params:c,abort:!1}).abort)return;break;case 7:for(var f=this._csiHandlers[a<<8|r],_=f?f.length-1:-1;_>=0&&!1===f[_](c);_--);_<0&&this._csiHandlerFb(a<<8|r,c),this.precedingCodepoint=0;break;case 8:do{switch(r){case 59:c.addParam(0);break;case 58:c.addSubParam(-1);break;default:c.addDigit(r-48)}}while(++h47&&r<60);h--;break;case 9:a<<=8,a|=r;break;case 10:for(var d=this._escHandlers[a<<8|r],p=d?d.length-1:-1;p>=0&&!1===d[p]();p--);p<0&&this._escHandlerFb(a<<8|r),this.precedingCodepoint=0;break;case 11:c.reset(),c.addParam(0),a=0;break;case 12:s.hook(a<<8|r,c);break;case 13:for(var v=h+1;;++v)if(v>=t||24===(r=e[v])||26===r||27===r||r>127&&r<160){s.put(e,h,v),h=v-1;break}break;case 14:s.unhook(24!==r&&26!==r),27===r&&(i|=1),c.reset(),c.addParam(0),a=0,this.precedingCodepoint=0;break;case 4:o.start();break;case 5:for(var g=h+1;;g++)if(g>=t||(r=e[g])<32||r>127&&r<=159){o.put(e,h,g),h=g-1;break}break;case 6:o.end(24!==r&&26!==r),27===r&&(i|=1),c.reset(),c.addParam(0),a=0,this.precedingCodepoint=0}n=15&i}this._collect=a,this.currentState=n},r}(o.Disposable);t.EscapeSequenceParser=u},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),o=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(40),c=r(46),l=r(47),h=r(48),u=r(27),f=r(2),_=r(4),d=r(1),p=r(23),v=r(0),g=1,y=function(e){function t(t,r,i,n,o,s,f,_,d){var p=e.call(this)||this;p._colors=t,p._screenElement=r,p.linkifier=i,p.linkifier2=n,p._bufferService=o,p._charSizeService=s,p._optionsService=f,p.coreService=_,p.coreBrowserService=d,p._id=g++,p._onRequestRefreshRows=new v.EventEmitter;var y=p._optionsService.options.allowTransparency;return p._characterJoinerRegistry=new u.CharacterJoinerRegistry(p._bufferService),p._renderLayers=[new a.TextRenderLayer(p._screenElement,0,p._colors,p._characterJoinerRegistry,y,p._id,p._bufferService,f),new c.SelectionRenderLayer(p._screenElement,1,p._colors,p._id,p._bufferService,f),new h.LinkRenderLayer(p._screenElement,2,p._colors,p._id,i,n,p._bufferService,f),new l.CursorRenderLayer(p._screenElement,3,p._colors,p._id,p._onRequestRefreshRows,p._bufferService,f,_,d)],p.dimensions={scaledCharWidth:0,scaledCharHeight:0,scaledCellWidth:0,scaledCellHeight:0,scaledCharLeft:0,scaledCharTop:0,scaledCanvasWidth:0,scaledCanvasHeight:0,canvasWidth:0,canvasHeight:0,actualCellWidth:0,actualCellHeight:0},p._devicePixelRatio=window.devicePixelRatio,p._updateDimensions(),p.onOptionsChanged(),p}return n(t,e),Object.defineProperty(t.prototype,"onRequestRefreshRows",{get:function(){return this._onRequestRefreshRows.event},enumerable:!0,configurable:!0}),t.prototype.dispose=function(){e.prototype.dispose.call(this),this._renderLayers.forEach((function(e){return e.dispose()})),p.removeTerminalFromCache(this._id)},t.prototype.onDevicePixelRatioChange=function(){this._devicePixelRatio!==window.devicePixelRatio&&(this._devicePixelRatio=window.devicePixelRatio,this.onResize(this._bufferService.cols,this._bufferService.rows))},t.prototype.setColors=function(e){var t=this;this._colors=e,this._renderLayers.forEach((function(e){e.setColors(t._colors),e.reset()}))},t.prototype.onResize=function(e,t){var r=this;this._updateDimensions(),this._renderLayers.forEach((function(e){return e.resize(r.dimensions)})),this._screenElement.style.width=this.dimensions.canvasWidth+"px",this._screenElement.style.height=this.dimensions.canvasHeight+"px"},t.prototype.onCharSizeChanged=function(){this.onResize(this._bufferService.cols,this._bufferService.rows)},t.prototype.onBlur=function(){this._runOperation((function(e){return e.onBlur()}))},t.prototype.onFocus=function(){this._runOperation((function(e){return e.onFocus()}))},t.prototype.onSelectionChanged=function(e,t,r){void 0===r&&(r=!1),this._runOperation((function(i){return i.onSelectionChanged(e,t,r)}))},t.prototype.onCursorMove=function(){this._runOperation((function(e){return e.onCursorMove()}))},t.prototype.onOptionsChanged=function(){this._runOperation((function(e){return e.onOptionsChanged()}))},t.prototype.clear=function(){this._runOperation((function(e){return e.reset()}))},t.prototype._runOperation=function(e){this._renderLayers.forEach((function(t){return e(t)}))},t.prototype.renderRows=function(e,t){this._renderLayers.forEach((function(r){return r.onGridChanged(e,t)}))},t.prototype._updateDimensions=function(){this._charSizeService.hasValidSize&&(this.dimensions.scaledCharWidth=Math.floor(this._charSizeService.width*window.devicePixelRatio),this.dimensions.scaledCharHeight=Math.ceil(this._charSizeService.height*window.devicePixelRatio),this.dimensions.scaledCellHeight=Math.floor(this.dimensions.scaledCharHeight*this._optionsService.options.lineHeight),this.dimensions.scaledCharTop=1===this._optionsService.options.lineHeight?0:Math.round((this.dimensions.scaledCellHeight-this.dimensions.scaledCharHeight)/2),this.dimensions.scaledCellWidth=this.dimensions.scaledCharWidth+Math.round(this._optionsService.options.letterSpacing),this.dimensions.scaledCharLeft=Math.floor(this._optionsService.options.letterSpacing/2),this.dimensions.scaledCanvasHeight=this._bufferService.rows*this.dimensions.scaledCellHeight,this.dimensions.scaledCanvasWidth=this._bufferService.cols*this.dimensions.scaledCellWidth,this.dimensions.canvasHeight=Math.round(this.dimensions.scaledCanvasHeight/window.devicePixelRatio),this.dimensions.canvasWidth=Math.round(this.dimensions.scaledCanvasWidth/window.devicePixelRatio),this.dimensions.actualCellHeight=this.dimensions.canvasHeight/this._bufferService.rows,this.dimensions.actualCellWidth=this.dimensions.canvasWidth/this._bufferService.cols)},t.prototype.registerCharacterJoiner=function(e){return this._characterJoinerRegistry.registerCharacterJoiner(e)},t.prototype.deregisterCharacterJoiner=function(e){return this._characterJoinerRegistry.deregisterCharacterJoiner(e)},t=o([s(4,d.IBufferService),s(5,_.ICharSizeService),s(6,d.IOptionsService),s(7,d.ICoreService),s(8,_.ICoreBrowserService)],t)}(f.Disposable);t.Renderer=y},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=r(41),s=r(13),a=r(6),c=r(3),l=r(27),h=r(5),u=function(e){function t(t,r,i,n,s,a,c,l){var u=e.call(this,t,"text",r,s,i,a,c,l)||this;return u.bufferService=c,u.optionsService=l,u._characterWidth=0,u._characterFont="",u._characterOverlapCache={},u._workCell=new h.CellData,u._state=new o.GridCache,u._characterJoinerRegistry=n,u}return n(t,e),t.prototype.resize=function(t){e.prototype.resize.call(this,t);var r=this._getFont(!1,!1);this._characterWidth===t.scaledCharWidth&&this._characterFont===r||(this._characterWidth=t.scaledCharWidth,this._characterFont=r,this._characterOverlapCache={}),this._state.clear(),this._state.resize(this._bufferService.cols,this._bufferService.rows)},t.prototype.reset=function(){this._state.clear(),this._clearAll()},t.prototype._forEachCell=function(e,t,r,i){for(var n=e;n<=t;n++)for(var o=n+this._bufferService.buffer.ydisp,s=this._bufferService.buffer.lines.get(o),a=r?r.getJoinedCharacters(o):[],h=0;h0&&h===a[0][0]){f=!0;var d=a.shift();u=new l.JoinedCellData(this._workCell,s.translateToString(!0,d[0],d[1]),d[1]-d[0]),_=d[1]-1}!f&&this._isOverlapping(u)&&_this._characterWidth;return this._ctx.restore(),this._characterOverlapCache[t]=r,r},t}(s.BaseRenderLayer);t.TextRenderLayer=u},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this.cache=[]}return e.prototype.resize=function(e,t){for(var r=0;r>>24,n=t.rgba>>>16&255,o=t.rgba>>>8&255,s=0;s=this.capacity)r=this._head,this._unlinkNode(r),delete this._map[r.key],r.key=e,r.value=t,this._map[e]=r;else{var i=this._nodePool;i.length>0?((r=i.pop()).key=e,r.value=t):r={prev:null,next:null,key:e,value:t},this._map[e]=r,this.size++}this._appendNode(r)},e}();t.LRUMap=i},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t,r,i,n,o,s){var a=e.call(this,t,"selection",r,!0,i,n,o,s)||this;return a.bufferService=o,a.optionsService=s,a._clearState(),a}return n(t,e),t.prototype._clearState=function(){this._state={start:void 0,end:void 0,columnSelectMode:void 0,ydisp:void 0}},t.prototype.resize=function(t){e.prototype.resize.call(this,t),this._clearState()},t.prototype.reset=function(){this._state.start&&this._state.end&&(this._clearState(),this._clearAll())},t.prototype.onSelectionChanged=function(e,t,r){if(this._didStateChange(e,t,r,this._bufferService.buffer.ydisp))if(this._clearAll(),e&&t){var i=e[1]-this._bufferService.buffer.ydisp,n=t[1]-this._bufferService.buffer.ydisp,o=Math.max(i,0),s=Math.min(n,this._bufferService.rows-1);if(!(o>=this._bufferService.rows||s<0)){if(this._ctx.fillStyle=this._colors.selection.css,r){var a=e[0],c=t[0]-a,l=s-o+1;this._fillCells(a,o,c,l)}else{a=i===o?e[0]:0;var h=o===s?t[0]:this._bufferService.cols;this._fillCells(a,o,h-a,1);var u=Math.max(s-o-1,0);if(this._fillCells(0,o+1,this._bufferService.cols,u),o!==s){var f=n===s?t[0]:this._bufferService.cols;this._fillCells(0,s,f,1)}}this._state.start=[e[0],e[1]],this._state.end=[t[0],t[1]],this._state.columnSelectMode=r,this._state.ydisp=this._bufferService.buffer.ydisp}}else this._clearState()},t.prototype._didStateChange=function(e,t,r,i){return!this._areCoordinatesEqual(e,this._state.start)||!this._areCoordinatesEqual(t,this._state.end)||r!==this._state.columnSelectMode||i!==this._state.ydisp},t.prototype._areCoordinatesEqual=function(e,t){return!(!e||!t)&&(e[0]===t[0]&&e[1]===t[1])},t}(r(13).BaseRenderLayer);t.SelectionRenderLayer=o},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=r(13),s=r(5),a=function(e){function t(t,r,i,n,o,a,c,l,h){var u=e.call(this,t,"cursor",r,!0,i,n,a,c)||this;return u._onRequestRefreshRowsEvent=o,u.bufferService=a,u.optionsService=c,u._coreService=l,u._coreBrowserService=h,u._cell=new s.CellData,u._state={x:0,y:0,isFocused:!1,style:"",width:0},u._cursorRenderers={bar:u._renderBarCursor.bind(u),block:u._renderBlockCursor.bind(u),underline:u._renderUnderlineCursor.bind(u)},u}return n(t,e),t.prototype.resize=function(t){e.prototype.resize.call(this,t),this._state={x:0,y:0,isFocused:!1,style:"",width:0}},t.prototype.reset=function(){this._clearCursor(),this._cursorBlinkStateManager&&(this._cursorBlinkStateManager.dispose(),this._cursorBlinkStateManager=void 0,this.onOptionsChanged())},t.prototype.onBlur=function(){this._cursorBlinkStateManager&&this._cursorBlinkStateManager.pause(),this._onRequestRefreshRowsEvent.fire({start:this._bufferService.buffer.y,end:this._bufferService.buffer.y})},t.prototype.onFocus=function(){this._cursorBlinkStateManager?this._cursorBlinkStateManager.resume():this._onRequestRefreshRowsEvent.fire({start:this._bufferService.buffer.y,end:this._bufferService.buffer.y})},t.prototype.onOptionsChanged=function(){var e,t=this;this._optionsService.options.cursorBlink?this._cursorBlinkStateManager||(this._cursorBlinkStateManager=new c(this._coreBrowserService.isFocused,(function(){t._render(!0)}))):(null===(e=this._cursorBlinkStateManager)||void 0===e||e.dispose(),this._cursorBlinkStateManager=void 0),this._onRequestRefreshRowsEvent.fire({start:this._bufferService.buffer.y,end:this._bufferService.buffer.y})},t.prototype.onCursorMove=function(){this._cursorBlinkStateManager&&this._cursorBlinkStateManager.restartBlinkAnimation()},t.prototype.onGridChanged=function(e,t){!this._cursorBlinkStateManager||this._cursorBlinkStateManager.isPaused?this._render(!1):this._cursorBlinkStateManager.restartBlinkAnimation()},t.prototype._render=function(e){if(this._coreService.isCursorInitialized&&!this._coreService.isCursorHidden){var t=this._bufferService.buffer.ybase+this._bufferService.buffer.y,r=t-this._bufferService.buffer.ydisp;if(r<0||r>=this._bufferService.rows)this._clearCursor();else{var i=Math.min(this._bufferService.buffer.x,this._bufferService.cols-1);if(this._bufferService.buffer.lines.get(t).loadCell(i,this._cell),void 0!==this._cell.content){if(!this._coreBrowserService.isFocused){this._clearCursor(),this._ctx.save(),this._ctx.fillStyle=this._colors.cursor.css;var n=this._optionsService.options.cursorStyle;return n&&"block"!==n?this._cursorRenderers[n](i,r,this._cell):this._renderBlurCursor(i,r,this._cell),this._ctx.restore(),this._state.x=i,this._state.y=r,this._state.isFocused=!1,this._state.style=n,void(this._state.width=this._cell.getWidth())}if(!this._cursorBlinkStateManager||this._cursorBlinkStateManager.isCursorVisible){if(this._state){if(this._state.x===i&&this._state.y===r&&this._state.isFocused===this._coreBrowserService.isFocused&&this._state.style===this._optionsService.options.cursorStyle&&this._state.width===this._cell.getWidth())return;this._clearCursor()}this._ctx.save(),this._cursorRenderers[this._optionsService.options.cursorStyle||"block"](i,r,this._cell),this._ctx.restore(),this._state.x=i,this._state.y=r,this._state.isFocused=!1,this._state.style=this._optionsService.options.cursorStyle,this._state.width=this._cell.getWidth()}else this._clearCursor()}}}else this._clearCursor()},t.prototype._clearCursor=function(){this._state&&(this._clearCells(this._state.x,this._state.y,this._state.width,1),this._state={x:0,y:0,isFocused:!1,style:"",width:0})},t.prototype._renderBarCursor=function(e,t,r){this._ctx.save(),this._ctx.fillStyle=this._colors.cursor.css,this._fillLeftLineAtCell(e,t,this._optionsService.options.cursorWidth),this._ctx.restore()},t.prototype._renderBlockCursor=function(e,t,r){this._ctx.save(),this._ctx.fillStyle=this._colors.cursor.css,this._fillCells(e,t,r.getWidth(),1),this._ctx.fillStyle=this._colors.cursorAccent.css,this._fillCharTrueColor(r,e,t),this._ctx.restore()},t.prototype._renderUnderlineCursor=function(e,t,r){this._ctx.save(),this._ctx.fillStyle=this._colors.cursor.css,this._fillBottomLineAtCells(e,t),this._ctx.restore()},t.prototype._renderBlurCursor=function(e,t,r){this._ctx.save(),this._ctx.strokeStyle=this._colors.cursor.css,this._strokeRectAtCell(e,t,r.getWidth(),1),this._ctx.restore()},t}(o.BaseRenderLayer);t.CursorRenderLayer=a;var c=function(){function e(e,t){this._renderCallback=t,this.isCursorVisible=!0,e&&this._restartInterval()}return Object.defineProperty(e.prototype,"isPaused",{get:function(){return!(this._blinkStartTimeout||this._blinkInterval)},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this._blinkInterval&&(window.clearInterval(this._blinkInterval),this._blinkInterval=void 0),this._blinkStartTimeout&&(window.clearTimeout(this._blinkStartTimeout),this._blinkStartTimeout=void 0),this._animationFrame&&(window.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)},e.prototype.restartBlinkAnimation=function(){var e=this;this.isPaused||(this._animationTimeRestarted=Date.now(),this.isCursorVisible=!0,this._animationFrame||(this._animationFrame=window.requestAnimationFrame((function(){e._renderCallback(),e._animationFrame=void 0}))))},e.prototype._restartInterval=function(e){var t=this;void 0===e&&(e=600),this._blinkInterval&&window.clearInterval(this._blinkInterval),this._blinkStartTimeout=window.setTimeout((function(){if(t._animationTimeRestarted){var e=600-(Date.now()-t._animationTimeRestarted);if(t._animationTimeRestarted=void 0,e>0)return void t._restartInterval(e)}t.isCursorVisible=!1,t._animationFrame=window.requestAnimationFrame((function(){t._renderCallback(),t._animationFrame=void 0})),t._blinkInterval=window.setInterval((function(){if(t._animationTimeRestarted){var e=600-(Date.now()-t._animationTimeRestarted);return t._animationTimeRestarted=void 0,void t._restartInterval(e)}t.isCursorVisible=!t.isCursorVisible,t._animationFrame=window.requestAnimationFrame((function(){t._renderCallback(),t._animationFrame=void 0}))}),600)}),e)},e.prototype.pause=function(){this.isCursorVisible=!0,this._blinkInterval&&(window.clearInterval(this._blinkInterval),this._blinkInterval=void 0),this._blinkStartTimeout&&(window.clearTimeout(this._blinkStartTimeout),this._blinkStartTimeout=void 0),this._animationFrame&&(window.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)},e.prototype.resume=function(){this.pause(),this._animationTimeRestarted=void 0,this._restartInterval(),this.restartBlinkAnimation()},e}()},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=r(13),s=r(9),a=r(24),c=function(e){function t(t,r,i,n,o,s,a,c){var l=e.call(this,t,"link",r,!0,i,n,a,c)||this;return l.bufferService=a,l.optionsService=c,o.onLinkHover((function(e){return l._onLinkHover(e)})),o.onLinkLeave((function(e){return l._onLinkLeave(e)})),s.onLinkHover((function(e){return l._onLinkHover(e)})),s.onLinkLeave((function(e){return l._onLinkLeave(e)})),l}return n(t,e),t.prototype.resize=function(t){e.prototype.resize.call(this,t),this._state=void 0},t.prototype.reset=function(){this._clearCurrentLink()},t.prototype._clearCurrentLink=function(){if(this._state){this._clearCells(this._state.x1,this._state.y1,this._state.cols-this._state.x1,1);var e=this._state.y2-this._state.y1-1;e>0&&this._clearCells(0,this._state.y1+1,this._state.cols,e),this._clearCells(0,this._state.y2,this._state.x2,1),this._state=void 0}},t.prototype._onLinkHover=function(e){if(e.fg===s.INVERTED_DEFAULT_COLOR?this._ctx.fillStyle=this._colors.background.css:e.fg&&a.is256Color(e.fg)?this._ctx.fillStyle=this._colors.ansi[e.fg].css:this._ctx.fillStyle=this._colors.foreground.css,e.y1===e.y2)this._fillBottomLineAtCells(e.x1,e.y1,e.x2-e.x1);else{this._fillBottomLineAtCells(e.x1,e.y1,e.cols-e.x1);for(var t=e.y1+1;t=e.lines.length)){for(var r=e.ydisp+Math.min(this._rowsToLinkify.end,this._bufferService.rows)+1,i=Math.ceil(2e3/this._bufferService.cols),n=this._bufferService.buffer.iterator(!1,t,r,i,i);n.hasNext();)for(var o=n.next(),s=0;s=0;t--)if(e.priority<=this._linkMatchers[t].priority)return void this._linkMatchers.splice(t+1,0,e);this._linkMatchers.splice(0,0,e)}else this._linkMatchers.push(e)},e.prototype.deregisterLinkMatcher=function(e){for(var t=0;t>9&511:void 0;r.validationCallback?r.validationCallback(a,(function(e){n._rowsTimeoutId||e&&n._addLink(l[1],l[0]-n._bufferService.buffer.ydisp,a,r,f)})):c._addLink(l[1],l[0]-c._bufferService.buffer.ydisp,a,r,f)},c=this;null!==(i=o.exec(t));){if("break"===a())break}},e.prototype._addLink=function(e,t,r,i,n){var s=this;if(this._mouseZoneManager&&this._element){var a=this._unicodeService.getStringCellWidth(r),c=e%this._bufferService.cols,l=t+Math.floor(e/this._bufferService.cols),h=(c+a)%this._bufferService.cols,u=l+Math.floor((c+a)/this._bufferService.cols);0===h&&(h=this._bufferService.cols,u--),this._mouseZoneManager.add(new o(c+1,l+1,h+1,u+1,(function(e){if(i.handler)return i.handler(e,r);var t=window.open();t?(t.opener=null,t.location.href=r):console.warn("Opening link blocked as opener could not be cleared")}),(function(){s._onLinkHover.fire(s._createLinkHoverEvent(c,l,h,u,n)),s._element.classList.add("xterm-cursor-pointer")}),(function(e){s._onLinkTooltip.fire(s._createLinkHoverEvent(c,l,h,u,n)),i.hoverTooltipCallback&&i.hoverTooltipCallback(e,r,{start:{x:c,y:l},end:{x:h,y:u}})}),(function(){s._onLinkLeave.fire(s._createLinkHoverEvent(c,l,h,u,n)),s._element.classList.remove("xterm-cursor-pointer"),i.hoverLeaveCallback&&i.hoverLeaveCallback()}),(function(e){return!i.willLinkActivate||i.willLinkActivate(e,r)})))}},e.prototype._createLinkHoverEvent=function(e,t,r,i,n){return{x1:e,y1:t,x2:r,y2:i,cols:this._bufferService.cols,fg:n}},e._timeBeforeLatency=200,e}();t.Linkifier=n;var o=function(e,t,r,i,n,o,s,a,c){this.x1=e,this.y1=t,this.x2=r,this.y2=i,this.clickCallback=n,this.hoverCallback=o,this.tooltipCallback=s,this.leaveCallback=a,this.willLinkActivate=c};t.MouseZone=o},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(11),s=r(51),a=r(5),c=r(0),l=r(4),h=r(1),u=r(28),f=r(52),_=String.fromCharCode(160),d=new RegExp(_,"g"),p=function(){function e(e,t,r,i,n,o,l,h){var u=this;this._scrollLines=e,this._element=t,this._screenElement=r,this._charSizeService=i,this._bufferService=n,this._coreService=o,this._mouseService=l,this._optionsService=h,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new a.CellData,this._mouseDownTimeStamp=0,this._onLinuxMouseSelection=new c.EventEmitter,this._onRedrawRequest=new c.EventEmitter,this._onSelectionChange=new c.EventEmitter,this._mouseMoveListener=function(e){return u._onMouseMove(e)},this._mouseUpListener=function(e){return u._onMouseUp(e)},this._coreService.onUserInput((function(){u.hasSelection&&u.clearSelection()})),this._trimListener=this._bufferService.buffer.lines.onTrim((function(e){return u._onTrim(e)})),this._bufferService.buffers.onBufferActivate((function(e){return u._onBufferActivate(e)})),this.enable(),this._model=new s.SelectionModel(this._bufferService),this._activeSelectionMode=0}return Object.defineProperty(e.prototype,"onLinuxMouseSelection",{get:function(){return this._onLinuxMouseSelection.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onRedrawRequest",{get:function(){return this._onRedrawRequest.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onSelectionChange",{get:function(){return this._onSelectionChange.event},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this._removeMouseDownListeners()},e.prototype.reset=function(){this.clearSelection()},e.prototype.disable=function(){this.clearSelection(),this._enabled=!1},e.prototype.enable=function(){this._enabled=!0},Object.defineProperty(e.prototype,"selectionStart",{get:function(){return this._model.finalSelectionStart},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"selectionEnd",{get:function(){return this._model.finalSelectionEnd},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"hasSelection",{get:function(){var e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;return!(!e||!t)&&(e[0]!==t[0]||e[1]!==t[1])},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"selectionText",{get:function(){var e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;if(!e||!t)return"";var r=this._bufferService.buffer,i=[];if(3===this._activeSelectionMode){if(e[0]===t[0])return"";for(var n=e[1];n<=t[1];n++){var s=r.translateBufferLineToString(n,!0,e[0],t[0]);i.push(s)}}else{var a=e[1]===t[1]?t[0]:void 0;i.push(r.translateBufferLineToString(e[1],!0,e[0],a));for(n=e[1]+1;n<=t[1]-1;n++){var c=r.lines.get(n);s=r.translateBufferLineToString(n,!0);c&&c.isWrapped?i[i.length-1]+=s:i.push(s)}if(e[1]!==t[1]){c=r.lines.get(t[1]),s=r.translateBufferLineToString(t[1],!0,0,t[0]);c&&c.isWrapped?i[i.length-1]+=s:i.push(s)}}return i.map((function(e){return e.replace(d," ")})).join(o.isWindows?"\r\n":"\n")},enumerable:!0,configurable:!0}),e.prototype.clearSelection=function(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()},e.prototype.refresh=function(e){var t=this;(this._refreshAnimationFrame||(this._refreshAnimationFrame=window.requestAnimationFrame((function(){return t._refresh()}))),o.isLinux&&e)&&(this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText))},e.prototype._refresh=function(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:3===this._activeSelectionMode})},e.prototype.isClickInSelection=function(e){var t=this._getMouseBufferCoords(e),r=this._model.finalSelectionStart,i=this._model.finalSelectionEnd;return!!(r&&i&&t)&&this._areCoordsInSelection(t,r,i)},e.prototype._areCoordsInSelection=function(e,t,r){return e[1]>t[1]&&e[1]=t[0]&&e[0]=t[0]},e.prototype.selectWordAtCursor=function(e){var t=this._getMouseBufferCoords(e);t&&(this._selectWordAt(t,!1),this._model.selectionEnd=void 0,this.refresh(!0))},e.prototype.selectAll=function(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()},e.prototype.selectLines=function(e,t){this._model.clearSelection(),e=Math.max(e,0),t=Math.min(t,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,e],this._model.selectionEnd=[this._bufferService.cols,t],this.refresh(),this._onSelectionChange.fire()},e.prototype._onTrim=function(e){this._model.onTrim(e)&&this.refresh()},e.prototype._getMouseBufferCoords=function(e){var t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(t)return t[0]--,t[1]--,t[1]+=this._bufferService.buffer.ydisp,t},e.prototype._getMouseEventScrollAmount=function(e){var t=u.getCoordsRelativeToElement(e,this._screenElement)[1],r=this._bufferService.rows*Math.ceil(this._charSizeService.height*this._optionsService.options.lineHeight);return t>=0&&t<=r?0:(t>r&&(t-=r),t=Math.min(Math.max(t,-50),50),(t/=50)/Math.abs(t)+Math.round(14*t))},e.prototype.shouldForceSelection=function(e){return o.isMac?e.altKey&&this._optionsService.options.macOptionClickForcesSelection:e.shiftKey},e.prototype.onMouseDown=function(e){if(this._mouseDownTimeStamp=e.timeStamp,(2!==e.button||!this.hasSelection)&&0===e.button){if(!this._enabled){if(!this.shouldForceSelection(e))return;e.stopPropagation()}e.preventDefault(),this._dragScrollAmount=0,this._enabled&&e.shiftKey?this._onIncrementalClick(e):1===e.detail?this._onSingleClick(e):2===e.detail?this._onDoubleClick(e):3===e.detail&&this._onTripleClick(e),this._addMouseDownListeners(),this.refresh(!0)}},e.prototype._addMouseDownListeners=function(){var e=this;this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=window.setInterval((function(){return e._dragScroll()}),50)},e.prototype._removeMouseDownListeners=function(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0},e.prototype._onIncrementalClick=function(e){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(e))},e.prototype._onSingleClick=function(e){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(e)?3:0,this._model.selectionStart=this._getMouseBufferCoords(e),this._model.selectionStart){this._model.selectionEnd=void 0;var t=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);t&&t.length!==this._model.selectionStart[0]&&0===t.hasWidth(this._model.selectionStart[0])&&this._model.selectionStart[0]++}},e.prototype._onDoubleClick=function(e){var t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=1,this._selectWordAt(t,!0))},e.prototype._onTripleClick=function(e){var t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=2,this._selectLineAt(t[1]))},e.prototype.shouldColumnSelect=function(e){return e.altKey&&!(o.isMac&&this._optionsService.options.macOptionClickForcesSelection)},e.prototype._onMouseMove=function(e){if(e.stopImmediatePropagation(),this._model.selectionStart){var t=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(e),this._model.selectionEnd){2===this._activeSelectionMode?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));var r=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(e.ydisp+this._bufferService.rows,e.lines.length-1)):(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=e.ydisp),this.refresh()}},e.prototype._onMouseUp=function(e){var t=e.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&t<500){if(e.altKey&&this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){var r=this._mouseService.getCoords(e,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(r&&void 0!==r[0]&&void 0!==r[1]){var i=f.moveToCellSequence(r[0]-1,r[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(i,!0)}}}else this.hasSelection&&this._onSelectionChange.fire()},e.prototype._onBufferActivate=function(e){var t=this;this.clearSelection(),this._trimListener.dispose(),this._trimListener=e.activeBuffer.lines.onTrim((function(e){return t._onTrim(e)}))},e.prototype._convertViewportColToCharacterIndex=function(e,t){for(var r=t[0],i=0;t[0]>=i;i++){var n=e.loadCell(i,this._workCell).getChars().length;0===this._workCell.getWidth()?r--:n>1&&t[0]!==i&&(r+=n-1)}return r},e.prototype.setSelection=function(e,t,r){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[e,t],this._model.selectionStartLength=r,this.refresh()},e.prototype._getWordAt=function(e,t,r,i){if(void 0===r&&(r=!0),void 0===i&&(i=!0),!(e[0]>=this._bufferService.cols)){var n=this._bufferService.buffer,o=n.lines.get(e[1]);if(o){var s=n.translateBufferLineToString(e[1],!1),a=this._convertViewportColToCharacterIndex(o,e),c=a,l=e[0]-a,h=0,u=0,f=0,_=0;if(" "===s.charAt(a)){for(;a>0&&" "===s.charAt(a-1);)a--;for(;c1&&(_+=v-1,c+=v-1);d>0&&a>0&&!this._isCharWordSeparator(o.loadCell(d-1,this._workCell));){o.loadCell(d-1,this._workCell);var g=this._workCell.getChars().length;0===this._workCell.getWidth()?(h++,d--):g>1&&(f+=g-1,a-=g-1),a--,d--}for(;p1&&(_+=y-1,c+=y-1),c++,p++}}c++;var b=a+l-h+f,m=Math.min(this._bufferService.cols,c-a+h+u-f-_);if(t||""!==s.slice(a,c).trim()){if(r&&0===b&&32!==o.getCodePoint(0)){var S=n.lines.get(e[1]-1);if(S&&o.isWrapped&&32!==S.getCodePoint(this._bufferService.cols-1)){var C=this._getWordAt([this._bufferService.cols-1,e[1]-1],!1,!0,!1);if(C){var w=this._bufferService.cols-C.start;b-=w,m+=w}}}if(i&&b+m===this._bufferService.cols&&32!==o.getCodePoint(this._bufferService.cols-1)){var E=n.lines.get(e[1]+1);if(E&&E.isWrapped&&32!==E.getCodePoint(0)){var L=this._getWordAt([0,e[1]+1],!1,!1,!0);L&&(m+=L.length)}}return{start:b,length:m}}}}},e.prototype._selectWordAt=function(e,t){var r=this._getWordAt(e,t);if(r){for(;r.start<0;)r.start+=this._bufferService.cols,e[1]--;this._model.selectionStart=[r.start,e[1]],this._model.selectionStartLength=r.length}},e.prototype._selectToWordAt=function(e){var t=this._getWordAt(e,!0);if(t){for(var r=e[1];t.start<0;)t.start+=this._bufferService.cols,r--;if(!this._model.areSelectionValuesReversed())for(;t.start+t.length>this._bufferService.cols;)t.length-=this._bufferService.cols,r++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?t.start:t.start+t.length,r]}},e.prototype._isCharWordSeparator=function(e){return 0!==e.getWidth()&&this._optionsService.options.wordSeparator.indexOf(e.getChars())>=0},e.prototype._selectLineAt=function(e){var t=this._bufferService.buffer.getWrappedRangeForLine(e);this._model.selectionStart=[0,t.first],this._model.selectionEnd=[this._bufferService.cols,t.last],this._model.selectionStartLength=0},e=i([n(3,l.ICharSizeService),n(4,h.IBufferService),n(5,h.ICoreService),n(6,l.IMouseService),n(7,h.IOptionsService)],e)}();t.SelectionService=p},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e){this._bufferService=e,this.isSelectAllActive=!1,this.selectionStartLength=0}return e.prototype.clearSelection=function(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0},Object.defineProperty(e.prototype,"finalSelectionStart",{get:function(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"finalSelectionEnd",{get:function(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){var e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[e,this.selectionStart[1]]}return this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]?[Math.max(this.selectionStart[0]+this.selectionStartLength,this.selectionEnd[0]),this.selectionEnd[1]]:this.selectionEnd}},enumerable:!0,configurable:!0}),e.prototype.areSelectionValuesReversed=function(){var e=this.selectionStart,t=this.selectionEnd;return!(!e||!t)&&(e[1]>t[1]||e[1]===t[1]&&e[0]>t[0])},e.prototype.onTrim=function(e){return this.selectionStart&&(this.selectionStart[1]-=e),this.selectionEnd&&(this.selectionEnd[1]-=e),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)},e}();t.SelectionModel=i},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(12);function n(e,t,r,i){var n=e-o(r,e),s=t-o(r,t);return h(Math.abs(n-s)-function(e,t,r){for(var i=0,n=e-o(r,e),s=t-o(r,t),c=0;c=0&&t0?i-o(s,i):t,e=r&&ct?"A":"B"}function c(e,t,r,i,n,o){for(var s=e,a=t,c="";s!==r||a!==i;)s+=n?1:-1,n&&s>o.cols-1?(c+=o.buffer.translateBufferLineToString(a,!1,e,s),s=0,e=0,a++):!n&&s<0&&(c+=o.buffer.translateBufferLineToString(a,!1,0,e+1),e=s=o.cols-1,a--);return c+o.buffer.translateBufferLineToString(a,!1,e,s)}function l(e,t){var r=t?"O":"[";return i.C0.ESC+r+e}function h(e,t){e=Math.floor(e);for(var r="",i=0;i0?i-o(a,i):t;var _=i,d=s(e,t,r,i,a,u);return h(c(e,f,r,_,"C"===d,a).length,l(d,u))}(u,f,e,t,r,i);if(f===t)return a=u>e?"D":"C",h(Math.abs(u-e),l(a,i));a=f>t?"D":"C";var _=Math.abs(f-t);return h(function(e,t){return t.cols-e}(f>t?e:u,r)+(_-1)*r.cols+1+((f>t?u:e)-1),l(a,i))}},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1),s=function(){function e(e){this._optionsService=e}return Object.defineProperty(e,"audioContext",{get:function(){if(!e._audioContext){var t=window.AudioContext||window.webkitAudioContext;if(!t)return console.warn("Web Audio API is not supported by this browser. Consider upgrading to the latest version"),null;e._audioContext=new t}return e._audioContext},enumerable:!0,configurable:!0}),e.prototype.playBellSound=function(){var t=e.audioContext;if(t){var r=t.createBufferSource();t.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._optionsService.options.bellSound)),(function(e){r.buffer=e,r.connect(t.destination),r.start(0)}))}},e.prototype._base64ToArrayBuffer=function(e){for(var t=window.atob(e),r=t.length,i=new Uint8Array(r),n=0;n=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(2),c=r(8),l=r(4),h=r(1),u=function(e){function t(t,r,i,n,o){var s=e.call(this)||this;return s._element=t,s._screenElement=r,s._bufferService=i,s._mouseService=n,s._selectionService=o,s._zones=[],s._areZonesActive=!1,s._lastHoverCoords=[void 0,void 0],s._initialSelectionLength=0,s.register(c.addDisposableDomListener(s._element,"mousedown",(function(e){return s._onMouseDown(e)}))),s._mouseMoveListener=function(e){return s._onMouseMove(e)},s._mouseLeaveListener=function(e){return s._onMouseLeave(e)},s._clickListener=function(e){return s._onClick(e)},s}return n(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this),this._deactivate()},t.prototype.add=function(e){this._zones.push(e),1===this._zones.length&&this._activate()},t.prototype.clearAll=function(e,t){if(0!==this._zones.length){e&&t||(e=0,t=this._bufferService.rows-1);for(var r=0;re&&i.y1<=t+1||i.y2>e&&i.y2<=t+1||i.y1t+1)&&(this._currentZone&&this._currentZone===i&&(this._currentZone.leaveCallback(),this._currentZone=void 0),this._zones.splice(r--,1))}0===this._zones.length&&this._deactivate()}},t.prototype._activate=function(){this._areZonesActive||(this._areZonesActive=!0,this._element.addEventListener("mousemove",this._mouseMoveListener),this._element.addEventListener("mouseleave",this._mouseLeaveListener),this._element.addEventListener("click",this._clickListener))},t.prototype._deactivate=function(){this._areZonesActive&&(this._areZonesActive=!1,this._element.removeEventListener("mousemove",this._mouseMoveListener),this._element.removeEventListener("mouseleave",this._mouseLeaveListener),this._element.removeEventListener("click",this._clickListener))},t.prototype._onMouseMove=function(e){this._lastHoverCoords[0]===e.pageX&&this._lastHoverCoords[1]===e.pageY||(this._onHover(e),this._lastHoverCoords=[e.pageX,e.pageY])},t.prototype._onHover=function(e){var t=this,r=this._findZoneEventAt(e);r!==this._currentZone&&(this._currentZone&&(this._currentZone.leaveCallback(),this._currentZone=void 0,this._tooltipTimeout&&clearTimeout(this._tooltipTimeout)),r&&(this._currentZone=r,r.hoverCallback&&r.hoverCallback(e),this._tooltipTimeout=setTimeout((function(){return t._onTooltip(e)}),500)))},t.prototype._onTooltip=function(e){this._tooltipTimeout=void 0;var t=this._findZoneEventAt(e);t&&t.tooltipCallback&&t.tooltipCallback(e)},t.prototype._onMouseDown=function(e){if(this._initialSelectionLength=this._getSelectionLength(),this._areZonesActive){var t=this._findZoneEventAt(e);(null==t?void 0:t.willLinkActivate(e))&&(e.preventDefault(),e.stopImmediatePropagation())}},t.prototype._onMouseLeave=function(e){this._currentZone&&(this._currentZone.leaveCallback(),this._currentZone=void 0,this._tooltipTimeout&&clearTimeout(this._tooltipTimeout))},t.prototype._onClick=function(e){var t=this._findZoneEventAt(e),r=this._getSelectionLength();t&&r===this._initialSelectionLength&&(t.clickCallback(e),e.preventDefault(),e.stopImmediatePropagation())},t.prototype._getSelectionLength=function(){var e=this._selectionService.selectionText;return e?e.length:0},t.prototype._findZoneEventAt=function(e){var t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows);if(t)for(var r=t[0],i=t[1],n=0;n=o.x1&&r=o.x1||i===o.y2&&ro.y1&&ie;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()},t.prototype._createAccessibilityTreeNode=function(){var e=document.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e},t.prototype._onTab=function(e){for(var t=0;t0)this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e);else this._charsToAnnounce+=e;"\n"===e&&(this._liveRegionLineCount++,21===this._liveRegionLineCount&&(this._liveRegion.textContent+=o.tooMuchOutput)),s.isMac&&this._liveRegion.textContent&&this._liveRegion.textContent.length>0&&!this._liveRegion.parentNode&&setTimeout((function(){t._accessibilityTreeRoot.appendChild(t._liveRegion)}),0)}},t.prototype._clearLiveRegion=function(){this._liveRegion.textContent="",this._liveRegionLineCount=0,s.isMac&&this._liveRegion.parentNode&&this._accessibilityTreeRoot.removeChild(this._liveRegion)},t.prototype._onKey=function(e){this._clearLiveRegion(),this._charsToConsume.push(e)},t.prototype._refreshRows=function(e,t){this._renderRowsDebouncer.refresh(e,t,this._terminal.rows)},t.prototype._renderRows=function(e,t){for(var r=this._terminal.buffer,i=r.lines.length.toString(),n=e;n<=t;n++){var o=r.translateBufferLineToString(r.ydisp+n,!0),s=(r.ydisp+n+1).toString(),a=this._rowElements[n];a&&(0===o.length?a.innerHTML=" ":a.textContent=o,a.setAttribute("aria-posinset",s),a.setAttribute("aria-setsize",i))}this._announceCharacters()},t.prototype._refreshRowsDimensions=function(){if(this._renderService.dimensions.actualCellHeight){this._rowElements.length!==this._terminal.rows&&this._onResize(this._terminal.rows);for(var e=0;e=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(57),c=r(9),l=r(2),h=r(4),u=r(1),f=r(0),_=r(10),d=1,p=function(e){function t(t,r,i,n,o,s,c,l,h){var u=e.call(this)||this;return u._colors=t,u._element=r,u._screenElement=i,u._viewportElement=n,u._linkifier=o,u._linkifier2=s,u._charSizeService=c,u._optionsService=l,u._bufferService=h,u._terminalClass=d++,u._rowElements=[],u._onRequestRefreshRows=new f.EventEmitter,u._rowContainer=document.createElement("div"),u._rowContainer.classList.add("xterm-rows"),u._rowContainer.style.lineHeight="normal",u._rowContainer.setAttribute("aria-hidden","true"),u._refreshRowElements(u._bufferService.cols,u._bufferService.rows),u._selectionContainer=document.createElement("div"),u._selectionContainer.classList.add("xterm-selection"),u._selectionContainer.setAttribute("aria-hidden","true"),u.dimensions={scaledCharWidth:0,scaledCharHeight:0,scaledCellWidth:0,scaledCellHeight:0,scaledCharLeft:0,scaledCharTop:0,scaledCanvasWidth:0,scaledCanvasHeight:0,canvasWidth:0,canvasHeight:0,actualCellWidth:0,actualCellHeight:0},u._updateDimensions(),u._injectCss(),u._rowFactory=new a.DomRendererRowFactory(document,u._optionsService,u._colors),u._element.classList.add("xterm-dom-renderer-owner-"+u._terminalClass),u._screenElement.appendChild(u._rowContainer),u._screenElement.appendChild(u._selectionContainer),u._linkifier.onLinkHover((function(e){return u._onLinkHover(e)})),u._linkifier.onLinkLeave((function(e){return u._onLinkLeave(e)})),u._linkifier2.onLinkHover((function(e){return u._onLinkHover(e)})),u._linkifier2.onLinkLeave((function(e){return u._onLinkLeave(e)})),u}return n(t,e),Object.defineProperty(t.prototype,"onRequestRefreshRows",{get:function(){return this._onRequestRefreshRows.event},enumerable:!0,configurable:!0}),t.prototype.dispose=function(){this._element.classList.remove("xterm-dom-renderer-owner-"+this._terminalClass),this._screenElement.removeChild(this._rowContainer),this._screenElement.removeChild(this._selectionContainer),this._screenElement.removeChild(this._themeStyleElement),this._screenElement.removeChild(this._dimensionsStyleElement),e.prototype.dispose.call(this)},t.prototype._updateDimensions=function(){var e=this;this.dimensions.scaledCharWidth=this._charSizeService.width*window.devicePixelRatio,this.dimensions.scaledCharHeight=Math.ceil(this._charSizeService.height*window.devicePixelRatio),this.dimensions.scaledCellWidth=this.dimensions.scaledCharWidth+Math.round(this._optionsService.options.letterSpacing),this.dimensions.scaledCellHeight=Math.floor(this.dimensions.scaledCharHeight*this._optionsService.options.lineHeight),this.dimensions.scaledCharLeft=0,this.dimensions.scaledCharTop=0,this.dimensions.scaledCanvasWidth=this.dimensions.scaledCellWidth*this._bufferService.cols,this.dimensions.scaledCanvasHeight=this.dimensions.scaledCellHeight*this._bufferService.rows,this.dimensions.canvasWidth=Math.round(this.dimensions.scaledCanvasWidth/window.devicePixelRatio),this.dimensions.canvasHeight=Math.round(this.dimensions.scaledCanvasHeight/window.devicePixelRatio),this.dimensions.actualCellWidth=this.dimensions.canvasWidth/this._bufferService.cols,this.dimensions.actualCellHeight=this.dimensions.canvasHeight/this._bufferService.rows,this._rowElements.forEach((function(t){t.style.width=e.dimensions.canvasWidth+"px",t.style.height=e.dimensions.actualCellHeight+"px",t.style.lineHeight=e.dimensions.actualCellHeight+"px",t.style.overflow="hidden"})),this._dimensionsStyleElement||(this._dimensionsStyleElement=document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));var t=this._terminalSelector+" .xterm-rows span { display: inline-block; height: 100%; vertical-align: top; width: "+this.dimensions.actualCellWidth+"px}";this._dimensionsStyleElement.innerHTML=t,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=this.dimensions.canvasWidth+"px",this._screenElement.style.height=this.dimensions.canvasHeight+"px"},t.prototype.setColors=function(e){this._colors=e,this._injectCss()},t.prototype._injectCss=function(){var e=this;this._themeStyleElement||(this._themeStyleElement=document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));var t=this._terminalSelector+" .xterm-rows { color: "+this._colors.foreground.css+"; font-family: "+this._optionsService.options.fontFamily+"; font-size: "+this._optionsService.options.fontSize+"px;}";t+=this._terminalSelector+" span:not(."+a.BOLD_CLASS+") { font-weight: "+this._optionsService.options.fontWeight+";}"+this._terminalSelector+" span."+a.BOLD_CLASS+" { font-weight: "+this._optionsService.options.fontWeightBold+";}"+this._terminalSelector+" span."+a.ITALIC_CLASS+" { font-style: italic;}",t+="@keyframes blink_box_shadow_"+this._terminalClass+" { 50% { box-shadow: none; }}",t+="@keyframes blink_block_"+this._terminalClass+" { 0% { background-color: "+this._colors.cursor.css+"; color: "+this._colors.cursorAccent.css+"; } 50% { background-color: "+this._colors.cursorAccent.css+"; color: "+this._colors.cursor.css+"; }}",t+=this._terminalSelector+" .xterm-rows:not(.xterm-focus) ."+a.CURSOR_CLASS+"."+a.CURSOR_STYLE_BLOCK_CLASS+" { outline: 1px solid "+this._colors.cursor.css+"; outline-offset: -1px;}"+this._terminalSelector+" .xterm-rows.xterm-focus ."+a.CURSOR_CLASS+"."+a.CURSOR_BLINK_CLASS+":not(."+a.CURSOR_STYLE_BLOCK_CLASS+") { animation: blink_box_shadow_"+this._terminalClass+" 1s step-end infinite;}"+this._terminalSelector+" .xterm-rows.xterm-focus ."+a.CURSOR_CLASS+"."+a.CURSOR_BLINK_CLASS+"."+a.CURSOR_STYLE_BLOCK_CLASS+" { animation: blink_block_"+this._terminalClass+" 1s step-end infinite;}"+this._terminalSelector+" .xterm-rows.xterm-focus ."+a.CURSOR_CLASS+"."+a.CURSOR_STYLE_BLOCK_CLASS+" { background-color: "+this._colors.cursor.css+"; color: "+this._colors.cursorAccent.css+";}"+this._terminalSelector+" .xterm-rows ."+a.CURSOR_CLASS+"."+a.CURSOR_STYLE_BAR_CLASS+" { box-shadow: "+this._optionsService.options.cursorWidth+"px 0 0 "+this._colors.cursor.css+" inset;}"+this._terminalSelector+" .xterm-rows ."+a.CURSOR_CLASS+"."+a.CURSOR_STYLE_UNDERLINE_CLASS+" { box-shadow: 0 -1px 0 "+this._colors.cursor.css+" inset;}",t+=this._terminalSelector+" .xterm-selection { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}"+this._terminalSelector+" .xterm-selection div { position: absolute; background-color: "+this._colors.selection.css+";}",this._colors.ansi.forEach((function(r,i){t+=e._terminalSelector+" .xterm-fg-"+i+" { color: "+r.css+"; }"+e._terminalSelector+" .xterm-bg-"+i+" { background-color: "+r.css+"; }"})),t+=this._terminalSelector+" .xterm-fg-"+c.INVERTED_DEFAULT_COLOR+" { color: "+_.color.opaque(this._colors.background).css+"; }"+this._terminalSelector+" .xterm-bg-"+c.INVERTED_DEFAULT_COLOR+" { background-color: "+this._colors.foreground.css+"; }",this._themeStyleElement.innerHTML=t},t.prototype.onDevicePixelRatioChange=function(){this._updateDimensions()},t.prototype._refreshRowElements=function(e,t){for(var r=this._rowElements.length;r<=t;r++){var i=document.createElement("div");this._rowContainer.appendChild(i),this._rowElements.push(i)}for(;this._rowElements.length>t;)this._rowContainer.removeChild(this._rowElements.pop())},t.prototype.onResize=function(e,t){this._refreshRowElements(e,t),this._updateDimensions()},t.prototype.onCharSizeChanged=function(){this._updateDimensions()},t.prototype.onBlur=function(){this._rowContainer.classList.remove("xterm-focus")},t.prototype.onFocus=function(){this._rowContainer.classList.add("xterm-focus")},t.prototype.onSelectionChanged=function(e,t,r){for(;this._selectionContainer.children.length;)this._selectionContainer.removeChild(this._selectionContainer.children[0]);if(e&&t){var i=e[1]-this._bufferService.buffer.ydisp,n=t[1]-this._bufferService.buffer.ydisp,o=Math.max(i,0),s=Math.min(n,this._bufferService.rows-1);if(!(o>=this._bufferService.rows||s<0)){var a=document.createDocumentFragment();if(r)a.appendChild(this._createSelectionElement(o,e[0],t[0],s-o+1));else{var c=i===o?e[0]:0,l=o===s?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(o,c,l));var h=s-o-1;if(a.appendChild(this._createSelectionElement(o+1,0,this._bufferService.cols,h)),o!==s){var u=n===s?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(s,0,u))}}this._selectionContainer.appendChild(a)}}},t.prototype._createSelectionElement=function(e,t,r,i){void 0===i&&(i=1);var n=document.createElement("div");return n.style.height=i*this.dimensions.actualCellHeight+"px",n.style.top=e*this.dimensions.actualCellHeight+"px",n.style.left=t*this.dimensions.actualCellWidth+"px",n.style.width=this.dimensions.actualCellWidth*(r-t)+"px",n},t.prototype.onCursorMove=function(){},t.prototype.onOptionsChanged=function(){this._updateDimensions(),this._injectCss()},t.prototype.clear=function(){this._rowElements.forEach((function(e){return e.innerHTML=""}))},t.prototype.renderRows=function(e,t){for(var r=this._bufferService.buffer.ybase+this._bufferService.buffer.y,i=Math.min(this._bufferService.buffer.x,this._bufferService.cols-1),n=this._optionsService.options.cursorBlink,o=e;o<=t;o++){var s=this._rowElements[o];s.innerHTML="";var a=o+this._bufferService.buffer.ydisp,c=this._bufferService.buffer.lines.get(a),l=this._optionsService.options.cursorStyle;s.appendChild(this._rowFactory.createRow(c,a===r,l,i,n,this.dimensions.actualCellWidth,this._bufferService.cols))}},Object.defineProperty(t.prototype,"_terminalSelector",{get:function(){return".xterm-dom-renderer-owner-"+this._terminalClass},enumerable:!0,configurable:!0}),t.prototype.registerCharacterJoiner=function(e){return-1},t.prototype.deregisterCharacterJoiner=function(e){return!1},t.prototype._onLinkHover=function(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!0)},t.prototype._onLinkLeave=function(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!1)},t.prototype._setCellUnderline=function(e,t,r,i,n,o){for(;e!==t||r!==i;){var s=this._rowElements[r];if(!s)return;var a=s.children[e];a&&(a.style.textDecoration=o?"underline":"none"),++e>=n&&(e=0,r++)}},t=o([s(6,h.ICharSizeService),s(7,u.IOptionsService),s(8,u.IBufferService)],t)}(l.Disposable);t.DomRenderer=p},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(9),n=r(3),o=r(5),s=r(10);t.BOLD_CLASS="xterm-bold",t.DIM_CLASS="xterm-dim",t.ITALIC_CLASS="xterm-italic",t.UNDERLINE_CLASS="xterm-underline",t.CURSOR_CLASS="xterm-cursor",t.CURSOR_BLINK_CLASS="xterm-cursor-blink",t.CURSOR_STYLE_BLOCK_CLASS="xterm-cursor-block",t.CURSOR_STYLE_BAR_CLASS="xterm-cursor-bar",t.CURSOR_STYLE_UNDERLINE_CLASS="xterm-cursor-underline";var a=function(){function e(e,t,r){this._document=e,this._optionsService=t,this._colors=r,this._workCell=new o.CellData}return e.prototype.setColors=function(e){this._colors=e},e.prototype.createRow=function(e,r,o,a,l,h,u){for(var f=this._document.createDocumentFragment(),_=0,d=Math.min(e.length,u)-1;d>=0;d--)if(e.loadCell(d,this._workCell).getCode()!==n.NULL_CELL_CODE||r&&d===a){_=d+1;break}for(d=0;d<_;d++){e.loadCell(d,this._workCell);var p=this._workCell.getWidth();if(0!==p){var v=this._document.createElement("span");if(p>1&&(v.style.width=h*p+"px"),r&&d===a)switch(v.classList.add(t.CURSOR_CLASS),l&&v.classList.add(t.CURSOR_BLINK_CLASS),o){case"bar":v.classList.add(t.CURSOR_STYLE_BAR_CLASS);break;case"underline":v.classList.add(t.CURSOR_STYLE_UNDERLINE_CLASS);break;default:v.classList.add(t.CURSOR_STYLE_BLOCK_CLASS)}this._workCell.isBold()&&v.classList.add(t.BOLD_CLASS),this._workCell.isItalic()&&v.classList.add(t.ITALIC_CLASS),this._workCell.isDim()&&v.classList.add(t.DIM_CLASS),this._workCell.isUnderline()&&v.classList.add(t.UNDERLINE_CLASS),this._workCell.isInvisible()?v.textContent=n.WHITESPACE_CELL_CHAR:v.textContent=this._workCell.getChars()||n.WHITESPACE_CELL_CHAR;var g=this._workCell.getFgColor(),y=this._workCell.getFgColorMode(),b=this._workCell.getBgColor(),m=this._workCell.getBgColorMode(),S=!!this._workCell.isInverse();if(S){var C=g;g=b,b=C;var w=y;y=m,m=w}switch(y){case 16777216:case 33554432:this._workCell.isBold()&&g<8&&this._optionsService.options.drawBoldTextInBrightColors&&(g+=8),this._applyMinimumContrast(v,this._colors.background,this._colors.ansi[g])||v.classList.add("xterm-fg-"+g);break;case 50331648:var E=s.rgba.toColor(g>>16&255,g>>8&255,255&g);this._applyMinimumContrast(v,this._colors.background,E)||this._addStyle(v,"color:#"+c(g.toString(16),"0",6));break;case 0:default:this._applyMinimumContrast(v,this._colors.background,this._colors.foreground)||S&&v.classList.add("xterm-fg-"+i.INVERTED_DEFAULT_COLOR)}switch(m){case 16777216:case 33554432:v.classList.add("xterm-bg-"+b);break;case 50331648:this._addStyle(v,"background-color:#"+c(b.toString(16),"0",6));break;case 0:default:S&&v.classList.add("xterm-bg-"+i.INVERTED_DEFAULT_COLOR)}f.appendChild(v)}}return f},e.prototype._applyMinimumContrast=function(e,t,r){if(1===this._optionsService.options.minimumContrastRatio)return!1;var i=this._colors.contrastCache.getColor(this._workCell.bg,this._workCell.fg);return void 0===i&&(i=s.color.ensureContrastRatio(t,r,this._optionsService.options.minimumContrastRatio),this._colors.contrastCache.setColor(this._workCell.bg,this._workCell.fg,null!=i?i:null)),!!i&&(this._addStyle(e,"color:"+i.css),!0)},e.prototype._addStyle=function(e,t){e.setAttribute("style",""+(e.getAttribute("style")||"")+t+";")},e}();function c(e,t,r){for(;e.length"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']};t.evaluateKeyboardEvent=function(e,t,r,o){var s={type:0,cancel:!1,key:void 0},a=(e.shiftKey?1:0)|(e.altKey?2:0)|(e.ctrlKey?4:0)|(e.metaKey?8:0);switch(e.keyCode){case 0:"UIKeyInputUpArrow"===e.key?s.key=t?i.C0.ESC+"OA":i.C0.ESC+"[A":"UIKeyInputLeftArrow"===e.key?s.key=t?i.C0.ESC+"OD":i.C0.ESC+"[D":"UIKeyInputRightArrow"===e.key?s.key=t?i.C0.ESC+"OC":i.C0.ESC+"[C":"UIKeyInputDownArrow"===e.key&&(s.key=t?i.C0.ESC+"OB":i.C0.ESC+"[B");break;case 8:if(e.shiftKey){s.key=i.C0.BS;break}if(e.altKey){s.key=i.C0.ESC+i.C0.DEL;break}s.key=i.C0.DEL;break;case 9:if(e.shiftKey){s.key=i.C0.ESC+"[Z";break}s.key=i.C0.HT,s.cancel=!0;break;case 13:s.key=i.C0.CR,s.cancel=!0;break;case 27:s.key=i.C0.ESC,s.cancel=!0;break;case 37:if(e.metaKey)break;a?(s.key=i.C0.ESC+"[1;"+(a+1)+"D",s.key===i.C0.ESC+"[1;3D"&&(s.key=i.C0.ESC+(r?"b":"[1;5D"))):s.key=t?i.C0.ESC+"OD":i.C0.ESC+"[D";break;case 39:if(e.metaKey)break;a?(s.key=i.C0.ESC+"[1;"+(a+1)+"C",s.key===i.C0.ESC+"[1;3C"&&(s.key=i.C0.ESC+(r?"f":"[1;5C"))):s.key=t?i.C0.ESC+"OC":i.C0.ESC+"[C";break;case 38:if(e.metaKey)break;a?(s.key=i.C0.ESC+"[1;"+(a+1)+"A",r||s.key!==i.C0.ESC+"[1;3A"||(s.key=i.C0.ESC+"[1;5A")):s.key=t?i.C0.ESC+"OA":i.C0.ESC+"[A";break;case 40:if(e.metaKey)break;a?(s.key=i.C0.ESC+"[1;"+(a+1)+"B",r||s.key!==i.C0.ESC+"[1;3B"||(s.key=i.C0.ESC+"[1;5B")):s.key=t?i.C0.ESC+"OB":i.C0.ESC+"[B";break;case 45:e.shiftKey||e.ctrlKey||(s.key=i.C0.ESC+"[2~");break;case 46:s.key=a?i.C0.ESC+"[3;"+(a+1)+"~":i.C0.ESC+"[3~";break;case 36:s.key=a?i.C0.ESC+"[1;"+(a+1)+"H":t?i.C0.ESC+"OH":i.C0.ESC+"[H";break;case 35:s.key=a?i.C0.ESC+"[1;"+(a+1)+"F":t?i.C0.ESC+"OF":i.C0.ESC+"[F";break;case 33:e.shiftKey?s.type=2:s.key=i.C0.ESC+"[5~";break;case 34:e.shiftKey?s.type=3:s.key=i.C0.ESC+"[6~";break;case 112:s.key=a?i.C0.ESC+"[1;"+(a+1)+"P":i.C0.ESC+"OP";break;case 113:s.key=a?i.C0.ESC+"[1;"+(a+1)+"Q":i.C0.ESC+"OQ";break;case 114:s.key=a?i.C0.ESC+"[1;"+(a+1)+"R":i.C0.ESC+"OR";break;case 115:s.key=a?i.C0.ESC+"[1;"+(a+1)+"S":i.C0.ESC+"OS";break;case 116:s.key=a?i.C0.ESC+"[15;"+(a+1)+"~":i.C0.ESC+"[15~";break;case 117:s.key=a?i.C0.ESC+"[17;"+(a+1)+"~":i.C0.ESC+"[17~";break;case 118:s.key=a?i.C0.ESC+"[18;"+(a+1)+"~":i.C0.ESC+"[18~";break;case 119:s.key=a?i.C0.ESC+"[19;"+(a+1)+"~":i.C0.ESC+"[19~";break;case 120:s.key=a?i.C0.ESC+"[20;"+(a+1)+"~":i.C0.ESC+"[20~";break;case 121:s.key=a?i.C0.ESC+"[21;"+(a+1)+"~":i.C0.ESC+"[21~";break;case 122:s.key=a?i.C0.ESC+"[23;"+(a+1)+"~":i.C0.ESC+"[23~";break;case 123:s.key=a?i.C0.ESC+"[24;"+(a+1)+"~":i.C0.ESC+"[24~";break;default:if(!e.ctrlKey||e.shiftKey||e.altKey||e.metaKey)if(r&&!o||!e.altKey||e.metaKey)r&&!e.altKey&&!e.ctrlKey&&e.metaKey?65===e.keyCode&&(s.type=1):e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.keyCode>=48&&1===e.key.length?s.key=e.key:e.key&&e.ctrlKey&&"_"===e.key&&(s.key=i.C0.US);else{var c=n[e.keyCode],l=c&&c[e.shiftKey?1:0];if(l)s.key=i.C0.ESC+l;else if(e.keyCode>=65&&e.keyCode<=90){var h=e.ctrlKey?e.keyCode-64:e.keyCode+32;s.key=i.C0.ESC+String.fromCharCode(h)}}else e.keyCode>=65&&e.keyCode<=90?s.key=String.fromCharCode(e.keyCode-64):32===e.keyCode?s.key=i.C0.NUL:e.keyCode>=51&&e.keyCode<=55?s.key=String.fromCharCode(e.keyCode-51+27):56===e.keyCode?s.key=i.C0.DEL:219===e.keyCode?s.key=i.C0.ESC:220===e.keyCode?s.key=i.C0.FS:221===e.keyCode&&(s.key=i.C0.GS)}return s}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(3);t.updateWindowsModeWrappedState=function(e){var t=e.buffer.lines.get(e.buffer.ybase+e.buffer.y-1),r=null==t?void 0:t.get(e.cols-1),n=e.buffer.lines.get(e.buffer.ybase+e.buffer.y);n&&r&&(n.isWrapped=r[i.CHAR_DATA_CODE_INDEX]!==i.NULL_CELL_CODE&&r[i.CHAR_DATA_CODE_INDEX]!==i.WHITESPACE_CELL_CODE)}},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),o=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(29),c=r(0),l=r(2),h=r(30),u=r(8),f=r(1),_=r(4),d=function(e){function t(t,r,i,n,o){var s=e.call(this)||this;if(s._renderer=t,s._rowCount=r,s.screenElement=i,s.optionsService=n,s.charSizeService=o,s._isPaused=!1,s._needsFullRefresh=!1,s._canvasWidth=0,s._canvasHeight=0,s._onDimensionsChange=new c.EventEmitter,s._onRender=new c.EventEmitter,s._onRefreshRequest=new c.EventEmitter,s._renderDebouncer=new a.RenderDebouncer((function(e,t){return s._renderRows(e,t)})),s.register(s._renderDebouncer),s._screenDprMonitor=new h.ScreenDprMonitor,s._screenDprMonitor.setListener((function(){return s.onDevicePixelRatioChange()})),s.register(s._screenDprMonitor),s.register(n.onOptionChange((function(){return s._renderer.onOptionsChanged()}))),s.register(o.onCharSizeChange((function(){return s.onCharSizeChanged()}))),s._renderer.onRequestRefreshRows((function(e){return s.refreshRows(e.start,e.end)})),s.register(u.addDisposableDomListener(window,"resize",(function(){return s.onDevicePixelRatioChange()}))),"IntersectionObserver"in window){var l=new IntersectionObserver((function(e){return s._onIntersectionChange(e[e.length-1])}),{threshold:0});l.observe(i),s.register({dispose:function(){return l.disconnect()}})}return s}return n(t,e),Object.defineProperty(t.prototype,"onDimensionsChange",{get:function(){return this._onDimensionsChange.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onRender",{get:function(){return this._onRender.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onRefreshRequest",{get:function(){return this._onRefreshRequest.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"dimensions",{get:function(){return this._renderer.dimensions},enumerable:!0,configurable:!0}),t.prototype._onIntersectionChange=function(e){this._isPaused=0===e.intersectionRatio,!this._isPaused&&this._needsFullRefresh&&(this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)},t.prototype.refreshRows=function(e,t){this._isPaused?this._needsFullRefresh=!0:this._renderDebouncer.refresh(e,t,this._rowCount)},t.prototype._renderRows=function(e,t){this._renderer.renderRows(e,t),this._onRender.fire({start:e,end:t})},t.prototype.resize=function(e,t){this._rowCount=t,this._fireOnCanvasResize()},t.prototype.changeOptions=function(){this._renderer.onOptionsChanged(),this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize()},t.prototype._fireOnCanvasResize=function(){this._renderer.dimensions.canvasWidth===this._canvasWidth&&this._renderer.dimensions.canvasHeight===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.dimensions)},t.prototype.dispose=function(){this._renderer.dispose(),e.prototype.dispose.call(this)},t.prototype.setRenderer=function(e){var t=this;this._renderer.dispose(),this._renderer=e,this._renderer.onRequestRefreshRows((function(e){return t.refreshRows(e.start,e.end)})),this.refreshRows(0,this._rowCount-1)},t.prototype._fullRefresh=function(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)},t.prototype.setColors=function(e){this._renderer.setColors(e),this._fullRefresh()},t.prototype.onDevicePixelRatioChange=function(){this._renderer.onDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1)},t.prototype.onResize=function(e,t){this._renderer.onResize(e,t),this._fullRefresh()},t.prototype.onCharSizeChanged=function(){this._renderer.onCharSizeChanged()},t.prototype.onBlur=function(){this._renderer.onBlur()},t.prototype.onFocus=function(){this._renderer.onFocus()},t.prototype.onSelectionChanged=function(e,t,r){this._renderer.onSelectionChanged(e,t,r)},t.prototype.onCursorMove=function(){this._renderer.onCursorMove()},t.prototype.clear=function(){this._renderer.clear()},t.prototype.registerCharacterJoiner=function(e){return this._renderer.registerCharacterJoiner(e)},t.prototype.deregisterCharacterJoiner=function(e){return this._renderer.deregisterCharacterJoiner(e)},t=o([s(3,f.IOptionsService),s(4,_.ICharSizeService)],t)}(l.Disposable);t.RenderService=d},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(0),n=r(11),o=r(31);t.DEFAULT_BELL_SOUND="data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",t.DEFAULT_OPTIONS=Object.freeze({cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,bellSound:t.DEFAULT_BELL_SOUND,bellStyle:"none",drawBoldTextInBrightColors:!0,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"courier-new, courier, monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",lineHeight:1,letterSpacing:0,logLevel:"info",scrollback:1e3,scrollSensitivity:1,screenReaderMode:!1,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rightClickSelectsWord:n.isMac,rendererType:"canvas",windowOptions:{},windowsMode:!1,wordSeparator:" ()[]{}',\"`",convertEol:!1,termName:"xterm",cancelEvents:!1});var s=["cols","rows"],a=function(){function e(e){var r=this;this._onOptionChange=new i.EventEmitter,this.options=o.clone(t.DEFAULT_OPTIONS),Object.keys(e).forEach((function(t){if(t in r.options){var i=e[t];r.options[t]=i}}))}return Object.defineProperty(e.prototype,"onOptionChange",{get:function(){return this._onOptionChange.event},enumerable:!0,configurable:!0}),e.prototype.setOption=function(e,r){if(!(e in t.DEFAULT_OPTIONS))throw new Error('No option with key "'+e+'"');if(-1!==s.indexOf(e))throw new Error('Option "'+e+'" can only be set in the constructor');this.options[e]!==r&&(r=this._sanitizeAndValidateOption(e,r),this.options[e]!==r&&(this.options[e]=r,this._onOptionChange.fire(e)))},e.prototype._sanitizeAndValidateOption=function(e,r){switch(e){case"bellStyle":case"cursorStyle":case"fontWeight":case"fontWeightBold":case"rendererType":case"wordSeparator":r||(r=t.DEFAULT_OPTIONS[e]);break;case"cursorWidth":r=Math.floor(r);case"lineHeight":case"tabStopWidth":if(r<1)throw new Error(e+" cannot be less than 1, value: "+r);break;case"minimumContrastRatio":r=Math.max(1,Math.min(21,Math.round(10*r)/10));break;case"scrollback":if((r=Math.min(r,4294967295))<0)throw new Error(e+" cannot be less than 0, value: "+r);break;case"fastScrollSensitivity":case"scrollSensitivity":if(r<=0)throw new Error(e+" cannot be less than or equal to 0, value: "+r)}return r},e.prototype.getOption=function(e){if(!(e in t.DEFAULT_OPTIONS))throw new Error('No option with key "'+e+'"');return this.options[e]},e}();t.OptionsService=a},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1),s=r(0),a=function(){function e(e,t,r){this.document=e,this.parentElement=t,this._optionsService=r,this.width=0,this.height=0,this._onCharSizeChange=new s.EventEmitter,this._measureStrategy=new c(e,t,this._optionsService)}return Object.defineProperty(e.prototype,"hasValidSize",{get:function(){return this.width>0&&this.height>0},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onCharSizeChange",{get:function(){return this._onCharSizeChange.event},enumerable:!0,configurable:!0}),e.prototype.measure=function(){var e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())},e=i([n(2,o.IOptionsService)],e)}();t.CharSizeService=a;var c=function(){function e(e,t,r){this._document=e,this._parentElement=t,this._optionsService=r,this._result={width:0,height:0},this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W",this._measureElement.setAttribute("aria-hidden","true"),this._parentElement.appendChild(this._measureElement)}return e.prototype.measure=function(){this._measureElement.style.fontFamily=this._optionsService.options.fontFamily,this._measureElement.style.fontSize=this._optionsService.options.fontSize+"px";var e=this._measureElement.getBoundingClientRect();return 0!==e.width&&0!==e.height&&(this._result.width=e.width,this._result.height=Math.ceil(e.height)),this._result},e}()},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1),s=r(64);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;var a=function(){function e(e){this._optionsService=e,this.cols=Math.max(e.options.cols,t.MINIMUM_COLS),this.rows=Math.max(e.options.rows,t.MINIMUM_ROWS),this.buffers=new s.BufferSet(e,this)}return Object.defineProperty(e.prototype,"buffer",{get:function(){return this.buffers.active},enumerable:!0,configurable:!0}),e.prototype.resize=function(e,t){this.cols=e,this.rows=t},e.prototype.reset=function(){this.buffers=new s.BufferSet(this._optionsService,this)},e=i([n(0,o.IOptionsService)],e)}();t.BufferService=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(65),n=r(0),o=function(){function e(e,t){this.optionsService=e,this.bufferService=t,this._onBufferActivate=new n.EventEmitter,this._normal=new i.Buffer(!0,e,t),this._normal.fillViewportRows(),this._alt=new i.Buffer(!1,e,t),this._activeBuffer=this._normal,this.setupTabStops()}return Object.defineProperty(e.prototype,"onBufferActivate",{get:function(){return this._onBufferActivate.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"alt",{get:function(){return this._alt},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"active",{get:function(){return this._activeBuffer},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"normal",{get:function(){return this._normal},enumerable:!0,configurable:!0}),e.prototype.activateNormalBuffer=function(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))},e.prototype.activateAltBuffer=function(e){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(e),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))},e.prototype.resize=function(e,t){this._normal.resize(e,t),this._alt.resize(e,t)},e.prototype.setupTabStops=function(e){this._normal.setupTabStops(e),this._alt.setupTabStops(e)},e}();t.BufferSet=o},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(66),n=r(16),o=r(5),s=r(3),a=r(67),c=r(68),l=r(18);t.MAX_BUFFER_SIZE=4294967295;var h=function(){function e(e,t,r){this._hasScrollback=e,this._optionsService=t,this._bufferService=r,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.savedY=0,this.savedX=0,this.savedCurAttrData=n.DEFAULT_ATTR_DATA.clone(),this.savedCharset=l.DEFAULT_CHARSET,this.markers=[],this._nullCell=o.CellData.fromCharData([0,s.NULL_CELL_CHAR,s.NULL_CELL_WIDTH,s.NULL_CELL_CODE]),this._whitespaceCell=o.CellData.fromCharData([0,s.WHITESPACE_CELL_CHAR,s.WHITESPACE_CELL_WIDTH,s.WHITESPACE_CELL_CODE]),this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new i.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}return e.prototype.getNullCell=function(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg):(this._nullCell.fg=0,this._nullCell.bg=0),this._nullCell},e.prototype.getWhitespaceCell=function(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0),this._whitespaceCell},e.prototype.getBlankLine=function(e,t){return new n.BufferLine(this._bufferService.cols,this.getNullCell(e),t)},Object.defineProperty(e.prototype,"hasScrollback",{get:function(){return this._hasScrollback&&this.lines.maxLength>this._rows},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"isCursorInViewport",{get:function(){var e=this.ybase+this.y-this.ydisp;return e>=0&&et.MAX_BUFFER_SIZE?t.MAX_BUFFER_SIZE:r},e.prototype.fillViewportRows=function(e){if(0===this.lines.length){void 0===e&&(e=n.DEFAULT_ATTR_DATA);for(var t=this._rows;t--;)this.lines.push(this.getBlankLine(e))}},e.prototype.clear=function(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new i.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()},e.prototype.resize=function(e,t){var r=this.getNullCell(n.DEFAULT_ATTR_DATA),i=this._getCorrectBufferLength(t);if(i>this.lines.maxLength&&(this.lines.maxLength=i),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+s+1?(this.ybase--,s++,this.ydisp>0&&this.ydisp--):this.lines.push(new n.BufferLine(e,r)));else for(a=this._rows;a>t;a--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(i0&&(this.lines.trimStart(c),this.ybase=Math.max(this.ybase-c,0),this.ydisp=Math.max(this.ydisp-c,0),this.savedY=Math.max(this.savedY-c,0)),this.lines.maxLength=i}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),s&&(this.y+=s),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(o=0;othis._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))},e.prototype._reflowLarger=function(e,t){var r=a.reflowLargerGetLinesToRemove(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(n.DEFAULT_ATTR_DATA));if(r.length>0){var i=a.reflowLargerCreateNewLayout(this.lines,r);a.reflowLargerApplyNewLayout(this.lines,i.layout),this._reflowLargerAdjustViewport(e,t,i.countRemoved)}},e.prototype._reflowLargerAdjustViewport=function(e,t,r){for(var i=this.getNullCell(n.DEFAULT_ATTR_DATA),o=r;o-- >0;)0===this.ybase?(this.y>0&&this.y--,this.lines.length=0;s--){var c=this.lines.get(s);if(!(!c||!c.isWrapped&&c.getTrimmedLength()<=e)){for(var l=[c];c.isWrapped&&s>0;)c=this.lines.get(--s),l.unshift(c);var h=this.ybase+this.y;if(!(h>=s&&h0&&(i.push({start:s+l.length+o,newLines:p}),o+=p.length),l.push.apply(l,p);var y=f.length-1,b=f[y];0===b&&(b=f[--y]);for(var m=l.length-_-1,S=u;m>=0;){var C=Math.min(S,b);if(l[y].copyCellsFrom(l[m],S-C,b-C,C,!0),0===(b-=C)&&(b=f[--y]),0===(S-=C)){m--;var w=Math.max(m,0);S=a.getWrappedLineTrimmedLength(l,w,this._cols)}}for(v=0;v0;)0===this.ybase?this.y0){var L=[],A=[];for(v=0;v=0;v--)if(D&&D.start>R+T){for(var M=D.newLines.length-1;M>=0;M--)this.lines.set(v--,D.newLines[M]);v++,L.push({index:R+1,amount:D.newLines.length}),T+=D.newLines.length,D=i[++x]}else this.lines.set(v,A[R--]);var O=0;for(v=L.length-1;v>=0;v--)L[v].index+=O,this.lines.onInsertEmitter.fire(L[v]),O+=L[v].amount;var P=Math.max(0,k+o-this.lines.maxLength);P>0&&this.lines.onTrimEmitter.fire(P)}},e.prototype.stringIndexToBufferIndex=function(e,t,r){for(void 0===r&&(r=!1);t;){var i=this.lines.get(e);if(!i)return[-1,-1];for(var n=r?i.getTrimmedLength():i.length,o=0;o0&&this.lines.get(t).isWrapped;)t--;for(;r+10;);return e>=this._cols?this._cols-1:e<0?0:e},e.prototype.nextStop=function(e){for(null==e&&(e=this.x);!this.tabs[++e]&&e=this._cols?this._cols-1:e<0?0:e},e.prototype.addMarker=function(e){var t=this,r=new c.Marker(e);return this.markers.push(r),r.register(this.lines.onTrim((function(e){r.line-=e,r.line<0&&r.dispose()}))),r.register(this.lines.onInsert((function(e){r.line>=e.index&&(r.line+=e.amount)}))),r.register(this.lines.onDelete((function(e){r.line>=e.index&&r.linee.index&&(r.line-=e.amount)}))),r.register(r.onDispose((function(){return t._removeMarker(r)}))),r},e.prototype._removeMarker=function(e){this.markers.splice(this.markers.indexOf(e),1)},e.prototype.iterator=function(e,t,r,i,n){return new u(this,e,t,r,i,n)},e}();t.Buffer=h;var u=function(){function e(e,t,r,i,n,o){void 0===r&&(r=0),void 0===i&&(i=e.lines.length),void 0===n&&(n=0),void 0===o&&(o=0),this._buffer=e,this._trimRight=t,this._startIndex=r,this._endIndex=i,this._startOverscan=n,this._endOverscan=o,this._startIndex<0&&(this._startIndex=0),this._endIndex>this._buffer.lines.length&&(this._endIndex=this._buffer.lines.length),this._current=this._startIndex}return e.prototype.hasNext=function(){return this._currentthis._endIndex+this._endOverscan&&(e.last=this._endIndex+this._endOverscan),e.first=Math.max(e.first,0),e.last=Math.min(e.last,this._buffer.lines.length);for(var t="",r=e.first;r<=e.last;++r)t+=this._buffer.translateBufferLineToString(r,this._trimRight);return this._current=e.last+1,{range:e,content:t}},e}();t.BufferStringIterator=u},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(0),n=function(){function e(e){this._maxLength=e,this.onDeleteEmitter=new i.EventEmitter,this.onInsertEmitter=new i.EventEmitter,this.onTrimEmitter=new i.EventEmitter,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}return Object.defineProperty(e.prototype,"onDelete",{get:function(){return this.onDeleteEmitter.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onInsert",{get:function(){return this.onInsertEmitter.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onTrim",{get:function(){return this.onTrimEmitter.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"maxLength",{get:function(){return this._maxLength},set:function(e){if(this._maxLength!==e){for(var t=new Array(e),r=0;rthis._length)for(var t=this._length;t=e;n--)this._array[this._getCyclicIndex(n+r.length)]=this._array[this._getCyclicIndex(n)];for(n=0;nthis._maxLength){var o=this._length+r.length-this._maxLength;this._startIndex+=o,this._length=this._maxLength,this.onTrimEmitter.fire(o)}else this._length+=r.length},e.prototype.trimStart=function(e){e>this._length&&(e=this._length),this._startIndex+=e,this._length-=e,this.onTrimEmitter.fire(e)},e.prototype.shiftElements=function(e,t,r){if(!(t<=0)){if(e<0||e>=this._length)throw new Error("start argument out of range");if(e+r<0)throw new Error("Cannot shift elements in list beyond index 0");if(r>0){for(var i=t-1;i>=0;i--)this.set(e+i+r,this.get(e+i));var n=e+t+r-this._length;if(n>0)for(this._length+=n;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(i=0;i=a&&n0&&(m>u||0===h[m].getTrimmedLength());m--)b++;b>0&&(s.push(a+h.length-b),s.push(b)),a+=h.length-1}}}return s},t.reflowLargerCreateNewLayout=function(e,t){for(var r=[],i=0,n=t[i],o=0,s=0;sl&&(s-=l,a++);var h=2===e[a].getWidth(s-1);h&&s--;var u=h?r-1:r;n.push(u),c+=u}return n},t.getWrappedLineTrimmedLength=i},function(e,t,r){"use strict";var i,n=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)},function(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0});var o=r(0),s=function(e){function t(r){var i=e.call(this)||this;return i.line=r,i._id=t._nextId++,i.isDisposed=!1,i._onDispose=new o.EventEmitter,i}return n(t,e),Object.defineProperty(t.prototype,"id",{get:function(){return this._id},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onDispose",{get:function(){return this._onDispose.event},enumerable:!0,configurable:!0}),t.prototype.dispose=function(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire())},t._nextId=1,t}(r(2).Disposable);t.Marker=s},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(4),s=r(28),a=function(){function e(e,t){this._renderService=e,this._charSizeService=t}return e.prototype.getCoords=function(e,t,r,i,n){return s.getCoords(e,t,r,i,this._charSizeService.hasValidSize,this._renderService.dimensions.actualCellWidth,this._renderService.dimensions.actualCellHeight,n)},e.prototype.getRawByteCoords=function(e,t,r,i){var n=this.getCoords(e,t,r,i);return s.getRawByteCoords(n)},e=i([n(0,o.IRenderService),n(1,o.ICharSizeService)],e)}();t.MouseService=a},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1),s=r(0),a=r(31),c=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,origin:!1,wraparound:!0}),l=function(){function e(e,t,r,i){this._scrollToBottom=e,this._bufferService=t,this._logService=r,this._optionsService=i,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=new s.EventEmitter,this._onUserInput=new s.EventEmitter,this._onBinary=new s.EventEmitter,this.decPrivateModes=a.clone(c)}return Object.defineProperty(e.prototype,"onData",{get:function(){return this._onData.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onUserInput",{get:function(){return this._onUserInput.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onBinary",{get:function(){return this._onBinary.event},enumerable:!0,configurable:!0}),e.prototype.reset=function(){this.decPrivateModes=a.clone(c)},e.prototype.triggerDataEvent=function(e,t){if(void 0===t&&(t=!1),!this._optionsService.options.disableStdin){var r=this._bufferService.buffer;r.ybase!==r.ydisp&&this._scrollToBottom(),t&&this._onUserInput.fire(),this._logService.debug('sending data "'+e+'"',(function(){return e.split("").map((function(e){return e.charCodeAt(0)}))})),this._onData.fire(e)}},e.prototype.triggerBinaryEvent=function(e){this._optionsService.options.disableStdin||(this._logService.debug('sending binary "'+e+'"',(function(){return e.split("").map((function(e){return e.charCodeAt(0)}))})),this._onBinary.fire(e))},e=i([n(1,o.IBufferService),n(2,o.ILogService),n(3,o.IOptionsService)],e)}();t.CoreService=l},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}},o=this&&this.__spreadArrays||function(){for(var e=0,t=0,r=arguments.length;t=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1),s=function(){function e(e){this._bufferService=e,this.clearRange()}return Object.defineProperty(e.prototype,"start",{get:function(){return this._start},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"end",{get:function(){return this._end},enumerable:!0,configurable:!0}),e.prototype.clearRange=function(){this._start=this._bufferService.buffer.y,this._end=this._bufferService.buffer.y},e.prototype.markDirty=function(e){ethis._end&&(this._end=e)},e.prototype.markRangeDirty=function(e,t){if(e>t){var r=e;e=t,t=r}ethis._end&&(this._end=t)},e.prototype.markAllDirty=function(){this.markRangeDirty(0,this._bufferService.rows-1)},e=i([n(0,o.IBufferService)],e)}();t.DirtyRowService=s},function(e,t,r){"use strict";var i=this&&this.__spreadArrays||function(){for(var e=0,t=0,r=arguments.length;t0?n[0].index:t.length;if(t.length!==u)throw new Error("[createInstance] First service dependency of "+e.name+" at position "+(u+1)+" conflicts with "+t.length+" static arguments");return new(e.bind.apply(e,i([void 0],i(t,s))))},e}();t.InstantiationService=a},function(e,t,r){"use strict";var i=this&&this.__decorate||function(e,t,r,i){var n,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(s=(o<3?n(s):o>3?n(t,r,s):n(t,r))||s);return o>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1),s=r(0),a={NONE:{events:0,restrict:function(){return!1}},X10:{events:1,restrict:function(e){return 4!==e.button&&1===e.action&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)}},VT200:{events:19,restrict:function(e){return 32!==e.action}},DRAG:{events:23,restrict:function(e){return 32!==e.action||3!==e.button}},ANY:{events:31,restrict:function(e){return!0}}};function c(e,t){var r=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4===e.button?(r|=64,r|=e.action):(r|=3&e.button,4&e.button&&(r|=64),8&e.button&&(r|=128),32===e.action?r|=32:0!==e.action||t||(r|=3)),r}var l=String.fromCharCode,h={DEFAULT:function(e){var t=[c(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?"":""+l(t[0])+l(t[1])+l(t[2])},SGR:function(e){var t=0===e.action&&4!==e.button?"m":"M";return"[<"+c(e,!0)+";"+e.col+";"+e.row+t}},u=function(){function e(e,t){var r=this;this._bufferService=e,this._coreService=t,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._onProtocolChange=new s.EventEmitter,this._lastEvent=null,Object.keys(a).forEach((function(e){return r.addProtocol(e,a[e])})),Object.keys(h).forEach((function(e){return r.addEncoding(e,h[e])})),this.reset()}return e.prototype.addProtocol=function(e,t){this._protocols[e]=t},e.prototype.addEncoding=function(e,t){this._encodings[e]=t},Object.defineProperty(e.prototype,"activeProtocol",{get:function(){return this._activeProtocol},set:function(e){if(!this._protocols[e])throw new Error('unknown protocol "'+e+'"');this._activeProtocol=e,this._onProtocolChange.fire(this._protocols[e].events)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"activeEncoding",{get:function(){return this._activeEncoding},set:function(e){if(!this._encodings[e])throw new Error('unknown encoding "'+e+'"');this._activeEncoding=e},enumerable:!0,configurable:!0}),e.prototype.reset=function(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null},Object.defineProperty(e.prototype,"onProtocolChange",{get:function(){return this._onProtocolChange.event},enumerable:!0,configurable:!0}),e.prototype.triggerMouseEvent=function(e){if(e.col<0||e.col>=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows)return!1;if(4===e.button&&32===e.action)return!1;if(3===e.button&&32!==e.action)return!1;if(4!==e.button&&(2===e.action||3===e.action))return!1;if(e.col++,e.row++,32===e.action&&this._lastEvent&&this._compareEvents(this._lastEvent,e))return!1;if(!this._protocols[this._activeProtocol].restrict(e))return!1;var t=this._encodings[this._activeEncoding](e);return t&&("DEFAULT"===this._activeEncoding?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0},e.prototype.explainEvents=function(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}},e.prototype._compareEvents=function(e,t){return e.col===t.col&&(e.row===t.row&&(e.button===t.button&&(e.action===t.action&&(e.ctrl===t.ctrl&&(e.alt===t.alt&&e.shift===t.shift)))))},e=i([n(0,o.IBufferService),n(1,o.ICoreService)],e)}();t.CoreMouseService=u},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e){this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0}return e.prototype.writeSync=function(e){if(this._writeBuffer.length){for(var t=this._bufferOffset;t5e7)throw new Error("write data discarded, use flow control to avoid losing data");this._writeBuffer.length||(this._bufferOffset=0,setTimeout((function(){return r._innerWrite()}))),this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)},e.prototype._innerWrite=function(){for(var e=this,t=Date.now();this._writeBuffer.length>this._bufferOffset;){var r=this._writeBuffer[this._bufferOffset],i=this._callbacks[this._bufferOffset];if(this._bufferOffset++,this._action(r),this._pendingData-=r.length,i&&i(),Date.now()-t>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((function(){return e._innerWrite()}),0)):(this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0)},e}();t.WriteBuffer=i},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(0),n=function(){function e(e){this._bufferService=e,this._linkProviders=[],this._linkCacheDisposables=[],this._onLinkHover=new i.EventEmitter,this._onLinkLeave=new i.EventEmitter}return Object.defineProperty(e.prototype,"onLinkHover",{get:function(){return this._onLinkHover.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onLinkLeave",{get:function(){return this._onLinkLeave.event},enumerable:!0,configurable:!0}),e.prototype.registerLinkProvider=function(e){var t=this;return this._linkProviders.push(e),{dispose:function(){var r=t._linkProviders.indexOf(e);-1!==r&&t._linkProviders.splice(r,1)}}},e.prototype.attachToDom=function(e,t,r){this._element=e,this._mouseService=t,this._renderService=r,this._element.addEventListener("mousemove",this._onMouseMove.bind(this)),this._element.addEventListener("click",this._onMouseDown.bind(this))},e.prototype._onMouseMove=function(e){if(this._lastMouseEvent=e,this._element&&this._mouseService){var t=this._positionFromMouseEvent(e,this._element,this._mouseService);t&&(this._lastBufferCell&&t.x===this._lastBufferCell.x&&t.y===this._lastBufferCell.y||(this._onHover(t),this._lastBufferCell=t))}},e.prototype._onHover=function(e){this._currentLink?this._linkAtPosition(this._currentLink,e)||(this._clearCurrentLink(),this._askForLink(e)):this._askForLink(e)},e.prototype._askForLink=function(e){var t=this,r=new Map,i=!1;this._linkProviders.forEach((function(n,o){n.provideLink(e,(function(e){r.set(o,e);for(var n=!1,s=0;s=e&&this._currentLink.range.end.y<=t)&&(this._linkLeave(this._element,this._currentLink,this._lastMouseEvent),this._currentLink=void 0,this._linkCacheDisposables.forEach((function(e){return e.dispose()})),this._linkCacheDisposables=[])},e.prototype._handleNewLink=function(e){var t=this;if(this._element&&this._lastMouseEvent&&this._mouseService){var r=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);r&&this._linkAtPosition(e,r)&&(this._currentLink=e,this._linkHover(this._element,e,this._lastMouseEvent),this._renderService&&this._linkCacheDisposables.push(this._renderService.onRender((function(e){t._clearCurrentLink(e.start+1+t._bufferService.buffer.ydisp,e.end+1+t._bufferService.buffer.ydisp)}))))}},e.prototype._linkHover=function(e,t,r){var i=t.range,n=this._bufferService.buffer.ydisp;this._onLinkHover.fire(this._createLinkHoverEvent(i.start.x-1,i.start.y-n-1,i.end.x,i.end.y-n-1,void 0)),e.classList.add("xterm-cursor-pointer"),t.hover&&t.hover(r,t.text)},e.prototype._linkLeave=function(e,t,r){var i=t.range,n=this._bufferService.buffer.ydisp;this._onLinkLeave.fire(this._createLinkHoverEvent(i.start.x-1,i.start.y-n-1,i.end.x,i.end.y-n-1,void 0)),e.classList.remove("xterm-cursor-pointer"),t.leave&&t.leave(r,t.text)},e.prototype._linkAtPosition=function(e,t){var r=e.range.start.y===e.range.end.y,i=e.range.start.yt.y;return(r&&e.range.start.x<=t.x&&e.range.end.x>=t.x||i&&e.range.end.x>=t.x||n&&e.range.start.x<=t.x||i&&n)&&e.range.start.y<=t.y&&e.range.end.y>=t.y},e.prototype._positionFromMouseEvent=function(e,t,r){var i=r.getCoords(e,t,this._bufferService.cols,this._bufferService.rows);if(i)return{x:i[0],y:i[1]+this._bufferService.buffer.ydisp}},e.prototype._createLinkHoverEvent=function(e,t,r,i,n){return{x1:e,y1:t,x2:r,y2:i,cols:this._bufferService.cols,fg:n}},e}();t.Linkifier2=n},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e){this._textarea=e}return Object.defineProperty(e.prototype,"isFocused",{get:function(){return document.activeElement===this._textarea&&document.hasFocus()},enumerable:!0,configurable:!0}),e}();t.CoreBrowserService=i},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(0),n=r(79),o=function(){function e(){this._providers=Object.create(null),this._active="",this._onChange=new i.EventEmitter;var e=new n.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}return Object.defineProperty(e.prototype,"onChange",{get:function(){return this._onChange.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"versions",{get:function(){return Object.keys(this._providers)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"activeVersion",{get:function(){return this._active},set:function(e){if(!this._providers[e])throw new Error('unknown Unicode version "'+e+'"');this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)},enumerable:!0,configurable:!0}),e.prototype.register=function(e){this._providers[e.version]=e},e.prototype.wcwidth=function(e){return this._activeProvider.wcwidth(e)},e.prototype.getStringCellWidth=function(e){for(var t=0,r=e.length,i=0;i=r)return t+this.wcwidth(n);var o=e.charCodeAt(i);56320<=o&&o<=57343?n=1024*(n-55296)+o-56320+65536:t+=this.wcwidth(o)}t+=this.wcwidth(n)}return t},e}();t.UnicodeService=o},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,n=r(15),o=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],s=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]];var a=function(){function e(){if(this.version="6",!i){i=new Uint8Array(65536),n.fill(i,1),i[0]=0,n.fill(i,0,1,32),n.fill(i,0,127,160),n.fill(i,2,4352,4448),i[9001]=2,i[9002]=2,n.fill(i,2,11904,42192),i[12351]=1,n.fill(i,2,44032,55204),n.fill(i,2,63744,64256),n.fill(i,2,65040,65050),n.fill(i,2,65072,65136),n.fill(i,2,65280,65377),n.fill(i,2,65504,65511);for(var e=0;et[n][1])return!1;for(;n>=i;)if(e>t[r=i+n>>1][1])i=r+1;else{if(!(e=131072&&e<=196605||e>=196608&&e<=262141?2:1},e}();t.UnicodeV6=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this.charsets=[],this.glevel=0}return e.prototype.reset=function(){this.charset=void 0,this.charsets=[],this.glevel=0},e.prototype.setgLevel=function(e){this.glevel=e,this.charset=this.charsets[e]},e.prototype.setgCharset=function(e,t){this.charsets[e]=t,this.glevel===e&&(this.charset=t)},e}();t.CharsetService=i},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this._addons=[]}return e.prototype.dispose=function(){for(var e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()},e.prototype.loadAddon=function(e,t){var r=this,i={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(i),t.dispose=function(){return r._wrappedAddonDispose(i)},t.activate(e)},e.prototype._wrappedAddonDispose=function(e){if(!e.isDisposed){for(var t=-1,r=0;r